yabause-0.9.13.1/doc/Doxyfile.in000644 001750 001750 00000156344 12256006202 020276 0ustar00guillaumeguillaume000000 000000 # Doxyfile 1.5.5 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project # # All text after a hash (#) is considered a comment and will be ignored # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" ") #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = Yabause # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, # Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, # Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, # and Ukrainian. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = @CMAKE_CURRENT_SOURCE_DIR@/.. # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful is your file systems # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the DETAILS_AT_TOP tag is set to YES then Doxygen # will output the detailed description near the top, like JavaDoc. # If set to NO, the detailed description appears after the member # documentation. DETAILS_AT_TOP = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespace are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or define consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and defines in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be abled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = @CMAKE_CURRENT_SOURCE_DIR@/../src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx # *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix filesystem feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER # is applied to all files. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES (the default) # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES (the default) # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. Otherwise they will link to the documentstion. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # This tag can be used to set the number of enum values (range [1..20]) # that doxygen will group on one line in the generated HTML documentation. ENUM_VALUES_PER_LINE = 4 # If the GENERATE_TREEVIEW tag is set to YES, a side panel will be # generated containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, # Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are # probably better off using the HTML help feature. GENERATE_TREEVIEW = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, a4wide, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = YES # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = YES # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. This is useful # if you want to understand what is going on. On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = YES # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # in the INCLUDE_PATH (see below) will be search if a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all function-like macros that are alone # on a line, have an all uppercase name, and do not end with a semicolon. Such # function macros are typically used for boiler-plate code, and will confuse # the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option is superseded by the HAVE_DOT option below. This is only a # fallback. It is recommended to install and use dot, since it yields more # powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = @DOXYGEN_DOT_FOUND@ # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are png, jpg, or gif # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is enabled by default, which results in a transparent # background. Warning: Depending on the platform used, enabling this option # may lead to badly anti-aliased labels on the edges of a graph (i.e. they # become hard to read). DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- # The SEARCHENGINE tag specifies whether or not a search engine should be # used. If set to NO the values of all tags below this one will be ignored. SEARCHENGINE = NO yabause-0.9.13.1/doc/CMakeLists.txt000644 001750 001750 00000000571 12256006202 020711 0ustar00guillaumeguillaume000000 000000 project(yabause-doc) find_package(Doxygen) if(DOXYGEN_FOUND) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) add_custom_target(doc ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating documentation with Doxygen" VERBATIM ) endif(DOXYGEN_FOUND) yabause-0.9.13.1/INSTALL000644 001750 001750 00000022024 12256006204 016434 0ustar00guillaumeguillaume000000 000000 Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== These are generic installation instructions. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. (Caching is disabled by default to prevent problems with accidental use of stale cache files.) If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You only need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. If you're using `csh' on an old version of System V, you might need to type `sh ./configure' instead to prevent `csh' from trying to execute `configure' itself. Running `configure' takes awhile. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you must use a version of `make' that supports the `VPATH' variable, such as GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. If you have to use a `make' that does not support the `VPATH' variable, you have to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' will install the package's files in `/usr/local/bin', `/usr/local/man', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PATH'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you give `configure' the option `--exec-prefix=PATH', the package will use PATH as the prefix for installing programs and libraries. Documentation and other data files will still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=PATH' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the `--target=TYPE' option to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc will cause the specified gcc to be used as the C compiler (unless it is overridden in the site shell script). `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. yabause-0.9.13.1/README.LIN000644 001750 001750 00000007476 12256006204 016722 0ustar00guillaumeguillaume000000 000000 _ _ / \_/ \ ___ _ ____ \ /___ ___ / || | __ / \ ____ \ // || \ / || | \ \\ \_// \ / // || // _ || |__\ \\ \ __/ \_// _ || \\_/ \_||______/ \ \\ \__ \_/ \_||___/ \____/ \____\ Yet Another Buggy And Uncomplete Saturn Emulator ____________________________________ Copyright (c) 2002-2011 Yabause team 1) Introduction.............................................19 2) Compiling instructions...................................25 3) How to use Yabause......................................102 1 Introduction________________________________________________ This file documents the gtk version only, for general information check the README file. 2 Compiling instructions______________________________________ The Gtk+ port of Yabause is written in C and depends on the Gtk+ library (thus the name). The recommended setup of the Gtk+ port is to link it against OpenGL and gtkglext libraries, but this is not mandatory; see "Full Software mode" for further instructions. Yabause currently provides two build system, a legacy build process using the autotools and a newer build process using CMake. 2.1 Recommended setup_________________________________________ You need a working C compiler, such as gcc and the above libraries runtime and development packages: * http://www.gtk.org * http://gtkglext.sourceforge.net * OpenGL should be included with your compiler, if it isn't, check on your distribution's website for links. * http://www.cmake.org, you'll need a CMake version >= 2.8 With those libraries, you'll get a working Yabause, but with some restrictions: * No sound * No translations * Depending on your OS, keyboard input only You may want to install some optional dependencies for a better experience. 2.2 Optional libraries________________________________________ Yabause can use a number of optional libraries: * SDL: provides sound and joystick support http://www.libsdl.org/ * OpenAL: provides sound support * mini18n: provides translation support 2.3 Compiling_________________________________________________ For the build process, we recommend using two directories: one for the Yabause sources (SOURCES) and one for the build (BUILD) Uncompress the Yabause source archive into the $SOURCES dir and create the $BUILD directory. Move to the build directory and type "cmake $SOURCES" then "make" it will generate one program: "yabause" in the "src/gtk" directory. You can even type "make install" to install that program on your system (in /usr/local/ by default), but we don't support desinstalling it. 2.4 Full Software mode________________________________________ The Gtk+ supports building without OpenGL support. cmake -DYAB_WANT_OPENGL=NO $SOURCES make 3 How to use Yabause__________________________________________ Before using Yabause, you need to configure a few things in the Preferences dialog (Yabause>Preferences). 3.1 Configuration_____________________________________________ First, set the BIOS path. Yabause can run some games without a BIOS, but most of them needs it. If you want to use the emulated BIOS, just let the BIOS entry blank. Next, set the cdrom device. It can be a cd device, an iso or a cue file. Set the cd type accordingly. The last thing you have to configure is the keys. Once eveything is set, you can start emulation with the "Yabause>run" entry. 3.2 Command line arguments____________________________________ -b (or --bios=) Specify bios file. -c (or --cdrom=) Specify cd device. You can know which file is used as cd device by looking in /etc/fstab. It is commonly something like /dev/hdc or /dev/hdd for IDE devices and /dev/scd0 for SCSI devices. -i (or --iso=) Specify iso file. yabause-0.9.13.1/ChangeLog000644 001750 001750 00000107037 12256006202 017163 0ustar00guillaumeguillaume000000 000000 0.9.12 -> 0.9.13 general: - Removed Carbon, PSP, Wii and Windows ports - Removed autotools based build - Removed autopackage - mdf/mds images support (CyberWarriorX) - Safeguard to reject unsupported cue files (CyberWarriorX) - CD+G support (CyberWarriorX) - Improved "manual mode" (Guillaume) - Made it possible to build Yabause to ouput 16BPP (Guillaume) - Hat support in SDL joystick code (anonymous) - gdb stub (Guillaume) - Improved emulated bios (CyberWarriorX) sound: - Fixed most of the distorted cd audio issues with the scsp core (CyberWarriorX) video: - Improvements to the software renderer (transparency, frameskip, (Guillaume) qt port: - Memory editor and search (CyberWarriorX) - Improvements to the SH2 debugger: Back trace support, step over, step out (CyberWarriorX) - Infinite loop tracking (CyberWarriorX) - Don't restart emulation after every settings change (AmonX) - Made Yabause compilable with Qt5 and SDL2 (CyberWarriorX) - Log window can now be hidden (Guillaume) - Save and restore window position (Guillaume) - Can use .ini files in the application dir (Guillaume) - Mouse and 3D control pad support (CyberWarriorX) - Made drives and languages settings more user friendly (CyberWarriorX) - Changing to fullscreen now updates resolution too (CyberWarriorX) - Shortcuts editing (CyberWarriorX) 0.9.11 -> 0.9.12 general: - Fixes to the dynamic recompiler (Ari64) - Added ARMv5 support to the dynarec (Ari64) - New OSD system (Guillaume) - Added "built-in" DDK to make it easier to compile on Windows (Guillaume) sound: - Improvements / Fixes in both SCSP and SCSP2 (Cwiiis) video: - Major improvements to the OpenGL renderer (Devmiyax) - Major improvements to the software renderer (Guillaume) - Some fixes to register emulation (Guillaume) - Improvements to line drawing functions in the software renderer (Cwiiis) - Fixed endianess bugs (Guillaume) cocoa port: - Added "load image" feature (BlueCrab) - Fixed the resize bug (BlueCrab) qt port: - Added shortcuts to toggle vdp2 layers (Benjamin Siskoo) - Fixed the "mute sound" feature (Guillaume) - It's now possible to compile the Qt port in "full software" mode (Guillaume) - Added an autostart option, disabled by default (Guillaume) - Now using a XDG compliant location for config file (Guillaume) - Added a debugger to the Qt port (CyberWarriorX) - DirectX cores can now be used in Qt port (CyberWarriorX) - Cheat search function (CyberWarriorX) - Option to show/hide menu and toolbar (Guillaume) - Close button in pad settings (guillaume) 0.9.10 -> 0.9.11 general: - Now using CMake as the default build system. For now, autotools based build and "custom" build systems are still supported. - New Cocoa port - Added a dynamic recompiling SH2 core for x86 and ARM - New SCSP implementation - Major update of the software renderer from the yabause-rr team - Added an option to allow to execute from the cache - Improvements to the OpenGL renderer carbon port: - Improvements gtk port: - Added command line option to enable/disable frame skipping / limiting. - Added frame skipping/limiting configuration in settings. - Added --autoload command line option - Vdp2 layers can be toggled from the Vdp2 debug window psp port: - Added support for Media Engine CPU - Improvements to the PSP port qt port: - Added command line support wii port: - Merged some stuff from the wiibrew fork, mostly related to SH2 emulation windows port: - Fixed the XBox controller driver - Fixed the "open iso then cancel bug" 0.9.9 -> 0.9.10 scsp/68k: - Added code to make SCSP emulation frame-accurate (optional, enabled with --enable-scsp-frame-accurate configure switch). - Added a new 68000 emulation core. software video core: - Added line scroll emulation. - Improved user clipping. - Added some basic vertical scroll emulation, enough to get Sonic Jam working. gtk port - Gtk port is now compiling on Mac OS X. - Fixed full software screenshots. - Fixed store function in transfer dialog. windows port: - Added 12 player support. - Fixed a bug that was causing the memory transfer dialog to register the wrong filename after pressing "Browse". - Fixed bugs in Goto Address dialog. - Fixed a bug that was causing the vdp2 viewer dialog to register the wrong filename after pressing "Browse". - Added MD saving in SCU DSP debug dialog. - Added new Ram Watch dialog. - Added video recording feature. - Added move recording feature. general: - Added Lithuanian translation. - New sound core using OpenAL. - Added joystick core for Mac OS X. - Added a joystick core for Linux. - Added a PSP port. - Added support for loading ELF binaries. - Now using gettimeofday when available for better resolution. - Fixed save states. 0.9.8 -> 0.9.9 opengl video core: - Fixed a bug that was causing some games to crash (albert odyssey, dragon ball, etc.) gtk port: - Automatic detection of current locale. qt port: - Added support of DESTDIR and --program-prefix - Automatic detection of current locale. - Added support for multiple players. windows port: - Fixed crash when going into settings. - Fixed mouse wheel usage in disassembler. - Rewrote as an unicode application. - Fixed the key configuration problem. - Fixed joypad support. - Partial fix for mouse wheel and slider problem. - Fixed fullscreen bug. general: - Hooks for renaming .desktop on installation. - .yts file are now installed. - Fixed parallel builds. - SDL peripheral core now handles all connected joysticks. 0.9.7 -> 0.9.8 vdp2: - Fixed a bug in software renderer with rotating backgrounds. opengl video core: - Added gouraud shading and mesh processing. This is not enabled by default. software video core: - Fixed user clipping. gtk port: - Added mouse support. - Configuration dialog now displays key names instead of values. Also made it so each different configuration is saved. This broke compatibility with old .ini files. windows port: - Support for spaces in filenames when using CLI. - Added mouse support. - Added cheat search. general: - Added mouse emulation. - Added de, es, it, pt-br and sv translations - Support for "out of src" build. - Fixed compilation for non supported platforms. For instance this should fix compilation on dragonfly bsd. Fixed compilation on GNU/Hurd too. 0.9.6 -> 0.9.7 vdp1: - Added clipping for line-based drawing to software renderer. vdp2: - Toggling a screen is now core independent. - Added per-character priority to software renderer. gtk port: - Fixed fullscreen setting and added a keep ratio one. - Fixed a bug in the vdp2 debugger that was causing the emu to crash. - Full software mode can be compiled again. - Fixed segfault when taking screenshots in full software mode. - Fixed default value for region. - Window position is now saved and restored when re-opening the emu. - Fixed a problem when changing input cores. qt port: - Improved compilation process: make (un)install now works. - Fix crash when configuring input while using translated version. windows port: - Changed resolution list generation so it adds the resolution to the list, regardless of whether it supports 60 hz or not. - Fixed error when trying to add blank cheat code. - Fixed all code that allowed the user to choose filename for saving so it automatically places a default extension. - Save and Clear buttons are now enabled when loading a cheat file. - Fixed a bug with AR code adding where it was tracking the wrong edit window. - Fixed a bug when adding raw cheat codes. - Fixed bugs in vdp1 debugger. - Fixed a bug where saving/loading a save state and an error occured would cause sound looping. - Scroll bar in memory editor now works properly when you move the thumb control. - Added support for x64 builds in Visual Studio. general: - Fixed a bug that was causing older save states to fail. 0.9.5 -> 0.9.6 sdl joystick core: - Fixed it... software video core: - Improvements and bug fixes. carbon port: - Added detection of sdk in the build process. - Changed the cd core so that the first device found is used. Users shouldn't have anything to set up when using cd device now. gtk port: - Tagged more strings to be translatable. - Fixed bugs when setting a resolution in settings. - Fixed controller settings so keys can now be configured even if emulation is not started. qt port: - Removed libsjw core. wii port: - Updated to use the last devkitppc. - Added support for classic controller and for wiimote, disabled keyboard support. windows port: - Added command line support. general: - Updated copyright for some files where it was missing or inaccurate. - Fixes and improvements to the build process: fixed cross compilation of Qt port, added Wii port support, found a better way to "trigger" compilation of gen68k, fixed a bug when calling the sub-configure, .inc files are now cleaned, added MINI18N variable support, forwarded distclean rule to qt makefiles, configure now make sure the compiler is a cross compiler when cross compiling - Added a workaround for the "limits.h" problem... now distros should fix their headers... - Fixed the .desktop files for linux (gtk + qt ports) - Added translation files for fr and pt in the repository. 0.9.4 -> 0.9.5 68k: - Added 1010 and 1111 line emulator support. cd block: - Reworked bin/cue support. Reading should be a lot more accurate now on tracks 2 and greater. emulated bios: - Fixed a bug in BupGetDate year calculation. - Fixed a bug where interrupt mask wasn't being set correctly when using emulated bios. smpc: - Added support for SMPC NMIREQ command. - Added reset button emulation. software video core: - Improved software renderer: window, line scroll, mosaic are now available and color offset and scroll screen has been fixed. gtk port: - Tagged most of gtk port strings to be translatable. qt port: - Added ability to specify address where binaries are loaded when using command line. - Other bug fixes. wii port: - Added support for bios and game loading from sd card. - Added sound support. - Added usb keyboard support. windows port: - Added pause emulation function. - Other bug fixes. dreamcast port: - Rewrote all of the Dreamcast CD Interface in hand-optimized assembly. - Enabled use of the emulated bios if there is no saturn.bin on the CD. general: - Updated peripheral interface so both ports can now be used and multiple pads can now be connected to each port. - Added translation support through mini18n library. 0.9.3 -> 0.9.4 scsp: - Fixed a timer bug. - Fixed a bug with mcire word writes. - Added wave file output core to available sound cores. - Fixed a bug in total level attenuation. - Fixed a bug in EG. gtk port: - Redesigned memory dump window. - Redesigned SH2 debug window. - Other bug fixes. qt port: - Added initial support. It should be pretty much on par with the gtk port. wii port: - Added initial support. windows port: - Fixed a bug where emulation wasn't paused when save/load state as was selected from the menu. - Changed disassembler so it can scroll up and down. - Tweaked error messages so it doesn't report invalid opcode errors when running the fast interpreter. - Added SCSP common control register debug info to SCSP debug dialog - Other bug fixes. general: - Added a few internal tweaks that should yield some performance gains. - Added support for saving and loading cheats. 0.9.2 -> 0.9.3 cart: - Fixed a couple of bugs with Netlink emulation. cd block: - Tweaked error handling for cue files so it's more helpful to the user. scu: - Fixed a bug in DSP MVI instruction. - Fixed a bug with DSP Program Ram Address. - Fixed ALU behaviour on NOP. - Other bug fixes. vdp2: - Fixed a bug where coefficient reading wasn't making sure reads weren't going out of bounds. - Tweaked frame-skipping so it only skips if frame time is faster/slower than a 1/2 a frame. The results are much better now. - Added general VDP2 debug info functionality. - Added partial end code support to VDP1 texture debugging. opengl video core: - Fixed a bug in 16 BPP sprites where pixels 0x0001-0x7FFF weren't transparent when transparency was enabled. gtk port: - Redesigned the window so each part can now be resized. - Added a toolbar and removed the buttons. - The sprite list now displays texture thumbnails. - Added tooltips to "run" and "pause" buttons. - Redesigned VDP2 debug window. windows port: - Fixed a bug that was causing Yabause to crash when run for the first time. - Added screen capture. - Reworked Input dialog so it'll allow for more than one peripheral(in the future). - Added a bunch of tools tips for basic and input settings. - Fixed a bug that was causing wrong VDP1 command information to sometimes be displayed. - Other bug fixes. - Fixed a bug that was causing the wrong breakpoint to be removed from the breakpoint list. - Text length is now limited correctly in breakpoint edit text controls. general: - Tweaked memory breakpoints so that regardless of whether you're using cached or cache-through addresses variations of an address, it'll still detect and break when the memory is accessed. - Other bug fixes. 0.9.1 -> 0.9.2 cd block: - Fixed a bug in periodic timing. Most movies should play correctly now. - Other bug fixes scsp: - Fixed a bug that was causing reversed panning. - Fixed a bug in SCSP slot debug stats. sh2: - Fixed a bug that caused Yabause to crash when fetching instructions from some areas. vdp2: - Fixed undocumented plane size setting when debugging vdp2 - Special Color Calculation mode added to vdp2 debugging opengl video core: - Added the eight missing sprite types in Vdp1ReadPriority. software video core: - Fixed a bug where Polygons that used non-RGB values had the wrong priority. - Fixed a bug that was causing some scrolling issues. gtk port: - CD, sound, and video cores can now be changed without restarting the emulator. - Added basic support for save states. windows port: - Fixed compilation with MSYS. - Changed SCSP debug dialog so it allows for individual slot saving. - Fixed a bug when using goto address in memory editor. - Fixed a bug where Yabause crashed when joystick was unplugged. - Added memory search support. - Fixed cheat dialog. Codes should show up after re-opening it. general: - Fixed some bugs where vdp1/vdp2 layers wouldn't be drawn after switching video cores. - Fixed a bug when switching between opengl and software video cores. - Added memory search function. 0.9.0 -> 0.9.1 scsp: - Fixed slot pitch LFO. Amplitude LFO is probably more accurate now too. emulated bios: - Added Backup RAM manager functions. opengl video core: - Fixed a bug with VDP2 2x2 plane size rotation screens. - Optimized tile mode rotation screens - Added support for VDP1 polyline. software video core: - FPS display now working. - Added support for VDP2 rotation without coefficient tables. - Fixed a bug in VDP2 24 BPP bitmap mode. - Fixed several clipping bugs in Normal and Scaled Sprites. - Fixed a bug with VDP2 2x2 plane size rotation screens. - Optimized tile mode rotation screens. linux port: - Cursor now disappears after 2 seconds of inactivity in the gtk port. macos port: - New high resolution icon. - Add some missing OS X application property list keys. windows port: - Fixed window position bug. - Other bug fixes. general: - Tweaked frame timing code so it's more accurate. - Re-implemented save states. - Some internal changes do so that sound, video, and cd cores can be changed at runtime. 0.8.6 -> 0.9.0 opengl video core: - Added support for VDP1 line draw. - Added support for VDP2 Rotation with coefficient tables. - Other bug fixes. software video core: - Added support for VDP1 frame buffer switching. - Added support for VDP2 Rotation with coefficient tables. - Fixed a bug in frame buffer erasing. - Other bug fixes. linux port: - Fixed a bug on 64 bits CPU. - Hanged the location of the ini file to conform to XDG specification. - Removed some old useless code. - Added a "subclass" to GtkDrawingArea so sprite textures and screenshots can now be saved through a popup menu. macos port: - Added fullscreen support. - Added graphics layer toggling. windows port: - Fixed a stack corruption bug in DirectInput code. - Fixed(hopefully this time) the joystick centering bug. 0.8.5 -> 0.8.6 68k: - Fixed a bug which caused the emulator to crash if 68k execution jumped to an invalid address. scsp: - Fixed a bug where the slot buffer pointers weren't set correctly. - Added a function for debugging SCSP registers vdp1: - MODR returns the correct version number now. - Fixed a bug that caused Local Coordinates, etc. commands to not get executed correctly. software video core: - Added vdp2 horizontal flip for cell mode. linux port: - Improved vdp1 window a bit. - Updated website url. - Some cleanups macos port: - Added browse buttons for some settings. - Added universal build support. - Emulation loop was optimized. - Fixed bug when "Run" is selected from the menu. - Audio is now muted when emulator is paused. - Fixed Backup RAM saving. - Fixed a bug that was causing filenames to be parsed wrong. - Other bug fixes and cleanups. windows port: - msys compiling is now fixed. - Windows position is now saved when program exits. - Fixed sound volume adjustment. Should be more accurate now. - Fixed centering bug on joysticks. - Fixed POV hat diagonals. - Sound is now muted in the about dialog. - Other bug fixes. general: - Added COFF file support. 0.8.0 -> 0.8.5 scsp: - Added functions for dumping individual slots to wav files. scu: - Fixed SCU execution speed sh2: - Added DVDNTL/DVDNTH mirrors - Added overflow interrupt vdp1: - Added function for displaying vdp1 textures for debugging - Other bug fixes vdp2: - Added more RBG0 debug info 68k: - Added a core system for m68k and a c68k core interface. - Added a dummy m68k core based on old yabause code, working enough to boot the bios. emulated bios: - Registers are now reset correctly - Fixed bug in BiosSetSh2Interrupt - Added Read/Write Save support - Added undocumented CD Authentication function opengl video core: - RBG0 bug fixes - Rotation Screen improvements software video core: - Added 32 BPP cell draw mode bsd port: - Added support for OpenBSD linux port: - Fixed the segfault that occured when opening the preferences dialog. - Added texture display in vdp1 debug dialog - Other GUI improvements macos port: - Added browse button for bios setting - Other bug fixes windows port: - Fixed a bug that was causing sound to not work on some people's computers. - Added texture display in vdp1 debug dialog - Added window/full screen resizing - Added full screen on startup - Settings changed to use tabs instead of what was previously used - Other bug fixes - Logging now is done to a logging window when DEBUG is defined while compiling. - Added cheat dialog - Added memory editor - Added Visual C++ project file general: - Added Cheat support. Largely untested. 0.7.2 -> 0.8.0 cart: - Moved Netlink code to its own file: netlink.c - Improved Netlink AT command handling. Most games using the X-Band software should work now. - Fixed a number of bugs that were causing strange behaviour in Netlink emulation. - Added Modem states. Online Mode is now handled correctly. - Added Networking code that allows two Yabause instances to communicate with each other. Still somewhat buggy. cd block: - Fixed an issue where games that didn't specify an index along with the track when playing cd audio didn't work correctly. vdp1: - Code cleanups. vdp2: - Code cleanups. - Adjusted frameskip code so it skips up to a maximum of 9 frames at a time. direct sound core: - Fixed a bug that was screwing up the buffer position. Now it's almost perfect(at the very least there's no clicks or pops anymore). sdl sound core: - Fixed a bug that was screwing up the buffer position. Now it's almost perfect(at the very least there's no clicks or pops anymore). software video core: - Polygon drawing improvements - Removed the silly y-axis clipping technique - Added a filter for clipping detection - Added vdp1 "end codes" in textures, but didn't find a game that use it yet, please report bugs. - Code Cleanups - Fixed a potential bug in polygons - Fixed a bug in polygon clipping linux port: - Code cleanups - Changed a few things in configure script to fix compilation problems when OpenGL and/or gtkglext were not present. - Added a log popup window. - Added a screenshot window on gtk port. - Fixed Pause/Screenshot bug. - Removed the "Keep ratio" setting as it can't be done in gtk and replaced it by a "Fullscreen" setting. - Added a yabause entry in gnome and KDE application menus - Changed configure script so it fails on linux if --with-opengl is used and gtlglext is not installed. dreamcast port: - Compiles and runs again. - Added Normal Sprite support. - Added Distorted Sprite support. - Added Scaled Sprite support. - Added in YabauseGetTicks support. - Ported VDP2 portion of software renderer. - Added new cd core. - Added very simple GUI. - Other bug fixes. netbsd port: - Added patch to get yabause working on netbsd with cd support thanks to Takashi Kyohara. windows port: - Added pad configuration(first pad only). - Added support for gamepads/joysticks. - Removed duplicate cd code. - Added a separate thread for cd access. SPTICDGetStatus is the only function making use of it for now. - Fixed fullscreen bug - Added dialog and settings saving/loading for Netlink stuff(disabled for now). - Other bug fixes. general: - Commited mac port fix by Antime. - Coordinate Increment Registers are now set to 1 when using the quick load function. It seems there's at least one game out there that doesn't want to set it. - Improved Backup Ram bios emulation functions. The only functions that still aren't functioning correctly are Bup Write, Bup Read, Bup Verify, and Bup Set/Get Date. So still no saving, but at least there's no errors when running games now. 0.7.1 -> 0.7.2 cart: - A few Netlink changes(still doesn't work). cd block: - CD Block play disc command fixes and improvements. Play Modes now handled correctly. - Added correct Repeat counter support. - CD audio data is now written to its own buffer, which is then played by the SCSP. scsp: - CD audio data is now played by the SCSP. EFSDL and EFPAN support still needs to be added. opengl video core: - glutInit is now called before any other glut function(except for on the windows port). software video core: - Added normal sprite flipping(copied from scaled sprites). - Corrected a bug with 8 bpp color calculation in scaled and distorted sprites. - Fixed a bug that caused duplicated textures in 8 bpp regular sprites. - Distorted sprites made safer (won't read outside the texture) - Fixed transparency for distorted sprites. - Fixed scaled sprites bug in zoom points modes two points mode and C point upper than A. - Fixed a bug that was causing sprite priority problems. linux port: - Fixed a gtk warning. - Added Joystick support. - Added a test in configuration dialog so input tab is displayed only when emulation is initialized. - Added NTSC/PAL setting - Input settings are now disabled when PERCore isn't initialized. - Added a sound setting tab. macos port: - Added code to handle settings (everything should be working now, except the "browse" buttons). - Controls are now using the new Per* functions. - Fixed some bugs in combo boxes. windows port: - EC Compatibility list link added to help menu. - Fixed an issue where default values weren't set correctly when yabause.ini wasn't present. - Fixed an issue where Yabause would go into an endless loop if bios path was incorrect. - DirectX error messages now return more info when there's an error. - Fixed an issue where people without hardware sound buffers on their sound card would have problems trying to run with sound. - Fixed some inaccurate information in the README.WIN file. - Fixed cut-off text in Memory Transfer dialog. - All dialog windows can now be closed using the X icon in the top-right corner. general: - Fixed an issue where in certain cases Yabause would crash when sound settings were altered. - Some useless files were removed. - Moved SDL detection in "global" part of configure script as it may be used by all ports. - Fixed a weird issue where a few functions were trying to return a value when they obviously can't(How come GNU C compilers won't detect this?). - Fixed a number of things that were causing compilation issues in VC++(VC++ still doesn't completely compile Yabause yet). - Configure now checks if c99 variadic macros are available. 0.7.0 -> 0.7.1 opengl video core: - Added polygons that use a palette. software video core: - Added scaled sprites with clipping and flipping. - Full screen mode now working correctly. - Added correct support for vdp2 resolutions other than 320x224. - Fixed compilation issue on big endian systems. - Added function to software renderer for fetching width/height of the display buffer - Memory leak when clearing VDP1 frame buffer fixed. linux port: - Added autostart and fullscreen command line switches. - Fixed a bug that was causing the emulator to sometimes start in using PAL timing. - Added an option to choose the peripheral interface at configure time. - Started to move the gtk controls code into a proper peripheral core. - Added code so software renderer can be used without OpenGL. - Added --without-opengl switch to configure script to prevent OpenGL detection. - Resizing is now enabled when using software renderer and opengl. macos port: - Fixed a bug that was causing the emulator to sometimes start in using PAL timing. - Some fixes to carbon interface (preferences should works now). windows port: - Fixed a bug that was causing the emulator to sometimes start in using PAL timing. - Added shortcuts to the Yabause website, forum, donation page, and the submit bug page to the main menu. - Added About dialog. general: - Fixed a potential issue when enabling/disabling auto frameskipping. 0.6.0 -> 0.7.0 cart: - Added Action Replay flash emulation. cd block: - Fixed Read Directory/Change Directory commands. This fixes Duke Nukem 3D and a few others that have Netlink support. - Audio data is no longer stored when read by the cd block. This fixes Guardian Heroes. - other bug fixes. scsp: - Added function that allows developers to get easy to read information on the requested scsp sound slot. - Fixed a bug where the phase wasn't getting updated if DISDL was set to 0. This fixes Falcom Classics, Nadesico, and many other games using ADX. - Fixed a bug that was causing OCT with a setting of 0x8 to play at the wrong octave. - Fixed a bug that was causing King of Fighters 95(and possibly others) to go into an endless loop. scu: - Improved SCU interrupt handling. sh2: - Fixed a bug in exts.b opcode. - Corrected some bugs in sh2idle - SCI emulation improvements smpc: - Added proper DOTSEL reporting. - Region settings are now properly preserved. - Changed region autodetection so it defaults to the japanese region if it can't autodetect. 68k: - Fixed a few bugs. vdp2: - Debug info bug fixes - Implemented one mode of external HV latching. This fixes King of Fighters 95. - External latch and sync flags are now cleared on TVSTAT reading. - Added speed throttle(basically skips 6 frame draws). - Added long writes for VCSTA, LSTA0, and LSTA1 registers. software video core: - Rewrote it so it's no longer dependent on SDL. - Added NBG2/NBG3 support. - Added tile mode rendering. - Added frame buffer emulation. - Added normal sprite drawing. - Changed Normal Sprite drawing so that Scaled Sprite and Distorted Sprite functions can use it too. - Added some support for Scaled/Distorted Sprites. - Added VDP1 Polyline and Line drawing to Software renderer. - Fixed a bunch of bugs. opengl video core: - Fixed a few issues with OpenGL initialization. - Fixed a window/fullscreen bug. - Added a smart Line Scroll/Vertical Cell Scroll interpreter. - Changed Color Offset so it uses the same method as the Software renderer. - Fixed Rotation Table reading. - Fixed a bug in VIDOGLVdp1PolylineDraw where coordinate reads were writing to invalid areas. linux port: - Removed some useless debug messages and fixed the "quit" menu entry. - Added vdp1 debug dialog in new gtk interface. - Added dialog for sh2, video core switching. - Added reset menu entry. - Added about dialog. - Added MSH2 and SSH2 debug dialogs to the GTK interface. - Added transfer dialog to the new gtk ui. - Added empty Memory Dump dialog. - Added the dialog box for scsp - Added shortcut F7 for command Step - Added support for memory breakpoints in sh2 debug dialog - Sound is now muted when emulation is paused (in gtk interface). - The window data is now saved while emulation is paused. - Screenshot function added. macos port: - Added carbon interface - Can now build .dmg image from .app directory - Other improvements windows port: - Added SCSP Debug Dialog. - Added Reset option to menu. - Now uses DirectInput and DirectSound instead of SDL. - Added dialog for video, sound and input core switching. - Fixed window/fullscreen switching. - Added support for memory breakpoints in sh2 debug dialog. - Sound volume can now be adjusted in the settings dialog. - Sound is now muted when dialog window has focus. - Auto frameskip can be be enabled via video settings menu. - Other bug fixes. general: - Better handling of NULL string when opening a file - Fixed a few memory leaks - ISO support fixes - PAL support added - Fixed v-blank timing - Added auto frameskipping(still not working correctly) - Improved sound buffering - Fixed handling of invalid SH2 opcodes - Dummy sound core bug fixes - Fixed some warnings - Added experimental bios emulation - Added memory breakpoints - Added a function to the sound cores for setting the volume. 0.5.0 -> 0.6.0 cart: - accesses to Netlink addresses when Netlink was not present was causing errors, this has been fixed. scu: - fixed DSP debugging. - fixed a Timer 0 bug. Fixes Shining the Holy Ark. sh2: - added SH2 idle detection. Speed should be significantly faster. - separated original core(now the "debug interpreter core") from the core with idle detection. - sh2 cores are now selectable. 68k: - added 68k disassembler. - fixed some warnings. vdp1: - added debugging functions. - fixed bug that was causing garbage graphics in Albert Odyssey. - fixed bug that was causing graphics in Legend of Oasis to not get drawn. - other bug fixes. vdp2: - fixed a few priority bugs. - added initial special priority emulation. general: - added fullscreen and fixed resize in Windows. Still needs quite a bit of work. - changed event handling a bit. Gained quite a bit of speed from it. - fixed some Mac OS X port bugs. - fixed some Dreamcast port bugs. - added proper Linux gui. - Fixed YGL initialization. - fixed some Windows ports bugs - other bug fixes. 0.0.7 -> 0.5.0 cd block: - bug fixes. - improved timing. cart: - added Action Replay emulation. - added 8/32 Mbit dram emulation. - added 4/8/32 Mbit backup ram emulation. - added 16 Mbit rom emulation. - added very early Netlink emulation. scsp: - added Stephane Dallongeville's SCSP's core. Thanks again Stef! - fixed a couple of bugs that were causing movies to lock up. 68k: - added Stephane Dallongeville's 68k's core. Thanks again Stef! - fixed a few endian related bugs. - added debugger(still need disassembler though). scu: - added dsp emulation. - added dsp debugger. - added indirect dma emulation. - added timer0 emulation. - bug fixes. smpc: - added very basic SH2 direct peripheral mode. - added clock change commands. - added slave sh2 off/on commands. - fixed intback command timing. - bug fixes. sh2: - added FRT, WDT, and partial UBC emulation. - fixed a couple of opcode bugs. - re-added debugger. - added some early dynarec code. vdp1: - added sprite priorities. - added color offset. - bug fixes. vdp2: - added basic rbg0 emulation(no rotation, etc.). - added backscreen emulation. - added caching. - added color offset. - added video mode changing. - added screen scrolling. - fix caching bug. - other bug fixes. - added early software video rendering. It's still pretty much unuseable at this point. general: - added binary execution. - rewrote entire code in C for portability and speed. - fixed a number of configure bugs, added a few more command-line options. - fixed code so it's 64-bit friendly. - added iso and bin/cue files support. - changed several parts of yabause to allow for multiple implementations of video, sound, and peripheral code.. - added save states(currently broken unfortunately). 0.0.6 -> 0.0.7 cd block: - added cd interface for porters. - whole bunch of cd commands were added. Most games should now start to boot. - added region auto-detection. mpeg card: - added basic emulation. - added mpeg rom loading support. scsp: - bug fixes. scu: - bug fixes. smpc: - bug fixes. superh: - fixed dma. - lots of other bugfixes. - opcode optimizations. vdp1: - added sprite caching. - added scaled sprites. - added sprite color modes 0, 1, 2, 3, 4. - macosx color bug fixed. - bug fixes. vdp2: - macosx color bug fixed. - bug fixes. general: - added fps counter. - switched to OpenGL, removed SDL_gfx. - yui interface added. Now each port should be able to provide a nice custom ui. - threads removed, program should be more stable now. - added save ram loading ability. 0.0.5 -> 0.0.6 scu - added direct dma. superh - added division unit. - fixed endianess issue. vdp2 - added NBG3. - fixed color bug. 0.0.4 -> 0.0.5 vdp2: - lot of work, the vdp2 is now capable of displaying the set-clock screen of the bios. monitor/debugger: - added memory dump possibility. 0.0.2 -> 0.0.4 monitor/debugger: - added debugging possibility, can now pause/resume emulation and execute instructions step by step; - opcodes are disassembled interactively. general: - early emulations of different cpu/onchip modules: scu, vdp1 and dmac; - translate most of the code from french to english; - added synchronisation between processors; - yabause is now using SDL, remove all fork/ipc code and use SDL_Thread instead. 0.0.1 -> 0.0.2 sh2: - "mull" is now decoded; - changed the way the opcodes are decoded, now using a table with pointers to function, should be faster. intc: - now tests if the interrupt level is correct before accepting one. vdp2: - early emulation, just throws an interrupt every half-frame. general: - vdp1 and vdp2 are now synchronized with the master sh; - fixed some memory bug, all the shared memory allocated is de-allocated; - now using configure/make, should be more portable; - modified things to be more c++ and less linux/c. yabause-0.9.13.1/CMakeLists.txt000644 001750 001750 00000002216 12261545746 020163 0ustar00guillaumeguillaume000000 000000 project(yabause) cmake_minimum_required(VERSION 2.8) set(YAB_PACKAGE yabause) set(YAB_VERSION_MAJOR 0) set(YAB_VERSION_MINOR 9) set(YAB_VERSION_PATCH 13.1) set(YAB_VERSION "${YAB_VERSION_MAJOR}.${YAB_VERSION_MINOR}.${YAB_VERSION_PATCH}") set(CPACK_SOURCE_GENERATOR TGZ) set(CPACK_PACKAGE_VERSION_MAJOR ${YAB_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${YAB_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${YAB_VERSION_PATCH}) set(CPACK_PACKAGE_VENDOR "Yabause team") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") set(CPACK_SOURCE_PACKAGE_FILE_NAME "yabause-${YAB_VERSION}") if (APPLE) set(CPACK_GENERATOR DragNDrop) set(CPACK_PACKAGE_FILE_NAME yabause-${YAB_VERSION}-mac) endif () if (WIN32) SET(CPACK_NSIS_INSTALLED_ICON_NAME yabause.exe) set(CPACK_NSIS_MENU_LINKS yabause.exe;Yabause) set(CPACK_NSIS_URL_INFO_ABOUT "http://yabause.org") set(CPACK_NSIS_COMPRESSOR "/SOLID lzma") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") set(CPACK_SYSTEM_NAME "win64") endif () endif () include(CPack) add_subdirectory(doc) add_subdirectory(l10n) add_subdirectory(src) yabause-0.9.13.1/GOALS000644 001750 001750 00000000707 12256006202 016175 0ustar00guillaumeguillaume000000 000000 Goals for a 1.0.0 release -------------------------- -Full "stock" saturn emulation. At the very least it should be "feature complete"(most major bugs should be fixed at this point too) -Any external carts or peripheral do not need to be fully emulated at this point -Switchable SH2 Dynarec/Interpreter cores -Full debugging support -Speed should be high at this point(It's too tough ironing out those bugs with Yabause at its current state) -Save States yabause-0.9.13.1/README000644 001750 001750 00000006157 12256006202 016272 0ustar00guillaumeguillaume000000 000000 _ _ / \_/ \ ___ _ ____ \ /___ ___ / || | __ / \ ____ \ // || \ / || | \ \\ \_// \ / // || // _ || |__\ \\ \ __/ \_// _ || \\_/ \_||______/ \ \\ \__ \_/ \_||___/ \____/ \____\ Yet Another Buggy And Uncomplete Saturn Emulator ____________________________________ Copyright (c) 2002-2013 Yabause team 1) Introduction.............................................20 2) Staff/Thanks.............................................37 3) Contact information......................................73 4) Disclaimer...............................................84 1 Introduction________________________________________________ Yabause is a Sega Saturn emulator under GNU GPL. Yabause can boot Saturn bios and games, some of those games are playable. For installation/how to use information, check the ports specific README files: * README.DC for the dreamcast port * README.LIN for the linux port * README.MAC for the mac port * README.QT for the crossplatform Qt 4 port * README.WIN for the windows port 2 Staff/Thanks________________________________________________ See the AUTHORS file for team members list. Thanks to: * Runik (author of Saturnin), for all his help, especially on the vdp2. http://saturnin.consollection.com * Fabien Autrel (author of Satourne), for letting me browse his sources. http://satourne.consollection.com * Chuck Mason (author of semu), for releasing semu sources and for his handy debugger. * Romain Vallet for winning the "Find a name for my emu" contest, for his contribution to this README and for this nice web site he did. http://romanito.free.fr/ * Stefano and all of segadev for their help. * Josquin Debaz for writing the man page. * Bart Trzynadlowski for his disassembler. * Charles MacDonald for his saturn sample programs, docs, and being a major source for saturn information. 3 Contact information_________________________________________ E-mail: guillaume@yabause.org Web: http://yabause.org IRC: irc://irc.freenode.net/yabause Please don't ask for roms, bios files or any other copyrighted stuff. Please use the forum when you have any questions if possible. 4 Disclaimer__________________________________________________ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA See the GNU General Public License details in COPYING. yabause-0.9.13.1/AUTHORS000644 001750 001750 00000001101 12256006052 016445 0ustar00guillaumeguillaume000000 000000 Current team ------------ Theo Berkau Guillaume Duhamel Filipe Azevedo (pasnox) Lawrence Sebald (BlueCrab) Contributors ------------ Anders Montonen (antime) Andrew Church Ari64 Michele Balistreri Richard Dolinsky (menace690) Lucas Newman (Adam Green) Joost Peters Jerome Vernet Takashi Kiyohara Weston Yager Ex-Cyber Romulo Fernandes (nightz) pa__ Fabien Coulon trap15 devmiyax Translators ----------- Benjamin Siskoo Felipe2 (people we grabbed code from) ------------ Stephane Dallongeville - c68k and scsp Patrick Kooman - profiler Bart Trzynadlowski - sh2 disassembler yabause-0.9.13.1/README.DC000644 001750 001750 00000006546 12256006052 016564 0ustar00guillaumeguillaume000000 000000 _ _ / \_/ \ ___ _ ____ \ /___ ___ / || | __ / \ ____ \ // || \ / || | \ \\ \_// \ / // || // _ || |__\ \\ \ __/ \_// _ || \\_/ \_||______/ \ \\ \__ \_/ \_||___/ \____/ \____\ Yet Another Buggy And Uncomplete Saturn Emulator ____________________________________ Copyright (c) 2002-2011 Yabause team 1) Introduction.............................................20 2) Compiling instructions...................................31 2) How to use Yabause.......................................48 4) Final Thoughts...........................................71 1 Introduction________________________________________________ This file documents the Dreamcast version only, for general information check the README file. Note, that if you are just a casual user, and not a developer, you probably won't have to worry about the Compiling instructions below. Pick up with the section "How to use Yabause" to learn how to make a CD image you can burn to use Yabause (or use a program like Selfboot). 2 Compiling instructions______________________________________ Yabause is written in C using KallistiOS. So, in order to compile it, you need a working sh-elf targetted C compiler, such as gcc and a working KallistiOS environment: * http://gamedev.allusion.net Once KallistiOS is set up, you should be ready to build Yabause. Uncompress the Yabause source archive, move to the newly created directory, type "cd src", then "make -f Makefile.dc", it will generate one binary: "yabause.bin" in the "src" directory. 3 How to use Yabause__________________________________________ Before using Yabause, you need to build a CD with a few files in the correct places. In order to build a CD with the compiled binary, you must first scramble the binary. This can be done with the scramble utility which can be obtained from http://mc.pp.se/dc/files/scramble.c . This will have to be compiled with your native compiler. Once you run the scramble program on the yabause.bin file created in section 2 above, you will have a binary file suitable for booting on a real Dreamcast. Once you have your scrambled binary, you may choose to include a Sega Saturn BIOS image on your disc as well. As of 0.9.5, this is not a requirement for the Dreamcast port, but if you choose to use an actual BIOS image, it must be put on the root of the CD and named "saturn.bin". From this point, follow your favorite guide for how to build a selfbooting CD out of plain files. I use the one available at http://mc.pp.se/dc/cdr.html . 4 Final Thoughts______________________________________________ The Dreamcast port of Yabause is quite slow. I have done very little in the way of optimizing any of the core of Yabause toward the Dreamcast (there is quite a bit that could be done when time allows). Do not expect the Dreamcast port of Yabause to run your favorite Sega Saturn games at any sort of playable speed for now (or the near future). As Yabause matures, and I have more time to work on it, hopefully speed will improve. For now, think of it as a fancy tech demo. Note that in 0.9.5, a small bit of assembly has appeared in the Dreamcast port. Hopefully as time goes on more of the Dreamcast specific code will be rewritten in assembly for at least some small speed increase. yabause-0.9.13.1/l10n/yabause_pt_BR.yts000644 001750 001750 00000030013 12256006204 021432 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|%1 Imagens (*.%2) %1/%2 blocks free|%1/%2 blocos livres &About...|&Sobre... &Action Replay|&Action Replay &Backup Manager...|&Gerenciador de Backups... &Browse|&Explorar &Cancel|&Cancelar &Capture Screen|&Capturar Tela &Cheat List|Lista de &Trapaças &Cheats|&Trapaças &Cheats List...|&Lista de Trapaças... &Clear|&Limpar &Close|&Fechar &Debug|&Debug &Delete|&Apagar &Emulation|&Emulação &File|&Arquivo &Frame Skip/Limiter|&Frame Skip/Limitador &Fullscreen|&Tela cheia &Help|&Ajuda &Layer|&Camada &Load From File|&Carregar Do Arquivo &Log|&Log &Master SH2|&SH2 Mestre &Memory Transfer|&Transferência de Memória &OK|&OK &Pause|&Pausar &Quit|&Sair &Raw Memory Address|&Endereço da Memória Original &Reset|&Resetar &Save To File|&Salvar como Arquivo &Settings...|&Configurações... &Slave SH2|&SH2 Escravo &Tools|&Ferramentas &Transfer|&Transferência &View|&Visualizar 16 Mbit Backup Ram|Ram de Backup de 16 Mbits 16 Mbit ROM|ROM de 16 Mbits 16-bit|16-bits 16-bit Relative value(s)|Valor(es) de 16-bits relativo(s) 32 Mbit Backup Ram|Ram de Backup de 32 Mbits 32 Mbit Dram|Dram de 32 Mbits 32-bit|32-bits 3D Control Pad|Pad de Controle 3D 4 Mbit Backup Ram|Ram de Backup de 4 Mbits 503/512 blocks free|503/512 blocos livres 510/512 blocks free|510/512 blocos livres 8 Mbit Backup Ram|Ram de Backup de 8 Mbits 8 Mbit Dram|Dram de 8 Mbits 8-bit|8-bits 8-bit Relative value(s)|Valor(es) de 8-bits relativo(s) WARNING: Master Codes are NOT supported.|AVISO: Os Códigos Mestres NÃO são suportados. About...|Sobre... Action Replay Code :|Código do Action Replay Add|Adicionar Add Action Replay Code|Adicionar Código do Action Replay Add Cheat|Adicionar Trapaça Add Codes...|Adicionar Códigos... Add Raw Memory Code|Adicionar o Código da Memória Original Address|Endereço Address :|Endereço : Advanced|Avançado Always|Sempre Are you sure you want to delete '%1' ?|Você tem certeza que você quer apagar '%1' ? Are you sure you want to format '%1' ?|Você tem certeza que você quer formatar '%1' ? Asia (NTSC)|Ãsia (NTSC) Asia (PAL)|Ãsia (PAL) Auto-detect|Auto-detectar Autostart|Auto-iniciar Awaiting input for|Esperando a entrada de dados para Axis|Axis Backtrace|Backtrace Backup Ram Manager|Gerenciador da Ram de Backup Binary Files (*.bin)|Arquivos Binário (*.bin) Bios|Bios Block Size :|Tamanho dos Blocos : Browse|Explorar Byte|Byte Byte Write|Gravação do Byte CD Images (*.iso *.cue *.bin)|Imagens de CD (*.iso *.cue *.bin) Cancel|Cancelar Cannot initialize|Não consegue inicializar Cannot initialize Windows SPTI Driver|Não consegue inicializar o Driver SPTI do Windows Cart/Memory|Cartucho/Memória Cartridge|Cartucho Cd-Rom|Cd-Rom Central/South America (NTSC)|América Central/do Sul (NTSC) Central/South America (PAL)|América Central/do Sul (PAL) Cheat &Search...|Busca de &Trapaças... Cheat Search|Busca de Trapaças Cheat Type|Tipo de Trapaça Cheats|Trapaças Cheats File|Arquivo das Trapaças Choose a binary file|Escolha um arquivo binário Choose a cartridge file|Escolha um arquivo de cartucho Choose a cdrom drive/mount point|Escolha um drive de cdrom/ponto de montagem Choose a cheat file to open|Escolha um arquivo de trapaça para abrir Choose a cheat file to save to|Escolha um arquivo de trapaça para salvar Choose a file to save your state|Escolha um arquivo para salvar seu estado Choose a location for binary file|Escolha um local pro arquivo binário Choose a location for your screenshot|Escolha um local para seu screenshot Choose a memory file|Escolha um arquivo de memória Choose a mpeg rom|Escolha uma rom mpeg Clear configuration|Limpar configuração Close|Fechar Code|Código Code Breakpoints|Breakpoints do Código Comment :|Comentário : Common Control Registers|Registros de Controle Comum Compare Type|Tipo de Comparação DSP Control Registers|Registros do Controle do DSP DSP Registers|Registros do DSP Data Size|Tamanho dos Dados Data Size :|Tamanho dos Dados : Data Type|Tipo de Dados Debug CPU|Fazer Debug da CPU Debug M68K|Fazer Debug do M68K Debug Master SH2|Fazer Debug do SH2 Mestre Debug SCU DSP|Fazer Debug do SCU DSP Debug Slave SH2|Fazer Debug do SH2 Escravo Debug VDP1|Fazer Debug do VDP1 Debug VDP2Viewer|Fazer Debug do VDP2Viewer Del|Apagar Delete|Apagar Description|Descrição Description :|Descrição : Device List|Lista de Dispositivos DirectX Input Interface|Interface dos Controles do DirectX DirectX Sound Interface|Interface de Som do DirectX Disabled|Desativado Disassembled Code|Código Convertido pra Assembler Display Enabled|Exibidor Ativado Display on Hover|Exibir ao Pairar Sobre Down|Pra baixo Download|Baixar Draw End|Fim do Desenho Dummy CD Drive|Imitação do Drive de CD Dummy Input Interface|Imitação da Interface de Entrada de Dados Dummy OSD Interface|Imitação da Interface OSD Dummy Sound Interface|Imitação da Interface de Som Dummy Video Interface|Imitação da Interface de Vídeo Edit configuration|Editar configuração Emu-Compatibility|Compatibilidade com o Emu Emulation|Emulação Enable|Ativar Enable Frame Skip/Limiter|Ativar o Frame Skip/Limitador Enabled|Ativado End Address:|Endereço Final: English|Inglês Enter New Value|Inserir Novo Valor Europe + others (PAL)|Europa + outros (PAL) Exact|Exato FPS|FPS File|Arquivo File Name :|Nome do Arquivo : File transfer|Transferência de arquivo File:|Arquivo: Format|Formato Frame Skip/Limiter|Frame Skip/Limitador French|Francês From|De From File|Do Arquivo From File...|Do Arquivo... Fullscreen|Tela cheia Fullscreen Resolution|Resolução da Tela Cheia General|Geral General Info|Informação Geral German|Alemão Glut OSD Interface|Interface OSD do Glut Goto Address|Endereço Goto Greater then|Maior do que Hard Reset|Reset Rígido Height|Altura Hex value(s)|Valor(es) hex Hide Menubar|Esconder a Barra do Menu Hide Toolbar|Esconder a Barra de Ferramentas ISO-File Virtual Drive|Drive Virtual do Arquivo-ISO Information...|Informação... Input|Controles Invalid Address|Endereço Inválido Invalid Value|Valor Inválido Italian|Italiano Japan (NTSC)|Japão (NTSC) Japanese|Japonês Japanese Modem|Modem Japonês Korea (NTSC)|Coréia (NTSC) L&oad State|C&arregar o State Language :|Idioma : Layer|Camada Left|Esquerda Left trigger|Gatilho esquerdo Less than|Menor do que Linux CD Drive|Drive de CD do Linux Load|Carregar Load State|Carregar State Load State As|Carregar State Como Load as executable|Carregar como executável Local Coordinates|Coordenadas Locais Log|Log Long|Comprimento Long Write|Gravação Longa Loop Track Clear|Limpar o Rastreamento do Loop Loop Track Start|Iniciar o Rastreamento do Loop M68K|M68K M68K Registers|Registros do M68K MSH2|MSH2 Master SH2|SH2 Mestre Memory|Memória Memory &Editor|Editor de &Memória Memory Breakpoints|Breakpoints da Memória Memory Dump|Dump da Memória Memory Editor|Editor de Memória Memory Search|Busca pela Memória Memory Transfer|Transferência de Memória Memory dump|Dump da Memória Middle|No meio Mouse|Mouse Mouse Configuration|Configuração do Mouse Mpeg ROM|ROM Mpeg NBG0|NBG0 NBG0/RBG1 Info|Informação do NBG0/RBG1 NBG1|NBG1 NBG1 Info|Informação do NBG1 NBG2|NBG2 NBG2 Info|Informação do NBG2 NBG3|NBG3 NBG3 Info|Informação do NBG3 Netlink|Netlink Never|Nunca None|Nenhum Normal Sprite|Sprite Normal North America (NTSC)|América do Norte (NTSC) OSD Core|Núcleo do OSD Ok|Ok On fullscreen|Na tela cheia On message|Na mensagem Open &CD Rom...|Abrir &CD Rom... Open &ISO...|Abrir &ISO... Open CD Rom|Abrir o CD Rom OpenGL Video Interface|Interface de Vídeo do OpenGL Options|Opções Other Debug|Outro Debug Pad|Pad Pad Configuration|Configuração do Pad Pause|Pausar Press Esc key to cancel|Pressione a tecla Esc pra cancelar Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Interface da Entrada de Dados do Teclado do Qt Quit|Sair R&un|E&xecutar RBG0|RBG0 RBG0 Info|Informação do RBG0 Read|Ler Region|Região Registers|Registros Reset|Resetar Resolution|Resolução Right|Direita Right trigger|Gatilho direito Run|Executar S&ave State|S&ave State SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|Interface SDL do Joystick SDL Sound Interface|Interface de Som SDL SH2 Debugger Interpreter|Interpretador do Debugger SH2 SH2 Dynamic Recompiler|Recompilador Dinâmico do SH2 SH2 Interpreter|Interpretador SH2 SH2 Registers|Registros do SH2 SSH2|SSH2 Save|Salvar Save As WAV|Salvar Como WAV Save Bitmap|Salvar Bitmap Save Information|Informação dos Saves Save List|Lista dos Saves Save MD0|Salvar MD0 Save MD1|Salvar MD1 Save MD2|Salvar MD2 Save MD3|Salvar MD3 Save Program|Salvar Programa Save Selected|Salvar os Selecionados Save Slot Registers|Registros do Slot do Save Save State|Salvar o State Save State As|Salvar o State Como Save States|Salvar os States Sc&reenshot|Sc&reenshot Screen Enabled|Tela Ativada Screenshot|Screenshot Search|Busca Search Criteria|Critério da Busca Search Memory|Procurar na Memória Search Value:|Procurar Valor: Search/Add Cheats|Procurar/Adicionar Trapaças Select a file to load your state|Selecione um arquivo para carregar seu state Select your iso/cue/bin file|Selecione seu arquivo iso/cue/bin Set PC to Start Address|Configurar o PC pra Iniciar no Endereço Settings|Configurações Shortcuts|Atalhos Show FPS|Mostrar FPS Show Log Window|Mostrar a Janela do Log Signed|Assinado Signed 16-bit value|Valor de 16-bits assinado Signed 32-bit value|Valor de 32-bits assinado Signed 8-bit value|Valor de 8-bits assinado Slave SH2|SH2 Escravo Slot Info|Informação do Slot Slot Number:|Número do Slot: Software OSD Interface|Interface OSD do Software Software Video Interface|Interface de Vídeo do Software Sound|Som Sound Core|Núcleo do Som Spanish|Espanhol Start|Iniciar Start Address:|Endereço Inicial: Start in Fullscreen|Iniciar em Tela Cheia Status|Status Step Into|Entrar Step Out|Sair Step Over|Atravessar Store|Armazenar Synchronize Saturn internal clock with set time|Sincronizar o relógio interno do Saturno com o tempo definido System Clipping Coordinates|Coordenadas de Corte do Sistema Text|Texto Texture|Textura To|Para To File...|Do Arquivo... Tools|Ferramentas Track Inf Loop Results|Rastrear Resultados do Loop do Inf Transfer|Transferência Translation|Tradução Type:|Tipo: Unable to add code|Incapaz de adicionar o código Unable to change description|Incapaz de mudar a descrição Unable to open file for loading|Incapaz de abrir o arquivo para carregar Unable to open file for saving|Incapaz de abrir o arquivo para salvar Unable to remove code|Incapaz de remover o código Unknow (%1)|(%1) Desconhecido Unsigned|Não Assinado Unsigned 16-bit value|Valor de 16-bits não assinado Unsigned 32-bit value|Valor de 32-bits não assinado Unsigned 8-bit value|Valor de 8-bits não assinado Up|Para cima Upload|Upload Use System Locale|Usar o Idioma do Sistema VDP1|VDP1 VDP1 Command Info|Informação do Comando do VDP1 VDP1 Command List|Lista de Comandos do VDP1 VDP2|VDP2 Value|Valor Value :|Valor : Value:|Valor: Vdp1|VDP1 Vdp2|VDP2 Video|Vídeo Video Core|Núcleo do Vídeo Video Driver|Driver do Vídeo Video Format|Formato do Vídeo View|Visualizar Viewer|Visualizador Waiting Input...|Esperando a Entrada dos Dados... Width|Largura Window Resolution|Resolução da Janela Windows SPTI Driver|Driver SPTI do Windows Word|Palavra Word Write|Gravação das Palavras Write|Gravar Yabause Cheat Files (*.yct);;All Files (*)|Arquivos das Trapaças do Yabause (*.yct);;Todos os Arquivos (*) Yabause Qt GUI|GUI Qt do Yabause Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Gui Qt do Yabause
Baseada no Yabause %1
http://yabause.org
O Time do Yabause
Filipe AZEVEDO Yabause Save State (*.yss)|Save State do Yabause (*.yss) Yabause is not initialized, can't manage backup ram.|O Yabause não está inicializado, não pode gerenciar a ram do backup. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Barra de Ferramentas yabause-0.9.13.1/l10n/yabause_sv.yts000644 001750 001750 00000024667 12256006204 021076 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|%1 Avbilder (*.%2) %1/%2 blocks free|%1/%2 block fria &About...|&Om... &Action Replay|&Action Replay &Backup Manager...|&Backup-hantering... &Browse|&Bläddra &Cancel|&Avbryt &Capture Screen|&Skärmdump &Cheat List|&Lista över fusk &Cheats|&Fusk &Cheats List...|&Fusklista... &Clear|&Rensa &Close|St&äng &Debug|%Felsök &Delete|Ta bo&rt &Emulation|&Emulering &File|&Fil &Frame Skip/Limiter|&Frameskip/-begränsning &Fullscreen|&Helskärm &Help|&Hjälp &Layer|&Lager &Load From File|Ladda från fil &Log|&Logg &Master SH2|&Master SH2 &Memory Transfer|&Minnesöverföring &OK|&OK &Pause|&Paus &Quit|&Avsluta &Raw Memory Address|&Rå minnesadress &Reset|&Reset &Save To File|&Spara till fil &Settings...|&Inställningar... &Slave SH2|&Slav SH2 &Tools|Verk&tyg &Transfer|Ö&verföring &View|&Visa 16 Mbit Backup Ram|16 Mbit Backup Ram 16 Mbit ROM|16 Mbit ROM 16-bit|16-bit 16-bit Relative value(s)|16-bit Relative value(s) 32 Mbit Backup Ram|32 Mbit Backup Ram 32 Mbit Dram|32 Mbit Dram 32-bit|32-bit 3D Control Pad|3D Control Pad 4 Mbit Backup Ram|4 Mbit Backup Ram 503/512 blocks free|503/512 block fria 510/512 blocks free|510/512 block fria 8 Mbit Backup Ram|8 Mbit Backup Ram 8 Mbit Dram|8 Mbit Dram 8-bit|8-bit 8-bit Relative value(s)|8-bit Relative value(s) WARNING: Master Codes are NOT supported.|VARNING: Master Codes stöds INTE About...|Om... Action Replay Code :|Action Replay-kod Add|Add Add Action Replay Code|Lägg till en Action Replay-kod Add Cheat|Lägg till fusk Add Codes...|Lägg till kod... Add Raw Memory Code|Lägg till råminneskod Address|Adress Address :|Adress : Advanced|Avancerat Always|Alltid Are you sure you want to delete '%1' ?|Är du säker på att du vill ta bort '%1' ? Are you sure you want to format '%1' ?|Är du säker på att du vill formatera '%1' ? Asia (NTSC)|Asien (NTSC) Asia (PAL)|Asien (PAL) Auto-detect|Auto-detect Autostart|Autostart Backtrace|Backtrace Backup Ram Manager|Backup Ram Manager Binary Files (*.bin)|Binary Files (*.bin) Bios|Bios Block Size :|Blockstorlek Browse|Bläddra Byte|Byte Byte Write|Byteskrivning CD Images (*.iso *.cue *.bin)|CD-avbild (*.iso *.cue *.bin) Cancel|Avbryt Cannot initialize|Kan inte initiera Cannot initialize Windows SPTI Driver|Kan inte initiera Windows SPTI-drivrutin Cart/Memory|Kassett/Minne Cartridge|Kassett Cd-Rom|CD-ROM Central/South America (NTSC)|Central-/Sydamerika (NTSC) Central/South America (PAL)|Central-/Sydamerika (PAL) Cheat &Search...|&Sök fusk... Cheat Search|Sök efter fusk Cheat Type|Typ av fusk Cheats|Fusk Cheats File|Fuskfil Choose a binary file|Choose a binary file Choose a cdrom drive/mount point|Välj en CD-läsare eller en monteringspunkt Choose a cheat file to open|Välj fuskfil att öppna Choose a cheat file to save to|Välj fuskfil att spara till Choose a file to save your state|Välj en fil att spara ditt läge till Choose a location for binary file|Choose a location for binary file Choose a location for your screenshot|Välj plats att spara skärmdump till Clear configuration|Rensa inställningar Close|Stäng Code|Kod Code Breakpoints|Code Breakpoints Comment :|Kommentar : Common Control Registers|Common Control Registers Compare Type|Jämför typ DSP Control Registers|DSP Control Registers DSP Registers|DSP Registers Data Size|Datastorlek Data Size :|Datastorlek : Data Type|Datatyp Debug CPU|Debug CPU Debug M68K|Debug M68K Debug Master SH2|Debug Master SH2 Debug SCU DSP|Debug SCU DSP Debug Slave SH2|Debug Slave SH2 Debug VDP1|Debug VDP1 Debug VDP2Viewer|Debug VDP2Viewer Del|Del Delete|Ta bort Description|Beskrivning Description :|Beskrivning : Device List|Lista över enheter DirectX Input Interface|DirectX Input Interface DirectX Sound Interface|DirectX Sound Interface Disabled|Avaktiverad Disassembled Code|Disassembled Code Display Enabled|Display Enabled Display on Hover|Display on Hover Down|Ner Download|Nedladdning Draw End|Draw End Dummy CD Drive|Provisorisk CD-enhet Dummy Input Interface|Provisoriskt indatagränssnitt Dummy OSD Interface|Provisoriskt OSD-gränssnitt Dummy Sound Interface|Provisoriskt ljudgränssnitt Dummy Video Interface|Provisoriskt videogränssnitt Edit configuration|Ändra inställningar Emu-Compatibility|Emu-kompatibilitet Emulation|Emulering Enable|Aktivera Enable Frame Skip/Limiter|Aktivera frameskip/-begränsning Enabled|Aktiverad End Address:|Slutadress English|Engelska Enter New Value|Enter New Value Europe + others (PAL)|Europa + övriga (PAL) Exact|Exakt FPS|FPS File|Fil File Name :|Filnamn : File transfer|Filöverföring File:|Fil: Format|Formatera Frame Skip/Limiter|Frame Skip/Limiter French|Franska From|Från From File|Från fil From File...|Från fil... Fullscreen|Helskärm Fullscreen Resolution|Fullscreen Resolution General|Allmänt General Info|General Info German|Tyska Glut OSD Interface|Glut OSD-gränssnitt Goto Address|Goto Address Greater then|Större än Hard Reset|Hård omstart Height|Höjd Hex value(s)|Hex value(s) Hide Menubar|Hide Menubar Hide Toolbar|Hide Toolbar ISO-File Virtual Drive|ISO-fil virtuell enhet Information...|Information... Input|Kontroller Invalid Address|Ogiltig adress Invalid Value|Ogiltigt värde Italian|Italienska Japan (NTSC)|Japan (NTSC) Japanese|Japanska Japanese Modem|Japanese Modem Korea (NTSC)|Korea (NTSC) L&oad State|L&adda tillstånd Language :|Språk : Layer|Lager Left|Vänster Left trigger|Vänster avtryckare Less than|Mindre än Linux CD Drive|Linux CD-enhet Load|Ladda Load State|Ladda läge Load State As|Ladda läge som Load as executable|Ladda som exekverbar Local Coordinates|Local Coordinates Log|Logg Long|Long Long Write|Lång skrivning Loop Track Clear|Loop Track Clear Loop Track Start|Loop Track Start M68K|M68K M68K Registers|M68K Registers MSH2|MSH2 Master SH2|Master SH2 Memory|Minne Memory &Editor|Minnes&editor Memory Breakpoints|Memory Breakpoints Memory Dump|Minnesdump Memory Editor|Minnesredigerare Memory Search|Memory Search Memory Transfer|Minnesöverföring Memory dump|Minnesdump Mouse|Mus Mpeg ROM|MPEG ROM NBG0|NBG0 NBG0/RBG1 Info|NBG0/RBG1 Info NBG1|NBG1 NBG1 Info|NBG1 Info NBG2|NBG2 NBG2 Info|NBG2 Info NBG3|NBG3 NBG3 Info|NBG3 Info Netlink|Netlink Never|Aldrig None|Ingen Normal Sprite|Normal Sprite North America (NTSC)|Nordamerika (NTSC) OSD Core|OSD-kärna Ok|OK On fullscreen|Helskärm On message|On message Open &CD Rom...|Öppna &CD-ROM... Open &ISO...|Öppna &ISO... Open CD Rom|Öppna CD-ROM OpenGL Video Interface|OpenGL-videogränssnitt Options|Options Other Debug|Other Debug Pad|Kontroll Pad Configuration|Kontrollinställningar Pause|Paus Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Qt tangentbordsindatagränssnitt Quit|Avsluta R&un|&Kör RBG0|RBG0 RBG0 Info|RBG0 Info Read|Read Region|Region Registers|Registers Reset|Reset Resolution|Upplösning Right|Höger Right trigger|Höger avtryckare Run|Kör S&ave State|Sp&ara tillstånd SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|SDL joystick-gränssnitt SDL Sound Interface|SDL ljudgränssnitt SH2 Debugger Interpreter|SH2 felsökningstolk SH2 Dynamic Recompiler|SH2 dynamisk omkompilering SH2 Interpreter|SH2-tolkare SH2 Registers|SH2 Registers SSH2|SSH2 Save|Spara Save As WAV|Save As WAV Save Bitmap|Save Bitmap Save Information|Spara information Save List|Spara lista Save MD0|Save MD0 Save MD1|Save MD1 Save MD2|Save MD2 Save MD3|Save MD3 Save Program|Save Program Save Selected|Save Selected Save Slot Registers|Save Slot Registers Save State|Spara läge Save State As|Spara läge som Save States|Spara lägen Sc&reenshot|Skä&rmbild Screen Enabled|Screen Enabled Screenshot|Skärmdump Search|Sök Search Criteria|Search Criteria Search Memory|Search Memory Search Value:|Sök värde: Search/Add Cheats|Sök/Lägg till fusk Select a file to load your state|Välj fil för att ladda ditt läge Select your iso/cue/bin file|Välj din iso/cue/bin-fil Set PC to Start Address|Sätt PC till startadress Settings|Inställningar Shortcuts|Shortcuts Show FPS|Visa FPS Show Log Window|Show Log Window Signed|Signerad Signed 16-bit value|Signed 16-bit value Signed 32-bit value|Signed 32-bit value Signed 8-bit value|Signed 8-bit value Slave SH2|Slav SH2 Slot Info|Slot Info Slot Number:|Slot Number: Software OSD Interface|Mjukvaru OSD-gränssnitt Software Video Interface|Mjukvaruvideogränssnitt Sound|Ljud Sound Core|Ljudgränssnitt Spanish|Spanska Start|Start Start Address:|Startadress Start in Fullscreen|Start in Fullscreen Status|Status Step Into|Step Into Step Out|Step Out Step Over|Step Over Store|Lagra Synchronize Saturn internal clock with set time|Synchronize Saturn internal clock with set time System Clipping Coordinates|System Clipping Coordinates Text|Text Texture|Texture To|Till To File...|Till fil... Tools|Verktyg Track Inf Loop Results|Track Inf Loop Results Transfer|Överföring Translation|Översättning Type:|Type: Unable to add code|Kan inte lägga till kod Unable to change description|Kan inte ändra beskrivning Unable to open file for loading|Kan inte öppna fil för att ladda Unable to open file for saving|Kan inte öppna fil för att spara Unable to remove code|Kan inte ta bort kod Unknow (%1)|Okänt (%1) Unsigned|Osignerad Unsigned 16-bit value|Unsigned 16-bit value Unsigned 32-bit value|Unsigned 32-bit value Unsigned 8-bit value|Unsigned 8-bit value Up|Upp Upload|Uppladdning VDP1|VDP1 VDP1 Command Info|VDP1 Command Info VDP1 Command List|VDP1 Command List VDP2|VDP2 Value|Värde Value :|Värde : Value:|Value: Vdp1|Vdp1 Vdp2|Vdp2 Video|Video Video Core|Videogränssnitt Video Driver|Videodrivrutin Video Format|Videoformat View|Visa Viewer|Viewer Waiting Input...|Väntar på input... Width|Bredd Window Resolution|Window Resolution Windows SPTI Driver|Windows SPTI-drivrutin Word|Word Word Write|Ordskrivning Write|Write Yabause Cheat Files (*.yct);;All Files (*)|Yabause fuskfil (*.yct);;Alla filer (*) Yabause Qt GUI|Yabause Qt GUI Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt-gränssnitt
Baserat op Yabause %1
http://yabause.org
Yabause-gruppen
Filipe AZEVEDO Yabause Save State (*.yss)|Yabause Sparat Läge (*.yss) Yabause is not initialized, can't manage backup ram.|Yabause är inte initierat, kan inte hantera backup-RAM. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Verktygsfält yabause-0.9.13.1/l10n/yabause_de.yts000644 001750 001750 00000025601 12256006204 021023 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|%1 Bilder (*.%2) %1/%2 blocks free|%1/%2 Blöcke frei &About...|&Über... &Action Replay|&Action Replay &Backup Manager...|&Backup Manager... &Browse|&Suchen &Cancel|&Abbrechen &Capture Screen|&Bildschirm fotografieren &Cheat List|&Cheatliste &Cheats|&Cheats &Cheats List...|&Cheatliste... &Clear|&Leeren &Close|&Schliessen &Debug|&Debug &Delete|&Löschen &Emulation|&Emulation &File|&Datei &Frame Skip/Limiter|&Frames Überspringen/Limitieren &Fullscreen|&Vollbild &Help|&Hilfe &Layer|&Schicht &Load From File|&Von Datei laden &Log|&Protokoll &Master SH2|&Master SH2 &Memory Transfer|&Speichertransfer &OK|&OK &Pause|&Pause &Quit|&Schliessen &Raw Memory Address|&Raw Speicheradresse &Reset|&Zurücksetzen &Save To File|&In Datei speichern &Settings...|Einstellungen... &Slave SH2|&Slave SH2 &Tools|&Werkzeuge &Transfer|&Transferieren &View|&Ansicht 16 Mbit Backup Ram|16 Mbit Backup Ram 16 Mbit ROM|16 Mbit ROM 16-bit|16-bit 16-bit Relative value(s)|16-bit Relative value(s) 32 Mbit Backup Ram|32 Mbit Backup Ram 32 Mbit Dram|32 Mbit Dram 32-bit|32-bit 3D Control Pad|3D Control Pad 4 Mbit Backup Ram|4 Mbit Backup Ram 503/512 blocks free|503/512 Blöcke frei 510/512 blocks free|510/512 Blöcke frei 8 Mbit Backup Ram|8 Mbit Backup Ram 8 Mbit Dram|8 Mbit Dram 8-bit|8-bit 8-bit Relative value(s)|8-bit Relative value(s) WARNING: Master Codes are NOT supported.|WARNUNG:Mastercodes werden NICHT unterstützt. About...|Über... Action Replay Code :|Action Replay Code : Add|Add Add Action Replay Code|Action Replay Code hinzufügen Add Cheat|Cheat hinzufügen Add Codes...|Codes hinzufügen... Add Raw Memory Code|Raw Speicher Code hinzufügen Address|Adresse Address :|Adresse : Advanced|Erweitert Always|Immer Are you sure you want to delete '%1' ?|Sind Sie sicher, dass Sie '%1' löschen möchten ? Are you sure you want to format '%1' ?|Sind Sie sicher, dass Sie '%1' formatieren möchten ? Asia (NTSC)|Asien (NTSC) Asia (PAL)|Asien (PAL) Auto-detect|Automatische Erkennung Autostart|Autostart Backtrace|Backtrace Backup Ram Manager|Backup Ram Manager Binary Files (*.bin)|Binary Files (*.bin) Bios|Bios Block Size :|Blockgröße : Browse|Suchen Byte|Byte Byte Write|Byte schreiben CD Images (*.iso *.cue *.bin)|CD Images (*.iso *.cue *.bin) Cancel|Abbrechen Cannot initialize|Initialisierung fehlgeschlagen Cannot initialize Windows SPTI Driver|Windows SPTI Treiber kann nicht initialisiert werden Cart/Memory|Cartridge/Speicher Cartridge|Cartridge Cd-Rom|CD-Rom Central/South America (NTSC)|Zentral/Südamerika (NTSC) Central/South America (PAL)|Zentral/Südamerika (PAL) Cheat &Search...|Cheat &Suche Cheat Search|Cheat Suche Cheat Type|Cheattyp Cheats|Cheats Cheats File|Cheatdatei Choose a binary file|Choose a binary file Choose a cdrom drive/mount point|CD-Rom Laufwerk/Mount-Punkt auswählen Choose a cheat file to open|Cheatdatei zum öffnen auswählen Choose a cheat file to save to|Cheatdatei zum Speichern auswählen Choose a file to save your state|Datei zum Speichern des Spielstandes auswählen Choose a location for binary file|Choose a location for binary file Choose a location for your screenshot|Speicherort für das Bildschirmfoto auswählen Clear configuration|Konfiguration leeren Close|Schliessen Code|Code Code Breakpoints|Code Breakpoints Comment :|Kommentar : Common Control Registers|Common Control Registers Compare Type|Vergleiche Typen DSP Control Registers|DSP Control Registers DSP Registers|DSP Registers Data Size|Datengrösse Data Size :|Datengrösse : Data Type|Dateityp Debug CPU|Debug CPU Debug M68K|Debug M68K Debug Master SH2|Debug Master SH2 Debug SCU DSP|Debug SCU DSP Debug Slave SH2|Debug Slave SH2 Debug VDP1|Debug VDP1 Debug VDP2Viewer|Debug VDP2Viewer Del|Del Delete|Löschen Description|Beschreibung Description :|Beschreibung : Device List|Geräteliste DirectX Input Interface|DirectX Input Interface DirectX Sound Interface|DirectX Sound Interface Disabled|Deaktiviert Disassembled Code|Disassembled Code Display Enabled|Display Enabled Display on Hover|Display on Hover Down|Runter Download|Herunterladen Draw End|Draw End Dummy CD Drive|Dummy CD-Rom Laufwerk Dummy Input Interface|Dummy Eingabe Oberfläche Dummy OSD Interface|Dummy OSD Oberfläche Dummy Sound Interface|Dummy Audio Oberfläche Dummy Video Interface|Dummy Video Oberfläche Edit configuration|Einstellungen bearbeiten Emu-Compatibility|Emu-Kompatibilität Emulation|Emulation Enable|Aktivieren Enable Frame Skip/Limiter|Aktiviere Frame Überspringer/Limitierer Enabled|Aktiviert End Address:|Adressende: English|Englisch Enter New Value|Enter New Value Europe + others (PAL)|Europe + andere (PAL) Exact|Entpacken FPS|BPS File|Datei File Name :|Dateiname : File transfer|Dateiübertragung File:|Datei: Format|Formatieren Frame Skip/Limiter|Frames Überspringen/Limitieren French|Französisch From|Von From File|Von Datei From File...|Von Datei... Fullscreen|Vollbild Fullscreen Resolution|Fullscreen Resolution General|Allgemein General Info|General Info German|Deutsch Glut OSD Interface|Glut OSD Oberfläche Goto Address|Goto Address Greater then|Größer als Hard Reset|Harter Neustart Height|Höhe Hex value(s)|Hex value(s) Hide Menubar|Hide Menubar Hide Toolbar|Hide Toolbar ISO-File Virtual Drive|ISO-Datei Virtuelles Laufwerk Information...|Informationen... Input|Eingabe Invalid Address|Ungültige Adresse Invalid Value|Ungültiger Wert Italian|Italienisch Japan (NTSC)|Japan (NTSC) Japanese|Japanisch Japanese Modem|Japanese Modem Korea (NTSC)|Korea (NTSC) L&oad State|L&ade Spielstand Language :|Sprache : Layer|Schicht Left|Links Left trigger|Linke Schultertaste Less than|Weniger als Linux CD Drive|Linux CD Laufwerk Load|Laden Load State|Spielstand laden Load State As|Spielstand laden als Load as executable|Als ausführbar laden Local Coordinates|Local Coordinates Log|Protokoll Long|Long Long Write|Langes Schreiben Loop Track Clear|Loop Track Clear Loop Track Start|Loop Track Start M68K|M68K M68K Registers|M68K Registers MSH2|MSH2 Master SH2|Master SH2 Memory|Speicher Memory &Editor|Speicher&editierer Memory Breakpoints|Memory Breakpoints Memory Dump|Speicherabzug Memory Editor|Speichereditierer Memory Search|Memory Search Memory Transfer|Speichertransfer Memory dump|Speicherabzug Mouse|Maus Mpeg ROM|Mpeg ROM NBG0|NBG0 NBG0/RBG1 Info|NBG0/RBG1 Info NBG1|NBG1 NBG1 Info|NBG1 Info NBG2|NBG2 NBG2 Info|NBG2 Info NBG3|NBG3 NBG3 Info|NBG3 Info Netlink|Netlink Never|Niemals None|Keine Normal Sprite|Normal Sprite North America (NTSC)|Nordamerika (NTSC) OSD Core|OSD Kern Ok|Ok On fullscreen|Am Vollbild On message|On message Open &CD Rom...|&CD-Rom öffnen... Open &ISO...|&ISO öffnen Open CD Rom|CD-Rom öffnen OpenGL Video Interface|OpenGL Video Oberfläche Options|Options Other Debug|Other Debug Pad|Pad Pad Configuration|Pad Konfiguration Pause|Pause Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Qt Tastatureingabenoberfäche Quit|Schliessen R&un|S&tarten RBG0|RBG0 RBG0 Info|RBG0 Info Read|Read Region|Region Registers|Registers Reset|Zurücksetzen Resolution|Auflösung Right|Rechts Right trigger|Rechte Schultertaste Run|Starten S&ave State|Spielstand s&peichern SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|SDL Joystick Oberfläche SDL Sound Interface|SDL Geräusch Oberfläche SH2 Debugger Interpreter|SH2 Debugger Interpreter SH2 Dynamic Recompiler|SH2 Dynamischer Rekompilierer SH2 Interpreter|SH2 Interpreter SH2 Registers|SH2 Registers SSH2|SSH2 Save|Speichern Save As WAV|Save As WAV Save Bitmap|Save Bitmap Save Information|Informationen speichern Save List|Liste speichern Save MD0|Save MD0 Save MD1|Save MD1 Save MD2|Save MD2 Save MD3|Save MD3 Save Program|Save Program Save Selected|Save Selected Save Slot Registers|Save Slot Registers Save State|Spielstand speichern Save State As|Spielstand speichern als Save States|Spielstände Sc&reenshot|Bi&ldschirmfoto Screen Enabled|Screen Enabled Screenshot|Bildschirmfoto Search|Suche Search Criteria|Search Criteria Search Memory|Search Memory Search Value:|Suchwert: Search/Add Cheats|Suche/Cheats hinzufügen Select a file to load your state|Wählen Sie eine Datei zum Laden des Spielstandes aus Select your iso/cue/bin file|Wählen Sie Ihre iso/cue/bin Datei Set PC to Start Address|PC zur Startadresse setzen Settings|Einstellungen Shortcuts|Shortcuts Show FPS|FPS anzeigen Show Log Window|Show Log Window Signed|Signiert Signed 16-bit value|Signed 16-bit value Signed 32-bit value|Signed 32-bit value Signed 8-bit value|Signed 8-bit value Slave SH2|Slave SH2 Slot Info|Slot Info Slot Number:|Slot Number: Software OSD Interface|Software OSD Oberfläche Software Video Interface|Software Video Oberfläche Sound|Sound Sound Core|Sound Kern Spanish|Spanisch Start|Start Start Address:|Adressenanfang: Start in Fullscreen|Start in Fullscreen Status|Status Step Into|Step Into Step Out|Step Out Step Over|Step Over Store|Sichern Synchronize Saturn internal clock with set time|Synchronize Saturn internal clock with set time System Clipping Coordinates|System Clipping Coordinates Text|Text Texture|Texture To|Zu To File...|Zur Datei... Tools|Werkzeuge Track Inf Loop Results|Track Inf Loop Results Transfer|Transferieren Translation|Übersetzung Type:|Type: Unable to add code|Code konnte nicht zugefügt werden Unable to change description|Beschreibung konnte nicht geändert werden Unable to open file for loading|Datei konnte nicht geöffnet werden Unable to open file for saving|Datei konnte nicht geschrieben werden Unable to remove code|Code konnte nicht entfernt werden Unknow (%1)|Unbekannt (%1) Unsigned|Unsigniert Unsigned 16-bit value|Unsigned 16-bit value Unsigned 32-bit value|Unsigned 32-bit value Unsigned 8-bit value|Unsigned 8-bit value Up|Hoch Upload|Hochladen VDP1|VDP1 VDP1 Command Info|VDP1 Command Info VDP1 Command List|VDP1 Command List VDP2|VDP2 Value|Wert Value :|Wert : Value:|Value: Vdp1|Vdp1 Vdp2|Vdp2 Video|Video Video Core|Video Kern Video Driver|Video Treiber Video Format|Video Format View|Ansicht Viewer|Viewer Waiting Input...|Warte auf Eingabe... Width|Breite Window Resolution|Window Resolution Windows SPTI Driver|Windows SPTI Treiber Word|Word Word Write|Word Schreiben Write|Write Yabause Cheat Files (*.yct);;All Files (*)|Yabause Cheat Dateien (*ycf);;Alle Dateien (*) Yabause Qt GUI|Yabause Qt Grafikoberfäche Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Grafikoberfäche
Basierend auf Yabause %1
http://yabause.org
Das Yabause Team
Filipe AZEVEDO Yabause Save State (*.yss)|Yabause Speicherstände (*.yss) Yabause is not initialized, can't manage backup ram.|Yabause ist nicht initialisiert, Backup Ram kann nicht verwaltet werden. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=de http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Werkzeugleiste yabause-0.9.13.1/l10n/yabause_lt.yts000644 001750 001750 00000025411 12256006204 021051 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|%1 Images (*.%2) %1/%2 blocks free|%1/%2 blokų laisvų &About...|&Apie... &Action Replay|&Veiksmo pakartojimas &Backup Manager...|&Backup Manager... &Browse|&NarÅ¡yti &Cancel|&Cancel &Capture Screen|&Nufotografuoti ekranÄ… &Cheat List|&Kodų sÄ…raÅ¡as &Cheats|&Kodai &Cheats List...|&Cheats List... &Clear|&IÅ¡valyti &Close|&Close &Debug|&Klaidų taisymas &Delete|&Trinti &Emulation|&Emulation &File|&Failas &Frame Skip/Limiter|&Frame Skip/Limiter &Fullscreen|&Pilnas ekranas &Help|&Pagalba &Layer|&Sluoksniai &Load From File|&Ä®kelti iÅ¡ failo &Log|&Log &Master SH2|&Master SH2 &Memory Transfer|&Atminties pervedimas &OK|&Gerai &Pause|&PauzÄ— &Quit|&IÅ¡eiti &Raw Memory Address|&PriÄ—jimas prie Raw atminties &Reset|&Perkrauti &Save To File|&IÅ¡saugoti į failÄ… &Settings...|&Nuostatos &Slave SH2|&Slave SH2 &Tools|&Tools &Transfer|&Transfer &View|&ŽiÅ«rÄ—ti 16 Mbit Backup Ram|16 Mbit Backup Ram 16 Mbit ROM|16 Mbit ROM 16-bit|16-bit 16-bit Relative value(s)|16-bit Relative value(s) 32 Mbit Backup Ram|32 Mbit Backup Ram 32 Mbit Dram|32 Mbit Dram 32-bit|32-bit 3D Control Pad|3D Control Pad 4 Mbit Backup Ram|4 Mbit Backup Ram 503/512 blocks free|503/512 blokų laisvų 510/512 blocks free|510/512 blokų laisvų 8 Mbit Backup Ram|8 Mbit Backup Ram 8 Mbit Dram|8 Mbit Dram 8-bit|8-bit 8-bit Relative value(s)|8-bit Relative value(s) WARNING: Master Codes are NOT supported.|WARNING: Master Codes are NOT supported. About...|Apie... Action Replay Code :|Veiksmo pakartojimo kodas: Add|Add Add Action Replay Code|PridÄ—ti veiksmo pakartojimo kodÄ…: Add Cheat|Add Cheat Add Codes...|PridÄ—ti kodų... Add Raw Memory Code|PridÄ—ti "Raw" atminties kodÄ… Address|Address Address :|Adresas : Advanced|Papildoma Always|Always Are you sure you want to delete '%1' ?|Ar tu įsitikinÄ™s,kad nori iÅ¡trinti '%1' ? Are you sure you want to format '%1' ?|Ar tu įsitikinÄ™s,kad nori suformatuoti '%1' ? Asia (NTSC)|Asia (NTSC) Asia (PAL)|Asia (PAL) Auto-detect|Auto-detect Autostart|Autostart Backtrace|Backtrace Backup Ram Manager|AtsarginÄ—s darbinÄ—s atminties tvarkyklÄ— Binary Files (*.bin)|Binary Files (*.bin) Bios|Bios'as Block Size :|Bloko dydis: Browse|Browse Byte|Byte Byte Write|Byte Write CD Images (*.iso *.cue *.bin)|CD atvaizdai (*.iso *.cue *.bin) Cancel|AtÅ¡aukti Cannot initialize|Negalima pradÄ—ti Cannot initialize Windows SPTI Driver|Cannot initialize Windows SPTI Driver Cart/Memory|DarbinÄ— atmintis Cartridge|DisketÄ— Cd-Rom|CD-ROM Central/South America (NTSC)|Central/South America (NTSC) Central/South America (PAL)|Central/South America (PAL) Cheat &Search...|Cheat &Search... Cheat Search|Cheat Search Cheat Type|Kodų tipas Cheats|Kodai Cheats File|Kodų failas Choose a binary file|Choose a binary file Choose a cdrom drive/mount point|Pasirink CD-ROM prijungimo taÅ¡kÄ… Choose a cheat file to open|Pasirink kodų failÄ… Choose a cheat file to save to|Pasirink kodų failÄ… iÅ¡saugojimui į Choose a file to save your state|Pasirink failÄ… iÅ¡saugoti žaidimui Choose a location for binary file|Choose a location for binary file Choose a location for your screenshot|Pasirink vietÄ… žaidimo ekranvaizdžiui Clear configuration|Clear configuration Close|Uždaryti Code|Kodas Code Breakpoints|Code Breakpoints Comment :|Komentaras: Common Control Registers|Common Control Registers Compare Type|Compare Type DSP Control Registers|DSP Control Registers DSP Registers|DSP Registers Data Size|Data Size Data Size :|Duomenų dydis: Data Type|Data Type Debug CPU|Debug CPU Debug M68K|Debug M68K Debug Master SH2|Debug Master SH2 Debug SCU DSP|Debug SCU DSP Debug Slave SH2|Debug Slave SH2 Debug VDP1|Debug VDP1 Debug VDP2Viewer|Debug VDP2Viewer Del|Del Delete|Trinti Description|ApraÅ¡ymas Description :|ApraÅ¡ymas : Device List|Ä®renginių sÄ…raÅ¡as DirectX Input Interface|DirectX Input Interface DirectX Sound Interface|DirectX Sound Interface Disabled|Neįgalinta Disassembled Code|Disassembled Code Display Enabled|Display Enabled Display on Hover|Display on Hover Down|Žemyn Download|Atsisiųsti Draw End|Draw End Dummy CD Drive|Dummy CD Drive Dummy Input Interface|Dummy Input Interface Dummy OSD Interface|Dummy OSD Interface Dummy Sound Interface|Dummy Sound Interface Dummy Video Interface|Dummy Video Interface Edit configuration|Edit configuration Emu-Compatibility|Emuliatoriaus suderinamumas Emulation|Emuliacija Enable|Ä®galinti Enable Frame Skip/Limiter|Enable Frame Skip/Limiter Enabled|Ä®galinta End Address:|End Adress: English|Anglų Enter New Value|Enter New Value Europe + others (PAL)|Europe + others (PAL) Exact|Exact FPS|KPS File|Failas File Name :|Failo pavadinimas: File transfer|Failo perkÄ—limas File:|File: Format|Formatuoti Frame Skip/Limiter|Kadrų praleidimas French|PrancÅ«zų From|IÅ¡ From File|IÅ¡ failo From File...|IÅ¡ failo... Fullscreen|Pilnas ekranas Fullscreen Resolution|Fullscreen Resolution General|Pagrindiniai General Info|General Info German|VokieÄių Glut OSD Interface|Glut OSD Interface Goto Address|Goto Address Greater then|Greater then Hard Reset|"Gilus" perkrovimas Height|AukÅ¡tis Hex value(s)|Hex value(s) Hide Menubar|Hide Menubar Hide Toolbar|Hide Toolbar ISO-File Virtual Drive|ISO-File Virtual Drive Information...|Information... Input|Ä®vedimas Invalid Address|Neteisingas adresas Invalid Value|Neteisinga vertÄ— Italian|Italų Japan (NTSC)|Japan (NTSC) Japanese|Japonų Japanese Modem|Japanese Modem Korea (NTSC)|Korea (NTSC) L&oad State|L&oad State Language :|Kalba : Layer|Sluoksniai Left|KairÄ— Left trigger|Kairysis mygtukas Less than|Less than Linux CD Drive|Linux CD Drive Load|Ä®krauti Load State|Ä®kelti iÅ¡saugotÄ… žaidimÄ… Load State As|IÅ¡saugoti žaidimÄ… kaip Load as executable|Ä®krauti kaip paleidžiamÄ…jį failÄ… Local Coordinates|Local Coordinates Log|Log'as Long|Long Long Write|Long Write Loop Track Clear|Loop Track Clear Loop Track Start|Loop Track Start M68K|M68K M68K Registers|M68K Registers MSH2|MSH2 Master SH2|Master SH2 Memory|Atmintis Memory &Editor|Memory &Editor Memory Breakpoints|Memory Breakpoints Memory Dump|Atminties "iÅ¡metimas" Memory Editor|Atminties tvarkymas Memory Search|Memory Search Memory Transfer|Atminties pervedimas Memory dump|Atminties "iÅ¡metimas" Mouse|Mouse Mpeg ROM|Mpeg ROM'as NBG0|NBG0 NBG0/RBG1 Info|NBG0/RBG1 Info NBG1|NBG1 NBG1 Info|NBG1 Info NBG2|NBG2 NBG2 Info|NBG2 Info NBG3|NBG3 NBG3 Info|NBG3 Info Netlink|Netlink Never|Never None|None Normal Sprite|Normal Sprite North America (NTSC)|North America (NTSC) OSD Core|OSD Core Ok|Gerai On fullscreen|On fullscreen On message|On message Open &CD Rom...|Open &CD Rom... Open &ISO...|Open &ISO... Open CD Rom|Atverti CD-ROM'Ä… OpenGL Video Interface|OpenGL Video Interface Options|Options Other Debug|Other Debug Pad|Pad Pad Configuration|Pad Configuration Pause|PauzÄ— Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Qt Keyboard Input Interface Quit|IÅ¡eiti R&un|&Leisti RBG0|RBG0 RBG0 Info|RBG0 Info Read|Read Region|Regionas Registers|Registers Reset|Perkrauti Resolution|RaiÅ¡ka Right|DeÅ¡inÄ— Right trigger|DeÅ¡inysis mygtukas Run|Leisti S&ave State|S&ave State SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|SDL Joystick Interface SDL Sound Interface|SDL Sound Interface SH2 Debugger Interpreter|SH2 Debugger Interpreter SH2 Dynamic Recompiler|SH2 Dynamic Recompiler SH2 Interpreter|SH2 Interpretatorius SH2 Registers|SH2 Registers SSH2|SSH2 Save|IÅ¡saugoti Save As WAV|Save As WAV Save Bitmap|Save Bitmap Save Information|IÅ¡saugoti informacijÄ… Save List|IÅ¡saugojimų sÄ…raÅ¡as Save MD0|Save MD0 Save MD1|Save MD1 Save MD2|Save MD2 Save MD3|Save MD3 Save Program|Save Program Save Selected|Save Selected Save Slot Registers|Save Slot Registers Save State|IÅ¡saugoti žaidimÄ… Save State As|IÅ¡saugoti žaidimÄ… kaip Save States|Žaidimų iÅ¡saugojimo failai Sc&reenshot|Sc&reenshot Screen Enabled|Screen Enabled Screenshot|Ekrano nuotrauka Search|Search Search Criteria|Search Criteria Search Memory|Search Memory Search Value:|Search Value: Search/Add Cheats|Search/Add Cheats Select a file to load your state|Pasirink žaidimo iÅ¡saugojimo failÄ… įkÄ—limui Select your iso/cue/bin file|Pasirink iso/cue/bin failÄ… Set PC to Start Address|Set PC to Start Address Settings|Nuostatos Shortcuts|Shortcuts Show FPS|Show FPS Show Log Window|Show Log Window Signed|Signed Signed 16-bit value|Signed 16-bit value Signed 32-bit value|Signed 32-bit value Signed 8-bit value|Signed 8-bit value Slave SH2|Slave SH2 Slot Info|Slot Info Slot Number:|Slot Number: Software OSD Interface|Software OSD Interface Software Video Interface|Software Video Interface Sound|Garsas Sound Core|Garso įranga Spanish|Ispanų Start|PradÄ—ti Start Address:|Pradžios adresas: Start in Fullscreen|Start in Fullscreen Status|Statusas Step Into|Step Into Step Out|Step Out Step Over|Step Over Store|Saugoti Synchronize Saturn internal clock with set time|Synchronize Saturn internal clock with set time System Clipping Coordinates|System Clipping Coordinates Text|Text Texture|Texture To|To To File...|Ä® failÄ…... Tools|Ä®rankiai Track Inf Loop Results|Track Inf Loop Results Transfer|Pervedimas Translation|Vertimas Type:|Type: Unable to add code|Neįmanoma pridÄ—ti kodo Unable to change description|Neįmanoma keisti apraÅ¡ymo Unable to open file for loading|Neįmanoma atverti failo įkrovimui Unable to open file for saving|Neįmanoma atverti failo saugojimui Unable to remove code|Neįmanoma paÅ¡alinti kodo Unknow (%1)|Nežinoma (%1) Unsigned|Unsigned Unsigned 16-bit value|Unsigned 16-bit value Unsigned 32-bit value|Unsigned 32-bit value Unsigned 8-bit value|Unsigned 8-bit value Up|AukÅ¡tyn Upload|Ä®krauti VDP1|VDP1 VDP1 Command Info|VDP1 Command Info VDP1 Command List|VDP1 Command List VDP2|VDP2 Value|Value Value :|VertÄ—: Value:|Value: Vdp1|Vdp1 Vdp2|Vdp2 Video|Video Video Core|Video įranga Video Driver|Video tvarkyklÄ— Video Format|Video formatas View|View Viewer|Viewer Waiting Input...|Laukiama įvedimo Width|Plotis Window Resolution|Window Resolution Windows SPTI Driver|Windows SPTI Driver Word|Word Word Write|Žodžių raÅ¡ymas Write|Write Yabause Cheat Files (*.yct);;All Files (*)|Yabause kodų failai (*.yct);;Visi failai (*) Yabause Qt GUI|Yabause Qt grafinÄ— sÄ…saja Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO Yabause Save State (*.yss)|Yabause žaidimo išsaugojimo failas (*.yss) Yabause is not initialized, can't manage backup ram.|Yabause is not initialized, can't manage backup ram. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Įrankų juosta yabause-0.9.13.1/l10n/CMakeLists.txt000644 001750 001750 00000000573 12256006204 020722 0ustar00guillaumeguillaume000000 000000 project(yabause-l10n) set(LANGS de es fr it lt nl pt pt_BR sv) if (UNIX AND NOT APPLE) foreach(LANG ${LANGS}) install(FILES "yabause_${LANG}.yts" DESTINATION "share/yabause/yts" RENAME "${LANG}.yts") endforeach() elseif (WIN32) foreach(LANG ${LANGS}) install(FILES "yabause_${LANG}.yts" DESTINATION "trans" RENAME "${LANG}.yts") endforeach() endif () yabause-0.9.13.1/l10n/yabause_es.yts000644 001750 001750 00000030114 12256006204 021035 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|%1 Imágenes (*.%2) %1/%2 blocks free|%1/%2 bloques libres &About...|&Acerca de... &Action Replay|&Action Replay &Backup Manager...|Gestor de memoria de &backup... &Browse|&Examinar &Cancel|&Cancelar &Capture Screen|&Capturar pantalla &Cheat List|Listado de &Trucos &Cheats|&Trucos &Cheats List...|Lista de tru&cos... &Clear|&Limpiar &Close|&Cerrar &Debug|&Depurar &Delete|&Borrar &Emulation|&Emulación &File|&Archivo &Frame Skip/Limiter|&Limitador/esquivador de cuadros &Fullscreen|&Pantalla completa &Help|&Ayuda &Layer|&Capa &Load From File|&Cargar desde archivo &Log|&Registro &Master SH2|SH2 &Maestro &Memory Transfer|Transferir &memoria &OK|&Aceptar &Pause|&Pausar &Quit|&Salir &Raw Memory Address|&Dirección de memoria Raw &Reset|&Resetear &Save To File|&Guardar a archivo &Settings...|&Configuración... &Slave SH2|SH2 E&sclavo &Tools|Herramien&tas &Transfer|&Transferir &View|&Ver 16 Mbit Backup Ram|RAM de respaldo de 16 Mbit 16 Mbit ROM|ROM de 16 Mbit 16-bit|16-bit 16-bit Relative value(s)|Valor(es) relativo(s) a 16-bit 32 Mbit Backup Ram|RAM de respaldo de 32 Mbit 32 Mbit Dram|Dram de 32 Mbit 32-bit|32-bit 3D Control Pad|Controlador Analógico 3D 4 Mbit Backup Ram|RAM de respaldo de 4 Mbit 503/512 blocks free|503/512 bloques libres 510/512 blocks free|510/512 bloques libres 8 Mbit Backup Ram|RAM de respaldo de 8 Mbit 8 Mbit Dram|Dram de 8 Mbit 8-bit|8-bit 8-bit Relative value(s)|Valor(es) relativo(s) a 8-bit WARNING: Master Codes are NOT supported.|AVISO: Códigos Maestros NO soportados. About...|Acerca de... Action Replay Code :|Código de Action Replay Add|Añadir Add Action Replay Code|Añadir Código de Action Replay Add Cheat|Añadir Truco Add Codes...|Añadir Códigos... Add Raw Memory Code|Añadir código dirección de memoria Address|Dirección Address :|Dirección : Advanced|Avanzado Always|Siempre Are you sure you want to delete '%1' ?|¿Estás seguro de borrar '%1' ? Are you sure you want to format '%1' ?|¿Estás seguro de formatear '%1' ? Asia (NTSC)|Asia (NTSC) Asia (PAL)|Asia (PAL) Auto-detect|Auto-detectar Autostart|Iniciar automáticamente Awaiting input for|Esperando entrada para Axis|Eje Backtrace|Registro de funciones llamadas Backup Ram Manager|Gestor de RAM de backup Binary Files (*.bin)|Archivos binarios (*.bin) Bios|Bios Block Size :|Tamaño de bloque: Browse|Examinar Byte|Byte Byte Write|Escritura de byte CD Images (*.iso *.cue *.bin)|Imágenes de CD (*.iso *.cue *.bin) Cancel|Cancelar Cannot initialize|Imposible iniciar Cannot initialize Windows SPTI Driver|No se pudo inicializar el Driver SPTI de Windows Cart/Memory|Cartucho/Memoria Cartridge|Cartucho Cd-Rom|Unidad de CD-Rom Central/South America (NTSC)|América Central y Sudamérica (NTSC) Central/South America (PAL)|América Central y Sudamérica (PAL) Cheat &Search...|Bú&squeda de trucos... Cheat Search|Búsqueda de trucos... Cheat Type|Tipo de truco Cheats|Trucos Cheats File|Archivo de trucos Choose a binary file|Seleccione un archivo binario Choose a cartridge file|Seleccione un archivo de cartucho Choose a cdrom drive/mount point|Seleccione una unidad de cdrom/punto de montaje Choose a cheat file to open|Seleccione un archivo de trucos para abrir Choose a cheat file to save to|Seleccione el archivo de trucos para guardar Choose a file to save your state|Seleccione un archivo para guardar tu estado Choose a location for binary file|Seleccione ubicación para el archivo binario Choose a location for your screenshot|Seleccione ubicación para tu captura Choose a memory file|Seleccione un archivo de memoria Choose a mpeg rom|Seleccione un rom mpeg Clear configuration|Limpiar configuración Close|Cerrar Code|Código Code Breakpoints|Puntos de detención de código Comment :|Comentario : Common Control Registers|Registros comúnes de control Compare Type|Tipo de comparación DSP Control Registers|Registros de control del DSP DSP Registers|Registros del DSP Data Size|Tamaño del dato Data Size :|Tamaño de datos : Data Type|Tipo de Dato Debug CPU|Depurar CPU Debug M68K|Depurar M68K Debug Master SH2|Depurar SH2 Maestro Debug SCU DSP|Depurar SCU DSP Debug Slave SH2|Depurar SH2 Esclavo Debug VDP1|Depurar VDP1 Debug VDP2Viewer|Depurar Visualizador VDP2 Del|Eliminar Delete|Eliminar Description|Descripción Description :|Descripción : Device List|Lista de Dispositivos DirectX Input Interface|Interfaz de entrada de DirectX DirectX Sound Interface|Interfaz de Sonido de DirectX Disabled|Desactivado Disassembled Code|Código desensamblado Display Enabled|Video habilitado Display on Hover|Habilitar video al pasar por encima Down|Abajo Download|Descargar Draw End|Fin del dibujado Dummy CD Drive|Unidad de CD falsa Dummy Input Interface|Interfaz de entrada falsa Dummy OSD Interface|Interfaz OSD falsa Dummy Sound Interface|Interfaz de sonido falsa Dummy Video Interface|Interfaz de video falsa Edit configuration|Editar configuración Emu-Compatibility|Compatibilidad del emulador Emulation|Emulación Enable|Activar Enable Frame Skip/Limiter|Habilitar limitación/esquivado de cuadros Enabled|Activado End Address:|Dirección final: English|Inglés Enter New Value|Ingrese el nuevo valor Europe + others (PAL)|Europa + otros (PAL) Exact|Exacto FPS|Cuadros por Segundo File|Archivo File Name :|Nombre de archivo : File transfer|Transferencia de archivo File:|Archivo: Format|Formato Frame Skip/Limiter|Limitador/esquivador de cuadros French|Francés From|De From File|De archivo From File...|De archivo... Fullscreen|Pantalla completa Fullscreen Resolution|Resolución de pantalla completa General|General General Info|Información general German|Alemán Glut OSD Interface|Interface OSD Glut Goto Address|Ir a dirección Greater then|Mayor a Hard Reset|Reiniciar en frío Height|Altura Hex value(s)|Valor(es) Hexadecimal(es) Hide Menubar|Esconder barra de menú Hide Toolbar|Esconder barra de herramientas ISO-File Virtual Drive|Archivo ISO de Unidad Virtual Information...|Información... Input|Entrada Invalid Address|Dirección inválida Invalid Value|Valor inválido Italian|Italiano Japan (NTSC)|Japón (NTSC) Japanese|Japonés Japanese Modem|Modem japonés Korea (NTSC)|Korea (NTSC) L&oad State|Cargar Estad&o Language :|Idioma : Layer|Capa Left|Izquierda Left trigger|Gatillo izquierdo Less than|Menor a Linux CD Drive|Unidad Linux de CD Load|Cargar Load State|Cargar estado Load State As|Cargar estado como Load as executable|Cargar como ejecutable Local Coordinates|Coordenadas locales Log|Registro Long|Long Long Write|Escribir LONG Loop Track Clear|Limpiar repetición de pista Loop Track Start|Comenzar repetición de pista M68K|M68K M68K Registers|Registros del M68K MSH2|MSH2 Master SH2|SH2 Maestro Memory|Memoria Memory &Editor|&Editor de memoria Memory Breakpoints|Puntos de detención de memoria Memory Dump|Volcado de memoria Memory Editor|Editor de memoria Memory Search|Búsqueda en memoria Memory Transfer|Transferir memoria Memory dump|Volcado de memoria Middle|Medio Mouse|Ratón Mouse Configuration|Configuración del Ratón Mpeg ROM|ROM Mpeg NBG0|NBG0 NBG0/RBG1 Info|Información de NBG0/RBG1 NBG1|NBG1 NBG1 Info|Información de NBG1 NBG2|NBG2 NBG2 Info|Información de NBG2 NBG3|NBG3 NBG3 Info|Información de NBG3 Netlink|Netlink Never|Nunca None|Ninguno Normal Sprite|Sprite normal North America (NTSC)|América del Norte (NTSC) OSD Core|OSD básico Ok|Aceptar On fullscreen|En pantalla completa On message|En mensaje Open &CD Rom...|Abrir unidad de &CD Rom Open &ISO...|Abrir &ISO... Open CD Rom|Abrir CD Rom OpenGL Video Interface|Interfaz de video OpenGL Options|Opciones Other Debug|Otra depuración Pad|Mando Pad Configuration|Configuración del mando Pause|Pausar Press Esc key to cancel|Presione la tecla ESC para cancelar Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Interfaz de entrada de teclado Qt Quit|Salir R&un|E&jecutar RBG0|RBG0 RBG0 Info|Información de RBG0 Read|Leer Region|Región Registers|Registros Reset|Resetear Resolution|Resolución Right|Derecha Right trigger|Gatillo derecho Run|Ejecutar S&ave State|Gu&ardar estado SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|Interfaz de Joystick SDL SDL Sound Interface|Interfaz de sonido SDL SH2 Debugger Interpreter|Intérprete Debugger SH2 SH2 Dynamic Recompiler|Recompilador dinámico SH2 SH2 Interpreter|Intérprete SH2 SH2 Registers|Registros del SH2 SSH2|SSH2 Save|Guardar Save As WAV|Guardar como WAV Save Bitmap|Guardar mapa de bits Save Information|Guardar información Save List|Lista de salvaguardados Save MD0|Guardar MD0 Save MD1|Guardar MD1 Save MD2|Guardar MD2 Save MD3|Guardar MD3 Save Program|Guardar programa Save Selected|Guardar seleccionado Save Slot Registers|Guardar Registros de ranuras Save State|Guardar Estado Save State As|Guardar estado como Save States|Guardar estados Sc&reenshot|Captu&rar pantalla Screen Enabled|Pantalla habilitada Screenshot|Captura de pantalla Search|Buscar Search Criteria|Criterio de búsqueda Search Memory|Buscar en memoria Search Value:|Valor a buscar: Search/Add Cheats|Buscar/Agregar trucos Select a file to load your state|Selecciona el archivo para cargar tu estado Select your iso/cue/bin file|Selecciona tu archivo iso/cue/bin Set PC to Start Address|Establecer PC a posición de inicio Settings|Configuración Shortcuts|Atajos Show FPS|Mostrar cuadros por segundo Show Log Window|Mostrar ventana de registro Signed|Con signo Signed 16-bit value|Valor de 16-bit con signo Signed 32-bit value|Valor de 32-bit con signo Signed 8-bit value|Valor de 8-bit con signo Slave SH2|SH2 Esclavo Slot Info|Información de ranura Slot Number:|Número de ranura: Software OSD Interface|Interface OSD software Software Video Interface|Interface de video software Sound|Sonido Sound Core|Núcleo de sonido Spanish|Español Start|Iniciar Start Address:|Dirección inicial: Start in Fullscreen|Comenzar a pantalla completa Status|Estado Step Into|Paso dentro Step Out|Paso sobre Step Over|Paso fuera Store|Almacenar Synchronize Saturn internal clock with set time|Sincronizar reloj interno de la Saturn con hora local System Clipping Coordinates|Coordenadas de recorte del sistema Text|Texto Texture|Textura To|Para To File...|A archivo... Tools|Herramientas Track Inf Loop Results|Resultado del Seguimiento de Bucles Infinitos Transfer|Transferencia Translation|Traducción Type:|Tipo: Unable to add code|Imposible añadir código Unable to change description|Imposible cambiar descripción Unable to open file for loading|Imposible abrir archivo para cargar Unable to open file for saving|Imposible abrir archivo para guardar Unable to remove code|Imposible eliminar código Unknow (%1)|Desconocido (%1) Unsigned|Sin signo Unsigned 16-bit value|Valor de 16-bit sin signo Unsigned 32-bit value|Valor de 32-bit sin signo Unsigned 8-bit value|Valor de 8-bit sin signo Up|Arriba Upload|Subir Use System Locale|Usar Configuración Regional del Sistema VDP1|Vdp1 VDP1 Command Info|Información de comandos del VDP1 VDP1 Command List|Listado de comandos del VDP1 VDP2|Vdp2 Value|Valor Value :|Valor : Value:|Valor: Vdp1|Vdp1 Vdp2|Vdp2 Video|Vídeo Video Core|Núcleo de Vídeo Video Driver|Driver de Vídeo Video Format|Formato de Vídeo View|Ver Viewer|Visor Waiting Input...|Esperando Entrada de Datos... Width|Ancho Window Resolution|Resolución de ventana Windows SPTI Driver|Driver SPTI para Windows Word|Word Word Write|Escribir WORD Write|Escribir Yabause Cheat Files (*.yct);;All Files (*)|Archivos de trucos para Yabause (*.yct);;Todos los archivos (*) Yabause Qt GUI|Interfaz gráfica Qt Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Basada en Yabause %1
http://yabause.org
El Equipo Yabause
Filipe AZEVEDO Yabause Save State (*.yss)|Salvaguardados de Yabause (*.yss) Yabause is not initialized, can't manage backup ram.|Yabause no está inicializado. No se puede gestionar el editor de la memoria de backup. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Barra de herramientas yabause-0.9.13.1/l10n/yabause_fr.yts000644 001750 001750 00000027442 12256006204 021047 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|Images %1 (*.%2) %1/%2 blocks free|%1/%2 blocs libre &About...|&A Propos De... &Action Replay|&Action Replay &Backup Manager...|&Gestion des Sauvegardes... &Browse|&Parcourir &Cancel|&Annuler &Capture Screen|&Capture d'Ecran &Cheat List|Liste de &Cheat &Cheats|&Cheats &Cheats List...|Liste de &Cheat... &Clear|&Effacer &Close|&Fermer &Debug|&Débug &Delete|&Effacer &Emulation|&Emulation &File|&Fichier &Frame Skip/Limiter|&Saut d'Image/Limitateur &Fullscreen|&Plein Ecran &Help|&Aide &Layer|&Couche &Load From File|&Charger à Partir d'un Fichier &Log|&Journal &Master SH2|SH2 &Maître &Memory Transfer|&Transfert de Mémoire &OK|&OK &Pause|&Pause &Quit|&Quitter &Raw Memory Address|Adresse Mémoire &Brut &Reset|&Redémarrer &Save To File|&Sauvegarder Vers le Fichier &Settings...|&Paramètres... &Slave SH2|SH2 E&sclave &Tools|Ou&tils &Transfer|&Transfert &View|&Visualiser 16 Mbit Backup Ram|16 Mbit Backup Ram 16 Mbit ROM|16 Mbit ROM 16-bit|16-bit 16-bit Relative value(s)|Valeur(s) Relative 16-bit 32 Mbit Backup Ram|32 Mbit Backup Ram 32 Mbit Dram|32 Mbit Dram 32-bit|32-bit 3D Control Pad|3D Control Pad 4 Mbit Backup Ram|4 Mbit Backup Ram 503/512 blocks free|503/512 blocs libre 510/512 blocks free|510/512 blocs libre 8 Mbit Backup Ram|8 Mbit Backup Ram 8 Mbit Dram|8 Mbit Dram 8-bit|8-bit 8-bit Relative value(s)|Valeurs Relative 8-bit WARNING: Master Codes are NOT supported.|AVERTISSEMENT : Les Codes Maîtres NE SONT PAS supportés. About...|A Propos De... Action Replay Code :|Code Action Replay : Add|Ajouter Add Action Replay Code|Ajouter un Code Action Replay Add Cheat|Ajouter un Cheat Add Codes...|Ajouter un Code... Add Raw Memory Code|Ajouter un Code Mémoire Brut Address|Adresse Address :|Adresse : Advanced|Avancé Always|Toujours Are you sure you want to delete '%1' ?|Etes vous sûr de vouloir supprimer '%1' ? Are you sure you want to format '%1' ?|Etes vous sûr de vouloir formater '%1' ? Asia (NTSC)|Asie (NTSC) Asia (PAL)|Asie (PAL) Auto-detect|Détection Auto Autostart|Démarrage Automatique Awaiting input for|Attente de l'entrée pour Axis|Axes Backtrace|Traceur Backup Ram Manager|Gestion des Sauvegardes Binary Files (*.bin)|Fichiers Binaire (*.bin) Bios|Bios : Block Size :|Taille du Bloc : Browse|Parcourir Byte|Byte Byte Write|Ecrire un Byte CD Images (*.iso *.cue *.bin)|Images CD (*.iso *.cue *.bin) Cancel|Annuler Cannot initialize|Ne peut pas Initialiser Cannot initialize Windows SPTI Driver|Ne peut pas initialiser le pilote SPTI Cart/Memory|Cartouche/Mémoire Cartridge|Cartouche : Cd-Rom|CD-Rom : Central/South America (NTSC)|Amérique du Sud/Centrale (NTSC) Central/South America (PAL)|Amérique du Sud/Centrale (PAL) Cheat &Search...|&Rechercher un Cheat... Cheat Search|Rechercher un Cheat Cheat Type|Type de Cheat Cheats|Cheats Cheats File|Fichier Cheat Choose a binary file|Choisir un Fichier Binaire Choose a cartridge file|Choisir un Fichier Cartouche Choose a cdrom drive/mount point|Choisir un lecteur CD ou un point de montage Choose a cheat file to open|Choisir un fichier de Cheat à ouvrir Choose a cheat file to save to|Choisir un fichier de Cheat à enregistrer Choose a file to save your state|Choisir un fichier où enregistrer l'état Choose a location for binary file|Choisir le Chemin du Fichier Binaire Choose a location for your screenshot|Choisir un nom de fichier pour votre capture d'écran Choose a memory file|Choisir un Fichier Mémoire Choose a mpeg rom|Choisir une Rom MPEG Clear configuration|Vider la Configuration Close|Fermer Code|Code Code Breakpoints|Interruptions du Code Comment :|Commentaire : Common Control Registers|Registres de Contrôle Commun Compare Type|Type de Comparaison DSP Control Registers|Registres de Contrôle DSP DSP Registers|Registres DSP Data Size|Taille Donnée Data Size :|Taille des Données : Data Type|Type Donnée Debug CPU|Débug CPU Debug M68K|Débug M68K Debug Master SH2|Débug Master SH2 Debug SCU DSP|Débug SCU DSP Debug Slave SH2|Débug SH2 Esclave Debug VDP1|Débug VDP1 Debug VDP2Viewer|Débug VDP2Viewer Del|Effacer Delete|Effacer Description|Description Description :|Description : Device List|Liste des Périphériques DirectX Input Interface|Interface DirectX Input DirectX Sound Interface|Interface DirectX Sound Disabled|Désactivé Disassembled Code|Code Désassemblé Display Enabled|Affichage Activé Display on Hover|Affichager en Passant le Curseur Down|Bas Download|Télécharger Draw End|Fin Dessin Dummy CD Drive|Lecteur de CD Factice Dummy Input Interface|Interface Contrôleur Factice Dummy OSD Interface|Interface OSD Factice Dummy Sound Interface|Interface Son Factice Dummy Video Interface|Interface Vidéo Factice Edit configuration|Editer la Configuration Emu-Compatibility|Emu-Compatibility Emulation|Emulation Enable|Activer Enable Frame Skip/Limiter|Active le Saut d'Image/Limitateur Enabled|Activé End Address:|Adresse de Fin : English|Anglais Enter New Value|Entrer une Nouvelle Valeur Europe + others (PAL)|Europe + Autres (PAL) Exact|Identique FPS|FPS File|Fichier File Name :|Nom du Fichier : File transfer|Transfert de Fichier File:|Fichier : Format|Formater Frame Skip/Limiter|Saut d'Image/Limitateur French|Français From|A Partir De From File|A Partir d'un Fichier From File...|A Partir d'un Fichier... Fullscreen|Plein Ecran Fullscreen Resolution|Résolution en Plein Ecran General|Général General Info|Info Général German|Allemand Glut OSD Interface|Interface OSD GLut Goto Address|Va sur l'Adresse Greater then|Plus grand que Hard Reset|Redémarrage Matériel Height|Hauteur : Hex value(s)|Valeur(s) Héx Hide Menubar|Cacher la Barre de Menu Hide Toolbar|Cacher la Barre d'Outil ISO-File Virtual Drive|Lecteur de Fichier ISO Virtuel Information...|Information... Input|Contrôleur Invalid Address|Adresse Invalide Invalid Value|Valeur Invalide Italian|Italien Japan (NTSC)|Japon (NTSC) Japanese|Japonais Japanese Modem|Modem Japonais Korea (NTSC)|Corée (NTSC) L&oad State|&Charger un Etat Language :|Langage : Layer|Couche Left|Gauche Left trigger|Gâchette Gauche Less than|Moins que Linux CD Drive|Lecteur CD Linux Load|Charger Load State|Charger l'Etat Load State As|Charger l'Etat sous Load as executable|Charger en tant qu'Executable Local Coordinates|Coordonnées Locals Log|Journal Long|Long Long Write|Ecrire un Long Loop Track Clear|Vider Piste en Boucle Loop Track Start|Démarrer Piste en Boucle M68K|M68K M68K Registers|Registres M68K MSH2|MSH2 Master SH2|SH2 Maître Memory|Mémoire : Memory &Editor|&Editeur de Mémoire Memory Breakpoints|Interruptions Mémoire Memory Dump|Copier la Mémoire Memory Editor|Editeur de Mémoire Memory Search|Rechercher dans la Mémoire Memory Transfer|Transfert de Mémoire Memory dump|Copier la Mémoire Middle|Moyen Mouse|Souris Mouse Configuration|Configuration de la Souris Mpeg ROM|ROM MPEG : NBG0|NBG0 NBG0/RBG1 Info|Info NBG0/RBG1 NBG1|NBG1 NBG1 Info|Info NBG1 NBG2|NBG2 NBG2 Info|Info NBG2 NBG3|NBG3 NBG3 Info|Info NBG3 Netlink|Réseau Never|Jamais None|Aucun Normal Sprite|Sprite Normal North America (NTSC)|Amérique du Nord (NTSC) OSD Core|Noyau OSD Ok|Ok On fullscreen|Plein Ecran On message|Sur le Message Open &CD Rom...|Ouvrir un &CD Rom... Open &ISO...|Ouvrir un &ISO... Open CD Rom|Ouvrir un CD ROM OpenGL Video Interface|Interface Vidéo OpenGL Options|Options Other Debug|Autre Débug Pad|Manette Pad Configuration|Configuration de la Manette Pause|Pause Press Esc key to cancel|Pressez Echap pour Annuler Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Interface Qt du Clavier Quit|Quitter R&un|L&ancer RBG0|RGB0 RBG0 Info|Info RBG0 Read|Ecrire Region|Région : Registers|Registres Reset|Redémarrer Resolution|Résolution : Right|Droite Right trigger|Gâchette Droite Run|Lancer S&ave State|S&auvegarder un Etat SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|Interface SDL du Joystick SDL Sound Interface|Interface SDL du Son SH2 Debugger Interpreter|Interpréteur de Débugeur SH2 SH2 Dynamic Recompiler|Recompileur Dynamique SH2 SH2 Interpreter|Interpréteur SH2 : SH2 Registers|Registres SH2 SSH2|SSH2 Save|Sauvegarder Save As WAV|Sauvegarder en WAV Save Bitmap|Sauvegarder en Bitmap Save Information|Informations sur la Sauvegarde Save List|Liste des Sauvegardes Save MD0|Sauvegarde MD0 Save MD1|Sauvegarde MD1 Save MD2|Sauvegarde MD2 Save MD3|Sauvegarde MD3 Save Program|Sauvegarde Programme Save Selected|Sauvegarde Sélectionné Save Slot Registers|Sauvegarder Emplacement Registres Save State|Sauvegarder l'Etat Save State As|Sauvegarder l'Etat sous Save States|Sauvegarde d'Etat : Sc&reenshot|Copie d'Ec&ran Screen Enabled|Ecran Activé Screenshot|Capture d'Ecran Search|Rechercher Search Criteria|Critère de Recherche Search Memory|Rechercher dans la Mémoire Search Value:|Rechercher une Valeur : Search/Add Cheats|Rechercher/Ajouter un Cheat Select a file to load your state|Choisissez un fichier pour charger l'état Select your iso/cue/bin file|Choisissez votre fichier iso/cue/bin Set PC to Start Address|Sélection PC vers l'Adresse de Démarrage Settings|Paramètres Shortcuts|Raccourcis Show FPS|Afficher les FPS Show Log Window|Afficher la Fenêtre Journal Signed|Signé Signed 16-bit value|Valeur Signé 16-bit Signed 32-bit value|Valeur Signé 32-bit Signed 8-bit value|Valeur Signé 8-bit Slave SH2|SH2 Esclave Slot Info|Info Emplacement Slot Number:|Numéro d'Emplacement : Software OSD Interface|Interface OSD Logiciel Software Video Interface|Interface Vidéo Logiciel Sound|Son Sound Core|Noyau Son : Spanish|Espagnol Start|Démarrer Start Address:|Adresse de Début : Start in Fullscreen|Lancer en Plein Ecran Status|Statut Step Into|Entrer Dans Step Out|Sortir Step Over|Sauter Store|Stocker Synchronize Saturn internal clock with set time|Synchroniser l'Horloge Interne de la Saturn avec l'Heure System Clipping Coordinates|Coordonnées Clippind Système Text|Texte Texture|Texture To|Vers To File...|Vers un Fichier... Tools|Outils Track Inf Loop Results|Résultats In Piste en Boucle Transfer|Transfert Translation|Traduction : Type:|Type : Unable to add code|Ne peut pas ajouter de Code Unable to change description|Ne peut pas changer la Description Unable to open file for loading|Ne peut pas ouvrir le fichier à Charger Unable to open file for saving|Ne peut pas ouvrir le fichier à Sauvegarder Unable to remove code|Ne peut pas enlever le Code Unknow (%1)|Inconnu (%1) Unsigned|Non Signé Unsigned 16-bit value|Valeur Non Signé 16-bit Unsigned 32-bit value|Valeur Non Signé 32-bit Unsigned 8-bit value|Valeur Non Signé 8-bit Up|Haut Upload|Upload Use System Locale|Utiliser les paramètres régionaux VDP1|VDP1 VDP1 Command Info|Info Commande VDP1 VDP1 Command List|Liste Commande VDP1 VDP2|VDP2 Value|Valeur Value :|Valeur : Value:|Valeur : Vdp1|VDP1 Vdp2|VDP2 Video|Vidéo Video Core|Noyau Vidéo : Video Driver|Pilote Vidéo : Video Format|Format Vidéo : View|Visualiser Viewer|Visualiseur Waiting Input...|En Attente du Contrôleur Width|Largeur : Window Resolution|Résolution Fenêtré Windows SPTI Driver|Pilote Windows SPTI Word|Word Word Write|Ecrire un Word Write|Ecrire Yabause Cheat Files (*.yct);;All Files (*)|Fichiers de Cheat Yabause (*ycf);;Tous les Fichiers (*) Yabause Qt GUI|Interface Graphique Qt pour Yabause Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Basé sur Yabause %1 :
http://yabause.org
L'équipe de Yabause :
Filipe AZEVEDO Yabause Save State (*.yss)|Sauvegarde d'État Yabause (*.yss) Yabause is not initialized, can't manage backup ram.|Yabause n'est pas initialisé. Ne peut pas gérer la sauvegarde. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=fr http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Barre d'Outils yabause-0.9.13.1/l10n/yabause_nl.yts000644 001750 001750 00000025244 12256006204 021047 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|%1 Afbeeldingen (*.%2) %1/%2 blocks free|%1/%2 Vrije Blokken &About...|&Over... &Action Replay|&Action Replay &Backup Manager...|&Backup Beheer... &Browse|&Bladeren &Cancel|&Annuleer &Capture Screen|&Neem Schermafbeelding &Cheat List|&Cheat Lijst &Cheats|&Cheats &Cheats List...|&Cheats Lijst... &Clear|&Herstel &Close|&Sluiten &Debug|&Debug &Delete|&Verwijder &Emulation|&Emulatie &File|&Bestand &Frame Skip/Limiter|&Frame Skip/Limitatie &Fullscreen|&Volledig Scherm &Help|&Help &Layer|&Laag &Load From File|&Bestand Laden &Log|&Log &Master SH2|&Master SH2 &Memory Transfer|&Geheugen Verplaatsen &OK|&OK &Pause|&Pauzeer &Quit|&Stoppen &Raw Memory Address|&Raw Geheugen Adres &Reset|&Reset &Save To File|&Bewaar Naar Bestand &Settings...|&Instellingen... &Slave SH2|&Slave SH2 &Tools|&Hulpmiddelen &Transfer|&Verplaats &View|&View 16 Mbit Backup Ram|16 Mbit Backup Ram 16 Mbit ROM|16 Mbit ROM 16-bit|16-bit 16-bit Relative value(s)|16-bit Relatieve waarde(n) 32 Mbit Backup Ram|32 Mbit Backup Ram 32 Mbit Dram|32 Mbit Dram 32-bit|32-bit 3D Control Pad|3D Control Pad 4 Mbit Backup Ram|4 Mbit Backup Ram 503/512 blocks free|503/512 vrije blokken 510/512 blocks free|510/512 vrije blokken 8 Mbit Backup Ram|8 Mbit Backup Ram 8 Mbit Dram|8 Mbit Dram 8-bit|8-bit 8-bit Relative value(s)|8-bit Relatieve waarden WARNING: Master Codes are NOT supported.|WARNING: Master Codes worden NIET ondersteund. About...|Over... Action Replay Code :|Action Replay Code : Add|Voeg Toe Add Action Replay Code|Voeg Action Replay Code toe Add Cheat|Cheat Toevoegen Add Codes...|Codes Toevoegen... Add Raw Memory Code|Toevoegen Add Raw Geheugen Code Address|Adres Address :|Adres : Advanced|Geavanceerd Always|Altijd Are you sure you want to delete '%1' ?|Bent u zeker dat u '%1' wil verwijderen? Are you sure you want to format '%1' ?|Bent u zeker dat u '%1' wil formatteren? Asia (NTSC)|Azië (NTSC) Asia (PAL)|Azië (PAL) Auto-detect|Automatisch Herkennen Autostart|Automatisch Starten Backtrace|Backtrace Backup Ram Manager|Backup RAM Beheer Binary Files (*.bin)|Binaire Bestanden (*.bin) Bios|Bios Block Size :|Blok grootte : Browse|Bladeren Byte|Byte Byte Write|Byte Schrijven CD Images (*.iso *.cue *.bin)|CD bestanden (*.iso *.cue *.bin) Cancel|Annuleer Cannot initialize|Initialiseren mislukt Cannot initialize Windows SPTI Driver|Initialiseren Windows SPTI Driver mislukt Cart/Memory|Kaart/Geheugen Cartridge|Cartridge Cd-Rom|Cd-Rom Central/South America (NTSC)|Centraal/Zuid-Amerika (NTSC) Central/South America (PAL)|Centraal/Zuid-Amerika (PAL) Cheat &Search...|Cheat &Zoeken... Cheat Search|Cheat Zoeken Cheat Type|Cheat Type Cheats|Cheats Cheats File|Cheats Bestand Choose a binary file|Kies een binair bestand Choose a cdrom drive/mount point|Kies een cdrom drive/mount point Choose a cheat file to open|Kies een cheat bestand om te openen Choose a cheat file to save to|Kies een cheat bestand om naar te bewaren Choose a file to save your state|Kies een bestand om uw save status te bewaren Choose a location for binary file|Kies een locatie voor het binair bestand Choose a location for your screenshot|Kies een locatie voor uw schermafbeelding Clear configuration|Herstel configuratie Close|Sluiten Code|Code Code Breakpoints|Code Breekpunten Comment :|Commentaar : Common Control Registers|Common Control Registers Compare Type|Vergelijk type DSP Control Registers|DSP Controle Registers DSP Registers|DSP Registers Data Size|Data Grootte Data Size :|Data Grootte : Data Type|Data Type Debug CPU|Debug CPU Debug M68K|Debug M86K Debug Master SH2|Debug Master SH2 Debug SCU DSP|Debug SCU DSP Debug Slave SH2|Debug Slave SH2 Debug VDP1|Debug VDP1 Debug VDP2Viewer|Debug VDP2Viewer Del|Del Delete|Verwijder Description|Beschrijving Description :|Beschrijving : Device List|Apparaat Lijst DirectX Input Interface|DirectX Input Interface DirectX Sound Interface|DirectX Geluid Interface Disabled|Uitgeschakeld Disassembled Code|Gedeassembleerde Code Display Enabled|Scherm Actief Display on Hover|Scherm op Hover Down|Beneden Download|Downloaden Draw End|Draw End Dummy CD Drive|Dummy CD Drive Dummy Input Interface|Dummy Input Interface Dummy OSD Interface|Dummy OSD Interface Dummy Sound Interface|Dummy Geluid Interface Dummy Video Interface|Dummy Video Interface Edit configuration|Editeer configuratie Emu-Compatibility|Emu-Compatibiliteit Emulation|Emulatie Enable|Activeer Enable Frame Skip/Limiter|Activeer Frame Skip/Limitatie Enabled|Geactiveerd End Address:|Eind adres: English|Engels Enter New Value|Geef Nieuwe Waarde in Europe + others (PAL)|Europa + andere (PAL) Exact|Exact FPS|FPS File|Bestand File Name :|Bestand Naam : File transfer|Bestand overzetten File:|Bestand: Format|Formatteer Frame Skip/Limiter|Frame Skip/Limitatie French|Frans From|Van From File|Van Bestand From File...|Van Bestand... Fullscreen|Volledig Scherm Fullscreen Resolution|Volledig Scherm Resolutie General|Algemeen General Info|Algemene info German|Duits Glut OSD Interface|Glut OSD Interface Goto Address|Ga Naar Adres Greater then|Groter dan Hard Reset|Koude Reset Height|Hoogte Hex value(s)|Hex waarde(n) Hide Menubar|Verberg Menubalk Hide Toolbar|Verberg Hulpmiddelbalk ISO-File Virtual Drive|ISO-Bestand Virtuele Drive Information...|Informatie... Input|Input Invalid Address|Ongeldig Adres Invalid Value|Ongeldige Waarde Italian|Italiaans Japan (NTSC)|Japan (NTSC) Japanese|Japans Japanese Modem|Japans (Modern) Korea (NTSC)|Korea (NTSC) L&oad State|L&aad Status Language :|Taal : Layer|Laag Left|Links Left trigger|Linkse trigger Less than|Minder dan Linux CD Drive|Linux CD Drive Load|Laden Load State|Status Laden Load State As|Status Laden Als Load as executable|Executable Laden Local Coordinates|Lokale Coördinaten Log|Log Long|Long Long Write|Long Write Loop Track Clear|Loop Track Herstel Loop Track Start|Loop Track Start M68K|M68K M68K Registers|M68K Registers MSH2|MSH2 Master SH2|Master SH2 Memory|Geheugen Memory &Editor|Geheugen &Editor Memory Breakpoints|Geheugen Breekpunten Memory Dump|Geheugen dump Memory Editor|Geheugen Editor Memory Search|Geheugen Zoeken Memory Transfer|Geheugen Overzetten Memory dump|Geheugen dump Mouse|Muis Mpeg ROM|Mpeg ROM NBG0|NBG0 NBG0/RBG1 Info|NBG0/RBG1 Info NBG1|NBG1 NBG1 Info|NBG1 Info NBG2|NBG2 NBG2 Info|NBG2 Info NBG3|NBG3 NBG3 Info|NBG3 Info Netlink|Netlink Never|Nooit None|Geen Normal Sprite|Normale Sprite North America (NTSC)|Noord-Amerika (NTSC) OSD Core|OSD Kern Ok|Ok On fullscreen|Op volledig scherm On message|Op boodschap Open &CD Rom...|Open &CD Rom... Open &ISO...|Open &ISO... Open CD Rom|Open CD Rom OpenGL Video Interface|OpenGL Video Interface Options|Opties Other Debug|Andere Debug Pad|Pad Pad Configuration|Pad Configuratie Pause|Pauze Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Qt Toetsenbord Input Interface Quit|Stoppen R&un|S&tarten RBG0|RBG0 RBG0 Info|RBG0 Info Read|Lezen Region|Regio Registers|Registers Reset|Reset Resolution|Resolutie Right|Rechts Right trigger|Rechtse trigger Run|Start S&ave State|B&ewaar Status SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|SDL Joystick Interface SDL Sound Interface|SDL Geluid Interface SH2 Debugger Interpreter|SH2 Debugger Interpreter SH2 Dynamic Recompiler|SH2 Dynamic Recompiler SH2 Interpreter|SH2 Interpreter SH2 Registers|SH2 Registers SSH2|SSH2 Save|Bewaar Save As WAV|Bewaar als WAV Save Bitmap|Bewaar Bitmap Save Information|Bewaar Informatie Save List|Bewaar Lijst Save MD0|Bewaar MD0 Save MD1|Bewaar MD1 Save MD2|Bewaar MD2 Save MD3|Bewaar MD3 Save Program|Bewaar Programma Save Selected|Bewaar Geselecteerd Save Slot Registers|Bewaar Slot Registers Save State|Bewaar Status Save State As|Bewaar Status Als Save States|Bewaar Statussen Sc&reenshot|Sc&hermafbeelding Screen Enabled|Scherm Geactiveerd Screenshot|Schermafbeelding Search|Zoeken Search Criteria|Zoek Criteria Search Memory|Zoek Geheugen Search Value:|Zoek Waarde: Search/Add Cheats|Zoek/Voeg Cheats Toe Select a file to load your state|Selecteer een bestand om uw status te laden Select your iso/cue/bin file|Selecteer uw ISO/CUE/BIN file Set PC to Start Address|Zet PC op Start Adres Settings|Instellingen Shortcuts|Shortcuts Show FPS|Toon FPS Show Log Window|Toon Log Venster Signed|Signed Signed 16-bit value|Signed 16-bit value Signed 32-bit value|Signed 32-bit value Signed 8-bit value|Signed 8-bit value Slave SH2|Slave SH2 Slot Info|Slot Info Slot Number:|Slot Nummer: Software OSD Interface|Software OSD Interface Software Video Interface|Software Video Interface Sound|Geluid Sound Core|Geluid Kern Spanish|Spaans Start|Start Start Address:|Start Adres: Start in Fullscreen|Start in Volledig Scherm Status|Status Step Into|Stap In Step Out|Stap Uit Step Over|Stap Over Store|Bewaar Synchronize Saturn internal clock with set time|Synchroniseer Saturn interne klok met set stijd System Clipping Coordinates|Systeem Clipping Coördinaten Text|Tekst Texture|Textuur To|Naar To File...|Naar Bestand... Tools|Hulpmiddelen Track Inf Loop Results|Traceer Inf Loop Results Transfer|Overzetten Translation|Vertaling Type:|Type: Unable to add code|Kan geen code toevoegen Unable to change description|Omschrijving aanpassen mislukt Unable to open file for loading|Bestand openen voor laden mislukt Unable to open file for saving|Bestand openen voor bewaren mislukt Unable to remove code|Code verwijderen mislukt Unknow (%1)|Onbekend (%1) Unsigned|Unsigned Unsigned 16-bit value|Unsigned 16-bit value Unsigned 32-bit value|Unsigned 32-bit value Unsigned 8-bit value|Unsigned 8-bit value Up|Omhoog Upload|Uploaden VDP1|Vdp1 VDP1 Command Info|VDP1 Commando Info VDP1 Command List|VDP1 Commando List VDP2|Vdp2 Value|Waarde Value :|Waarde : Value:|Waarde : Vdp1|Vdp1 Vdp2|Vdp2 Video|Video Video Core|Video Kern Video Driver|Video Driver Video Format|Video Formaat View|View Viewer|Viewer Waiting Input...|Wachten op input... Width|Breedte Window Resolution|Scherm Resolutie Windows SPTI Driver|Windows SPTI Driver Word|Woord Word Write|Woord Schrijven Write|Schrijven Yabause Cheat Files (*.yct);;All Files (*)|Yabause Cheat Files (*.yct);;Alle Bestanden (*) Yabause Qt GUI|Yabause Qt GUI Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Gebaseerd op Yabause %1
http://yabause.org
Het Yabause Team
Filipe AZEVEDO Yabause Save State (*.yss)|Yabause Save Status (*.yss) Yabause is not initialized, can't manage backup ram.|Yabause is niet geïnitialiseerd, kan backup ram niet beheren. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Hulpmiddelbalk yabause-0.9.13.1/l10n/yabause_pt.yts000644 001750 001750 00000025475 12256006204 021067 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|%1 Images (*.%2) %1/%2 blocks free|%1/%2 blocos livres &About...|&Sobre... &Action Replay|&Action Replay &Backup Manager...|&Backup Manager... &Browse|&Procurar &Cancel|&Cancel &Capture Screen|&Capturar Tela &Cheat List|Lista de &Trapaças &Cheats|&Trapaças &Cheats List...|&Cheats List... &Clear|&Limpar &Close|&Close &Debug|&Debug &Delete|&Excluir &Emulation|&Emulation &File|&Arquivo &Frame Skip/Limiter|&Frame Skip/Limiter &Fullscreen|Tela cheia &Help|&Ajuda &Layer|&Camada &Load From File|&Carregar Do Arquivo &Log|&Log &Master SH2|&Master SH2 &Memory Transfer|&Transferência de Memória &OK|&OK &Pause|&Pausar &Quit|&Sair &Raw Memory Address|&Endereço Original da Memória &Reset|&Reiniciar &Save To File|&Salvar Para Arquivo &Settings...|Configuraçõe&s... &Slave SH2|&Slave SH2 &Tools|&Tools &Transfer|&Transfer &View|&Visualizar 16 Mbit Backup Ram|16 Mbit Backup Ram 16 Mbit ROM|16 Mbit ROM 16-bit|16-bit 16-bit Relative value(s)|16-bit Relative value(s) 32 Mbit Backup Ram|32 Mbit Backup Ram 32 Mbit Dram|32 Mbit Dram 32-bit|32-bit 3D Control Pad|3D Control Pad 4 Mbit Backup Ram|4 Mbit Backup Ram 503/512 blocks free|503/512 blocos livres 510/512 blocks free|510/512 blocos livres 8 Mbit Backup Ram|8 Mbit Backup Ram 8 Mbit Dram|8 Mbit Dram 8-bit|8-bit 8-bit Relative value(s)|8-bit Relative value(s) WARNING: Master Codes are NOT supported.|AVISO: Códigos Mestres NÃO são suportados. About...|Sobre... Action Replay Code :|Código do Action Replay Add|Add Add Action Replay Code|Adicionar Código do Action Replay Add Cheat|Add Cheat Add Codes...|Adicionar Códigos... Add Raw Memory Code|Adicionar o Endereço Original da Memória Address|Address Address :|Endereço : Advanced|Avançado Always|Always Are you sure you want to delete '%1' ?|Você tem certeza de que deseja excluir '%1' ? Are you sure you want to format '%1' ?|Você tem certeza de que deseja formatar '%1' ? Asia (NTSC)|Asia (NTSC) Asia (PAL)|Asia (PAL) Auto-detect|Auto-detect Autostart|Autostart Backtrace|Backtrace Backup Ram Manager|Gerenciador da Ram de Backup Binary Files (*.bin)|Binary Files (*.bin) Bios|Bios Block Size :|Tamanho dos Blocos : Browse|Procurar Byte|Byte Byte Write|Gravar Byte CD Images (*.iso *.cue *.bin)|Imagens de CD (*.iso *.cue *.bin) Cancel|Cancelar Cannot initialize|Não pode inicializar Cannot initialize Windows SPTI Driver|Cannot initialize Windows SPTI Driver Cart/Memory|Cartucho/Memória Cartridge|Cartucho Cd-Rom|CD-Rom Central/South America (NTSC)|Central/South America (NTSC) Central/South America (PAL)|Central/South America (PAL) Cheat &Search...|Cheat &Search... Cheat Search|Cheat Search Cheat Type|Tipo de Trapaça Cheats|Trapaças Cheats File|Arquivo de Trapaças Choose a binary file|Choose a binary file Choose a cdrom drive/mount point|Escolha um leitor de CD ou um ponto de montagem Choose a cheat file to open|Escolha um arquivo de trapaças para abrir Choose a cheat file to save to|Escolha um arquivo de trapaças para salvar Choose a file to save your state|Escolha um arquivo para salvar seu estado Choose a location for binary file|Choose a location for binary file Choose a location for your screenshot|Escolha um local para sua captura de tela Clear configuration|Clear configuration Close|Fechar Code|Código Code Breakpoints|Code Breakpoints Comment :|Comentário : Common Control Registers|Common Control Registers Compare Type|Compare Type DSP Control Registers|DSP Control Registers DSP Registers|DSP Registers Data Size|Data Size Data Size :|Tamanho dos Dados : Data Type|Data Type Debug CPU|Debug CPU Debug M68K|Debug M68K Debug Master SH2|Debug Master SH2 Debug SCU DSP|Debug SCU DSP Debug Slave SH2|Debug Slave SH2 Debug VDP1|Debug VDP1 Debug VDP2Viewer|Debug VDP2Viewer Del|Del Delete|Excluir Description|Descrição Description :|Descrição : Device List|Lista de Dispositivos DirectX Input Interface|DirectX Input Interface DirectX Sound Interface|DirectX Sound Interface Disabled|Desativado Disassembled Code|Disassembled Code Display Enabled|Display Enabled Display on Hover|Display on Hover Down|Para baixo Download|Baixar Draw End|Draw End Dummy CD Drive|Dummy CD Drive Dummy Input Interface|Dummy Input Interface Dummy OSD Interface|Dummy OSD Interface Dummy Sound Interface|Dummy Sound Interface Dummy Video Interface|Dummy Video Interface Edit configuration|Edit configuration Emu-Compatibility|Compatibilidade com o Emu Emulation|Emulação Enable|Ativar Enable Frame Skip/Limiter|Enable Frame Skip/Limiter Enabled|Ativado End Address:|Endereço Final: English|Inglês Enter New Value|Enter New Value Europe + others (PAL)|Europe + others (PAL) Exact|Exact FPS|QPS File|Arquivo File Name :|Nome do Arquivo : File transfer|Transferência de arquivo File:|File: Format|Formato Frame Skip/Limiter|Pulo de quadros/Limitador French|Francês From|De From File|Do Arquivo From File...|Do Arquivo... Fullscreen|Tela cheia Fullscreen Resolution|Fullscreen Resolution General|Geral General Info|General Info German|Alemão Glut OSD Interface|Glut OSD Interface Goto Address|Goto Address Greater then|Greater then Hard Reset|Reiniciar Hardware Height|Altura Hex value(s)|Hex value(s) Hide Menubar|Hide Menubar Hide Toolbar|Hide Toolbar ISO-File Virtual Drive|ISO-File Virtual Drive Information...|Information... Input|Controles Invalid Address|Endereço Inválido Invalid Value|Valor Inválido Italian|Italiano Japan (NTSC)|Japan (NTSC) Japanese|Japonês Japanese Modem|Japanese Modem Korea (NTSC)|Korea (NTSC) L&oad State|L&oad State Language :|Idioma : Layer|Camada Left|Esquerda Left trigger|Gatilho esquerdo Less than|Less than Linux CD Drive|Linux CD Drive Load|Carregar Load State|Carregar Estado Load State As|Carregar Estado como Load as executable|Carregar como executável Local Coordinates|Local Coordinates Log|Log Long|Long Long Write|Escrita Longa Loop Track Clear|Loop Track Clear Loop Track Start|Loop Track Start M68K|M68K M68K Registers|M68K Registers MSH2|MSH2 Master SH2|Master SH2 Memory|Memória Memory &Editor|Memory &Editor Memory Breakpoints|Memory Breakpoints Memory Dump|Dump de memória Memory Editor|Editor de Memória Memory Search|Memory Search Memory Transfer|Transferência de Memória Memory dump|Dump de memória Mouse|Mouse Mpeg ROM|ROM Mpeg NBG0|NBG0 NBG0/RBG1 Info|NBG0/RBG1 Info NBG1|NBG1 NBG1 Info|NBG1 Info NBG2|NBG2 NBG2 Info|NBG2 Info NBG3|NBG3 NBG3 Info|NBG3 Info Netlink|Netlink Never|Never None|None Normal Sprite|Normal Sprite North America (NTSC)|North America (NTSC) OSD Core|OSD Core Ok|Ok On fullscreen|On fullscreen On message|On message Open &CD Rom...|Open &CD Rom... Open &ISO...|Open &ISO... Open CD Rom|Abrir CD Rom OpenGL Video Interface|OpenGL Video Interface Options|Options Other Debug|Other Debug Pad|Pad Pad Configuration|Pad Configuration Pause|Pausar Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Qt Keyboard Input Interface Quit|Sair R&un|Exec&utar RBG0|RBG0 RBG0 Info|RBG0 Info Read|Read Region|Região Registers|Registers Reset|Reiniciar Resolution|Resolução Right|Direita Right trigger|Gatilho direito Run|Executar S&ave State|S&ave State SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|SDL Joystick Interface SDL Sound Interface|SDL Sound Interface SH2 Debugger Interpreter|SH2 Debugger Interpreter SH2 Dynamic Recompiler|SH2 Dynamic Recompiler SH2 Interpreter|Interpretador SH2 SH2 Registers|SH2 Registers SSH2|SSH2 Save|Salvar Save As WAV|Save As WAV Save Bitmap|Save Bitmap Save Information|Informações sobre os Saves Save List|Lista dos Saves Save MD0|Save MD0 Save MD1|Save MD1 Save MD2|Save MD2 Save MD3|Save MD3 Save Program|Save Program Save Selected|Save Selected Save Slot Registers|Save Slot Registers Save State|Salvar Estado Save State As|Salvar Estado Como Save States|Salvar Estado Sc&reenshot|Sc&reenshot Screen Enabled|Screen Enabled Screenshot|Captura de tela Search|Search Search Criteria|Search Criteria Search Memory|Search Memory Search Value:|Search Value: Search/Add Cheats|Search/Add Cheats Select a file to load your state|Escolha um arquivo para carregar seu estado Select your iso/cue/bin file|Escolha seu arquivo iso/cue/bin Set PC to Start Address|Set PC to Start Address Settings|Configurações Shortcuts|Shortcuts Show FPS|Show FPS Show Log Window|Show Log Window Signed|Signed Signed 16-bit value|Signed 16-bit value Signed 32-bit value|Signed 32-bit value Signed 8-bit value|Signed 8-bit value Slave SH2|Slave SH2 Slot Info|Slot Info Slot Number:|Slot Number: Software OSD Interface|Software OSD Interface Software Video Interface|Software Video Interface Sound|Som Sound Core|Núcleo de Som Spanish|Espanhol Start|Iniciar Start Address:|Endereço Inicial: Start in Fullscreen|Start in Fullscreen Status|Estado Step Into|Step Into Step Out|Step Out Step Over|Step Over Store|Armazenar Synchronize Saturn internal clock with set time|Synchronize Saturn internal clock with set time System Clipping Coordinates|System Clipping Coordinates Text|Text Texture|Texture To|Para To File...|Para arquivo... Tools|Ferramentas Track Inf Loop Results|Track Inf Loop Results Transfer|Transferência Translation|Tradução Type:|Type: Unable to add code|Não foi possível adicionar código Unable to change description|Não foi possível modificar a descrição Unable to open file for loading|Não foi possível carregar o arquivo Unable to open file for saving|Não foi possível salvar o arquivo Unable to remove code|Não foi possível remover o código Unknow (%1)|Desconh (%1) Unsigned|Unsigned Unsigned 16-bit value|Unsigned 16-bit value Unsigned 32-bit value|Unsigned 32-bit value Unsigned 8-bit value|Unsigned 8-bit value Up|Para cima Upload|Subir VDP1|VDP1 VDP1 Command Info|VDP1 Command Info VDP1 Command List|VDP1 Command List VDP2|VDP2 Value|Value Value :|Valor : Value:|Value: Vdp1|Vdp1 Vdp2|Vdp2 Video|Vídeo Video Core|Núcleo de Vídeo Video Driver|Driver de Vídeo Video Format|Formato do Vídeo View|View Viewer|Viewer Waiting Input...|Aguardando Entrada de Dados... Width|Largura Window Resolution|Window Resolution Windows SPTI Driver|Windows SPTI Driver Word|Word Word Write|Escrita Word Write|Write Yabause Cheat Files (*.yct);;All Files (*)|Arquivos de trapaça Yabause (*.yct);;Todos os arquivos (*) Yabause Qt GUI|Interface Gráfica do Usuário em Qt do Yabause Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO Yabause Save State (*.yss)|Estado Salvo do Yabause Yabause is not initialized, can't manage backup ram.|Yabause is not initialized, can't manage backup ram. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=uk http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Barra de ferramentas yabause-0.9.13.1/l10n/yabause_it.yts000644 001750 001750 00000024777 12256006204 021064 0ustar00guillaumeguillaume000000 000000 %1 Images (*.%2)|%1 Immagini (*.%2) %1/%2 blocks free|%1%2 blocchi liberi &About...|&Informazioni... &Action Replay|&Action Replay &Backup Manager...|&Backup Manager... &Browse|&Sfoglia &Cancel|&Cancel &Capture Screen|&Cattura Schermo &Cheat List|Lista &Cheat &Cheats|&Cheats &Cheats List...|&Cheats List... &Clear|&Svuota &Close|&Close &Debug|&Debug &Delete|&Elimina &Emulation|&Emulation &File|&File &Frame Skip/Limiter|&Frame Skip/Limiter &Fullscreen|&Schermo Intero &Help|&Aiuto &Layer|&Layer &Load From File|Carica da&l file &Log|&Log &Master SH2|&Master SH2 &Memory Transfer|Trasferimento &Memoria &OK|&OK &Pause|&Pausa &Quit|&Esci &Raw Memory Address|Indirizzo Grezzo Memoria &Reset|&Reset &Save To File|$Salva su file &Settings...|Impo&stazioni... &Slave SH2|&Slave SH2 &Tools|&Tools &Transfer|&Transfer &View|&Visualizza 16 Mbit Backup Ram|16 Mbit Backup Ram 16 Mbit ROM|16 Mbit ROM 16-bit|16-bit 16-bit Relative value(s)|16-bit Relative value(s) 32 Mbit Backup Ram|32 Mbit Backup Ram 32 Mbit Dram|32 Mbit Dram 32-bit|32-bit 3D Control Pad|3D Control Pad 4 Mbit Backup Ram|4 Mbit Backup Ram 503/512 blocks free|503/512 blocchi liberi 510/512 blocks free|510/512 blocchi liberi 8 Mbit Backup Ram|8 Mbit Backup Ram 8 Mbit Dram|8 Mbit Dram 8-bit|8-bit 8-bit Relative value(s)|8-bit Relative value(s) WARNING: Master Codes are NOT supported.|ATTENZIONE: I Codici Master NON sono supportati. About...|Informazioni... Action Replay Code :|Codice Action Replay Add|Add Add Action Replay Code|Aggiungi codice Action Replay Add Cheat|Add Cheat Add Codes...|Aggiungi codici.... Add Raw Memory Code|Aggiungi Raw Memory Code Address|Address Address :|Indirizzo: Advanced|Avanzate Always|Always Are you sure you want to delete '%1' ?|Sicuro di voler cancellare '%1'? Are you sure you want to format '%1' ?|Sicuro di voler formattare '%1'? Asia (NTSC)|Asia (NTSC) Asia (PAL)|Asia (PAL) Auto-detect|Auto-detect Autostart|Autostart Backtrace|Backtrace Backup Ram Manager|Gestione Backup RAM Binary Files (*.bin)|Binary Files (*.bin) Bios|BIOS Block Size :|Dimensioni Blocco: Browse|Sfoglia Byte|Byte Byte Write|Scrivi Byte CD Images (*.iso *.cue *.bin)|Immagine CD (*.iso *.cue *.bin) Cancel|Annulla Cannot initialize|Impossibile Inizializzare Cannot initialize Windows SPTI Driver|Cannot initialize Windows SPTI Driver Cart/Memory|Cart/Memoria Cartridge|Cartuccia Cd-Rom|Cd-Rom Central/South America (NTSC)|Central/South America (NTSC) Central/South America (PAL)|Central/South America (PAL) Cheat &Search...|Cheat &Search... Cheat Search|Cheat Search Cheat Type|Tipo di Cheat Cheats|Cheats Cheats File|File Cheats Choose a binary file|Choose a binary file Choose a cdrom drive/mount point|Choose a cdrom drive/mount point Choose a cheat file to open|Selezionare un file cheat da aprire Choose a cheat file to save to|Selezionare un file cheat per salvare Choose a file to save your state|Selezionare un file per salvare lo stato Choose a location for binary file|Choose a location for binary file Choose a location for your screenshot|Selezionare una directory per gli screenshoot Clear configuration|Clear configuration Close|Chiudi Code|Codice Code Breakpoints|Code Breakpoints Comment :|Commento: Common Control Registers|Common Control Registers Compare Type|Compare Type DSP Control Registers|DSP Control Registers DSP Registers|DSP Registers Data Size|Data Size Data Size :|Dimensioni Dati: Data Type|Data Type Debug CPU|Debug CPU Debug M68K|Debug M68K Debug Master SH2|Debug Master SH2 Debug SCU DSP|Debug SCU DSP Debug Slave SH2|Debug Slave SH2 Debug VDP1|Debug VDP1 Debug VDP2Viewer|Debug VDP2Viewer Del|Del Delete|Elimina Description|Descrizione Description :|Descrizione: Device List|Lista periferiche DirectX Input Interface|DirectX Input Interface DirectX Sound Interface|DirectX Sound Interface Disabled|Disattivo Disassembled Code|Disassembled Code Display Enabled|Display Enabled Display on Hover|Display on Hover Down|Giù Download|Download Draw End|Draw End Dummy CD Drive|Dummy CD Drive Dummy Input Interface|Dummy Input Interface Dummy OSD Interface|Dummy OSD Interface Dummy Sound Interface|Dummy Sound Interface Dummy Video Interface|Dummy Video Interface Edit configuration|Edit configuration Emu-Compatibility|Compatibilità Emulatore Emulation|Emulazione Enable|Abilita Enable Frame Skip/Limiter|Enable Frame Skip/Limiter Enabled|Abilitato End Address:|Indirizzo Finale: English|Inglese Enter New Value|Enter New Value Europe + others (PAL)|Europe + others (PAL) Exact|Exact FPS|FPS File|File File Name :|Nome file: File transfer|Trasferimento file File:|File: Format|Formato Frame Skip/Limiter|Limitatore frame French|Francese From|Da From File|Dal file From File...|Dal file... Fullscreen|Schermo Intero Fullscreen Resolution|Fullscreen Resolution General|Generale General Info|General Info German|Tedesco Glut OSD Interface|Glut OSD Interface Goto Address|Goto Address Greater then|Greater then Hard Reset|Hard Reset Height|Altezza Hex value(s)|Hex value(s) Hide Menubar|Hide Menubar Hide Toolbar|Hide Toolbar ISO-File Virtual Drive|ISO-File Virtual Drive Information...|Information... Input|Input Invalid Address|Indirizzo non valido Invalid Value|Valore non valido Italian|Italiano Japan (NTSC)|Japan (NTSC) Japanese|Giapponese Japanese Modem|Japanese Modem Korea (NTSC)|Korea (NTSC) L&oad State|L&oad State Language :|Lingua: Layer|Layer Left|Sinistra Left trigger|Left trigger Less than|Less than Linux CD Drive|Linux CD Drive Load|Carica Load State|Carica stato Load State As|Carica stato come Load as executable|Carica come eseguibile Local Coordinates|Local Coordinates Log|Log Long|Long Long Write|Scrivi Log Loop Track Clear|Loop Track Clear Loop Track Start|Loop Track Start M68K|M68K M68K Registers|M68K Registers MSH2|MSH2 Master SH2|Master SH2 Memory|Memoria Memory &Editor|Memory &Editor Memory Breakpoints|Memory Breakpoints Memory Dump|Dump Memoria Memory Editor|Editor Memoria Memory Search|Memory Search Memory Transfer|Trasferimento Memoria Memory dump|Dump Memoria Mouse|Mouse Mpeg ROM|Mpeg ROM NBG0|NBG0 NBG0/RBG1 Info|NBG0/RBG1 Info NBG1|NBG1 NBG1 Info|NBG1 Info NBG2|NBG2 NBG2 Info|NBG2 Info NBG3|NBG3 NBG3 Info|NBG3 Info Netlink|Netlink Never|Never None|Nessuno Normal Sprite|Normal Sprite North America (NTSC)|North America (NTSC) OSD Core|OSD Core Ok|Ok On fullscreen|On fullscreen On message|On message Open &CD Rom...|Open &CD Rom... Open &ISO...|Open &ISO... Open CD Rom|Apri CD Rom OpenGL Video Interface|OpenGL Video Interface Options|Options Other Debug|Other Debug Pad|Pad Pad Configuration|Pad Configuration Pause|Pausa Pro Action Replay|Pro Action Replay Qt Keyboard Input Interface|Qt Keyboard Input Interface Quit|Esci R&un|Eseg&ui RBG0|RBG0 RBG0 Info|RBG0 Info Read|Read Region|Regione Registers|Registers Reset|Reset Resolution|Risoluzione Right|Destra Right trigger|Right trigger Run|Esegui S&ave State|S&ave State SCSP|SCSP SCU-DSP|SCU-DSP SDL Joystick Interface|SDL Joystick Interface SDL Sound Interface|SDL Sound Interface SH2 Debugger Interpreter|SH2 Debugger Interpreter SH2 Dynamic Recompiler|SH2 Dynamic Recompiler SH2 Interpreter|Interprete SH2 SH2 Registers|SH2 Registers SSH2|SSH2 Save|Salva Save As WAV|Save As WAV Save Bitmap|Save Bitmap Save Information|Salva Informazioni Save List|Salva lista Save MD0|Save MD0 Save MD1|Save MD1 Save MD2|Save MD2 Save MD3|Save MD3 Save Program|Save Program Save Selected|Save Selected Save Slot Registers|Save Slot Registers Save State|Salva stato Save State As|Salva stato come Save States|Salva stati Sc&reenshot|Sc&reenshot Screen Enabled|Screen Enabled Screenshot|Screenshot Search|Search Search Criteria|Search Criteria Search Memory|Search Memory Search Value:|Search Value: Search/Add Cheats|Search/Add Cheats Select a file to load your state|Selezionare un file per caricare il tuo stato Select your iso/cue/bin file|Selezionare un file iso/cue/bin Set PC to Start Address|Set PC to Start Address Settings|Impostazioni Shortcuts|Shortcuts Show FPS|Show FPS Show Log Window|Show Log Window Signed|Signed Signed 16-bit value|Signed 16-bit value Signed 32-bit value|Signed 32-bit value Signed 8-bit value|Signed 8-bit value Slave SH2|Slave SH2 Slot Info|Slot Info Slot Number:|Slot Number: Software OSD Interface|Software OSD Interface Software Video Interface|Software Video Interface Sound|Suono Sound Core|Sistema Audio Spanish|Spagnolo Start|Inizia Start Address:|Indirizzo Iniziale: Start in Fullscreen|Start in Fullscreen Status|Stato Step Into|Step Into Step Out|Step Out Step Over|Step Over Store|Store Synchronize Saturn internal clock with set time|Synchronize Saturn internal clock with set time System Clipping Coordinates|System Clipping Coordinates Text|Text Texture|Texture To|To To File...|Nel file... Tools|Strumenti Track Inf Loop Results|Track Inf Loop Results Transfer|Trasferimento Translation|Traduzione Type:|Type: Unable to add code|Impossibile aggiungere il codice Unable to change description|Impossibile cambiare la descrizione Unable to open file for loading|Impossibile aprire il file per il caricamento Unable to open file for saving|Impossibile aprire il file per il salvataggio Unable to remove code|Impossibile rimuovere il codice Unknow (%1)|Sconosciuto (%1) Unsigned|Unsigned Unsigned 16-bit value|Unsigned 16-bit value Unsigned 32-bit value|Unsigned 32-bit value Unsigned 8-bit value|Unsigned 8-bit value Up|Su Upload|Upload VDP1|VDP1 VDP1 Command Info|VDP1 Command Info VDP1 Command List|VDP1 Command List VDP2|VDP2 Value|Value Value :|Valore: Value:|Value: Vdp1|Vdp1 Vdp2|Vdp2 Video|Video Video Core|Sistema Video Video Driver|Driver Video Video Format|Formato Video View|View Viewer|Viewer Waiting Input...|In attesa di input... Width|Larghezza Window Resolution|Window Resolution Windows SPTI Driver|Windows SPTI Driver Word|Word Word Write|Word Write Write|Write Yabause Cheat Files (*.yct);;All Files (*)|Yabause File Cheat (*.yct);;Tutti i file (*) Yabause Qt GUI|Interfaccia Qt Yabause Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO|Yabause Qt Gui
Based on Yabause %1
http://yabause.org
The Yabause Team
Filipe AZEVEDO Yabause Save State (*.yss)|Yabause Salvataggio Stato (*.yss) Yabause is not initialized, can't manage backup ram.|Yabause is not initialized, can't manage backup ram. http://www.emu-compatibility.com/yabause/index.php?lang=uk|http://www.emu-compatibility.com/yabause/index.php?lang=it http://www.monkeystudio.org|http://www.monkeystudio.org toolBar|Barra degli Strumenti yabause-0.9.13.1/COPYING000644 001750 001750 00000043120 12256006204 016436 0ustar00guillaumeguillaume000000 000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. yabause-0.9.13.1/README.WIN000644 001750 001750 00000022142 12256006202 016716 0ustar00guillaumeguillaume000000 000000 _ _ / \_/ \ ___ _ ____ \ /___ ___ / || | __ / \ ____ \ // || \ / || | \ \\ \_// \ / // || // _ || |__\ \\ \ __/ \_// _ || \\_/ \_||______/ \ \\ \__ \_/ \_||___/ \____/ \____\ Yet Another Buggy And Uncomplete Saturn Emulator _________________________________________ Copyright (c) 2002-2011 Yabause team 1) Compiling instructions...................................20 2) How to use Yabause.......................................68 3) Contact information.....................................217 4) Disclaimer..............................................233 1 Compiling instructions______________________________________ Yabause is written in C using the DirectX 8.0, OpenGL, GLUT, and mini18n libraries, so you need a working C compiler(such as gcc) and these libraries, runtime and development packages: * You can find DirectX headers and libraries(for mingw) at http://alleg.sourceforge.net/wip.html as the file "dx80_mgw.zip". The actual runtime libraries(or headers/libraries for Visual C++) can be gotten from http://www.microsoft.com/DirectX * OpenGL should be included with your compiler, if it isn't, check on your compiler's website for links. * Check google for GLUT. I haven't been able to find a good source for it. * You can get mini18n from Yabause's sourceforge download page here: http://sourceforge.net/project/showfiles.php?group_id=89991&package_id=304859 Once these libraries installed, you should be ready to install Yabause. Compiling using mingw/cygwin__________________________________ All you have to do now is now is go into your mingw/cygwin shell environment, go into the directory where you extracted yabause, and type: "./configure". Once that's done(and there was no errors), type: "make". It should now take some time to compile so go grab yourself a sandwich or beer - whatever suits your fancy and it should be done in a few minutes. Now all you have to do is type "./src/yabause" in order to run it. Compiling using Visual C++____________________________________ Make sure you have the latest DirectX SDK and DDK installed. You can get both of them from Microsoft's website. Load up IDE that comes with Visual C++/Visual Studio, go into the file menu, open an existing project. Go into the yabause's src/windows directory and open yabause.sln. Now all you have to do is build it like any other Visual C++ project. You can compile for either x86 or x64(for those using Windows XP x64 or Vista x64. 2 How to use Yabause__________________________________________ While not necessarily needed, it is recommended you get a Saturn ROM BIOS image. Please don't ask us where to get one. Execute "yabause". The program will open a settings window. Basic Settings________________________________________________ The Disc Type setting allows you to choose whether you'd like to use a real cdrom or a cdrom image of the game you're trying to run. The Cue/Iso File setting allows you to specify the location of your Saturn game's cdrom image. The Drive Letter setting is for you to be able to choose which cdrom drive you want yabause to use when trying to boot a game. The SH2 Core setting is for you to be able to choose which SH2 Core to use. Unless you're a developer, chances are, you should leave it as the default: "Fast Interpreter". The Region setting allows you to choose which region of game you'll be booting. In most cases, it's best to leave it as "Auto-detect". The Bios ROM File setting allows you to specify the location of your Saturn ROM BIOS image. If you leave it blank, yabause will try to emulate the bios instead. It's better to specify a ROM BIOS image if you can since the emulated bios isn't 100% perfect and may not work with your games. The Backup RAM File setting allows you to specify the location of the Backup RAM file. This file allows yabause to store and load save games. The MPEG ROM File setting allows you to specify the location of a MPEG Card's ROM image. While not necessary, it does allow you to test out the saturn's vcd capabilities. The Cartridge Type setting allows you to choose which type of external cartridge to emulate. Some carts also require you to supply a rom filename, or a new filename for the emulator to write to. You can enter that information in the field below it. When you're done, just click on the "OK" button. If the bios location was specified correctly, emulation should start and you will see a brief animation of the saturn logo being formed. Special Note: Some settings require a restart of the program. There's also settings specifically for video, sound, and input. Video Settings________________________________________________ If you click on the "Video" tab another list of settings is displayed. You can set the Video Core to either do hardware rendering using OpenGL, software renderer(uses OpenGL the final draw though), or disable drawing completely with the "None" option. You can also "Enable Auto Frame-skipping" which basically tries to skip rendering video frames if emulation is lagging in an attempt to speed things up. The Full Screen on startup setting allows you to set Yabause to run using the full screen when started. You can also change what resolution is used while in full screen. The custom window size setting allows you to set the size of the video display for yabause. Sound Settings________________________________________________ If you click on the "Sound" tab another list of settings is displayed. You can set the Sound Core to either do sound mixing using DirectX Sound or disable sound completely with the "None" option. You can also adjust the sound volume using the volume slider underneath. Input Settings________________________________________________ If you click on the "Input" tab another list of settings is displayed. Here you can choose which peripheral(s) emulate. If you press "Config" another window will pop up. Here can set which device you'd like to use at the top of the window. Control settings can be changed by clicking on the equivalent button, and then when a new window pops up that says "waiting for input..." press a key/button and that will set the new setting for that control. Log Settings__________________________________________________ If you've compiled your own copy of Yabause with the processor define DEBUG, another tab will be available called "Log". This allows you to control whether or not the program should be logging emulation output using the "Enable Logging" setting. Log Type tells the program whether it should write the output to a file, or to a separate window so you can monitor the output while you're running the program. Here are the default key mappings(they may be subject to change): Up arrow - Up Left arrow - Left Down arrow - Down right arrow - Right k - A button l - B button m - C button u - X button i - Y button o - Z button x - Left Trigger z - Right Trigger j - Start button q - Quit program F1 - Toggle FPS display Alt-Enter - Toggle fullscreen/window mode ` - Enable Speed Throttle 1 - Toggle VDP2 NBG0 display 2 - Toggle VDP2 NBG1 display 3 - Toggle VDP2 NBG2 display 4 - Toggle VDP2 NBG3 display 5 - Toggle VDP2 RBG0 display 6 - Toggle VDP1 display F2 - Load State from slot 1 F3 - Load State from slot 2 F4 - Load State from slot 3 F5 - Load State from slot 4 F6 - Load State from slot 5 F7 - Load State from slot 6 F8 - Load State from slot 7 F9 - Load State from slot 8 F10 - Load State from slot 9 Shift-F2 - Save State to slot 1 Shift-F3 - Save State to slot 2 Shift-F4 - Save State to slot 3 Shift-F5 - Save State to slot 4 Shift-F6 - Save State to slot 5 Shift-F7 - Save State to slot 6 Shift-F8 - Save State to slot 7 Shift-F9 - Save State to slot 8 Shift-F10 - Save State to slot 9 Command-line Options__________________________________________ You can also run the program using command-line options. To see a full list, run "yabause --help" in the command prompt. 3 Contact information_________________________________________ General inquiries should go to: E-mail: guillaume@yabause.org E-mail: cwx@cyberwarriorx.com Windows Port-related inquiries should go to: E-mail: cwx@cyberwarriorx.com Web: http://yabause.org Please don't ask for roms, bios files or any other copyrighted stuff. Please use the forum when you have any questions if possible. 4 Disclaimer__________________________________________________ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA See the GNU General Public License details in COPYING. yabause-0.9.13.1/NEWS000644 001750 001750 00000000000 12256006204 016070 0ustar00guillaumeguillaume000000 000000 yabause-0.9.13.1/README.MAC000644 001750 001750 00000007154 12256006202 016667 0ustar00guillaumeguillaume000000 000000 _ _ / \_/ \ ___ _ ____ \ /___ ___ / || | __ / \ ____ \ // || \ / || | \ \\ \_// \ / // || // _ || |__\ \\ \ __/ \_// _ || \\_/ \_||______/ \ \\ \__ \_/ \_||___/ \____/ \____\ Yet Another Buggy And Uncomplete Saturn Emulator ____________________________________ Copyright (c) 2002-2012 Yabause team 1) Introduction.............................................20 2) Compiling instructions...................................26 3) How to use Yabause.......................................46 4) Known Issues.............................................92 1 Introduction________________________________________________ This file documents the mac version only, for general information check the README file. 2 Compiling instructions______________________________________ Yabause is written in C and Objective C using the Cocoa, IOKit, OpenGL, and CoreAudio frameworks. All of these frameworks should be installed by default on your Mac OS X system. You must have the Mac OS X 10.6 SDK installed by the Xcode installer in order to build Yabause. In addition, you will need at least Xcode 3.2. Once you have Xcode installed, you should be ready to build Yabause. Uncompress the Yabause source archive, and open the Yabause.xcodeproj in the src/cocoa directory. From there it should be as easy as hitting the Build or Build and Run button in Xcode. This should generate a Yabause.app file that can be run just like any other application bundle on Mac OS X. 3 How to use Yabause__________________________________________ Before using Yabause, you need to configure a few things in the Preferences dialog (Yabause > Preferences). 3.1 Configuration_____________________________________________ First, set the BIOS path. Yabause can run some games without a BIOS, but many of them need it. If you want to use the emulated BIOS, select the checkbox for that. Next, set up the video and sound cores. For the video core, you have 4 options: 1. OpenGL Hardware Video Core - Potentially the fastest video core choice, at least with a discrete video card. However, it is also the least accurate. 2. Software Video Core - The most accurate video core you can use, but also the slowest. 3. Grand Central Dispatch Software Core - A multithreaded version of the Software Video Core. On a multi-core system this should be significantly faster than the Software core with a similar accuracy level. 4. Disable Video - Does exactly what it sounds like. For the sound core, you only have two options: 1. Core Audio Sound Core - The default sound core. Select this one if you want sound. 2. Disable Sound - Does exactly what it sounds like. Next, set up keys for input. Go to the Input tab, and configure each button (at least on Controller 1). For the moment, this is limited to keyboard input only. There are other options you can configure as well in here, including BRAM (for saving), a MPEG ROM (for games that use the VideoCD/MPEG card), and a cartridge for the cartridge port on the Saturn. Once eveything is set, you can start emulation with the "File > Run BIOS", "File > Run CDROM" or "File > Run Image" menu options. Don't use the Run BIOS entry if you're using BIOS emulation. 4 Known Issues________________________________________________ When running in GDB, you should not use fullscreen mode. If Yabause crashes while running under GDB in fullscreen mode, you will probably get stuck with no way to exit. This should only affect developers and shouldn't ever be an issue for normal users. yabause-0.9.13.1/TODO000644 001750 001750 00000000052 12256006202 016066 0ustar00guillaumeguillaume000000 000000 Everything moved to sourceforge trackers. yabause-0.9.13.1/src/vdp2.c000644 001750 001750 00000062660 12256006112 017221 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2005 Guillaume Duhamel Copyright 2004-2007 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "vdp2.h" #include "debug.h" #include "scu.h" #include "sh2core.h" #include "vdp1.h" #include "yabause.h" #include "movie.h" #include "osdcore.h" u8 * Vdp2Ram; u8 * Vdp2ColorRam; Vdp2 * Vdp2Regs; Vdp2Internal_struct Vdp2Internal; Vdp2External_struct Vdp2External; static Vdp2 Vdp2Lines[270]; static int autoframeskipenab=0; static int throttlespeed=0; u64 lastticks=0; static int fps; ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL Vdp2RamReadByte(u32 addr) { addr &= 0x7FFFF; return T1ReadByte(Vdp2Ram, addr); } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL Vdp2RamReadWord(u32 addr) { addr &= 0x7FFFF; return T1ReadWord(Vdp2Ram, addr); } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL Vdp2RamReadLong(u32 addr) { addr &= 0x7FFFF; return T1ReadLong(Vdp2Ram, addr); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2RamWriteByte(u32 addr, u8 val) { addr &= 0x7FFFF; T1WriteByte(Vdp2Ram, addr, val); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2RamWriteWord(u32 addr, u16 val) { addr &= 0x7FFFF; T1WriteWord(Vdp2Ram, addr, val); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2RamWriteLong(u32 addr, u32 val) { addr &= 0x7FFFF; T1WriteLong(Vdp2Ram, addr, val); } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL Vdp2ColorRamReadByte(u32 addr) { addr &= 0xFFF; return T2ReadByte(Vdp2ColorRam, addr); } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL Vdp2ColorRamReadWord(u32 addr) { addr &= 0xFFF; return T2ReadWord(Vdp2ColorRam, addr); } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL Vdp2ColorRamReadLong(u32 addr) { addr &= 0xFFF; return T2ReadLong(Vdp2ColorRam, addr); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2ColorRamWriteByte(u32 addr, u8 val) { addr &= 0xFFF; T2WriteByte(Vdp2ColorRam, addr, val); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2ColorRamWriteWord(u32 addr, u16 val) { addr &= 0xFFF; T2WriteWord(Vdp2ColorRam, addr, val); // if (Vdp2Internal.ColorMode == 0) // T1WriteWord(Vdp2ColorRam, addr + 0x800, val); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2ColorRamWriteLong(u32 addr, u32 val) { addr &= 0xFFF; T2WriteLong(Vdp2ColorRam, addr, val); } ////////////////////////////////////////////////////////////////////////////// int Vdp2Init(void) { if ((Vdp2Regs = (Vdp2 *) calloc(1, sizeof(Vdp2))) == NULL) return -1; if ((Vdp2Ram = T1MemoryInit(0x80000)) == NULL) return -1; if ((Vdp2ColorRam = T2MemoryInit(0x1000)) == NULL) return -1; Vdp2Reset(); return 0; } ////////////////////////////////////////////////////////////////////////////// void Vdp2DeInit(void) { if (Vdp2Regs) free(Vdp2Regs); Vdp2Regs = NULL; if (Vdp2Ram) T1MemoryDeInit(Vdp2Ram); Vdp2Ram = NULL; if (Vdp2ColorRam) T2MemoryDeInit(Vdp2ColorRam); Vdp2ColorRam = NULL; } ////////////////////////////////////////////////////////////////////////////// void Vdp2Reset(void) { Vdp2Regs->TVMD = 0x0000; Vdp2Regs->EXTEN = 0x0000; Vdp2Regs->TVSTAT = Vdp2Regs->TVSTAT & 0x1; Vdp2Regs->VRSIZE = 0x0000; // fix me(version should be set) Vdp2Regs->RAMCTL = 0x0000; Vdp2Regs->BGON = 0x0000; Vdp2Regs->CHCTLA = 0x0000; Vdp2Regs->CHCTLB = 0x0000; Vdp2Regs->BMPNA = 0x0000; Vdp2Regs->MPOFN = 0x0000; Vdp2Regs->MPABN2 = 0x0000; Vdp2Regs->MPCDN2 = 0x0000; Vdp2Regs->SCXIN0 = 0x0000; Vdp2Regs->SCXDN0 = 0x0000; Vdp2Regs->SCYIN0 = 0x0000; Vdp2Regs->SCYDN0 = 0x0000; Vdp2Regs->ZMXN0.all = 0x00000000; Vdp2Regs->ZMYN0.all = 0x00000000; Vdp2Regs->SCXIN1 = 0x0000; Vdp2Regs->SCXDN1 = 0x0000; Vdp2Regs->SCYIN1 = 0x0000; Vdp2Regs->SCYDN1 = 0x0000; Vdp2Regs->ZMXN1.all = 0x00000000; Vdp2Regs->ZMYN1.all = 0x00000000; Vdp2Regs->SCXN2 = 0x0000; Vdp2Regs->SCYN2 = 0x0000; Vdp2Regs->SCXN3 = 0x0000; Vdp2Regs->SCYN3 = 0x0000; Vdp2Regs->ZMCTL = 0x0000; Vdp2Regs->SCRCTL = 0x0000; Vdp2Regs->VCSTA.all = 0x00000000; Vdp2Regs->BKTAU = 0x0000; Vdp2Regs->BKTAL = 0x0000; Vdp2Regs->RPMD = 0x0000; Vdp2Regs->RPRCTL = 0x0000; Vdp2Regs->KTCTL = 0x0000; Vdp2Regs->KTAOF = 0x0000; Vdp2Regs->OVPNRA = 0x0000; Vdp2Regs->OVPNRB = 0x0000; Vdp2Regs->WPSX0 = 0x0000; Vdp2Regs->WPSY0 = 0x0000; Vdp2Regs->WPEX0 = 0x0000; Vdp2Regs->WPEY0 = 0x0000; Vdp2Regs->WPSX1 = 0x0000; Vdp2Regs->WPSY1 = 0x0000; Vdp2Regs->WPEX1 = 0x0000; Vdp2Regs->WPEY1 = 0x0000; Vdp2Regs->WCTLA = 0x0000; Vdp2Regs->WCTLB = 0x0000; Vdp2Regs->WCTLC = 0x0000; Vdp2Regs->WCTLD = 0x0000; Vdp2Regs->SPCTL = 0x0000; Vdp2Regs->SDCTL = 0x0000; Vdp2Regs->CRAOFA = 0x0000; Vdp2Regs->CRAOFB = 0x0000; Vdp2Regs->LNCLEN = 0x0000; Vdp2Regs->SFPRMD = 0x0000; Vdp2Regs->CCCTL = 0x0000; Vdp2Regs->SFCCMD = 0x0000; Vdp2Regs->PRISA = 0x0000; Vdp2Regs->PRISB = 0x0000; Vdp2Regs->PRISC = 0x0000; Vdp2Regs->PRISD = 0x0000; Vdp2Regs->PRINA = 0x0000; Vdp2Regs->PRINB = 0x0000; Vdp2Regs->PRIR = 0x0000; Vdp2Regs->CCRNA = 0x0000; Vdp2Regs->CCRNB = 0x0000; Vdp2Regs->CLOFEN = 0x0000; Vdp2Regs->CLOFSL = 0x0000; Vdp2Regs->COAR = 0x0000; Vdp2Regs->COAG = 0x0000; Vdp2Regs->COAB = 0x0000; Vdp2Regs->COBR = 0x0000; Vdp2Regs->COBG = 0x0000; Vdp2Regs->COBB = 0x0000; yabsys.VBlankLineCount = 224; Vdp2Internal.ColorMode = 0; Vdp2External.disptoggle = 0xFF; } ////////////////////////////////////////////////////////////////////////////// void Vdp2VBlankIN(void) { VIDCore->Vdp2DrawEnd(); /* this should be done after a frame change or a plot trigger */ Vdp1Regs->COPR = 0; /* I'm not 100% sure about this, but it seems that when using manual change we should swap framebuffers in the "next field" and thus, clear the CEF... now we're lying a little here as we're not swapping the framebuffers. */ if (Vdp1External.manualchange) Vdp1Regs->EDSR >>= 1; Vdp2Regs->TVSTAT |= 0x0008; ScuSendVBlankIN(); if (yabsys.IsSSH2Running) SH2SendInterrupt(SSH2, 0x43, 0x6); } ////////////////////////////////////////////////////////////////////////////// void Vdp2HBlankIN(void) { Vdp2Regs->TVSTAT |= 0x0004; ScuSendHBlankIN(); if (yabsys.IsSSH2Running) SH2SendInterrupt(SSH2, 0x41, 0x2); } ////////////////////////////////////////////////////////////////////////////// void Vdp2HBlankOUT(void) { Vdp2Regs->TVSTAT &= ~0x0004; if (yabsys.LineCount < 270) memcpy(Vdp2Lines + yabsys.LineCount, Vdp2Regs, sizeof(Vdp2)); } ////////////////////////////////////////////////////////////////////////////// Vdp2 * Vdp2RestoreRegs(int line) { return line > 270 ? NULL : Vdp2Lines + line; } ////////////////////////////////////////////////////////////////////////////// static void FPSDisplay(void) { static int fpsframecount = 0; static u64 fpsticks; OSDPushMessage(OSDMSG_FPS, 1, "%02d/%02d FPS", fps, yabsys.IsPal ? 50 : 60); OSDPushMessage(OSDMSG_DEBUG, 1, "%d %d %s %s", framecounter, lagframecounter, MovieStatus, InputDisplayString); fpsframecount++; if(YabauseGetTicks() >= fpsticks + yabsys.tickfreq) { fps = fpsframecount; fpsframecount = 0; fpsticks = YabauseGetTicks(); } } ////////////////////////////////////////////////////////////////////////////// void SpeedThrottleEnable(void) { throttlespeed = 1; } ////////////////////////////////////////////////////////////////////////////// void SpeedThrottleDisable(void) { throttlespeed = 0; } ////////////////////////////////////////////////////////////////////////////// void Vdp2VBlankOUT(void) { static int framestoskip = 0; static int framesskipped = 0; static int skipnextframe = 0; static u64 curticks = 0; static u64 diffticks = 0; static u32 framecount = 0; static u64 onesecondticks = 0; static VideoInterface_struct * saved = NULL; Vdp2Regs->TVSTAT = (Vdp2Regs->TVSTAT & ~0x0008) | 0x0002; if (skipnextframe && (! saved)) { saved = VIDCore; VIDCore = &VIDDummy; } else if (saved && (! skipnextframe)) { VIDCore = saved; saved = NULL; } VIDCore->Vdp2DrawStart(); if (Vdp2Regs->TVMD & 0x8000) { VIDCore->Vdp2DrawScreens(); if (Vdp1Regs->PTMR == 2) Vdp1Draw(); } else if (Vdp1Regs->PTMR == 2) Vdp1NoDraw(); FPSDisplay(); if ((Vdp1Regs->FBCR & 2) && (Vdp1Regs->TVMR & 8)) Vdp1External.manualerase = 1; if (!skipnextframe) { framesskipped = 0; if (framestoskip > 0) skipnextframe = 1; } else { framestoskip--; if (framestoskip < 1) skipnextframe = 0; else skipnextframe = 1; framesskipped++; } // Do Frame Skip/Frame Limiting/Speed Throttling here if (throttlespeed) { // Should really depend on how fast we're rendering the frames if (framestoskip < 1) framestoskip = 6; } //when in frame advance, disable frame skipping else if (autoframeskipenab && FrameAdvanceVariable == 0) { framecount++; if (framecount > (yabsys.IsPal ? 50 : 60)) { framecount = 1; onesecondticks = 0; } curticks = YabauseGetTicks(); diffticks = curticks-lastticks; if ((onesecondticks+diffticks) > ((yabsys.OneFrameTime * (u64)framecount) + (yabsys.OneFrameTime / 2)) && framesskipped < 9) { // Skip the next frame skipnextframe = 1; // How many frames should we skip? framestoskip = 1; } else if ((onesecondticks+diffticks) < ((yabsys.OneFrameTime * (u64)framecount) - (yabsys.OneFrameTime / 2))) { // Check to see if we need to limit speed at all for (;;) { curticks = YabauseGetTicks(); diffticks = curticks-lastticks; if ((onesecondticks+diffticks) >= (yabsys.OneFrameTime * (u64)framecount)) break; } } onesecondticks += diffticks; lastticks = curticks; } ScuSendVBlankOUT(); } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL Vdp2ReadByte(u32 addr) { LOG("VDP2 register byte read = %08X\n", addr); addr &= 0x1FF; return 0; } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL Vdp2ReadWord(u32 addr) { addr &= 0x1FF; switch (addr) { case 0x000: return Vdp2Regs->TVMD; case 0x002: if (!(Vdp2Regs->EXTEN & 0x200)) { // Latch HV counter on read // Vdp2Regs->HCNT = ?; Vdp2Regs->VCNT = yabsys.LineCount; Vdp2Regs->TVSTAT |= 0x200; } return Vdp2Regs->EXTEN; case 0x004: // Clear External latch and sync flags Vdp2Regs->TVSTAT &= 0xFCFF; // if TVMD's DISP bit is cleared, TVSTAT's VBLANK bit is always set if (Vdp2Regs->TVMD & 0x8000) return Vdp2Regs->TVSTAT; else return (Vdp2Regs->TVSTAT | 0x8); case 0x006: return Vdp2Regs->VRSIZE; case 0x008: return Vdp2Regs->HCNT; case 0x00A: return Vdp2Regs->VCNT; default: { LOG("Unhandled VDP2 word read: %08X\n", addr); break; } } return 0; } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL Vdp2ReadLong(u32 addr) { LOG("VDP2 register long read = %08X\n", addr); addr &= 0x1FF; return 0; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2WriteByte(u32 addr, UNUSED u8 val) { LOG("VDP2 register byte write = %08X\n", addr); addr &= 0x1FF; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2WriteWord(u32 addr, u16 val) { addr &= 0x1FF; switch (addr) { case 0x000: Vdp2Regs->TVMD = val; yabsys.VBlankLineCount = 224+(val & 0x30); return; case 0x002: Vdp2Regs->EXTEN = val; return; case 0x004: // TVSTAT is read-only return; case 0x006: Vdp2Regs->VRSIZE = val; return; case 0x008: // HCNT is read-only return; case 0x00A: // VCNT is read-only return; case 0x00C: // Reserved return; case 0x00E: Vdp2Regs->RAMCTL = val; Vdp2Internal.ColorMode = (val >> 12) & 0x3; return; case 0x010: Vdp2Regs->CYCA0L = val; return; case 0x012: Vdp2Regs->CYCA0U = val; return; case 0x014: Vdp2Regs->CYCA1L = val; return; case 0x016: Vdp2Regs->CYCA1U = val; return; case 0x018: Vdp2Regs->CYCB0L = val; return; case 0x01A: Vdp2Regs->CYCB0U = val; return; case 0x01C: Vdp2Regs->CYCB1L = val; return; case 0x01E: Vdp2Regs->CYCB1U = val; return; case 0x020: Vdp2Regs->BGON = val; return; case 0x022: Vdp2Regs->MZCTL = val; return; case 0x024: Vdp2Regs->SFSEL = val; return; case 0x026: Vdp2Regs->SFCODE = val; return; case 0x028: Vdp2Regs->CHCTLA = val; return; case 0x02A: Vdp2Regs->CHCTLB = val; return; case 0x02C: Vdp2Regs->BMPNA = val; return; case 0x02E: Vdp2Regs->BMPNB = val; return; case 0x030: Vdp2Regs->PNCN0 = val; return; case 0x032: Vdp2Regs->PNCN1 = val; return; case 0x034: Vdp2Regs->PNCN2 = val; return; case 0x036: Vdp2Regs->PNCN3 = val; return; case 0x038: Vdp2Regs->PNCR = val; return; case 0x03A: Vdp2Regs->PLSZ = val; return; case 0x03C: Vdp2Regs->MPOFN = val; return; case 0x03E: Vdp2Regs->MPOFR = val; return; case 0x040: Vdp2Regs->MPABN0 = val; return; case 0x042: Vdp2Regs->MPCDN0 = val; return; case 0x044: Vdp2Regs->MPABN1 = val; return; case 0x046: Vdp2Regs->MPCDN1 = val; return; case 0x048: Vdp2Regs->MPABN2 = val; return; case 0x04A: Vdp2Regs->MPCDN2 = val; return; case 0x04C: Vdp2Regs->MPABN3 = val; return; case 0x04E: Vdp2Regs->MPCDN3 = val; return; case 0x050: Vdp2Regs->MPABRA = val; return; case 0x052: Vdp2Regs->MPCDRA = val; return; case 0x054: Vdp2Regs->MPEFRA = val; return; case 0x056: Vdp2Regs->MPGHRA = val; return; case 0x058: Vdp2Regs->MPIJRA = val; return; case 0x05A: Vdp2Regs->MPKLRA = val; return; case 0x05C: Vdp2Regs->MPMNRA = val; return; case 0x05E: Vdp2Regs->MPOPRA = val; return; case 0x060: Vdp2Regs->MPABRB = val; return; case 0x062: Vdp2Regs->MPCDRB = val; return; case 0x064: Vdp2Regs->MPEFRB = val; return; case 0x066: Vdp2Regs->MPGHRB = val; return; case 0x068: Vdp2Regs->MPIJRB = val; return; case 0x06A: Vdp2Regs->MPKLRB = val; return; case 0x06C: Vdp2Regs->MPMNRB = val; return; case 0x06E: Vdp2Regs->MPOPRB = val; return; case 0x070: Vdp2Regs->SCXIN0 = val; return; case 0x072: Vdp2Regs->SCXDN0 = val; return; case 0x074: Vdp2Regs->SCYIN0 = val; return; case 0x076: Vdp2Regs->SCYDN0 = val; return; case 0x078: Vdp2Regs->ZMXN0.part.I = val; return; case 0x07A: Vdp2Regs->ZMXN0.part.D = val; return; case 0x07C: Vdp2Regs->ZMYN0.part.I = val; return; case 0x07E: Vdp2Regs->ZMYN0.part.D = val; return; case 0x080: Vdp2Regs->SCXIN1 = val; return; case 0x082: Vdp2Regs->SCXDN1 = val; return; case 0x084: Vdp2Regs->SCYIN1 = val; return; case 0x086: Vdp2Regs->SCYDN1 = val; return; case 0x088: Vdp2Regs->ZMXN1.part.I = val; return; case 0x08A: Vdp2Regs->ZMXN1.part.D = val; return; case 0x08C: Vdp2Regs->ZMYN1.part.I = val; return; case 0x08E: Vdp2Regs->ZMYN1.part.D = val; return; case 0x090: Vdp2Regs->SCXN2 = val; return; case 0x092: Vdp2Regs->SCYN2 = val; return; case 0x094: Vdp2Regs->SCXN3 = val; return; case 0x096: Vdp2Regs->SCYN3 = val; return; case 0x098: Vdp2Regs->ZMCTL = val; return; case 0x09A: Vdp2Regs->SCRCTL = val; return; case 0x09C: Vdp2Regs->VCSTA.part.U = val; return; case 0x09E: Vdp2Regs->VCSTA.part.L = val; return; case 0x0A0: Vdp2Regs->LSTA0.part.U = val; return; case 0x0A2: Vdp2Regs->LSTA0.part.L = val; return; case 0x0A4: Vdp2Regs->LSTA1.part.U = val; return; case 0x0A6: Vdp2Regs->LSTA1.part.L = val; return; case 0x0A8: Vdp2Regs->LCTA.part.U = val; return; case 0x0AA: Vdp2Regs->LCTA.part.L = val; return; case 0x0AC: Vdp2Regs->BKTAU = val; return; case 0x0AE: Vdp2Regs->BKTAL = val; return; case 0x0B0: Vdp2Regs->RPMD = val; return; case 0x0B2: Vdp2Regs->RPRCTL = val; return; case 0x0B4: Vdp2Regs->KTCTL = val; return; case 0x0B6: Vdp2Regs->KTAOF = val; return; case 0x0B8: Vdp2Regs->OVPNRA = val; return; case 0x0BA: Vdp2Regs->OVPNRB = val; return; case 0x0BC: Vdp2Regs->RPTA.part.U = val; return; case 0x0BE: Vdp2Regs->RPTA.part.L = val; return; case 0x0C0: Vdp2Regs->WPSX0 = val; return; case 0x0C2: Vdp2Regs->WPSY0 = val; return; case 0x0C4: Vdp2Regs->WPEX0 = val; return; case 0x0C6: Vdp2Regs->WPEY0 = val; return; case 0x0C8: Vdp2Regs->WPSX1 = val; return; case 0x0CA: Vdp2Regs->WPSY1 = val; return; case 0x0CC: Vdp2Regs->WPEX1 = val; return; case 0x0CE: Vdp2Regs->WPEY1 = val; return; case 0x0D0: Vdp2Regs->WCTLA = val; return; case 0x0D2: Vdp2Regs->WCTLB = val; return; case 0x0D4: Vdp2Regs->WCTLC = val; return; case 0x0D6: Vdp2Regs->WCTLD = val; return; case 0x0D8: Vdp2Regs->LWTA0.part.U = val; return; case 0x0DA: Vdp2Regs->LWTA0.part.L = val; return; case 0x0DC: Vdp2Regs->LWTA1.part.U = val; return; case 0x0DE: Vdp2Regs->LWTA1.part.L = val; return; case 0x0E0: Vdp2Regs->SPCTL = val; return; case 0x0E2: Vdp2Regs->SDCTL = val; return; case 0x0E4: Vdp2Regs->CRAOFA = val; return; case 0x0E6: Vdp2Regs->CRAOFB = val; return; case 0x0E8: Vdp2Regs->LNCLEN = val; return; case 0x0EA: Vdp2Regs->SFPRMD = val; return; case 0x0EC: Vdp2Regs->CCCTL = val; return; case 0x0EE: Vdp2Regs->SFCCMD = val; return; case 0x0F0: Vdp2Regs->PRISA = val; return; case 0x0F2: Vdp2Regs->PRISB = val; return; case 0x0F4: Vdp2Regs->PRISC = val; return; case 0x0F6: Vdp2Regs->PRISD = val; return; case 0x0F8: Vdp2Regs->PRINA = val; return; case 0x0FA: Vdp2Regs->PRINB = val; return; case 0x0FC: Vdp2Regs->PRIR = val; return; case 0x0FE: // Reserved return; case 0x100: Vdp2Regs->CCRSA = val; return; case 0x102: Vdp2Regs->CCRSB = val; return; case 0x104: Vdp2Regs->CCRSC = val; return; case 0x106: Vdp2Regs->CCRSD = val; return; case 0x108: Vdp2Regs->CCRNA = val; return; case 0x10A: Vdp2Regs->CCRNB = val; return; case 0x10C: Vdp2Regs->CCRR = val; return; case 0x10E: Vdp2Regs->CCRLB = val; return; case 0x110: Vdp2Regs->CLOFEN = val; return; case 0x112: Vdp2Regs->CLOFSL = val; return; case 0x114: Vdp2Regs->COAR = val; return; case 0x116: Vdp2Regs->COAG = val; return; case 0x118: Vdp2Regs->COAB = val; return; case 0x11A: Vdp2Regs->COBR = val; return; case 0x11C: Vdp2Regs->COBG = val; return; case 0x11E: Vdp2Regs->COBB = val; return; default: { LOG("Unhandled VDP2 word write: %08X\n", addr); break; } } } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2WriteLong(u32 addr, u32 val) { Vdp2WriteWord(addr,val>>16); Vdp2WriteWord(addr+2,val&0xFFFF); return; } ////////////////////////////////////////////////////////////////////////////// int Vdp2SaveState(FILE *fp) { int offset; IOCheck_struct check; offset = StateWriteHeader(fp, "VDP2", 1); // Write registers ywrite(&check, (void *)Vdp2Regs, sizeof(Vdp2), 1, fp); // Write VDP2 ram ywrite(&check, (void *)Vdp2Ram, 0x80000, 1, fp); // Write CRAM ywrite(&check, (void *)Vdp2ColorRam, 0x1000, 1, fp); // Write internal variables ywrite(&check, (void *)&Vdp2Internal, sizeof(Vdp2Internal_struct), 1, fp); return StateFinishHeader(fp, offset); } ////////////////////////////////////////////////////////////////////////////// int Vdp2LoadState(FILE *fp, UNUSED int version, int size) { IOCheck_struct check; // Read registers yread(&check, (void *)Vdp2Regs, sizeof(Vdp2), 1, fp); // Read VDP2 ram yread(&check, (void *)Vdp2Ram, 0x80000, 1, fp); // Read CRAM yread(&check, (void *)Vdp2ColorRam, 0x1000, 1, fp); // Read internal variables yread(&check, (void *)&Vdp2Internal, sizeof(Vdp2Internal_struct), 1, fp); return size; } ////////////////////////////////////////////////////////////////////////////// void ToggleNBG0(void) { Vdp2External.disptoggle ^= 0x1; } ////////////////////////////////////////////////////////////////////////////// void ToggleNBG1(void) { Vdp2External.disptoggle ^= 0x2; } ////////////////////////////////////////////////////////////////////////////// void ToggleNBG2(void) { Vdp2External.disptoggle ^= 0x4; } ////////////////////////////////////////////////////////////////////////////// void ToggleNBG3(void) { Vdp2External.disptoggle ^= 0x8; } ////////////////////////////////////////////////////////////////////////////// void ToggleRBG0(void) { Vdp2External.disptoggle ^= 0x10; } ////////////////////////////////////////////////////////////////////////////// void ToggleFullScreen(void) { if (VIDCore->IsFullscreen()) { VIDCore->Resize(320, 224, 0); } else { VIDCore->Resize(640, 480, 1); } } ////////////////////////////////////////////////////////////////////////////// void EnableAutoFrameSkip(void) { autoframeskipenab = 1; lastticks = YabauseGetTicks(); } ////////////////////////////////////////////////////////////////////////////// void DisableAutoFrameSkip(void) { autoframeskipenab = 0; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/sndal.h000644 001750 001750 00000001602 12256006173 017450 0ustar00guillaumeguillaume000000 000000 /* Copyright 2009 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SNDAL_H #define SNDAL_H #define SNDCORE_AL 2 extern SoundInterface_struct SNDAL; #endif /* !SNDAL_H */ yabause-0.9.13.1/src/vdp1.h000644 001750 001750 00000007727 12256006174 017240 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2005 Guillaume Duhamel Copyright 2004-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VDP1_H #define VDP1_H #include "memory.h" #define VIDCORE_DEFAULT -1 #define VIDCORE_DUMMY 0 typedef struct { int id; const char *Name; int (*Init)(void); void (*DeInit)(void); void (*Resize)(unsigned int, unsigned int, int); int (*IsFullscreen)(void); // VDP1 specific int (*Vdp1Reset)(void); void (*Vdp1DrawStart)(void); void (*Vdp1DrawEnd)(void); void (*Vdp1NormalSpriteDraw)(void); void (*Vdp1ScaledSpriteDraw)(void); void (*Vdp1DistortedSpriteDraw)(void); void (*Vdp1PolygonDraw)(void); void (*Vdp1PolylineDraw)(void); void (*Vdp1LineDraw)(void); void (*Vdp1UserClipping)(void); void (*Vdp1SystemClipping)(void); void (*Vdp1LocalCoordinate)(void); // VDP2 specific int (*Vdp2Reset)(void); void (*Vdp2DrawStart)(void); void (*Vdp2DrawEnd)(void); void (*Vdp2DrawScreens)(void); void (*GetGlSize)(int *width, int *height); } VideoInterface_struct; extern VideoInterface_struct *VIDCore; extern VideoInterface_struct VIDDummy; extern u8 * Vdp1Ram; u8 FASTCALL Vdp1RamReadByte(u32); u16 FASTCALL Vdp1RamReadWord(u32); u32 FASTCALL Vdp1RamReadLong(u32); void FASTCALL Vdp1RamWriteByte(u32, u8); void FASTCALL Vdp1RamWriteWord(u32, u16); void FASTCALL Vdp1RamWriteLong(u32, u32); u8 FASTCALL Vdp1FrameBufferReadByte(u32); u16 FASTCALL Vdp1FrameBufferReadWord(u32); u32 FASTCALL Vdp1FrameBufferReadLong(u32); void FASTCALL Vdp1FrameBufferWriteByte(u32, u8); void FASTCALL Vdp1FrameBufferWriteWord(u32, u16); void FASTCALL Vdp1FrameBufferWriteLong(u32, u32); typedef struct { u16 TVMR; u16 FBCR; u16 PTMR; u16 EWDR; u16 EWLR; u16 EWRR; u16 ENDR; u16 EDSR; u16 LOPR; u16 COPR; u16 MODR; u32 addr; int disptoggle_dont_use_me; // not used anymore, see Vdp1External_struct u16 localX; u16 localY; u16 systemclipX1; u16 systemclipY1; u16 systemclipX2; u16 systemclipY2; u16 userclipX1; u16 userclipY1; u16 userclipX2; u16 userclipY2; } Vdp1; extern Vdp1 * Vdp1Regs; // struct for Vdp1 part that shouldn't be saved typedef struct { int disptoggle; int manualerase; int manualchange; } Vdp1External_struct; extern Vdp1External_struct Vdp1External; typedef struct { u16 CMDCTRL; u16 CMDLINK; u16 CMDPMOD; u16 CMDCOLR; u16 CMDSRCA; u16 CMDSIZE; s16 CMDXA; s16 CMDYA; s16 CMDXB; s16 CMDYB; s16 CMDXC; s16 CMDYC; s16 CMDXD; s16 CMDYD; u16 CMDGRDA; } vdp1cmd_struct; int Vdp1Init(void); void Vdp1DeInit(void); int VideoInit(int coreid); int VideoChangeCore(int coreid); void VideoDeInit(void); void Vdp1Reset(void); u8 FASTCALL Vdp1ReadByte(u32); u16 FASTCALL Vdp1ReadWord(u32); u32 FASTCALL Vdp1ReadLong(u32); void FASTCALL Vdp1WriteByte(u32, u8); void FASTCALL Vdp1WriteWord(u32, u16); void FASTCALL Vdp1WriteLong(u32, u32); void Vdp1Draw(void); void Vdp1NoDraw(void); void FASTCALL Vdp1ReadCommand(vdp1cmd_struct *cmd, u32 addr); int Vdp1SaveState(FILE *fp); int Vdp1LoadState(FILE *fp, int version, int size); char *Vdp1DebugGetCommandNumberName(u32 number); void Vdp1DebugCommand(u32 number, char *outstring); u32 *Vdp1DebugTexture(u32 number, int *w, int *h); void ToggleVDP1(void); #endif yabause-0.9.13.1/src/yabause.c000644 001750 001750 00000070245 12256006124 020000 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2005 Guillaume Duhamel Copyright 2004-2006 Theo Berkau Copyright 2006 Anders Montonen This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #ifdef WIN32 #include #endif #include #include "yabause.h" #include "cheat.h" #include "cs0.h" #include "cs2.h" #include "debug.h" #include "error.h" #include "memory.h" #include "m68kcore.h" #include "peripheral.h" #include "scsp.h" #include "scu.h" #include "sh2core.h" #include "smpc.h" #include "vdp2.h" #include "yui.h" #include "bios.h" #include "movie.h" #include "osdcore.h" #ifdef HAVE_LIBSDL #if defined(__APPLE__) || defined(GEKKO) #include #else #include "SDL.h" #endif #endif #if defined(_MSC_VER) || !defined(HAVE_SYS_TIME_H) #include #else #include #endif #ifdef _arch_dreamcast #include #endif #ifdef GEKKO #include #endif #ifdef PSP #include "psp/common.h" #endif #ifdef SYS_PROFILE_H #include SYS_PROFILE_H #else #define DONT_PROFILE #include "profile.h" #endif #if defined(SH2_DYNAREC) #include "sh2_dynarec/sh2_dynarec.h" #endif #if HAVE_GDBSTUB #include "gdb/stub.h" #endif ////////////////////////////////////////////////////////////////////////////// yabsys_struct yabsys; const char *bupfilename = NULL; u64 tickfreq; ////////////////////////////////////////////////////////////////////////////// #ifndef NO_CLI void print_usage(const char *program_name) { printf("Yabause v" VERSION "\n"); printf("\n" "Purpose:\n" " This program is intended to be a Sega Saturn emulator\n" "\n" "Usage: %s [OPTIONS]...\n", program_name); printf(" -h --help Print help and exit\n"); printf(" -b STRING --bios=STRING bios file\n"); printf(" -i STRING --iso=STRING iso/cue file\n"); printf(" -c STRING --cdrom=STRING cdrom path\n"); printf(" -ns --nosound turn sound off\n"); printf(" -a --autostart autostart emulation\n"); printf(" -f --fullscreen start in fullscreen mode\n"); } #endif ////////////////////////////////////////////////////////////////////////////// void YabauseChangeTiming(int freqtype) { // Setup all the variables related to timing const double freq_base = yabsys.IsPal ? 28437500.0 : (39375000.0 / 11.0) * 8.0; // i.e. 8 * 3.579545... = 28.636363... MHz const double freq_mult = (freqtype == CLKTYPE_26MHZ) ? 15.0/16.0 : 1.0; const double freq_shifted = (freq_base * freq_mult) * (1 << YABSYS_TIMING_BITS); const double usec_shifted = 1.0e6 * (1 << YABSYS_TIMING_BITS); const double deciline_time = yabsys.IsPal ? 1.0 / 50 / 313 / 10 : 1.0 / (60/1.001) / 263 / 10; yabsys.DecilineCount = 0; yabsys.LineCount = 0; yabsys.CurSH2FreqType = freqtype; yabsys.DecilineStop = (u32) (freq_shifted * deciline_time + 0.5); yabsys.SH2CycleFrac = 0; yabsys.DecilineUsec = (u32) (usec_shifted * deciline_time + 0.5); yabsys.UsecFrac = 0; } ////////////////////////////////////////////////////////////////////////////// int YabauseInit(yabauseinit_struct *init) { // Need to set this first, so init routines see it yabsys.UseThreads = init->usethreads; // Initialize both cpu's if (SH2Init(init->sh2coretype) != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("SH2")); return -1; } if ((BiosRom = T2MemoryInit(0x80000)) == NULL) return -1; if ((HighWram = T2MemoryInit(0x100000)) == NULL) return -1; if ((LowWram = T2MemoryInit(0x100000)) == NULL) return -1; if ((BupRam = T1MemoryInit(0x10000)) == NULL) return -1; if (LoadBackupRam(init->buppath) != 0) FormatBackupRam(BupRam, 0x10000); BupRamWritten = 0; bupfilename = init->buppath; if (CartInit(init->cartpath, init->carttype) != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("Cartridge")); return -1; } MappedMemoryInit(); if (VideoInit(init->vidcoretype) != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("Video")); return -1; } // Initialize input core if (PerInit(init->percoretype) != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("Peripheral")); return -1; } if (Cs2Init(init->carttype, init->cdcoretype, init->cdpath, init->mpegpath, init->netlinksetting) != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("CS2")); return -1; } if (ScuInit() != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("SCU")); return -1; } if (M68KInit(init->m68kcoretype) != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("M68K")); return -1; } if (ScspInit(init->sndcoretype) != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("SCSP/M68K")); return -1; } if (Vdp1Init() != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("VDP1")); return -1; } if (Vdp2Init() != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("VDP2")); return -1; } if (SmpcInit(init->regionid, init->clocksync, init->basetime) != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("SMPC")); return -1; } if (CheatInit() != 0) { YabSetError(YAB_ERR_CANNOTINIT, _("Cheat System")); return -1; } YabauseSetVideoFormat(init->videoformattype); YabauseChangeTiming(CLKTYPE_26MHZ); yabsys.DecilineMode = 1; if (init->frameskip) EnableAutoFrameSkip(); #ifdef YAB_PORT_OSD OSDChangeCore(init->osdcoretype); #else OSDChangeCore(OSDCORE_DEFAULT); #endif if (init->biospath != NULL && strlen(init->biospath)) { if (LoadBios(init->biospath) != 0) { YabSetError(YAB_ERR_FILENOTFOUND, (void *)init->biospath); return -2; } yabsys.emulatebios = 0; } else yabsys.emulatebios = 1; yabsys.usequickload = 0; #if defined(SH2_DYNAREC) if(SH2Core->id==2) { sh2_dynarec_init(); } #endif YabauseResetNoLoad(); if (yabsys.usequickload || yabsys.emulatebios) { if (YabauseQuickLoadGame() != 0) { if (yabsys.emulatebios) { YabSetError(YAB_ERR_CANNOTINIT, _("Game")); return -2; } else YabauseResetNoLoad(); } } #ifdef HAVE_GDBSTUB GdbStubInit(MSH2, 43434); #endif return 0; } ////////////////////////////////////////////////////////////////////////////// void YabauseDeInit(void) { SH2DeInit(); if (BiosRom) T2MemoryDeInit(BiosRom); BiosRom = NULL; if (HighWram) T2MemoryDeInit(HighWram); HighWram = NULL; if (LowWram) T2MemoryDeInit(LowWram); LowWram = NULL; if (BupRam) { if (T123Save(BupRam, 0x10000, 1, bupfilename) != 0) YabSetError(YAB_ERR_FILEWRITE, (void *)bupfilename); T1MemoryDeInit(BupRam); } BupRam = NULL; CartDeInit(); Cs2DeInit(); ScuDeInit(); ScspDeInit(); Vdp1DeInit(); Vdp2DeInit(); SmpcDeInit(); PerDeInit(); VideoDeInit(); CheatDeInit(); } ////////////////////////////////////////////////////////////////////////////// void YabauseSetDecilineMode(int on) { yabsys.DecilineMode = (on != 0); } ////////////////////////////////////////////////////////////////////////////// void YabauseResetNoLoad(void) { SH2Reset(MSH2); YabauseStopSlave(); memset(HighWram, 0, 0x100000); memset(LowWram, 0, 0x100000); // Reset CS0 area here // Reset CS1 area here Cs2Reset(); ScuReset(); ScspReset(); Vdp1Reset(); Vdp2Reset(); SmpcReset(); SH2PowerOn(MSH2); } ////////////////////////////////////////////////////////////////////////////// void YabauseReset(void) { YabauseResetNoLoad(); if (yabsys.usequickload || yabsys.emulatebios) { if (YabauseQuickLoadGame() != 0) { if (yabsys.emulatebios) YabSetError(YAB_ERR_CANNOTINIT, _("Game")); else YabauseResetNoLoad(); } } } ////////////////////////////////////////////////////////////////////////////// void YabauseResetButton(void) { // This basically emulates the reset button behaviour of the saturn. This // is the better way of reseting the system since some operations (like // backup ram access) shouldn't be interrupted and this allows for that. SmpcResetButton(); } ////////////////////////////////////////////////////////////////////////////// int YabauseExec(void) { //automatically advance lag frames, this should be optional later if (FrameAdvanceVariable > 0 && LagFrameFlag == 1){ FrameAdvanceVariable = NeedAdvance; //advance a frame YabauseEmulate(); FrameAdvanceVariable = Paused; //pause next time return(0); } if (FrameAdvanceVariable == Paused){ ScspMuteAudio(SCSP_MUTE_SYSTEM); return(0); } if (FrameAdvanceVariable == NeedAdvance){ //advance a frame FrameAdvanceVariable = Paused; //pause next time ScspUnMuteAudio(SCSP_MUTE_SYSTEM); YabauseEmulate(); } if (FrameAdvanceVariable == RunNormal ) { //run normally ScspUnMuteAudio(SCSP_MUTE_SYSTEM); YabauseEmulate(); } return 0; } ////////////////////////////////////////////////////////////////////////////// #ifndef USE_SCSP2 int saved_centicycles; #endif int YabauseEmulate(void) { int oneframeexec = 0; const u32 cyclesinc = yabsys.DecilineMode ? yabsys.DecilineStop : yabsys.DecilineStop * 10; const u32 usecinc = yabsys.DecilineMode ? yabsys.DecilineUsec : yabsys.DecilineUsec * 10; #ifndef USE_SCSP2 unsigned int m68kcycles; // Integral M68k cycles per call unsigned int m68kcenticycles; // 1/100 M68k cycles per call if (yabsys.IsPal) { /* 11.2896MHz / 50Hz / 313 lines / 10 calls/line = 72.20 cycles/call */ m68kcycles = yabsys.DecilineMode ? 72 : 722; m68kcenticycles = yabsys.DecilineMode ? 20 : 0; } else { /* 11.2896MHz / 60Hz / 263 lines / 10 calls/line = 71.62 cycles/call */ m68kcycles = yabsys.DecilineMode ? 71 : 716; m68kcenticycles = yabsys.DecilineMode ? 62 : 20; } #endif DoMovie(); #if defined(SH2_DYNAREC) if(SH2Core->id==2) { if (yabsys.IsPal) YabauseDynarecOneFrameExec(722,0); // m68kcycles,m68kcenticycles else YabauseDynarecOneFrameExec(716,20); return 0; } #endif while (!oneframeexec) { PROFILE_START("Total Emulation"); if (yabsys.DecilineMode) { // Since we run the SCU with half the number of cycles we send // to SH2Exec(), we always compute an even number of cycles here // and leave any odd remainder in SH2CycleFrac. u32 sh2cycles; yabsys.SH2CycleFrac += cyclesinc; sh2cycles = (yabsys.SH2CycleFrac >> (YABSYS_TIMING_BITS + 1)) << 1; yabsys.SH2CycleFrac &= ((YABSYS_TIMING_MASK << 1) | 1); PROFILE_START("MSH2"); SH2Exec(MSH2, sh2cycles); PROFILE_STOP("MSH2"); PROFILE_START("SSH2"); if (yabsys.IsSSH2Running) SH2Exec(SSH2, sh2cycles); PROFILE_STOP("SSH2"); #ifdef USE_SCSP2 PROFILE_START("SCSP"); ScspExec(1); PROFILE_STOP("SCSP"); #endif yabsys.DecilineCount++; if(yabsys.DecilineCount == 9) { // HBlankIN PROFILE_START("hblankin"); Vdp2HBlankIN(); PROFILE_STOP("hblankin"); } PROFILE_START("SCU"); ScuExec(sh2cycles / 2); PROFILE_STOP("SCU"); } else { // !DecilineMode const u32 decilinecycles = yabsys.DecilineStop >> YABSYS_TIMING_BITS; u32 sh2cycles; yabsys.SH2CycleFrac += cyclesinc; sh2cycles = (yabsys.SH2CycleFrac >> (YABSYS_TIMING_BITS + 1)) << 1; yabsys.SH2CycleFrac &= ((YABSYS_TIMING_MASK << 1) | 1); PROFILE_START("MSH2"); SH2Exec(MSH2, sh2cycles - decilinecycles); PROFILE_STOP("MSH2"); PROFILE_START("SSH2"); if (yabsys.IsSSH2Running) SH2Exec(SSH2, sh2cycles - decilinecycles); PROFILE_STOP("SSH2"); PROFILE_START("hblankin"); Vdp2HBlankIN(); PROFILE_STOP("hblankin"); PROFILE_START("MSH2"); SH2Exec(MSH2, decilinecycles); PROFILE_STOP("MSH2"); PROFILE_START("SSH2"); if (yabsys.IsSSH2Running) SH2Exec(SSH2, decilinecycles); PROFILE_STOP("SSH2"); #ifdef USE_SCSP2 PROFILE_START("SCSP"); ScspExec(10); PROFILE_STOP("SCSP"); #endif PROFILE_START("SCU"); ScuExec(sh2cycles / 2); PROFILE_STOP("SCU"); } // if (yabsys.DecilineMode) #ifndef USE_SCSP2 PROFILE_START("68K"); M68KSync(); // Wait for the previous iteration to finish PROFILE_STOP("68K"); #endif if (!yabsys.DecilineMode || yabsys.DecilineCount == 10) { // HBlankOUT PROFILE_START("hblankout"); Vdp2HBlankOUT(); PROFILE_STOP("hblankout"); #ifndef USE_SCSP2 PROFILE_START("SCSP"); ScspExec(); PROFILE_STOP("SCSP"); #endif yabsys.DecilineCount = 0; yabsys.LineCount++; if (yabsys.LineCount == yabsys.VBlankLineCount) { PROFILE_START("vblankin"); // VBlankIN SmpcINTBACKEnd(); Vdp2VBlankIN(); PROFILE_STOP("vblankin"); CheatDoPatches(); } else if (yabsys.LineCount == yabsys.MaxLineCount) { // VBlankOUT PROFILE_START("VDP1/VDP2"); Vdp2VBlankOUT(); yabsys.LineCount = 0; oneframeexec = 1; PROFILE_STOP("VDP1/VDP2"); } } yabsys.UsecFrac += usecinc; PROFILE_START("SMPC"); SmpcExec(yabsys.UsecFrac >> YABSYS_TIMING_BITS); PROFILE_STOP("SMPC"); PROFILE_START("CDB"); Cs2Exec(yabsys.UsecFrac >> YABSYS_TIMING_BITS); PROFILE_STOP("CDB"); yabsys.UsecFrac &= YABSYS_TIMING_MASK; #ifndef USE_SCSP2 { int cycles; PROFILE_START("68K"); cycles = m68kcycles; saved_centicycles += m68kcenticycles; if (saved_centicycles >= 100) { cycles++; saved_centicycles -= 100; } M68KExec(cycles); PROFILE_STOP("68K"); } #endif PROFILE_STOP("Total Emulation"); } #ifndef USE_SCSP2 M68KSync(); #endif return 0; } ////////////////////////////////////////////////////////////////////////////// void YabauseStartSlave(void) { if (yabsys.emulatebios) { CurrentSH2 = SSH2; MappedMemoryWriteLong(0xFFFFFFE0, 0xA55A03F1); // BCR1 MappedMemoryWriteLong(0xFFFFFFE4, 0xA55A00FC); // BCR2 MappedMemoryWriteLong(0xFFFFFFE8, 0xA55A5555); // WCR MappedMemoryWriteLong(0xFFFFFFEC, 0xA55A0070); // MCR MappedMemoryWriteWord(0xFFFFFEE0, 0x0000); // ICR MappedMemoryWriteWord(0xFFFFFEE2, 0x0000); // IPRA MappedMemoryWriteWord(0xFFFFFE60, 0x0F00); // VCRWDT MappedMemoryWriteWord(0xFFFFFE62, 0x6061); // VCRA MappedMemoryWriteWord(0xFFFFFE64, 0x6263); // VCRB MappedMemoryWriteWord(0xFFFFFE66, 0x6465); // VCRC MappedMemoryWriteWord(0xFFFFFE68, 0x6600); // VCRD MappedMemoryWriteWord(0xFFFFFEE4, 0x6869); // VCRWDT MappedMemoryWriteLong(0xFFFFFFA8, 0x0000006C); // VCRDMA1 MappedMemoryWriteLong(0xFFFFFFA0, 0x0000006D); // VCRDMA0 MappedMemoryWriteLong(0xFFFFFF0C, 0x0000006E); // VCRDIV MappedMemoryWriteLong(0xFFFFFE10, 0x00000081); // TIER CurrentSH2 = MSH2; SH2GetRegisters(SSH2, &SSH2->regs); SSH2->regs.R[15] = 0x06001000; SSH2->regs.VBR = 0x06000400; SSH2->regs.PC = MappedMemoryReadLong(0x06000250); if (MappedMemoryReadLong(0x060002AC) != 0) SSH2->regs.R[15] = MappedMemoryReadLong(0x060002AC); SH2SetRegisters(SSH2, &SSH2->regs); } else SH2PowerOn(SSH2); yabsys.IsSSH2Running = 1; } ////////////////////////////////////////////////////////////////////////////// void YabauseStopSlave(void) { SH2Reset(SSH2); yabsys.IsSSH2Running = 0; } ////////////////////////////////////////////////////////////////////////////// u64 YabauseGetTicks(void) { #ifdef WIN32 u64 ticks; QueryPerformanceCounter((LARGE_INTEGER *)&ticks); return ticks; #elif defined(_arch_dreamcast) return (u64) timer_ms_gettime64(); #elif defined(GEKKO) return gettime(); #elif defined(PSP) return sceKernelGetSystemTimeWide(); #elif defined(HAVE_GETTIMEOFDAY) struct timeval tv; gettimeofday(&tv, NULL); return (u64)tv.tv_sec * 1000000 + tv.tv_usec; #elif defined(HAVE_LIBSDL) return (u64)SDL_GetTicks(); #endif } ////////////////////////////////////////////////////////////////////////////// void YabauseSetVideoFormat(int type) { yabsys.IsPal = type; yabsys.MaxLineCount = type ? 313 : 263; #ifdef WIN32 QueryPerformanceFrequency((LARGE_INTEGER *)&yabsys.tickfreq); #elif defined(_arch_dreamcast) yabsys.tickfreq = 1000; #elif defined(GEKKO) yabsys.tickfreq = secs_to_ticks(1); #elif defined(PSP) yabsys.tickfreq = 1000000; #elif defined(HAVE_GETTIMEOFDAY) yabsys.tickfreq = 1000000; #elif defined(HAVE_LIBSDL) yabsys.tickfreq = 1000; #endif yabsys.OneFrameTime = type ? (yabsys.tickfreq / 50) : (yabsys.tickfreq * 1001 / 60000); Vdp2Regs->TVSTAT = Vdp2Regs->TVSTAT | (type & 0x1); ScspChangeVideoFormat(type); YabauseChangeTiming(yabsys.CurSH2FreqType); lastticks = YabauseGetTicks(); } ////////////////////////////////////////////////////////////////////////////// void YabauseSpeedySetup(void) { u32 data; int i; if (yabsys.emulatebios) BiosInit(); else { // Setup the vector table area, etc.(all bioses have it at 0x00000600-0x00000810) for (i = 0; i < 0x210; i+=4) { data = MappedMemoryReadLong(0x00000600+i); MappedMemoryWriteLong(0x06000000+i, data); } // Setup the bios function pointers, etc.(all bioses have it at 0x00000820-0x00001100) for (i = 0; i < 0x8E0; i+=4) { data = MappedMemoryReadLong(0x00000820+i); MappedMemoryWriteLong(0x06000220+i, data); } // I'm not sure this is really needed for (i = 0; i < 0x700; i+=4) { data = MappedMemoryReadLong(0x00001100+i); MappedMemoryWriteLong(0x06001100+i, data); } // Fix some spots in 0x06000210-0x0600032C area MappedMemoryWriteLong(0x06000234, 0x000002AC); MappedMemoryWriteLong(0x06000238, 0x000002BC); MappedMemoryWriteLong(0x0600023C, 0x00000350); MappedMemoryWriteLong(0x06000240, 0x32524459); MappedMemoryWriteLong(0x0600024C, 0x00000000); MappedMemoryWriteLong(0x06000268, MappedMemoryReadLong(0x00001344)); MappedMemoryWriteLong(0x0600026C, MappedMemoryReadLong(0x00001348)); MappedMemoryWriteLong(0x0600029C, MappedMemoryReadLong(0x00001354)); MappedMemoryWriteLong(0x060002C4, MappedMemoryReadLong(0x00001104)); MappedMemoryWriteLong(0x060002C8, MappedMemoryReadLong(0x00001108)); MappedMemoryWriteLong(0x060002CC, MappedMemoryReadLong(0x0000110C)); MappedMemoryWriteLong(0x060002D0, MappedMemoryReadLong(0x00001110)); MappedMemoryWriteLong(0x060002D4, MappedMemoryReadLong(0x00001114)); MappedMemoryWriteLong(0x060002D8, MappedMemoryReadLong(0x00001118)); MappedMemoryWriteLong(0x060002DC, MappedMemoryReadLong(0x0000111C)); MappedMemoryWriteLong(0x06000328, 0x000004C8); MappedMemoryWriteLong(0x0600032C, 0x00001800); // Fix SCU interrupts for (i = 0; i < 0x80; i+=4) MappedMemoryWriteLong(0x06000A00+i, 0x0600083C); } // Set the cpu's, etc. to sane states // Set CD block to a sane state Cs2Area->reg.HIRQ = 0xFC1; Cs2Area->isdiskchanged = 0; Cs2Area->reg.CR1 = (Cs2Area->status << 8) | ((Cs2Area->options & 0xF) << 4) | (Cs2Area->repcnt & 0xF); Cs2Area->reg.CR2 = (Cs2Area->ctrladdr << 8) | Cs2Area->track; Cs2Area->reg.CR3 = (Cs2Area->index << 8) | ((Cs2Area->FAD >> 16) & 0xFF); Cs2Area->reg.CR4 = (u16) Cs2Area->FAD; Cs2Area->satauth = 4; // Set Master SH2 registers accordingly SH2GetRegisters(MSH2, &MSH2->regs); for (i = 0; i < 15; i++) MSH2->regs.R[i] = 0x00000000; MSH2->regs.R[15] = 0x06002000; MSH2->regs.SR.all = 0x00000000; MSH2->regs.GBR = 0x00000000; MSH2->regs.VBR = 0x06000000; MSH2->regs.MACH = 0x00000000; MSH2->regs.MACL = 0x00000000; MSH2->regs.PR = 0x00000000; SH2SetRegisters(MSH2, &MSH2->regs); // Set SCU registers to sane states ScuRegs->D1AD = ScuRegs->D2AD = 0; ScuRegs->D0EN = 0x101; ScuRegs->IST = 0x2006; ScuRegs->AIACK = 0x1; ScuRegs->ASR0 = ScuRegs->ASR1 = 0x1FF01FF0; ScuRegs->AREF = 0x1F; ScuRegs->RSEL = 0x1; // Set SMPC registers to sane states SmpcRegs->COMREG = 0x10; SmpcInternalVars->resd = 0; // Set VDP1 registers to sane states Vdp1Regs->EDSR = 3; Vdp1Regs->localX = 160; Vdp1Regs->localY = 112; Vdp1Regs->systemclipX2 = 319; Vdp1Regs->systemclipY2 = 223; // Set VDP2 registers to sane states memset(Vdp2Regs, 0, sizeof(Vdp2)); Vdp2Regs->TVMD = 0x8000; Vdp2Regs->TVSTAT = 0x020A; Vdp2Regs->CYCA0L = 0x0F44; Vdp2Regs->CYCA0U = 0xFFFF; Vdp2Regs->CYCA1L = 0xFFFF; Vdp2Regs->CYCA1U = 0xFFFF; Vdp2Regs->CYCB0L = 0xFFFF; Vdp2Regs->CYCB0U = 0xFFFF; Vdp2Regs->CYCB1L = 0xFFFF; Vdp2Regs->CYCB1U = 0xFFFF; Vdp2Regs->BGON = 0x0001; Vdp2Regs->PNCN0 = 0x8000; Vdp2Regs->MPABN0 = 0x0303; Vdp2Regs->MPCDN0 = 0x0303; Vdp2Regs->ZMXN0.all = 0x00010000; Vdp2Regs->ZMYN0.all = 0x00010000; Vdp2Regs->ZMXN1.all = 0x00010000; Vdp2Regs->ZMYN1.all = 0x00010000; Vdp2Regs->BKTAL = 0x4000; Vdp2Regs->SPCTL = 0x0020; Vdp2Regs->PRINA = 0x0007; Vdp2Regs->CLOFEN = 0x0001; Vdp2Regs->COAR = 0x0200; Vdp2Regs->COAG = 0x0200; Vdp2Regs->COAB = 0x0200; } ////////////////////////////////////////////////////////////////////////////// int YabauseQuickLoadGame(void) { partition_struct * lgpartition; u8 *buffer; u32 addr; u32 size; u32 blocks; unsigned int i, i2; dirrec_struct dirrec; Cs2Area->outconcddev = Cs2Area->filter + 0; Cs2Area->outconcddevnum = 0; // read in lba 0/FAD 150 if ((lgpartition = Cs2ReadUnFilteredSector(150)) == NULL) return -1; // Make sure we're dealing with a saturn game buffer = lgpartition->block[lgpartition->numblocks - 1]->data; YabauseSpeedySetup(); if (memcmp(buffer, "SEGA SEGASATURN", 15) == 0) { // figure out how many more sectors we need to read size = (buffer[0xE0] << 24) | (buffer[0xE1] << 16) | (buffer[0xE2] << 8) | buffer[0xE3]; blocks = size >> 11; if ((size % 2048) != 0) blocks++; // Figure out where to load the first program addr = (buffer[0xF0] << 24) | (buffer[0xF1] << 16) | (buffer[0xF2] << 8) | buffer[0xF3]; // Free Block lgpartition->size = 0; Cs2FreeBlock(lgpartition->block[lgpartition->numblocks - 1]); lgpartition->blocknum[lgpartition->numblocks - 1] = 0xFF; lgpartition->numblocks = 0; // Copy over ip to 0x06002000 for (i = 0; i < blocks; i++) { if ((lgpartition = Cs2ReadUnFilteredSector(150+i)) == NULL) return -1; buffer = lgpartition->block[lgpartition->numblocks - 1]->data; if (size >= 2048) { for (i2 = 0; i2 < 2048; i2++) MappedMemoryWriteByte(0x06002000 + (i * 0x800) + i2, buffer[i2]); } else { for (i2 = 0; i2 < size; i2++) MappedMemoryWriteByte(0x06002000 + (i * 0x800) + i2, buffer[i2]); } size -= 2048; // Free Block lgpartition->size = 0; Cs2FreeBlock(lgpartition->block[lgpartition->numblocks - 1]); lgpartition->blocknum[lgpartition->numblocks - 1] = 0xFF; lgpartition->numblocks = 0; } SH2WriteNotify(0x6002000, blocks<<11); // Ok, now that we've loaded the ip, now it's time to load the // First Program // Figure out where the first program is located if ((lgpartition = Cs2ReadUnFilteredSector(166)) == NULL) return -1; // Figure out root directory's location // Retrieve directory record's lba Cs2CopyDirRecord(lgpartition->block[lgpartition->numblocks - 1]->data + 0x9C, &dirrec); // Free Block lgpartition->size = 0; Cs2FreeBlock(lgpartition->block[lgpartition->numblocks - 1]); lgpartition->blocknum[lgpartition->numblocks - 1] = 0xFF; lgpartition->numblocks = 0; // Now then, fetch the root directory's records if ((lgpartition = Cs2ReadUnFilteredSector(dirrec.lba+150)) == NULL) return -1; buffer = lgpartition->block[lgpartition->numblocks - 1]->data; // Skip the first two records, read in the last one for (i = 0; i < 3; i++) { Cs2CopyDirRecord(buffer, &dirrec); buffer += dirrec.recordsize; } size = dirrec.size; blocks = size >> 11; if ((dirrec.size % 2048) != 0) blocks++; // Free Block lgpartition->size = 0; Cs2FreeBlock(lgpartition->block[lgpartition->numblocks - 1]); lgpartition->blocknum[lgpartition->numblocks - 1] = 0xFF; lgpartition->numblocks = 0; // Copy over First Program to addr for (i = 0; i < blocks; i++) { if ((lgpartition = Cs2ReadUnFilteredSector(150+dirrec.lba+i)) == NULL) return -1; buffer = lgpartition->block[lgpartition->numblocks - 1]->data; if (size >= 2048) { for (i2 = 0; i2 < 2048; i2++) MappedMemoryWriteByte(addr + (i * 0x800) + i2, buffer[i2]); } else { for (i2 = 0; i2 < size; i2++) MappedMemoryWriteByte(addr + (i * 0x800) + i2, buffer[i2]); } size -= 2048; // Free Block lgpartition->size = 0; Cs2FreeBlock(lgpartition->block[lgpartition->numblocks - 1]); lgpartition->blocknum[lgpartition->numblocks - 1] = 0xFF; lgpartition->numblocks = 0; } SH2WriteNotify(addr, blocks<<11); // Now setup SH2 registers to start executing at ip code SH2GetRegisters(MSH2, &MSH2->regs); MSH2->regs.PC = 0x06002E00; SH2SetRegisters(MSH2, &MSH2->regs); } else { // Ok, we're not. Time to bail! // Free Block lgpartition->size = 0; Cs2FreeBlock(lgpartition->block[lgpartition->numblocks - 1]); lgpartition->blocknum[lgpartition->numblocks - 1] = 0xFF; lgpartition->numblocks = 0; return -1; } return 0; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/m68kd.c000644 001750 001750 00000105174 12256006143 017301 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "core.h" #include "m68kd.h" #include "scsp.h" // for c68k_word_read() typedef struct { u16 mask; u16 inst; const char *name; int (*disasm)(u32, u16, char *); } m68kdis_struct; ////////////////////////////////////////////////////////////////////////////// static int setsizestr(u16 size, char *outstring) { switch (size & 0x3) { case 0x1: return sprintf(outstring, ".b "); case 0x3: return sprintf(outstring, ".w "); case 0x2: return sprintf(outstring, ".l "); default: return sprintf(outstring, " "); } } ////////////////////////////////////////////////////////////////////////////// static int setsizestr2(u16 size, char *outstring) { switch (size & 0x3) { case 0x0: return sprintf(outstring, ".b "); case 0x1: return sprintf(outstring, ".w "); case 0x2: return sprintf(outstring, ".l "); default: return sprintf(outstring, " "); } } ////////////////////////////////////////////////////////////////////////////// static int setimmstr(u32 addr, u16 size, int *addsize, char *outstring) { switch (size & 0x3) { case 0x0: *addsize+=2; return sprintf(outstring, "#0x%X", (unsigned int)(c68k_word_read(addr) & 0xFF)); case 0x1: *addsize+=2; return sprintf(outstring, "#0x%X", (unsigned int)c68k_word_read(addr)); case 0x2: *addsize+=4; return sprintf(outstring, "#0x%X", (unsigned int)((c68k_word_read(addr) << 16) | c68k_word_read(addr+2))); default: return 0; } } ////////////////////////////////////////////////////////////////////////////// static int seteafieldstr(u32 addr, u16 modereg, int *addsize, char *outstring) { switch ((modereg >> 3) & 0x7) { case 0x0: // Dn return sprintf(outstring, "d%d", modereg & 0x7); case 0x1: // An return sprintf(outstring, "a%d", modereg & 0x7); case 0x2: // (An) return sprintf(outstring, "(a%d)", modereg & 0x7); case 0x3: // (An)+ return sprintf(outstring, "(a%d)+", modereg & 0x7); case 0x4: // -(An) return sprintf(outstring, "-(a%d)", modereg & 0x7); case 0x5: // (d16, An) *addsize += 2; return sprintf(outstring, "0x%X(a%d)", (unsigned int)c68k_word_read(addr), modereg & 0x7); case 0x6: // (d8,An,Xn) // fix me *addsize += 2; return sprintf(outstring, "0x%X(a%d, Xn)", (unsigned int)(c68k_word_read(addr) & 0xFF), modereg & 0x7); case 0x7: switch (modereg & 0x7) { case 0x0: // (xxx).W *addsize += 2; // fix me? return sprintf(outstring, "(0x%X).w", (unsigned int)c68k_word_read(addr)); case 0x1: // (xxx).L *addsize += 4; // fix me? return sprintf(outstring, "(0x%X).l", (unsigned int)((c68k_word_read(addr) << 16) | c68k_word_read(addr+2))); case 0x4: // # *addsize += 2; // fix me? return sprintf(outstring, "#0x%X", (unsigned int)c68k_word_read(addr)); case 0x2: // (d16,PC) *addsize += 2; return sprintf(outstring, "0x%X(PC)", (unsigned int)c68k_word_read(addr)); case 0x3: // (d8,PC,Xn) // fix me return 0; default: break; } default: break; } return 0; } ////////////////////////////////////////////////////////////////////////////// static int setcondstr(u16 cond, char *outstring) { switch (cond & 0xF) { case 0x0: // True return sprintf(outstring, "t "); case 0x1: // False return sprintf(outstring, "f "); case 0x2: // High return sprintf(outstring, "hi"); case 0x3: // Low or Same return sprintf(outstring, "ls"); case 0x4: // Carry Clear return sprintf(outstring, "cc"); case 0x5: // Carry Set return sprintf(outstring, "cs"); case 0x6: // Not Equal return sprintf(outstring, "ne"); case 0x7: // Equal return sprintf(outstring, "eq"); case 0x8: // Overflow Clear return sprintf(outstring, "vc"); case 0x9: // Overflow Set return sprintf(outstring, "vs"); case 0xA: // Plus return sprintf(outstring, "pl"); case 0xB: // Minus return sprintf(outstring, "mi"); case 0xC: // Greater or Equal return sprintf(outstring, "ge"); case 0xD: // Less Than return sprintf(outstring, "lt"); case 0xE: // Greater Than return sprintf(outstring, "gt"); case 0xF: // Less or Equal return sprintf(outstring, "le"); default: break; } return 0; } ////////////////////////////////////////////////////////////////////////////// static int setbranchdispstr(u32 addr, u16 op, int *addsize, char *outstring) { if ((op & 0xFF) == 0xFF) { // 32-bit displacement *addsize += 4; return sprintf(outstring, ".l %X", (unsigned int)(addr + ((c68k_word_read(addr) << 16) | c68k_word_read(addr+2)))); } else if ((op & 0xFF) == 0x00) { // 16-bit displacement *addsize += 2; return sprintf(outstring, ".w %X", (unsigned int)((s32)addr + (s32)(s16)c68k_word_read(addr))); } // 8-bit displacement return sprintf(outstring, ".s %X", (unsigned int)((s32)addr + (s32)(s8)(op & 0xFF))); } ////////////////////////////////////////////////////////////////////////////// static int disabcd(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "abcd"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disadd(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "add"); outstring += setsizestr2(op >> 6, outstring); outstring += sprintf(outstring, " "); if (op & 0x100) { // Dn, outstring += sprintf(outstring, "d%d, ", (op >> 9) & 7); seteafieldstr(addr+size, op, &size, outstring); } else { // , Dn outstring += seteafieldstr(addr+size, op, &size, outstring); sprintf(outstring, ", d%d", (op >> 9) & 7); } return size; } ////////////////////////////////////////////////////////////////////////////// static int disadda(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "adda"); if ((op & 0x1C0) == 0xC0) outstring += sprintf(outstring, ".w "); else outstring += sprintf(outstring, ".l "); outstring += seteafieldstr(addr+size, op, &size, outstring); outstring += sprintf(outstring, ", a%d", (op >> 9) & 0x7); return size; } ////////////////////////////////////////////////////////////////////////////// static int disaddi(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "addi"); outstring += setsizestr2(op >> 6, outstring); outstring += setimmstr(addr+size, op >> 6, &size, outstring); outstring += sprintf(outstring, ", "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disaddq(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "addq"); outstring += setsizestr2(op >> 6, outstring); outstring += sprintf(outstring, " "); outstring += sprintf(outstring, "#%d, ", (op >> 9) & 7); // fix me seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disaddx(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "addx"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disand(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "and"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disandi(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "andi"); outstring += setsizestr2(op >> 6, outstring); outstring += sprintf(outstring, " "); outstring += setimmstr(addr+size, op >> 6, &size, outstring); outstring += sprintf(outstring, ", "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disanditoccr(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "andi to CCR"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disasl(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "asl"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disasr(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "asr"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disbcc(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "b"); outstring += setcondstr(op >> 8, outstring); setbranchdispstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disbkpt(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "bkpt"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disbra(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "bra"); setbranchdispstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disbchg(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "bchg"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disbclrd(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "bclr"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disbclrs(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "bclr "); outstring += setimmstr(addr+size, 0, &size, outstring); outstring += sprintf(outstring, ", "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disbsetd(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "bset"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disbsets(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "bset "); outstring += setimmstr(addr+size, 0, &size, outstring); outstring += sprintf(outstring, ", "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disbtstd(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "btst"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disbtsts(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "btst "); outstring += setimmstr(addr+size, 0, &size, outstring); outstring += sprintf(outstring, ", "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disbsr(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "bsr"); setbranchdispstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int dischk(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "chk"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disclr(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "clr"); outstring += setsizestr2((op >> 6), outstring); outstring += sprintf(outstring, " "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disdbcc(u32 addr, u16 op, char *outstring) { outstring += sprintf(outstring, "db"); outstring += setcondstr(op >> 8, outstring); outstring += sprintf(outstring, " "); sprintf(outstring, " d%d, %X", op & 0x7, (unsigned int)((s32)addr+2+(s32)(s16)c68k_word_read(addr+2))); return 4; } ////////////////////////////////////////////////////////////////////////////// static int discmpb(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "cmp.b "); outstring += seteafieldstr(addr+size, op, &size, outstring); outstring += sprintf(outstring, ", d%d", (op >> 9) & 0x7); return size; } ////////////////////////////////////////////////////////////////////////////// static int discmpw(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "cmp.w "); outstring += seteafieldstr(addr+size, op, &size, outstring); outstring += sprintf(outstring, ", d%d", (op >> 9) & 0x7); return size; } ////////////////////////////////////////////////////////////////////////////// static int discmpl(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "cmp.l "); outstring += seteafieldstr(addr+size, op, &size, outstring); outstring += sprintf(outstring, ", d%d", (op >> 9) & 0x7); return size; } ////////////////////////////////////////////////////////////////////////////// static int discmpaw(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "cmpa.w"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int discmpal(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "cmpa.l"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int discmpi(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "cmpi"); outstring += setsizestr2((op >> 6), outstring); outstring += sprintf(outstring, " "); outstring += setimmstr(addr+size, op >> 6, &size, outstring); outstring += sprintf(outstring, ", "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disdivs(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "divs.w"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disdivu(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "divu.w"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int discmpm(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "cmpm"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int diseorb(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "eor.b"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int diseorw(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "eor.w"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int diseorl(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "eor.l"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int diseori(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "eori"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int diseoritoccr(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "eori to ccr"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disexg(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "exg"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disext(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "ext"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disillegal(UNUSED u32 addr, UNUSED u16 op, char *outstring) { sprintf(outstring, "illegal"); return 2; } ////////////////////////////////////////////////////////////////////////////// static int disjmp(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "jmp "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disjsr(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "jsr "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int dislea(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "lea "); outstring += seteafieldstr(addr+size, op, &size, outstring); outstring += sprintf(outstring, ", a%d", (op >> 9) & 0x7); return size; } ////////////////////////////////////////////////////////////////////////////// static int dislink(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "link"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dislsl(UNUSED u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "lsl"); outstring += setsizestr2(op >> 6, outstring); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dislsr(UNUSED u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "lsr"); outstring += setsizestr2(op >> 6, outstring); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dismove(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "move"); outstring += setsizestr((op >> 12), outstring); outstring += sprintf(outstring, " "); outstring += seteafieldstr(addr+size, op, &size, outstring); outstring += sprintf(outstring, ", "); seteafieldstr(addr+size, ((op >> 3) & 0x38) | ((op >> 9) & 0x7), &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int dismovea(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "movea"); outstring += setsizestr((op >> 12), outstring); outstring += seteafieldstr(addr+size, op, &size, outstring); outstring += sprintf(outstring, ", a%d", (op >> 9) & 0x7); return size; } ////////////////////////////////////////////////////////////////////////////// static int dismovetoccr(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "move to ccr"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dismovefromsr(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "move from sr"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dismovetosr(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "move "); outstring += seteafieldstr(addr+size, op, &size, outstring); sprintf(outstring, ", sr"); return size; } ////////////////////////////////////////////////////////////////////////////// static int dismovem(UNUSED u32 addr, UNUSED u16 op, char *outstring) { outstring += sprintf(outstring, "movem"); // fix me return 4; } ////////////////////////////////////////////////////////////////////////////// static int dismovep(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "movep"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dismoveq(UNUSED u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "moveq #0x%X, d%d", op & 0xFF, (op >> 9) & 0x7); return size; } ////////////////////////////////////////////////////////////////////////////// static int dismuls(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "muls"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dismulu(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "mulu"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disnbcd(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "nbcd"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disneg(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "neg"); outstring += setsizestr2((op >> 6), outstring); outstring += sprintf(outstring, " "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int disnegx(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "negx"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disnop(UNUSED u32 addr, UNUSED u16 op, char *outstring) { sprintf(outstring, "nop"); return 2; } ////////////////////////////////////////////////////////////////////////////// static int disnot(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "not"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disor(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "ori to CCR"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disori(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "ori"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disoritoccr(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "ori to CCR"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dispea(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "pea"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disrol(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "rol"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disror(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "ror"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disroxl(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "roxl"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disroxr(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "roxr"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disrtr(UNUSED u32 addr, UNUSED u16 op, char *outstring) { sprintf(outstring, "rtr"); return 2; } ////////////////////////////////////////////////////////////////////////////// static int disrts(UNUSED u32 addr, UNUSED u16 op, char *outstring) { sprintf(outstring, "rts"); return 2; } ////////////////////////////////////////////////////////////////////////////// static int dissbcd(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "sbcd"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disscc(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "scc"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dissub(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "sub"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dissuba(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "suba"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int dissubi(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "subi"); outstring += setsizestr2(op >> 6, outstring); outstring += sprintf(outstring, " "); outstring += setimmstr(addr+size, op >> 6, &size, outstring); outstring += sprintf(outstring, ", "); seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int dissubq(u32 addr, u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "subq"); outstring += setsizestr2(op >> 6, outstring); outstring += sprintf(outstring, " #%d, ", (op >> 9) & 7); // fix me seteafieldstr(addr+size, op, &size, outstring); return size; } ////////////////////////////////////////////////////////////////////////////// static int dissubx(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "subx"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disswap(UNUSED u32 addr, u16 op, char *outstring) { sprintf(outstring, "swap d%d", op & 0x7); return 2; } ////////////////////////////////////////////////////////////////////////////// static int distas(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "tas"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int distrap(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "trap"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int distrapv(UNUSED u32 addr, UNUSED u16 op, char *outstring) { sprintf(outstring, "trapv"); return 2; } ////////////////////////////////////////////////////////////////////////////// static int distst(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "tst"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static int disunlk(UNUSED u32 addr, UNUSED u16 op, char *outstring) { int size=2; outstring += sprintf(outstring, "unlk"); // fix me return size; } ////////////////////////////////////////////////////////////////////////////// static m68kdis_struct instruction[] = { { 0xFFFF, 0x023C, "andi #??, CCR", disanditoccr }, { 0xFFFF, 0x0A3C, "eori #??, CCR", diseoritoccr }, { 0xFFFF, 0x4AFC, "illegal", disillegal }, { 0xFFFF, 0x4E71, "nop", disnop }, { 0xFFFF, 0x003C, "ori #??, CCR", disoritoccr }, { 0xFFFF, 0x4E77, "rtr", disrtr }, { 0xFFFF, 0x4E75, "rts", disrts }, { 0xFFFF, 0x4E76, "trapv", distrapv }, { 0xFFF8, 0x4848, "bkpt", disbkpt }, { 0xFFF8, 0x4E50, "link", dislink }, { 0xFFF8, 0x4840, "swap", disswap }, { 0xFFF8, 0x4E58, "unlk", disunlk }, { 0xFFF0, 0x4E40, "trap", distrap }, { 0xF1F8, 0xD100, "addx.b", disaddx }, { 0xF1F8, 0xD140, "addx.w", disaddx }, { 0xF1F8, 0xD180, "addx.l", disaddx }, { 0xF1F8, 0xB108, "cmpm.b", discmpm }, { 0xF1F8, 0xB148, "cmpm.w", discmpm }, { 0xF1F8, 0xB188, "cmpm.l", discmpm }, { 0xFFC0, 0xE1C0, "asl", disasl }, { 0xFFC0, 0xE0C0, "asr", disasr }, { 0xFFC0, 0x0880, "bclr", disbclrs }, { 0xFFC0, 0x08C0, "bset", disbsets }, { 0xFFC0, 0x0800, "btst", disbtsts }, { 0xFFC0, 0x4EC0, "jmp", disjmp }, { 0xFFC0, 0x4E80, "jsr", disjsr }, { 0xFFC0, 0x44C0, "move ??, CCR", dismovetoccr }, { 0xFFC0, 0x40C0, "move SR, ??", dismovefromsr }, { 0xFFC0, 0x46C0, "move ??, SR", dismovetosr }, { 0xFFC0, 0x4800, "nbcd", disnbcd }, { 0xFFC0, 0x4840, "pea", dispea }, { 0xFFC0, 0x4AC0, "tas", distas }, { 0xFE38, 0x4800, "ext", disext }, { 0xF1F0, 0xC100, "abcd", disabcd }, { 0xF1F0, 0x8100, "sbcd", dissbcd }, { 0xF0F8, 0x50C8, "dbcc", disdbcc }, { 0xFB80, 0x4880, "movem", dismovem }, { 0xFF00, 0x0600, "addi", disaddi }, { 0xFF00, 0x0200, "andi", disandi }, { 0xFF00, 0x6000, "bra", disbra }, { 0xFF00, 0x6100, "bsr", disbsr }, { 0xFF00, 0x4200, "clr", disclr }, { 0xFF00, 0x0C00, "cmpi", discmpi }, { 0xFF00, 0x0A00, "eori", diseori }, { 0xFF00, 0x4400, "neg", disneg }, { 0xFF00, 0x4000, "negx", disnegx }, { 0xFF00, 0x4600, "not", disnot }, { 0xFF00, 0x0000, "ori", disori }, { 0xFF00, 0x0400, "subi", dissubi }, { 0xFF00, 0x4A00, "tst", distst }, { 0xF118, 0xE108, "lsl", dislsl }, { 0xF118, 0xE008, "lsr", dislsr }, { 0xF118, 0xE118, "rol", disrol }, { 0xF118, 0xE018, "ror", disror }, { 0xF118, 0xE110, "roxl", disroxl }, { 0xF118, 0xE010, "roxr", disroxr }, { 0xF1C0, 0xD0C0, "adda.w", disadda }, { 0xF1C0, 0xD1C0, "adda.l", disadda }, { 0xF1C0, 0x0140, "bchg", disbchg }, { 0xF1C0, 0x0180, "bclr", disbclrd }, { 0xF1C0, 0x01C0, "bset", disbsetd }, { 0xF1C0, 0x0100, "btst", disbtstd }, { 0xF1C0, 0xB000, "cmp.b", discmpb }, { 0xF1C0, 0xB040, "cmp.w", discmpw }, { 0xF1C0, 0xB080, "cmp.l", discmpl }, { 0xF1C0, 0xB0C0, "cmpa.w", discmpaw }, { 0xF1C0, 0xB1C0, "cmpa.l", discmpal }, { 0xF1C0, 0x81C0, "divs.w", disdivs }, { 0xF1C0, 0x80C0, "divu.w", disdivu }, { 0xF1C0, 0xB100, "eor.b", diseorb }, { 0xF1C0, 0xB140, "eor.w", diseorw }, { 0xF1C0, 0xB180, "eor.l", diseorl }, { 0xF1C0, 0x41C0, "lea", dislea }, { 0xF1C0, 0xC1C0, "muls", dismuls }, { 0xF1C0, 0xC0C0, "mulu", dismulu }, { 0xF130, 0x9100, "subx", dissubx }, { 0xF038, 0x0008, "movep", dismovep }, { 0xF0C0, 0x50C0, "scc", disscc }, { 0xC1C0, 0x0040, "movea", dismovea }, { 0xF040, 0x4000, "chk", dischk }, { 0xF100, 0x5000, "addq", disaddq }, { 0xF100, 0xC100, "exg", disexg }, { 0xF100, 0x7000, "moveq", dismoveq }, { 0xF100, 0x5100, "subq", dissubq }, { 0xF000, 0xD000, "add", disadd }, // fix me { 0xF000, 0xC000, "and", disand }, { 0xF000, 0x6000, "bcc", disbcc }, { 0xF000, 0x8000, "or", disor }, { 0xF000, 0x9000, "sub", dissub }, // fix me { 0xF000, 0x9000, "suba", dissuba }, // fix me { 0xC000, 0x0000, "move", dismove }, { 0x0000, 0x0000, NULL, NULL } }; ////////////////////////////////////////////////////////////////////////////// u32 M68KDisasm(u32 addr, char *outstring) { int i; outstring += sprintf(outstring, "%05X: ", (unsigned int)addr); for (i = 0; instruction[i].name != NULL; i++) { u16 op = (u16)c68k_word_read(addr); if ((op & instruction[i].mask) == instruction[i].inst) { addr += instruction[i].disasm(addr, op, outstring); return addr; } } sprintf(outstring, "unknown"); return (addr+2); } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/m68kd.h000644 001750 001750 00000001571 12256006161 017302 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef M68KD_H #define M68KD_H #include "core.h" u32 M68KDisasm(u32 addr, char *outstring); #endif yabause-0.9.13.1/src/titan/titan.c000644 001750 001750 00000021270 12256006143 020600 0ustar00guillaumeguillaume000000 000000 /* Copyright 2012 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "titan.h" #include /* private */ typedef u32 (*TitanBlendFunc)(u32 top, u32 bottom); typedef int FASTCALL (*TitanTransFunc)(u32 pixel); static struct TitanContext { int inited; u32 * vdp2framebuffer[8]; u32 * linescreen[4]; int vdp2width; int vdp2height; TitanBlendFunc blend; TitanTransFunc trans; } tt_context = { 0, { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL }, 320, 224 }; #if defined WORDS_BIGENDIAN #ifdef USE_RGB_555 static INLINE u32 TitanFixAlpha(u32 pixel) { return (((pixel >> 16) & 0xF800) | ((pixel >> 13) & 0x7C0) | ((pixel >> 10) & 0x3E)); } #elif USE_RGB_565 static INLINE u32 TitanFixAlpha(u32 pixel) { return (((pixel >> 16) & 0xF800) | ((pixel >> 13) & 0x7E0) | ((pixel >> 11) & 0x1F)); } #else static INLINE u32 TitanFixAlpha(u32 pixel) { return ((((pixel & 0x3F) << 2) + 0x03) | (pixel & 0xFFFFFF00)); } #endif static INLINE u8 TitanGetAlpha(u32 pixel) { return pixel & 0x3F; } static INLINE u8 TitanGetRed(u32 pixel) { return (pixel >> 8) & 0xFF; } static INLINE u8 TitanGetGreen(u32 pixel) { return (pixel >> 16) & 0xFF; } static INLINE u8 TitanGetBlue(u32 pixel) { return (pixel >> 24) & 0xFF; } static INLINE u32 TitanCreatePixel(u8 alpha, u8 red, u8 green, u8 blue) { return alpha | (red << 8) | (green << 16) | (blue << 24); } #else #ifdef USE_RGB_555 static INLINE u32 TitanFixAlpha(u32 pixel) { return (((pixel >> 3) & 0x1F) | ((pixel >> 6) & 0x3E0) | ((pixel >> 9) & 0x7C00)); } #elif USE_RGB_565 static INLINE u32 TitanFixAlpha(u32 pixel) { return (((pixel >> 3) & 0x1F) | ((pixel >> 5) & 0x7E0) | ((pixel >> 8) & 0xF800)); } #else static INLINE u32 TitanFixAlpha(u32 pixel) { return ((((pixel & 0x3F000000) << 2) + 0x03000000) | (pixel & 0x00FFFFFF)); } #endif static INLINE u8 TitanGetAlpha(u32 pixel) { return (pixel >> 24) & 0x3F; } static INLINE u8 TitanGetRed(u32 pixel) { return (pixel >> 16) & 0xFF; } static INLINE u8 TitanGetGreen(u32 pixel) { return (pixel >> 8) & 0xFF; } static INLINE u8 TitanGetBlue(u32 pixel) { return pixel & 0xFF; } static INLINE u32 TitanCreatePixel(u8 alpha, u8 red, u8 green, u8 blue) { return (alpha << 24) | (red << 16) | (green << 8) | blue; } #endif static u32 TitanBlendPixelsTop(u32 top, u32 bottom) { u8 alpha, ralpha, tr, tg, tb, br, bg, bb; alpha = (TitanGetAlpha(top) << 2) + 3; ralpha = 0xFF - alpha; tr = (TitanGetRed(top) * alpha) / 0xFF; tg = (TitanGetGreen(top) * alpha) / 0xFF; tb = (TitanGetBlue(top) * alpha) / 0xFF; br = (TitanGetRed(bottom) * ralpha) / 0xFF; bg = (TitanGetGreen(bottom) * ralpha) / 0xFF; bb = (TitanGetBlue(bottom) * ralpha) / 0xFF; return TitanCreatePixel(0x3F, tr + br, tg + bg, tb + bb); } static u32 TitanBlendPixelsBottom(u32 top, u32 bottom) { u8 alpha, ralpha, tr, tg, tb, br, bg, bb; if ((top & 0x80000000) == 0) return top; alpha = (TitanGetAlpha(bottom) << 2) + 3; ralpha = 0xFF - alpha; tr = (TitanGetRed(top) * alpha) / 0xFF; tg = (TitanGetGreen(top) * alpha) / 0xFF; tb = (TitanGetBlue(top) * alpha) / 0xFF; br = (TitanGetRed(bottom) * ralpha) / 0xFF; bg = (TitanGetGreen(bottom) * ralpha) / 0xFF; bb = (TitanGetBlue(bottom) * ralpha) / 0xFF; return TitanCreatePixel(TitanGetAlpha(top), tr + br, tg + bg, tb + bb); } static u32 TitanBlendPixelsAdd(u32 top, u32 bottom) { u32 r, g, b; r = TitanGetRed(top) + TitanGetRed(bottom); if (r > 0xFF) r = 0xFF; g = TitanGetGreen(top) + TitanGetGreen(bottom); if (g > 0xFF) g = 0xFF; b = TitanGetBlue(top) + TitanGetBlue(bottom); if (b > 0xFF) b = 0xFF; return TitanCreatePixel(0x3F, r, g, b); } static INLINE int FASTCALL TitanTransAlpha(u32 pixel) { return TitanGetAlpha(pixel) < 0x3F; } static INLINE int FASTCALL TitanTransBit(u32 pixel) { return pixel & 0x80000000; } static u32 TitanDigPixel(int priority, int pos) { u32 pixel = 0; while((priority > -1) && (! pixel)) { pixel = tt_context.vdp2framebuffer[priority][pos]; priority--; } tt_context.vdp2framebuffer[priority + 1][pos] = 0; if (priority == -1) return pixel; if (tt_context.trans(pixel)) { u32 bottom = TitanDigPixel(priority, pos); pixel = tt_context.blend(pixel, bottom); } else while (priority > 0) { tt_context.vdp2framebuffer[priority][pos] = 0; priority--; } return pixel; } /* public */ int TitanInit() { int i; if (! tt_context.inited) { for(i = 0;i < 8;i++) { if ((tt_context.vdp2framebuffer[i] = (u32 *)calloc(sizeof(u32), 704 * 512)) == NULL) return -1; } /* linescreen 0 is not initialized as it's not used... */ for(i = 1;i < 4;i++) { if ((tt_context.linescreen[i] = (u32 *)calloc(sizeof(u32), 512)) == NULL) return -1; } tt_context.inited = 1; } for(i = 0;i < 8;i++) memset(tt_context.vdp2framebuffer[i], 0, sizeof(u32) * 704 * 512); for(i = 1;i < 4;i++) memset(tt_context.linescreen[i], 0, sizeof(u32) * 512); return 0; } int TitanDeInit() { int i; for(i = 0;i < 8;i++) free(tt_context.vdp2framebuffer[i]); for(i = 1;i < 4;i++) free(tt_context.linescreen[i]); return 0; } void TitanSetResolution(int width, int height) { tt_context.vdp2width = width; tt_context.vdp2height = height; } void TitanGetResolution(int * width, int * height) { *width = tt_context.vdp2width; *height = tt_context.vdp2height; } void TitanSetBlendingMode(int blend_mode) { if (blend_mode == TITAN_BLEND_BOTTOM) { tt_context.blend = TitanBlendPixelsBottom; tt_context.trans = TitanTransBit; } else if (blend_mode == TITAN_BLEND_ADD) { tt_context.blend = TitanBlendPixelsAdd; tt_context.trans = TitanTransBit; } else { tt_context.blend = TitanBlendPixelsTop; tt_context.trans = TitanTransAlpha; } } void TitanPutBackHLine(s32 y, u32 color) { u32 * buffer = tt_context.vdp2framebuffer[0] + (y * tt_context.vdp2width); int i; for (i = 0; i < tt_context.vdp2width; i++) buffer[i] = color; } void TitanPutLineHLine(int linescreen, s32 y, u32 color) { if (linescreen == 0) return; { u32 * buffer = tt_context.linescreen[linescreen] + y; *buffer = color; } } void TitanPutPixel(int priority, s32 x, s32 y, u32 color, int linescreen) { if (priority == 0) return; { int pos = (y * tt_context.vdp2width) + x; u32 * buffer = tt_context.vdp2framebuffer[priority] + pos; if (linescreen) color = TitanBlendPixelsTop(color, tt_context.linescreen[linescreen][y]); if (tt_context.trans(color) && *buffer) color = tt_context.blend(color, *buffer); *buffer = color; } } void TitanPutHLine(int priority, s32 x, s32 y, s32 width, u32 color) { if (priority == 0) return; { u32 * buffer = tt_context.vdp2framebuffer[priority] + (y * tt_context.vdp2width) + x; int i; for (i = 0; i < width; i++) buffer[i] = color; } } void TitanPutShadow(int priority, s32 x, s32 y) { if (priority == 0) return; { int pos = (y * tt_context.vdp2width) + x; u32 * buffer = tt_context.vdp2framebuffer[priority] + pos; *buffer = *buffer ? TitanBlendPixelsTop(0x20000000, *buffer) : 0x20000000; } } void TitanRender(pixel_t * dispbuffer) { u32 dot; int i; for (i = 0; i < (tt_context.vdp2width * tt_context.vdp2height); i++) { dot = TitanDigPixel(7, i); if (dot) { dispbuffer[i] = TitanFixAlpha(dot); } } } #ifdef WORDS_BIGENDIAN void TitanWriteColor(pixel_t * dispbuffer, s32 bufwidth, s32 x, s32 y, u32 color) { int pos = (y * bufwidth) + x; pixel_t * buffer = dispbuffer + pos; *buffer = ((color >> 24) & 0xFF) | ((color >> 8) & 0xFF00) | ((color & 0xFF00) << 8) | ((color & 0xFF) << 24); } #else void TitanWriteColor(pixel_t * dispbuffer, s32 bufwidth, s32 x, s32 y, u32 color) { int pos = (y * bufwidth) + x; pixel_t * buffer = dispbuffer + pos; *buffer = color; } #endif yabause-0.9.13.1/src/titan/titan.h000644 001750 001750 00000003010 12256006143 020575 0ustar00guillaumeguillaume000000 000000 /* Copyright 2012 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef TITAN_H #define TITAN_H #include "../core.h" #define TITAN_BLEND_TOP 0 #define TITAN_BLEND_BOTTOM 1 #define TITAN_BLEND_ADD 2 int TitanInit(); int TitanDeInit(); void TitanSetResolution(int width, int height); void TitanGetResolution(int * width, int * height); void TitanSetBlendingMode(int blend_mode); void TitanPutBackHLine(s32 y, u32 color); void TitanPutLineHLine(int linescreen, s32 y, u32 color); void TitanPutPixel(int priority, s32 x, s32 y, u32 color, int linescreen); void TitanPutHLine(int priority, s32 x, s32 y, s32 width, u32 color); void TitanPutShadow(int priority, s32 x, s32 y); void TitanRender(pixel_t * dispbuffer); void TitanWriteColor(pixel_t * dispbuffer, s32 bufwidth, s32 x, s32 y, u32 color); #endif yabause-0.9.13.1/src/font.h000644 001750 001750 00000042065 12256006143 017322 0ustar00guillaumeguillaume000000 000000 /* Copyright 2012 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ static const char * font[] = { " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " .. ", " .##.", " .##. ", " .##. ", " .##. ", " .##. ", " .##. ", " .##. ", ".##. ", " .. ", " ..... ", " .#####. ", ".#######.", ".##...##.", ".##.#.##.", ".##.#.##.", ".##...##.", ".#######.", " .#####. ", " ..... ", " .... ", " .####. ", " .####. ", " ..##. ", " .##. ", " .##. ", " ...##.. ", ".#######.", ".#######.", " ....... ", " ...... ", ".######. ", ".#######.", " .....##.", ".#######.", ".######. ", ".##..... ", ".#######.", ".#######.", " ....... ", " ....... ", ".#######.", ".#######.", " .....##.", ".#######.", ".#######.", " .....##.", ".#######.", ".#######.", " ....... ", " .. ", ".##. ", ".##... ", ".##.##. ", ".##.##.. ", ".#######.", ".#######.", " ...##.. ", " .##. ", " .. ", " ....... ", ".#######.", ".#######.", ".##..... ", ".######. ", ".#######.", " .....##.", ".#######.", ".######. ", " ...... ", " ...... ", " .######.", ".#######.", ".##..... ", ".######. ", ".#######.", ".##...##.", ".#######.", " .#####. ", " ..... ", " ....... ", ".#######.", ".#######.", " .....##.", " .##.", " .##.", " .##.", " .##.", " .##.", " .. ", " ..... ", " .#####. ", ".#######.", ".##...##.", " .######.", ".######. ", ".##...##.", ".#######.", " .#####. ", " ..... ", " ..... ", " .#####. ", ".#######.", ".##...##.", ".#######.", " .######.", " .....##.", ".#######.", ".#######.", " ....... ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ..... ", " .#####. ", ".#######.", ".##...##.", ".#######.", ".#######.", ".##...##.", ".##. .##.", ".##. .##.", " .. .. ", " ...... ", ".######. ", ".#######.", ".##...##.", ".######. ", ".######. ", ".##...##.", ".#######.", ".######. ", " ...... ", " ....... ", ".#######.", ".#######.", ".##..... ", ".##. ", ".##. ", ".##..... ", ".#######.", ".#######.", " ....... ", " ...... ", ".######. ", ".#######.", ".##...##.", ".##. .##.", ".##. .##.", ".##...##.", ".#######.", ".######. ", " ...... ", " ....... ", ".#######.", ".#######.", ".##..... ", ".####. ", ".####. ", ".##..... ", ".#######.", ".#######.", " ....... ", " ....... ", ".#######.", ".#######.", ".##..... ", ".####. ", ".####. ", ".##.. ", ".##. ", ".##. ", " .. ", " ....... ", ".#######.", ".#######.", ".##..... ", ".##..###.", ".##..###.", ".##...##.", ".#######.", ".#######.", " ....... ", " .. .. ", ".##. .##.", ".##. .##.", ".##...##.", ".#######.", ".#######.", ".##...##.", ".##. .##.", ".##. .##.", " .. .. ", " ....... ", ".#######.", ".#######.", " ..###.. ", " .###. ", " .###. ", " ..###.. ", ".#######.", ".#######.", " ....... ", " ....... ", ".#######.", ".#######.", " ....###.", " .###.", " .. .###.", ".##..###.", ".#######.", " ..####. ", " .... ", " .. .. ", ".##. .##.", ".##. .##.", ".##..###.", ".######. ", ".#####. ", ".##.###. ", ".##..###.", ".##. .##.", " .. .. ", " .. ", ".##. ", ".##. ", ".##. ", ".##. ", ".##. ", ".##..... ", ".#######.", ".#######.", " ....... ", " .. .. ", ".##. .##.", ".###.###.", ".#######.", ".##.#.##.", ".##...##.", ".##. .##.", ".##. .##.", ".##. .##.", " .. .. ", " .. .. ", ".##. .##.", ".###..##.", ".####.##.", ".##.####.", ".##..###.", ".##. .##.", ".##. .##.", ".##. .##.", " .. .. ", " ... ", " .###. ", " .#####. ", ".###.###.", ".##. .##.", ".##. .##.", ".###.###.", " .#####. ", " .###. ", " ... ", " ...... ", ".######. ", ".#######. ", ".##...##.", ".#######.", ".######. ", ".##.... ", ".##. ", ".##. ", " .. ", " ... ", " .###. ", " .#####. ", ".##...##.", ".##...##.", ".##.#.##.", ".##..###.", " .######.", " .###.#.", " ... . ", " ...... ", ".######. ", ".#######.", ".##...##.", ".#######.", ".######. ", ".##..###.", ".##. .##.", ".##. .##.", " .. .. ", " ..... ", " .#####. ", ".######. ", ".##.... ", ".######. ", ".######. ", " ....##. ", ".######. ", ".#####. ", " ..... ", " ....... ", ".#######.", ".#######.", " ..###.. ", " .###. ", " .###. ", " .###. ", " .###. ", " .###. ", " ... ", " .. .. ", ".##. .##.", ".##. .##.", ".##. .##.", ".##. .##.", ".##. .##.", ".##...##.", ".#######.", " .#####. ", " ..... ", " .. .. ", ".##. .##.", ".##. .##.", ".##. .##.", " .##.##. ", " .##.##. ", " .##.##. ", " .###. ", " .###. ", " ... ", " .. .. ", ".##...##.", ".##.#.##.", ".##.#.##.", " .#####. ", " .#####. ", " .#####. ", " .###. ", " .#.#. ", " . . ", " .. .. ", ".##. .##.", ".##. .##.", ".##. .##.", " .##.##. ", " .###. ", " .##.##. ", ".##. .##.", ".##. .##.", " .. .. ", " .. .. ", ".##. .##.", ".##. .##.", ".##...##.", ".#######.", ".#######.", " .....##.", ".#######.", ".#######.", " ....... ", " ....... ", ".#######.", ".#######.", " ....##. ", " .##. ", " .##. ", " .##.... ", ".#######.", ".#######.", " ....... ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", }; yabause-0.9.13.1/src/logo.svg000644 001750 001750 00000017503 12256006143 017663 0ustar00guillaumeguillaume000000 000000 image/svg+xml Yabause Logo yabause-0.9.13.1/src/scsp2.h000644 001750 001750 00000012536 12256006126 017407 0ustar00guillaumeguillaume000000 000000 /* src/scsp2.h: Header for new SCSP implementation Copyright 2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SCSP_H // Not SCSP2_H (we substitute for the original scsp.h) #define SCSP_H #include "core.h" // For sized integer types /////////////////////////////////////////////////////////////////////////// // Module interface declaration #define SNDCORE_DEFAULT -1 #define SNDCORE_DUMMY 0 #define SNDCORE_WAV 10 // Should be 1, but left as is for backward compat #define SCSP_MUTE_SYSTEM 1 #define SCSP_MUTE_USER 2 typedef struct { int id; const char *Name; int (*Init)(void); void (*DeInit)(void); int (*Reset)(void); int (*ChangeVideoFormat)(int vertfreq); // FIXME/SCSP1: u32* should be s32* (they're signed samples) void (*UpdateAudio)(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples); u32 (*GetAudioSpace)(void); void (*MuteAudio)(void); void (*UnMuteAudio)(void); void (*SetVolume)(int volume); } SoundInterface_struct; extern SoundInterface_struct SNDDummy; extern SoundInterface_struct SNDWave; /////////////////////////////////////////////////////////////////////////// // Parameter block for M68K{Get,Set}Registers() typedef struct { u32 D[8]; u32 A[8]; u32 SR; u32 PC; } M68KRegs; // Breakpoint data structure (currently just an address) typedef struct { u32 addr; } M68KBreakpointInfo; // Maximum number of M68K breakpoints that can be set simultaneously #define M68K_MAX_BREAKPOINTS 10 /////////////////////////////////////////////////////////////////////////// // Data/function declarations extern u8 *SoundRam; extern int ScspInit(int coreid, void (*interrupt_handler)(void)); extern void ScspReset(void); extern int ScspChangeSoundCore(int coreid); extern int ScspChangeVideoFormat(int type); extern void ScspSetFrameAccurate(int on); extern void ScspMuteAudio(int flags); extern void ScspUnMuteAudio(int flags); extern void ScspSetVolume(int volume); extern void ScspDeInit(void); extern void ScspExec(int decilines); extern u8 FASTCALL SoundRamReadByte(u32 address); extern u16 FASTCALL SoundRamReadWord(u32 address); extern u32 FASTCALL SoundRamReadLong(u32 address); extern void FASTCALL SoundRamWriteByte(u32 address, u8 data); extern void FASTCALL SoundRamWriteWord(u32 address, u16 data); extern void FASTCALL SoundRamWriteLong(u32 address, u32 data); extern u8 FASTCALL ScspReadByte(u32 address); extern u16 FASTCALL ScspReadWord(u32 address); extern u32 FASTCALL ScspReadLong(u32 address); extern void FASTCALL ScspWriteByte(u32 address, u8 data); extern void FASTCALL ScspWriteWord(u32 address, u16 data); extern void FASTCALL ScspWriteLong(u32 address, u32 data); extern void ScspReceiveCDDA(const u8 *sector); extern int SoundSaveState(FILE *fp); extern int SoundLoadState(FILE *fp, int version, int size); extern void ScspSlotDebugStats(u8 slotnum, char *outstring); extern void ScspCommonControlRegisterDebugStats(char *outstring); extern int ScspSlotDebugSaveRegisters(u8 slotnum, const char *filename); extern int ScspSlotDebugAudioSaveWav(u8 slotnum, const char *filename); extern void ScspConvert32uto16s(s32 *srcL, s32 *srcR, s16 *dest, u32 len); extern void M68KStart(void); extern void M68KStop(void); extern void M68KStep(void); extern void M68KWriteNotify(u32 address, u32 size); extern void M68KGetRegisters(M68KRegs *regs); extern void M68KSetRegisters(const M68KRegs *regs); extern void M68KSetBreakpointCallBack(void (*func)(u32 address)); extern int M68KAddCodeBreakpoint(u32 address); extern int M68KDelCodeBreakpoint(u32 address); extern const M68KBreakpointInfo *M68KGetBreakpointList(void); extern void M68KClearCodeBreakpoints(void); extern u32 FASTCALL M68KReadByte(u32 address); extern u32 FASTCALL M68KReadWord(u32 address); extern void FASTCALL M68KWriteByte(u32 address, u32 data); extern void FASTCALL M68KWriteWord(u32 address, u32 data); /////////////////////////////////////////////////////////////////////////// // Compatibility macros to match scsp.h interface #define m68kregs_struct M68KRegs #define m68kcodebreakpoint_struct M68KBreakpointInfo #include "scu.h" #define ScspInit(coreid) ScspInit((coreid), ScuSendSoundRequest) #define scsp_r_b ScspReadByte #define scsp_r_w ScspReadWord #define scsp_r_d ScspReadLong #define scsp_w_b ScspWriteByte #define scsp_w_w ScspWriteWord #define scsp_w_d ScspWriteLong #define c68k_word_read M68KReadWord /////////////////////////////////////////////////////////////////////////// #endif // SCSP_H /* * Local variables: * c-file-style: "stroustrup" * c-basic-offset: 3 * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=3: */ yabause-0.9.13.1/src/snddx.c000644 001750 001750 00000020651 12256006201 017457 0ustar00guillaumeguillaume000000 000000 /* Copyright (C) 2005-2007 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "dx.h" #include "scsp.h" #include "snddx.h" #include "error.h" int SNDDXInit(void); void SNDDXDeInit(void); int SNDDXReset(void); int SNDDXChangeVideoFormat(int vertfreq); void SNDDXUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples); u32 SNDDXGetAudioSpace(void); void SNDDXMuteAudio(void); void SNDDXUnMuteAudio(void); void SNDDXSetVolume(int volume); SoundInterface_struct SNDDIRECTX = { SNDCORE_DIRECTX, "DirectX Sound Interface", SNDDXInit, SNDDXDeInit, SNDDXReset, SNDDXChangeVideoFormat, SNDDXUpdateAudio, SNDDXGetAudioSpace, SNDDXMuteAudio, SNDDXUnMuteAudio, SNDDXSetVolume }; LPDIRECTSOUND8 lpDS8; LPDIRECTSOUNDBUFFER lpDSB, lpDSB2; #define NUMSOUNDBLOCKS 4 static u16 *stereodata16; static u32 soundlen; static u32 soundoffset=0; static u32 soundbufsize; static LONG soundvolume; static int issoundmuted; HWND DXGetWindow (); ////////////////////////////////////////////////////////////////////////////// int SNDDXInit(void) { DSBUFFERDESC dsbdesc; WAVEFORMATEX wfx; HRESULT ret; char tempstr[512]; if ((ret = DirectSoundCreate8(NULL, &lpDS8, NULL)) != DS_OK) { sprintf(tempstr, "Sound. DirectSound8Create error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); YabSetError(YAB_ERR_CANNOTINIT, tempstr); return -1; } if ((ret = IDirectSound8_SetCooperativeLevel(lpDS8, DXGetWindow(), DSSCL_PRIORITY)) != DS_OK) { sprintf(tempstr, "Sound. IDirectSound8_SetCooperativeLevel error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); YabSetError(YAB_ERR_CANNOTINIT, tempstr); return -1; } memset(&dsbdesc, 0, sizeof(dsbdesc)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbdesc.dwBufferBytes = 0; dsbdesc.lpwfxFormat = NULL; if ((ret = IDirectSound8_CreateSoundBuffer(lpDS8, &dsbdesc, &lpDSB, NULL)) != DS_OK) { sprintf(tempstr, "Sound. Error when creating primary sound buffer: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); YabSetError(YAB_ERR_CANNOTINIT, tempstr); return -1; } soundlen = 44100 / 60; // 60 for NTSC or 50 for PAL. Initially assume it's going to be NTSC. soundbufsize = soundlen * NUMSOUNDBLOCKS * 2 * 2; memset(&wfx, 0, sizeof(wfx)); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 2; wfx.nSamplesPerSec = 44100; wfx.wBitsPerSample = 16; wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; if ((ret = IDirectSoundBuffer8_SetFormat(lpDSB, &wfx)) != DS_OK) { sprintf(tempstr, "Sound. IDirectSoundBuffer8_SetFormat error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); YabSetError(YAB_ERR_CANNOTINIT, tempstr); return -1; } memset(&dsbdesc, 0, sizeof(dsbdesc)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_STICKYFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCHARDWARE; dsbdesc.dwBufferBytes = soundbufsize; dsbdesc.lpwfxFormat = &wfx; if ((ret = IDirectSound8_CreateSoundBuffer(lpDS8, &dsbdesc, &lpDSB2, NULL)) != DS_OK) { if (ret == DSERR_CONTROLUNAVAIL || ret == DSERR_INVALIDCALL || ret == E_FAIL || ret == E_NOTIMPL) { // Try using a software buffer instead dsbdesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_STICKYFOCUS | DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE; if ((ret = IDirectSound8_CreateSoundBuffer(lpDS8, &dsbdesc, &lpDSB2, NULL)) != DS_OK) { sprintf(tempstr, "Sound. Error when creating secondary sound buffer: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); YabSetError(YAB_ERR_CANNOTINIT, tempstr); return -1; } } else { sprintf(tempstr, "Sound. Error when creating secondary sound buffer: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); YabSetError(YAB_ERR_CANNOTINIT, tempstr); return -1; } } IDirectSoundBuffer8_Play(lpDSB2, 0, 0, DSBPLAY_LOOPING); if ((stereodata16 = (u16 *)malloc(soundbufsize)) == NULL) return -1; memset(stereodata16, 0, soundbufsize); soundvolume = DSBVOLUME_MAX; issoundmuted = 0; return 0; } ////////////////////////////////////////////////////////////////////////////// void SNDDXDeInit(void) { DWORD status=0; if (lpDSB2) { IDirectSoundBuffer8_GetStatus(lpDSB2, &status); if(status == DSBSTATUS_PLAYING) IDirectSoundBuffer8_Stop(lpDSB2); IDirectSoundBuffer8_Release(lpDSB2); lpDSB2 = NULL; } if (lpDSB) { IDirectSoundBuffer8_Release(lpDSB); lpDSB = NULL; } if (lpDS8) { IDirectSound8_Release(lpDS8); lpDS8 = NULL; } } ////////////////////////////////////////////////////////////////////////////// int SNDDXReset(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// int SNDDXChangeVideoFormat(int vertfreq) { soundlen = 44100 / vertfreq; soundbufsize = soundlen * NUMSOUNDBLOCKS * 2 * 2; if (stereodata16) free(stereodata16); if ((stereodata16 = (u16 *)malloc(soundbufsize)) == NULL) return -1; memset(stereodata16, 0, soundbufsize); return 0; } ////////////////////////////////////////////////////////////////////////////// void SNDDXUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples) { LPVOID buffer1; LPVOID buffer2; DWORD buffer1_size, buffer2_size; DWORD status; IDirectSoundBuffer8_GetStatus(lpDSB2, &status); if (status & DSBSTATUS_BUFFERLOST) return; // fix me IDirectSoundBuffer8_Lock(lpDSB2, soundoffset, num_samples * sizeof(s16) * 2, &buffer1, &buffer1_size, &buffer2, &buffer2_size, 0); ScspConvert32uto16s((s32 *)leftchanbuffer, (s32 *)rightchanbuffer, (s16 *)stereodata16, num_samples); memcpy(buffer1, stereodata16, buffer1_size); if (buffer2) memcpy(buffer2, ((u8 *)stereodata16)+buffer1_size, buffer2_size); soundoffset += buffer1_size + buffer2_size; soundoffset %= soundbufsize; IDirectSoundBuffer8_Unlock(lpDSB2, buffer1, buffer1_size, buffer2, buffer2_size); } ////////////////////////////////////////////////////////////////////////////// u32 SNDDXGetAudioSpace(void) { DWORD playcursor, writecursor; u32 freespace=0; if (IDirectSoundBuffer8_GetCurrentPosition (lpDSB2, &playcursor, &writecursor) != DS_OK) return 0; if (soundoffset > playcursor) freespace = soundbufsize - soundoffset + playcursor; else freespace = playcursor - soundoffset; // if (freespace > 512) return (freespace / 2 / 2); // else // return 0; } ////////////////////////////////////////////////////////////////////////////// void SNDDXMuteAudio(void) { issoundmuted = 1; IDirectSoundBuffer8_SetVolume (lpDSB2, DSBVOLUME_MIN); } ////////////////////////////////////////////////////////////////////////////// void SNDDXUnMuteAudio(void) { issoundmuted = 0; IDirectSoundBuffer8_SetVolume (lpDSB2, soundvolume); } ////////////////////////////////////////////////////////////////////////////// void SNDDXSetVolume(int volume) { // Convert linear volume to logarithmic volume if (volume == 0) soundvolume = DSBVOLUME_MIN; else soundvolume = (LONG)(log10((double)volume / 100.0) * 2000.0); if (!issoundmuted) IDirectSoundBuffer8_SetVolume (lpDSB2, soundvolume); } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/screen.h000644 001750 001750 00000002261 12256006174 017631 0ustar00guillaumeguillaume000000 000000 /* Copyright 2013 Theo Berkau Copyright 2013 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SCREEN_H #define SCREEN_H typedef struct { int index; int width; int height; int bpp; int freq; } supportedRes_struct; typedef void * ResolutionList; ResolutionList ScreenGetResolutions(); int ScreenNextResolution(ResolutionList rl, supportedRes_struct * res); void ScreenChangeResolution(supportedRes_struct * res); void ScreenRestoreResolution(); #endif yabause-0.9.13.1/src/m68kc68k.c000644 001750 001750 00000010530 12256006126 017621 0ustar00guillaumeguillaume000000 000000 /* Copyright 2007 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "m68kc68k.h" #include "c68k/c68k.h" #include "memory.h" #include "yabause.h" /** * PROFILE_68K: Perform simple profiling of the 68000 emulation, reporting * the average time per 68000 clock cycle. (Realtime execution would be * around 88.5 nsec/cycle.) */ // #define PROFILE_68K static u8 *SoundDummy=NULL; static int M68KC68KInit(void) { int i; // Setup a 64k buffer filled with invalid 68k instructions to serve // as a default map if ((SoundDummy = T2MemoryInit(0x10000)) != NULL) memset(SoundDummy, 0xFF, 0x10000); C68k_Init(&C68K, NULL); // not sure if I need the int callback or not for (i = 0x10; i < 0x100; i++) M68K->SetFetch(i << 16, (i << 16) + 0xFFFF, (pointer)SoundDummy); return 0; } static void M68KC68KDeInit(void) { if (SoundDummy) T2MemoryDeInit(SoundDummy); SoundDummy = NULL; } static void M68KC68KReset(void) { C68k_Reset(&C68K); } static s32 FASTCALL M68KC68KExec(s32 cycle) { #ifdef PROFILE_68K static u32 tot_cycles = 0, tot_usec = 0, tot_ticks = 0, last_report = 0; u32 start, end; start = (u32) YabauseGetTicks(); int retval = C68k_Exec(&C68K, cycle); end = (u32) YabauseGetTicks(); tot_cycles += cycle; tot_ticks += end - start; if (tot_cycles/1000000 > last_report) { tot_usec += (u64)tot_ticks * 1000000 / yabsys.tickfreq; tot_ticks = 0; fprintf(stderr, "%ld cycles in %.3f sec = %.3f nsec/cycle\n", (long)tot_cycles, (double)tot_usec/1000000, ((double)tot_usec / (double)tot_cycles) * 1000); last_report = tot_cycles/1000000; } return retval; #else return C68k_Exec(&C68K, cycle); #endif } static void M68KC68KSync(void) { } static u32 M68KC68KGetDReg(u32 num) { return C68k_Get_DReg(&C68K, num); } static u32 M68KC68KGetAReg(u32 num) { return C68k_Get_AReg(&C68K, num); } static u32 M68KC68KGetPC(void) { return C68k_Get_PC(&C68K); } static u32 M68KC68KGetSR(void) { return C68k_Get_SR(&C68K); } static u32 M68KC68KGetUSP(void) { return C68k_Get_USP(&C68K); } static u32 M68KC68KGetMSP(void) { return C68k_Get_MSP(&C68K); } static void M68KC68KSetDReg(u32 num, u32 val) { C68k_Set_DReg(&C68K, num, val); } static void M68KC68KSetAReg(u32 num, u32 val) { C68k_Set_AReg(&C68K, num, val); } static void M68KC68KSetPC(u32 val) { C68k_Set_PC(&C68K, val); } static void M68KC68KSetSR(u32 val) { C68k_Set_SR(&C68K, val); } static void M68KC68KSetUSP(u32 val) { C68k_Set_USP(&C68K, val); } static void M68KC68KSetMSP(u32 val) { C68k_Set_MSP(&C68K, val); } static void M68KC68KSetFetch(u32 low_adr, u32 high_adr, pointer fetch_adr) { C68k_Set_Fetch(&C68K, low_adr, high_adr, fetch_adr); } static void FASTCALL M68KC68KSetIRQ(s32 level) { C68k_Set_IRQ(&C68K, level); } static void FASTCALL M68KC68KWriteNotify(u32 address, u32 size) { /* nothing to do */ } static void M68KC68KSetReadB(M68K_READ *Func) { C68k_Set_ReadB(&C68K, Func); } static void M68KC68KSetReadW(M68K_READ *Func) { C68k_Set_ReadW(&C68K, Func); } static void M68KC68KSetWriteB(M68K_WRITE *Func) { C68k_Set_WriteB(&C68K, Func); } static void M68KC68KSetWriteW(M68K_WRITE *Func) { C68k_Set_WriteW(&C68K, Func); } M68K_struct M68KC68K = { 1, "C68k Interface", M68KC68KInit, M68KC68KDeInit, M68KC68KReset, M68KC68KExec, M68KC68KSync, M68KC68KGetDReg, M68KC68KGetAReg, M68KC68KGetPC, M68KC68KGetSR, M68KC68KGetUSP, M68KC68KGetMSP, M68KC68KSetDReg, M68KC68KSetAReg, M68KC68KSetPC, M68KC68KSetSR, M68KC68KSetUSP, M68KC68KSetMSP, M68KC68KSetFetch, M68KC68KSetIRQ, M68KC68KWriteNotify, M68KC68KSetReadB, M68KC68KSetReadW, M68KC68KSetWriteB, M68KC68KSetWriteW }; yabause-0.9.13.1/src/yui.h000644 001750 001750 00000016625 12256006124 017164 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004-2005 Guillaume Duhamel Copyright 2004-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_H #define YUI_H #include "cdbase.h" #include "sh2core.h" #include "sh2int.h" #include "scsp.h" #include "smpc.h" #include "vdp1.h" #include "yabause.h" /* If Yabause encounters any fatal errors, it sends the error text to this function */ void YuiErrorMsg(const char *string); /* Tells the yui to exchange front and back video buffers. This may end up being moved to the Video Core. */ void YuiSwapBuffers(void); ////////////////////////////////////////////////////////////////////////////// // Helper functions(you can use these in your own port) ////////////////////////////////////////////////////////////////////////////// /* int MappedMemoryLoad(const char *filename, u32 addr); Loads the specified file(filename) to specified address(addr). Returns zero on success, less than zero if an error has occured. Note: Some areas in memory are read-only and won't acknowledge any writes. */ /* int MappedMemorySave(const char *filename, u32 addr, u32 size); Saves data from specified address(addr) by specified amount of bytes(size) to specified file(filename). Returns zero on success, less than zero if an error has occured. Note: Some areas in memory are write-only and will only return zero on reads. */ /* void MappedMemoryLoadExec(const char *filename, u32 pc); Loads the specified file(filename) to specified address(pc) and sets Master SH2 to execute from there. Note: Some areas in memory are read-only and won't acknowledge any writes. */ /* void FormatBackupRam(void *mem, u32 size); Formats the specified Backup Ram memory area(mem) of specified size(size). */ /* void SH2Disasm(u32 v_addr, u16 op, int mode, char *string); Generates a disassembled instruction into specified string(string) based on specified address(v_addr) and specified opcode(op). mode should always be 0. */ /* void SH2Step(SH2_struct *context); For the specified SH2 context(context), it executes 1 instruction. context should be either MSH2 or SSH2. */ /* void SH2GetRegisters(SH2_struct *context, sh2regs_struct * r); For the specified SH2 context(context), copies the current registers into the specified structure(r). context should be either MSH2 or SSH2. */ /* void SH2SetRegisters(SH2_struct *context, sh2regs_struct * r); For the specified SH2 context(context), copies the specified structure(r) to the current registers. context should be either MSH2 or SSH2. */ /* void SH2SetBreakpointCallBack(SH2_struct *context, void (*func)(void *, u32, void *), void *userdata); For the specified SH2 context(context), it sets the breakpoint handler function(func). context should be either MSH2 or SSH2. */ /* int SH2AddCodeBreakpoint(SH2_struct *context, u32 addr); For the specified SH2 context(context), it adds a code breakpoint for specified address(addr). context should be either MSH2 or SSH2. Returns zero on success, or less than zero if an error has occured(such as the breakpoint list being full) */ /* int SH2DelCodeBreakpoint(SH2_struct *context, u32 addr); For the specified SH2 context(context), it deletes a code breakpoint for specified address(addr). context should be either MSH2 or SSH2. Returns zero on success, or less than zero if an error has occured. */ /* codebreakpoint_struct *SH2GetBreakpointList(SH2_struct *context); For the specified SH2 context(context), it returns a pointer to the code breakpoint list for the processor. context should be either MSH2 or SSH2. */ /* void SH2ClearCodeBreakpoints(SH2_struct *context); For the specified SH2 context(context), it deletes every code breakpoint entry. context should be either MSH2 or SSH2. */ /* u32 M68KDisasm(u32 addr, char *outstring); Generates a disassembled instruction into specified string(string) based on instruction stored at specified address(addr). Returns address of next instruction. */ /* void M68KStep(); Executes 1 68k instruction. */ /* void M68KGetRegisters(m68kregs_struct *regs); Copies the current 68k registers into the specified structure(regs). */ /* void M68KSetRegisters(m68kregs_struct *regs); Copies the specified structure(regs) to the current 68k registers. */ /* void M68KSetBreakpointCallBack(void (*func)(u32)); It sets the breakpoint handler function(func) for the 68k. */ /* int M68KAddCodeBreakpoint(u32 addr); It adds a 68K code breakpoint for specified address(addr). Returns zero on success, or less than zero if an error has occured(such as the breakpoint list being full) */ /* int M68KDelCodeBreakpoint(u32 addr); It deletes a 68k code breakpoint for specified address(addr). Returns zero on success, or less than zero if an error has occured. */ /* m68kcodebreakpoint_struct *M68KGetBreakpointList(); It returns a pointer to the code breakpoint list for the 68k. */ /* void M68KClearCodeBreakpoints(); It deletes every code breakpoint entry for the 68k. */ /* void ScuDspDisasm(u8 addr, char *outstring); Generates a disassembled instruction into specified string(string) based on instruction stored at specified address(addr). */ /* void ScuDspStep(void); Executes 1 SCU DSP step */ /* void ScuDspGetRegisters(scudspregs_struct *regs); Copies the current SCU DSP registers into the specified structure(regs). */ /* void ScuDspSetRegisters(scudspregs_struct *regs); Copies the specified structure(regs) to the current SCU DSP registers. */ /* void ScuDspSetBreakpointCallBack(void (*func)(u32)); It sets the breakpoint handler function(func) for the SCU DSP. */ /* int ScuDspAddCodeBreakpoint(u32 addr); It adds a SCU DSP code breakpoint for specified address(addr). Returns zero on success, or less than zero if an error has occured(such as the breakpoint list being full) */ /* int ScuDspDelCodeBreakpoint(u32 addr); It deletes a SCU DSP code breakpoint for specified address(addr). Returns zero on success, or less than zero if an error has occured. */ /* scucodebreakpoint_struct *ScuDspGetBreakpointList(); It returns a pointer to the code breakpoint list for the SCU DSP. */ /* void ScuDspClearCodeBreakpoints(); It deletes every code breakpoint entry for the SCU DSP. */ /* void Vdp2DebugStatsRBG0(char *outstring, int *isenabled); void Vdp2DebugStatsNBG0(char *outstring, int *isenabled); void Vdp2DebugStatsNBG1(char *outstring, int *isenabled); void Vdp2DebugStatsNBG2(char *outstring, int *isenabled); void Vdp2DebugStatsNBG3(char *outstring, int *isenabled); Fills a specified string pointer(outstring) with debug information for the specified screen. It also fills a variable(isenabled) with the screen's current status */ #endif yabause-0.9.13.1/src/logo.bmp000644 001750 001750 00000006066 12256006124 017643 0ustar00guillaumeguillaume000000 000000 BM6 6(    ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽŽŽŽŽŽŽŽãßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÀ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ  STRQ 6 ÆÀ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿúûýøùýŒ„ˆ|}¾„€·vp«\WœB:A ÇÀÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðòúÏÖ勤⇘×r‡ÐlÎlÎtˆÑˆ™×¦°ÞÂÃßϾ˥“•o^^ÑËËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäèöœªÞVnÇ0Nº'F·'F·'F·'F·'F·'F·'F·'F·,J¹B]Àp„Ïž«ÜÑÖêûûþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýþþ¼ÆéSlÆ'F·'F·'F·'F·-K¹8U½>Z¿=Y¾7T¼-K¹'F·'F·'F·'F·'F·D_ÁzÓÐ×ïñóùÃÉàš¾‰“¹²¹Òíïöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²½å9U½'F·'F·§9›/„Tb™íïöÿÿÿüýþƒ•Ö'F·(G·‹œØ÷øüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÖ‡{ÛÜ‘„Þ˜}a^˜ŒŒáãî»Åä5M£$C®(G·(G·'F¶&D³:œ-|³ºÓÿÿÿéìø`wÊ)G¸VnÇìïùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà“†åšŒæœŽåšŒL&"& ËÆÉ¡¬×%@¤8UÀB_ÈB_È5S¾(G¸#@ª0†~вÿÿÿÞãôYqÈ(G·ŽŸÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ螑ð©›î¦˜N)%ýÀ¡­Ö*E§VrÒ|‘ârŠßNhÎ,Kº$A¯2Šx„®ÿÿÿàäõg|Ì'F·˜¦ÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽK&"ö°¡ù´¥ö°¡‚RJTAA½Åã5L¦ZtÒÎÖö¨·íSmÒ-L¼#A­1ˆž¦Çÿÿÿîðù‘¡Ú.L¹}ÔþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆÀ¿°shð©›ù´¥ÿ¼­ù´¥í¦™<‡’fw¸8SµyŽÛo†Û@\Æ(G· <¡8L’ÛßîÿÿÿûûþºÄèKdÃE`Áàäõÿÿÿÿÿÿÿÿÿÿÿÿñïï8"!xE>嚌ö°¡ù´¥ö°¡î¦˜¤i_*±¶ÏJ^«)E«/Kµ(F° <¡/E–¨²Óüüþþþÿÿÿÿçë÷‚”Õ.L¹ŸÚþþÿÿÿÿÿÿÿÿÿÿ€qqN#Ö†yà“†èž‘î¦˜ð©›î¦˜èž‘à“†]0+SACÌÒê|ŒÃLa«G]©k|·¹Áßö÷ûÿÿÿýþþÿÿÿûûþÕÛñlÎ:W½ºÄèÿÿÿÿÿÿÆÀ¿£YPÏ}q؉}à“†Øƒ³ujâ˜Šà“†Ø‰}Çwl7šðòùâæôáåóîðøüýþÿÿÿÿÿÿøùýÿÿÿÿÿÿóõûÃËëh~ÍE`ÁÊÑíñïï8"!e,&½f[ÇrgÏ}q]0+;Í€sÏ}qÇrg‰E>*ãßßþþÿÿÿÿÿÿÿþþÿýþþûûþñóúÿÿÿÿÿÿþþÿ÷øüÐÖïr†ÐºÄè€qqB«NEµZP½f[ƒE>*UBA[,'Åod½f[µZPNUBAÿÿÿÿÿÿÿÿÿüýþûûþ÷øüðòúÿÿÿÿÿÿÿÿÿþþÿúûýÕÛñýÀ€-&£C:«NE±WM5ª Ÿãßß™NE³XN«NE@70 ›ÿÿÿÿÿÿûûþûûþøùýñóúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêêî8#)R“.&š8/¡A8W UBAÿÿÿÿÿÿ€qqH¨JA¡A8š8/l *ãßßüýþûûþüüþùúýòôûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€rq8…‹$’,%*#ÓÏÏÿÿÿÿÿÿñïï*k% ˜4,’,%‹$A SACõöüüüþýþþüüþüüþÿÿÿÿÿÿÿÿÿÿÿÿ¸°¯g  „‰!> €qqóõûèë÷üýþÿÿÿª Ÿ/ Œ' ‰!„z +´­¯÷øüûûþüüþÿÿÿÿÿÿÿÿÿÿÿÿñïï*Oy z ~ T *ñïïÖÜñ€’Õ”¤Û°¼åÒÙðSACI ‚~ z O*ççìûûþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcQQ8y y y u +³¬°óõûßãôÑØð½Æé¦³áª¶ã¿¿Ìny y y 8`QUÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸°¯%33333UBAÿÿÿüýþüýþþþÿýþþûûþôöûîðùm`f33333%¸°¯ÿÿÿÿÿÿÿÿÿñïï*ãßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿýþþíëî)*ñïïÿÿÿÿÿÿãß߯À¿ÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ÔÏÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÔÏÏÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ÆÀ¿ãßßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyabause-0.9.13.1/src/cs1.c000644 001750 001750 00000004657 12256006124 017041 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2005 Guillaume Duhamel Copyright 2005 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "cs1.h" #include "cs0.h" ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL Cs1ReadByte(u32 addr) { addr &= 0xFFFFFF; if (addr == 0xFFFFFF) return CartridgeArea->cartid; return CartridgeArea->Cs1ReadByte(addr); } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL Cs1ReadWord(u32 addr) { addr &= 0xFFFFFF; if (addr == 0xFFFFFE) return (0xFF00 | CartridgeArea->cartid); return CartridgeArea->Cs1ReadWord(addr); } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL Cs1ReadLong(u32 addr) { addr &= 0xFFFFFF; if (addr == 0xFFFFFC) return (0xFF00FF00 | (CartridgeArea->cartid << 16) | CartridgeArea->cartid); return CartridgeArea->Cs1ReadLong(addr); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Cs1WriteByte(u32 addr, u8 val) { addr &= 0xFFFFFF; if (addr == 0xFFFFFF) return; CartridgeArea->Cs1WriteByte(addr, val); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Cs1WriteWord(u32 addr, u16 val) { addr &= 0xFFFFFF; if (addr == 0xFFFFFE) return; CartridgeArea->Cs1WriteWord(addr, val); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Cs1WriteLong(u32 addr, u32 val) { addr &= 0xFFFFFF; if (addr == 0xFFFFFC) return; CartridgeArea->Cs1WriteLong(addr, val); } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/vidshared.c000644 001750 001750 00000046605 12256006173 020327 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2006 Guillaume Duhamel Copyright 2004-2007 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ygl.h" #include "vidshared.h" #include "vdp2.h" #include "debug.h" ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2NBG0PlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFN & 0x7) << 6; u32 tmp=0; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABN0 & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABN0 >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDN0 & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDN0 >> 8); break; } CalcPlaneAddr(info, tmp); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2NBG1PlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFN & 0x70) << 2; u32 tmp=0; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABN1 & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABN1 >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDN1 & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDN1 >> 8); break; } CalcPlaneAddr(info, tmp); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2NBG2PlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFN & 0x700) >> 2; u32 tmp=0; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABN2 & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABN2 >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDN2 & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDN2 >> 8); break; } CalcPlaneAddr(info, tmp); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2NBG3PlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFN & 0x7000) >> 6; u32 tmp=0; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABN3 & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABN3 >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDN3 & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDN3 >> 8); break; } CalcPlaneAddr(info, tmp); } ////////////////////////////////////////////////////////////////////////////// void Vdp2ReadRotationTable(int which, vdp2rotationparameter_struct *parameter) { s32 i; u32 addr; addr = Vdp2Regs->RPTA.all << 1; if (which == 0) { // Rotation Parameter A addr &= 0x000FFF7C; parameter->coefenab = Vdp2Regs->KTCTL & 0x1; parameter->screenover = (Vdp2Regs->PLSZ >> 10) & 0x3; } else { // Rotation Parameter B addr = (addr & 0x000FFFFC) | 0x00000080; parameter->coefenab = Vdp2Regs->KTCTL & 0x100; parameter->screenover = (Vdp2Regs->PLSZ >> 14) & 0x3; } i = T1ReadLong(Vdp2Ram, addr); parameter->Xst = (float) (signed) ((i & 0x1FFFFFC0) | (i & 0x10000000 ? 0xF0000000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->Yst = (float) (signed) ((i & 0x1FFFFFC0) | (i & 0x10000000 ? 0xF0000000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->Zst = (float) (signed) ((i & 0x1FFFFFC0) | (i & 0x10000000 ? 0xF0000000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaXst = (float) (signed) ((i & 0x0007FFC0) | (i & 0x00040000 ? 0xFFFC0000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaYst = (float) (signed) ((i & 0x0007FFC0) | (i & 0x00040000 ? 0xFFFC0000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaX = (float) (signed) ((i & 0x0007FFC0) | (i & 0x00040000 ? 0xFFFC0000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaY = (float) (signed) ((i & 0x0007FFC0) | (i & 0x00040000 ? 0xFFFC0000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->A = (float) (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->B = (float) (signed) ((i & 0x000FFFC0) | ((i & 0x00080000) ? 0xFFF80000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->C = (float) (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->D = (float) (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->E = (float) (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->F = (float) (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)) / 65536; addr += 4; i = T1ReadWord(Vdp2Ram, addr); parameter->Px = (float) (signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000)); addr += 2; i = T1ReadWord(Vdp2Ram, addr); parameter->Py = (float) (signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000)); addr += 2; i = T1ReadWord(Vdp2Ram, addr); parameter->Pz = (float) (signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000)); addr += 4; i = T1ReadWord(Vdp2Ram, addr); parameter->Cx = (float) (signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000)); addr += 2; i = T1ReadWord(Vdp2Ram, addr); parameter->Cy = (float) (signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000)); addr += 2; i = T1ReadWord(Vdp2Ram, addr); parameter->Cz = (float) (signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->Mx = (float) (signed) ((i & 0x3FFFFFC0) | (i & 0x20000000 ? 0xE0000000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->My = (float) (signed) ((i & 0x3FFFFFC0) | (i & 0x20000000 ? 0xE0000000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->kx = (float) (signed) ((i & 0x00FFFFFF) | (i & 0x00800000 ? 0xFF800000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->ky = (float) (signed) ((i & 0x00FFFFFF) | (i & 0x00800000 ? 0xFF800000 : 0x00000000)) / 65536; addr += 4; if (parameter->coefenab) { // Read in coefficient values i = T1ReadLong(Vdp2Ram, addr); parameter->KAst = (float)(unsigned)(i & 0xFFFFFFC0) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaKAst = (float) (signed) ((i & 0x03FFFFC0) | (i & 0x02000000 ? 0xFE000000 : 0x00000000)) / 65536; addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaKAx = (float) (signed) ((i & 0x03FFFFC0) | (i & 0x02000000 ? 0xFE000000 : 0x00000000)) / 65536; addr += 4; if (which == 0) { parameter->coefdatasize = (Vdp2Regs->KTCTL & 0x2 ? 2 : 4); parameter->coeftbladdr = ((Vdp2Regs->KTAOF & 0x7) * 0x10000 + (int)(parameter->KAst)) * parameter->coefdatasize; parameter->coefmode = (Vdp2Regs->KTCTL >> 2) & 0x3; } else { parameter->coefdatasize = (Vdp2Regs->KTCTL & 0x200 ? 2 : 4); parameter->coeftbladdr = (((Vdp2Regs->KTAOF >> 8) & 0x7) * 0x10000 + (int)(parameter->KAst)) * parameter->coefdatasize; parameter->coefmode = (Vdp2Regs->KTCTL >> 10) & 0x3; } } VDP2LOG("Xst: %f, Yst: %f, Zst: %f, deltaXst: %f deltaYst: %f deltaX: %f\n" "deltaY: %f A: %f B: %f C: %f D: %f E: %f F: %f Px: %f Py: %f Pz: %f\n" "Cx: %f Cy: %f Cz: %f Mx: %f My: %f kx: %f ky: %f KAst: %f\n" "deltaKAst: %f deltaKAx: %f kadr %08X\n", parameter->Xst, parameter->Yst, parameter->Zst, parameter->deltaXst, parameter->deltaYst, parameter->deltaX, parameter->deltaY, parameter->A, parameter->B, parameter->C, parameter->D, parameter->E, parameter->F, parameter->Px, parameter->Py, parameter->Pz, parameter->Cx, parameter->Cy, parameter->Cz, parameter->Mx, parameter->My, parameter->kx, parameter->ky, parameter->KAst, parameter->deltaKAst, parameter->deltaKAx,parameter->coeftbladdr); } ////////////////////////////////////////////////////////////////////////////// void Vdp2ReadRotationTableFP(int which, vdp2rotationparameterfp_struct *parameter) { s32 i; u32 addr; addr = Vdp2Regs->RPTA.all << 1; if (which == 0) { // Rotation Parameter A addr &= 0x000FFF7C; parameter->coefenab = Vdp2Regs->KTCTL & 0x1; parameter->screenover = (Vdp2Regs->PLSZ >> 10) & 0x3; } else { // Rotation Parameter B addr = (addr & 0x000FFFFC) | 0x00000080; parameter->coefenab = Vdp2Regs->KTCTL & 0x100; parameter->screenover = (Vdp2Regs->PLSZ >> 14) & 0x3; } i = T1ReadLong(Vdp2Ram, addr); parameter->Xst = (signed) ((i & 0x1FFFFFC0) | (i & 0x10000000 ? 0xF0000000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->Yst = (signed) ((i & 0x1FFFFFC0) | (i & 0x10000000 ? 0xF0000000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->Zst = (signed) ((i & 0x1FFFFFC0) | (i & 0x10000000 ? 0xF0000000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaXst = (signed) ((i & 0x0007FFC0) | (i & 0x00040000 ? 0xFFFC0000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaYst = (signed) ((i & 0x0007FFC0) | (i & 0x00040000 ? 0xFFFC0000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaX = (signed) ((i & 0x0007FFC0) | (i & 0x00040000 ? 0xFFFC0000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaY = (signed) ((i & 0x0007FFC0) | (i & 0x00040000 ? 0xFFFC0000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->A = (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->B = (signed) ((i & 0x000FFFC0) | ((i & 0x00080000) ? 0xFFF80000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->C = (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->D = (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->E = (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->F = (signed) ((i & 0x000FFFC0) | (i & 0x00080000 ? 0xFFF80000 : 0x00000000)); addr += 4; i = T1ReadWord(Vdp2Ram, addr); parameter->Px = tofixed((signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000))); addr += 2; i = T1ReadWord(Vdp2Ram, addr); parameter->Py = tofixed((signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000))); addr += 2; i = T1ReadWord(Vdp2Ram, addr); parameter->Pz = tofixed((signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000))); addr += 4; i = T1ReadWord(Vdp2Ram, addr); parameter->Cx = tofixed((signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000))); addr += 2; i = T1ReadWord(Vdp2Ram, addr); parameter->Cy = tofixed((signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000))); addr += 2; i = T1ReadWord(Vdp2Ram, addr); parameter->Cz = tofixed((signed) ((i & 0x3FFF) | (i & 0x2000 ? 0xFFF80000 : 0x00000000))); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->Mx = (signed) ((i & 0x3FFFFFC0) | (i & 0x20000000 ? 0xE0000000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->My = (signed) ((i & 0x3FFFFFC0) | (i & 0x20000000 ? 0xE0000000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->kx = (signed) ((i & 0x00FFFFFF) | (i & 0x00800000 ? 0xFF800000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->ky = (signed) ((i & 0x00FFFFFF) | (i & 0x00800000 ? 0xFF800000 : 0x00000000)); addr += 4; if (parameter->coefenab) { // Read in coefficient values i = T1ReadLong(Vdp2Ram, addr); parameter->KAst = (unsigned)(i & 0xFFFFFFC0); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaKAst = (signed) ((i & 0x03FFFFC0) | (i & 0x02000000 ? 0xFE000000 : 0x00000000)); addr += 4; i = T1ReadLong(Vdp2Ram, addr); parameter->deltaKAx = (signed) ((i & 0x03FFFFC0) | (i & 0x02000000 ? 0xFE000000 : 0x00000000)); addr += 4; if (which == 0) { parameter->coefdatasize = (Vdp2Regs->KTCTL & 0x2 ? 2 : 4); parameter->coeftbladdr = ((Vdp2Regs->KTAOF & 0x7) * 0x10000 + touint(parameter->KAst)) * parameter->coefdatasize; parameter->coefmode = (Vdp2Regs->KTCTL >> 2) & 0x3; } else { parameter->coefdatasize = (Vdp2Regs->KTCTL & 0x200 ? 2 : 4); parameter->coeftbladdr = (((Vdp2Regs->KTAOF >> 8) & 0x7) * 0x10000 + touint(parameter->KAst)) * parameter->coefdatasize; parameter->coefmode = (Vdp2Regs->KTCTL >> 10) & 0x3; } } VDP2LOG("Xst: %f, Yst: %f, Zst: %f, deltaXst: %f deltaYst: %f deltaX: %f\n" "deltaY: %f A: %f B: %f C: %f D: %f E: %f F: %f Px: %f Py: %f Pz: %f\n" "Cx: %f Cy: %f Cz: %f Mx: %f My: %f kx: %f ky: %f KAst: %f\n" "deltaKAst: %f deltaKAx: %f\n", tofloat(parameter->Xst), tofloat(parameter->Yst), tofloat(parameter->Zst), tofloat(parameter->deltaXst), tofloat(parameter->deltaYst), tofloat(parameter->deltaX), tofloat(parameter->deltaY), tofloat(parameter->A), tofloat(parameter->B), tofloat(parameter->C), tofloat(parameter->D), tofloat(parameter->E), tofloat(parameter->F), tofloat(parameter->Px), tofloat(parameter->Py), tofloat(parameter->Pz), tofloat(parameter->Cx), tofloat(parameter->Cy), tofloat(parameter->Cz), tofloat(parameter->Mx), tofloat(parameter->My), tofloat(parameter->kx), tofloat(parameter->ky), tofloat(parameter->KAst), tofloat(parameter->deltaKAst), tofloat(parameter->deltaKAx)); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2ParameterAPlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFR & 0x7) << 6; u32 tmp=0; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABRA & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABRA >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDRA & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDRA >> 8); break; case 4: tmp = offset | (Vdp2Regs->MPEFRA & 0xFF); break; case 5: tmp = offset | (Vdp2Regs->MPEFRA >> 8); break; case 6: tmp = offset | (Vdp2Regs->MPGHRA & 0xFF); break; case 7: tmp = offset | (Vdp2Regs->MPGHRA >> 8); break; case 8: tmp = offset | (Vdp2Regs->MPIJRA & 0xFF); break; case 9: tmp = offset | (Vdp2Regs->MPIJRA >> 8); break; case 10: tmp = offset | (Vdp2Regs->MPKLRA & 0xFF); break; case 11: tmp = offset | (Vdp2Regs->MPKLRA >> 8); break; case 12: tmp = offset | (Vdp2Regs->MPMNRA & 0xFF); break; case 13: tmp = offset | (Vdp2Regs->MPMNRA >> 8); break; case 14: tmp = offset | (Vdp2Regs->MPOPRA & 0xFF); break; case 15: tmp = offset | (Vdp2Regs->MPOPRA >> 8); break; } CalcPlaneAddr(info, tmp); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Vdp2ParameterBPlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFR & 0x70) << 2; u32 tmp=0; // Parameter B switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABRB & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABRB >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDRB & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDRB >> 8); break; case 4: tmp = offset | (Vdp2Regs->MPEFRB & 0xFF); break; case 5: tmp = offset | (Vdp2Regs->MPEFRB >> 8); break; case 6: tmp = offset | (Vdp2Regs->MPGHRB & 0xFF); break; case 7: tmp = offset | (Vdp2Regs->MPGHRB >> 8); break; case 8: tmp = offset | (Vdp2Regs->MPIJRB & 0xFF); break; case 9: tmp = offset | (Vdp2Regs->MPIJRB >> 8); break; case 10: tmp = offset | (Vdp2Regs->MPKLRB & 0xFF); break; case 11: tmp = offset | (Vdp2Regs->MPKLRB >> 8); break; case 12: tmp = offset | (Vdp2Regs->MPMNRB & 0xFF); break; case 13: tmp = offset | (Vdp2Regs->MPMNRB >> 8); break; case 14: tmp = offset | (Vdp2Regs->MPOPRB & 0xFF); break; case 15: tmp = offset | (Vdp2Regs->MPOPRB >> 8); break; } CalcPlaneAddr(info, tmp); } ////////////////////////////////////////////////////////////////////////////// float Vdp2ReadCoefficientMode0_2(vdp2rotationparameter_struct *parameter, u32 addr) { s32 i; if (parameter->coefdatasize == 2) { addr &= 0x7FFFE; i = T1ReadWord(Vdp2Ram, addr); parameter->msb = (i >> 15) & 0x1; return (float) (signed) ((i & 0x7FFF) | (i & 0x4000 ? 0xFFFFC000 : 0x00000000)) / 1024; } else { addr &= 0x7FFFC; i = T1ReadLong(Vdp2Ram, addr); parameter->msb = (i >> 31) & 0x1; return (float) (signed) ((i & 0x00FFFFFF) | (i & 0x00800000 ? 0xFF800000 : 0x00000000)) / 65536; } } ////////////////////////////////////////////////////////////////////////////// fixed32 Vdp2ReadCoefficientMode0_2FP(vdp2rotationparameterfp_struct *parameter, u32 addr) { s32 i; if (parameter->coefdatasize == 2) { addr &= 0x7FFFE; i = T1ReadWord(Vdp2Ram, addr); parameter->msb = (i >> 15) & 0x1; return (signed) ((i & 0x7FFF) | (i & 0x4000 ? 0xFFFFC000 : 0x00000000)) * 64; } else { addr &= 0x7FFFC; i = T1ReadLong(Vdp2Ram, addr); parameter->linescreen = (i >> 24) & 0x7F; parameter->msb = (i >> 31) & 0x1; return (signed) ((i & 0x00FFFFFF) | (i & 0x00800000 ? 0xFF800000 : 0x00000000)); } } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/sndsdl.h000644 001750 001750 00000001562 12256006135 017641 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SNDSDL_H #define SNDSDL_H #define SNDCORE_SDL 1 extern SoundInterface_struct SNDSDL; #endif yabause-0.9.13.1/src/sock-windows.c000644 001750 001750 00000010547 12256006174 021002 0ustar00guillaumeguillaume000000 000000 /* Copyright 2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "core.h" #include "sock.h" #include "debug.h" #ifdef __MINGW32__ // I blame mingw for this #define _WIN32_WINNT 0x501 #endif #include #include static fd_set read_fds; static fd_set write_fds; ////////////////////////////////////////////////////////////////////////////// int YabSockInit() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) return -1; return 0; } int YabSockDeInit() { WSACleanup(); return 0; } int YabSockConnectSocket(const char *ip, int port, YabSock *sock) { struct addrinfo *result = NULL, hints; char port_str[256]; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; sprintf(port_str, "%d", port); if (getaddrinfo(ip, port_str, &hints, &result) != 0) { perror("getaddrinfo"); return -1; } // Create a Socket if ((sock[0] = socket(result->ai_family, result->ai_socktype, result->ai_protocol)) == -1) { freeaddrinfo(result); perror("socket"); return -1; } // Connect to the socket if (connect(sock[0], result->ai_addr, (int)result->ai_addrlen) == -1) { perror("connect"); freeaddrinfo(result); closesocket(sock[0]); return -1; } freeaddrinfo(result); return 0; } int YabSockListenSocket(int port, YabSock *sock) { struct sockaddr_in addr; char opt = 1; sock[0] = socket(AF_INET, SOCK_STREAM, 0); if (setsockopt(sock[0], SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int)) == -1) { perror("setsockopt"); return -1; } memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); if (bind(sock[0], (struct sockaddr *) &addr, sizeof(addr)) == -1) { fprintf(stderr, "Can't bind to port %d: %s\n", port, strerror(errno)); return -1; } if (listen(sock[0], 3) == -1) { perror("listen"); return -1; } return 0; } int YabSockCloseSocket(YabSock sock) { return closesocket(sock); } int YabSockSelect(YabSock sock, int check_read, int check_write ) { fd_set *read_fds_ptr; fd_set *write_fds_ptr; struct timeval tv; int ret; FD_ZERO(&read_fds); FD_ZERO(&write_fds); // Let's see if we can even connect at this point if (check_read) { FD_SET(sock, &read_fds); read_fds_ptr = &read_fds; } else read_fds_ptr = NULL; if (check_write) { FD_SET(sock, &write_fds); write_fds_ptr = &write_fds; } else write_fds_ptr = NULL; tv.tv_sec = 0; tv.tv_usec = 0; if ((ret=select(sock+1, read_fds_ptr, write_fds_ptr, NULL, &tv)) < 1) { if (ret == SOCKET_ERROR) LOG("select: err code %d\n", WSAGetLastError()); else LOG("select: %d\n", ret); return -1; } return 0; } int YabSockIsReadSet(YabSock sock) { return FD_ISSET(sock, &read_fds); } int YabSockIsWriteSet(YabSock sock) { return FD_ISSET(sock, &write_fds); } YabSock YabSockAccept(YabSock sock) { return accept(sock,NULL,NULL); } int YabSockSend(YabSock sock, const void *buf, int len, int flags) { return send(sock, buf, len, flags); } int YabSockReceive(YabSock sock, void *buf, int len, int flags) { return recv(sock, buf, len, flags); } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/profile.c000644 001750 001750 00000012573 12256006124 020007 0ustar00guillaumeguillaume000000 000000 /* copyright Patrick Kooman, 2002 Lightweight C & C++ profiler. Works both in debug- and release mode. For more info, read: http://www.2dgame-tutorial.com/sdl/profile.htm You are free to use / modify / re-distribute this code. */ #if !defined(SYS_PROFILE_H) && !defined(DONT_PROFILE) #include "profile.h" /* The time when the profiler initializes */ clock_t g_init_time ; /* Entries */ entry_t g_tag [NUM_TAGS] ; /* "high-water-mark" */ int g_i_hwm = 0 ; /* Is ProfileInit called? */ int g_init = 0 ; /* Looks up given tag and returns the name, of 0 if not found. */ static entry_t* LookupTag (char* str_tag) { int i ; for (i = 0; i < g_i_hwm; ++i) { if (strcmp (g_tag [i].str_name, str_tag) == 0) { return &g_tag [i] ; } } return 0 ; } /* Checks whether the given tag is already started (nesting). This is true when an entry with the given name is found, for which the start_time_t is not 0. */ static int Nested (char* str_tag) { int i ; for (i = 0; i < g_i_hwm; ++i) { if (strcmp (g_tag [i].str_name, str_tag) == 0 && g_tag [i].start_time > -1) { /* Already 'running': nested*/ return 1 ; } } /* Not running: not nested */ return 0 ; } /* Adds the given tag and return the entry it is stored into */ static entry_t* AddTag (char* str_tag) { if (g_i_hwm + 1 == NUM_TAGS) { /* Full */ return 0 ; } /* Copy the name */ strcpy (g_tag [g_i_hwm].str_name, str_tag) ; g_tag [g_i_hwm].start_time = -1 ; /* Increase the high-water-mark but return the current index */ return &g_tag [g_i_hwm++] ; } /* Compare function for 'qsort' */ static int CompareEntries (const void* p_1, const void* p_2) { entry_t* p_entry1, *p_entry2 ; /* Cast elements to entry_t type */ p_entry1 = (entry_t*) p_1 ; p_entry2 = (entry_t*) p_2 ; /* Compare */ return p_entry2->l_total_ms - p_entry1->l_total_ms ; } /* Called on the first start-call. It receives the start-time */ static void Init (void) { memset (g_tag, 0, sizeof (g_tag)) ; /* Retreive the time */ g_init_time = clock () ; /* Flag that this function has been called */ g_init = 1 ; g_i_hwm = 0 ; } /* Prints profiling statistice to stdout, sorted by percentage (descending) */ void ProfilePrint (void) { int i ; long l_prof_time ; if (g_i_hwm == 0) { fprintf (stdout, "ProfilePrint: nothing to print.\n") ; return ; } /* Retreive the time */ l_prof_time = clock () - g_init_time ; if (l_prof_time == 0) { /* Avoid division by 0 */ fprintf (stdout, "Warning: nothing to show because timer ran for less than 1 clock-tick.") ; } /* Print warnings for tags which are not stopped. */ for (i = 0; i < g_i_hwm; ++i) { if (g_tag [i].i_stopped == 0) { g_tag [i].l_total_ms += clock () - g_tag [i].start_time ; fprintf (stdout, "Warning: \"%s\" started but not stopped. (Done now, but result may be over-expensive!)\n", g_tag [i].str_name) ; } } /* Sort the array desending */ qsort (&g_tag, g_i_hwm, sizeof (entry_t), CompareEntries) ; fprintf (stdout, "Profiler results (descending by percentage):\n\n") ; for (i = 0; i < g_i_hwm; ++i) { /* Print statistics */ fprintf (stdout, "< calls: %2d, total ms: %3d, percentage: %3.1f%% > - \"%s\"\n", g_tag [i].i_calls, (int) ((double) g_tag [i].l_total_ms / CLOCKS_PER_SEC * 1000), (double) g_tag [i].l_total_ms / l_prof_time * 100, g_tag [i].str_name) ; } } /* Starts timer for given tag. If it does not exist yet, it is added. Note: 1. The tag may not be nested with the same name 2. The tag may not equal "" */ void ProfileStart (char* str_tag) { entry_t* p_entry ; /* One the first call, we must initialize the profiler. */ if (!g_init) { Init () ; } /* Test for "" */ if (*str_tag == '\0') { fprintf (stdout, "ERROR in ProfileStart: a tag may not be \"\". Call is denied.") ; return ; } /* Search the entry with the given name */ p_entry = LookupTag (str_tag) ; if (!p_entry) { /* New tag, add it*/ p_entry = AddTag (str_tag) ; if (!p_entry) { fprintf (stdout, "WARNING in ProfileStart: no more space to store the tag (\"%s\"). Increase NUM_TAGS in \"profile.h\". Call is denied.\n", str_tag) ; return ; } } /* Check for nesting of equal tag.*/ if (Nested (str_tag)) { fprintf (stdout, "ERROR in ProfileStart: nesting of equal tags not allowed (\"%s\"). Call is denied.\n", str_tag) ; return ; } /* Increase the number of hits */ ++p_entry->i_calls ; /* Set the start time */ p_entry->start_time = clock () ; p_entry->i_stopped = 0 ; } /* Stops timer for given tag. Checks for existence. Adds the time between now and the Start call to the total time.*/ void ProfileStop (char* str_tag) { clock_t end_time ; entry_t* p_entry ; /* Test for "" */ if (*str_tag == '\0') { fprintf (stdout, "ERROR in ProfileStop: a tag may not be \"\". Call is denied.") ; return ; } /* Check for a existing name */ p_entry = LookupTag (str_tag) ; if (!p_entry) { fprintf (stdout, "WARNING in ProfileStop: tag \"%s\" was never started. Call is denied.\n", str_tag) ; return ; } /* Get the time */ end_time = clock () ; p_entry->l_total_ms += end_time - p_entry->start_time ; /* Reset */ p_entry->start_time = -1 ; p_entry->i_stopped = 1 ; } /* Resets the profiler. */ void ProfileReset (void) { Init () ; } #endif /* !SYS_PROFILE_H && !DONT_PROFILE */ yabause-0.9.13.1/src/debug.c000644 001750 001750 00000010754 12256006124 017434 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "debug.h" #include #include #include ////////////////////////////////////////////////////////////////////////////// Debug * DebugInit(const char * n, DebugOutType t, char * s) { Debug * d; if ((d = (Debug *) malloc(sizeof(Debug))) == NULL) return NULL; d->output_type = t; if ((d->name = strdup(n)) == NULL) { free(d); return NULL; } switch(t) { case DEBUG_STREAM: d->output.stream = fopen(s, "w"); break; case DEBUG_STRING: d->output.string = s; break; case DEBUG_STDOUT: d->output.stream = stdout; break; case DEBUG_STDERR: d->output.stream = stderr; break; case DEBUG_CALLBACK: d->output.callback = (void (*) (char*))s; break; } return d; } ////////////////////////////////////////////////////////////////////////////// void DebugDeInit(Debug * d) { if (d == NULL) return; switch(d->output_type) { case DEBUG_STREAM: if (d->output.stream) fclose(d->output.stream); break; case DEBUG_STRING: case DEBUG_STDOUT: case DEBUG_STDERR: case DEBUG_CALLBACK: break; } if (d->name) free(d->name); free(d); } ////////////////////////////////////////////////////////////////////////////// void DebugChangeOutput(Debug * d, DebugOutType t, char * s) { if (t != d->output_type) { if (d->output_type == DEBUG_STREAM) { if (d->output.stream) fclose(d->output.stream); } d->output_type = t; } switch(t) { case DEBUG_STREAM: d->output.stream = fopen(s, "w"); break; case DEBUG_STRING: d->output.string = s; break; case DEBUG_CALLBACK: d->output.callback = (void (*) (char*))s; break; case DEBUG_STDOUT: d->output.stream = stdout; break; case DEBUG_STDERR: d->output.stream = stderr; break; } } ////////////////////////////////////////////////////////////////////////////// void DebugPrintf(Debug * d, const char * file, u32 line, const char * format, ...) { va_list l; static char strtmp[512]; static int strhash; if (d == NULL) return; va_start(l, format); switch(d->output_type) { case DEBUG_STDOUT: case DEBUG_STDERR: case DEBUG_STREAM: if (d->output.stream == NULL) break; fprintf(d->output.stream, "%s (%s:%ld): ", d->name, file, (long)line); vfprintf(d->output.stream, format, l); break; case DEBUG_STRING: { int i; if (d->output.string == NULL) break; i = sprintf(d->output.string, "%s (%s:%ld): ", d->name, file, (long)line); vsprintf(d->output.string + i, format, l); } break; case DEBUG_CALLBACK: { int i; int strnewhash = 0; i = sprintf(strtmp, "%s (%s:%ld): ", d->name, file, (long)line); i += vsprintf(strtmp + i, format, l); for ( ; i>0 ; i-- ) strnewhash += (int)(strtmp[i]); if ( strnewhash != strhash ) d->output.callback( strtmp ); strhash = strnewhash; } break; } va_end(l); } ////////////////////////////////////////////////////////////////////////////// Debug * MainLog; ////////////////////////////////////////////////////////////////////////////// void LogStart(void) { MainLog = DebugInit("main", DEBUG_STDOUT, NULL); // MainLog = DebugInit("main", DEBUG_STREAM, "stdout.txt"); } ////////////////////////////////////////////////////////////////////////////// void LogStop(void) { DebugDeInit(MainLog); MainLog = NULL; } ////////////////////////////////////////////////////////////////////////////// void LogChangeOutput(DebugOutType t, char * s) { DebugChangeOutput( MainLog, t, s ); } yabause-0.9.13.1/src/netlink.h000644 001750 001750 00000005243 12256006161 020015 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006, 2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef NETLINK_H #define NETLINK_H #include "sock.h" #define NETLINK_BUFFER_SIZE 1024 enum NL_RESULTCODE { NL_RESULTCODE_OK=0, NL_RESULTCODE_CONNECT, NL_RESULTCODE_RING, NL_RESULTCODE_NOCARRIER, NL_RESULTCODE_ERROR, NL_RESULTCODE_CONNECT1200, NL_RESULTCODE_NODIALTONE, NL_RESULTCODE_BUSY, NL_RESULTCODE_NOANSWER, }; enum NL_CONNECTSTATUS { NL_CONNECTSTATUS_IDLE, NL_CONNECTSTATUS_WAIT, NL_CONNECTSTATUS_CONNECT, NL_CONNECTSTATUS_LOGIN1, NL_CONNECTSTATUS_LOGIN2, NL_CONNECTSTATUS_LOGIN3, NL_CONNECTSTATUS_CONNECTED, }; enum NL_MODEMSTATE { NL_MODEMSTATE_COMMAND, NL_MODEMSTATE_DATA, }; typedef struct { u8 RBR; u8 THR; u8 IER; u8 DLL; u8 DLM; u8 IIR; u8 FCR; u8 LCR; u8 MCR; u8 LSR; u8 MSR; u8 SCR; u8 SREG[256]; } netlinkregs_struct; typedef struct { volatile u8 inbuffer[NETLINK_BUFFER_SIZE]; volatile u8 outbuffer[NETLINK_BUFFER_SIZE]; volatile u32 inbufferstart, inbufferend, inbuffersize; volatile int inbufferupdate; volatile u32 outbufferstart, outbufferend, outbuffersize; volatile int outbufferupdate; netlinkregs_struct reg; int isechoenab; YabSock listensocket; YabSock connectsocket; YabSock clientsocket; enum NL_CONNECTSTATUS connectstatus; u32 cycles; enum NL_MODEMSTATE modemstate; char ipstring[16]; char portstring[6]; u32 connect_time, connect_timeout; int internet_enable; volatile u32 thb_write_time; int escape_count; } Netlink; typedef struct { char ip[16]; int port; YabSock sock; } netlink_thread; extern Netlink *NetlinkArea; u8 FASTCALL NetlinkReadByte(u32 addr); void FASTCALL NetlinkWriteByte(u32 addr, u8 val); int NetlinkInit(const char *settingstring); void NetlinkDeInit(void); void NetlinkExec(u32 timing); #endif yabause-0.9.13.1/src/scsp.c000644 001750 001750 00000347422 12256006143 017324 0ustar00guillaumeguillaume000000 000000 /* * Copyright 2004 Stephane Dallongeville * Copyright 2004-2007 Theo Berkau * Copyright 2006 Guillaume Duhamel * Copyright 2012 Chris Lord * * This file is part of Yabause. * * Yabause is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Yabause 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Yabause; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ //////////////////////////////////////////////////////////////// // Custom Sound Processor // note: model2 scsp is mapped to 0x100000~0x100ee4 of the space, but seems to // have additional hw ports ($40a~$410) // note: it seems that the user interrupt is used by the sound driver to reset // the subsystem //-------------------------------------------------------------- // // Common Control Register (CCR) // // $+00 $+01 // $400 ---- --12 3333 4444 1:MEM4MB memory size 2:DAC18B dac for digital output 3:VER version number 4:MVOL // $402 ---- ---1 1222 2222 1:RBL ring buffer length 2:RBP lead address // $404 ---1 2345 6666 6666 1:MOFULL out fifo full 2:MOEMP empty 3:MIOVF overflow 4:MIFULL in 5:MIEMP 6:MIBUF // $406 ---- ---- 1111 1111 1:MOBUF midi output data buffer // $408 1111 1222 2334 4444 1:MSLC monitor slot 2:CA call address 3:SGC Slot phase 4:EG Slot envelope // $40a ---- ---- ---- ---- // $40c ---- ---- ---- ---- // $40e ---- ---- ---- ---- // $410 ---- ---- ---- ---- // $412 1111 1111 1111 111- 1:DMEAL transfer start address (sound) // $414 1111 2222 2222 222- 1:DMEAH transfer start address hi 2:DRGA start register address (dsp) // $416 -123 4444 4444 444- 1:DGATE transfer gate 0 clear 2:DDIR direction 3:DEXE start 4:DTLG data count // $418 ---- -111 2222 2222 1:TACTL timer a prescalar control 2:TIMA timer a count data // $41a ---- -111 2222 2222 1:TBCTL timer b prescalar control 2:TIMB timer b count data // $41c ---- -111 2222 2222 2:TCCTL timer c prescalar control 2:TIMC timer c count data // $41e ---- -111 1111 1111 1:SCIEB allow sound cpu interrupt // $420 ---- -111 1111 1111 1:SCIPD request sound cpu interrupt // $422 ---- -111 1111 1111 1:SCIRE reset sound cpu interrupt // $424 ---- ---- 1111 1111 1:SCILV0 sound cpu interrupt level bit0 // $426 ---- ---- 1111 1111 1:SCILV1 sound cpu interrupt level bit1 // $428 ---- ---- 1111 1111 1:SCILV2 sound cpu interrupt level bit2 // $42a ---- -111 1111 1111 1:MCIEB allow main cpu interrupt // $42c ---- -111 1111 1111 1:MCIPD request main cpu interrupt // $42e ---- -111 1111 1111 1:MCIRE reset main cpu interrupt // //-------------------------------------------------------------- // // Individual Slot Register (ISR) // // $+00 $+01 // $00 ---1 2334 4556 7777 1:KYONEX 2:KYONB 3:SBCTL 4:SSCTL 5:LPCTL 6:PCM8B 7:SA start address // $02 1111 1111 1111 1111 1:SA start address // $04 1111 1111 1111 1111 1:LSA loop start address // $06 1111 1111 1111 1111 1:LEA loop end address // $08 1111 1222 2234 4444 1:D2R decay 2 rate 2:D1R decay 1 rate 3:EGHOLD eg hold mode 4:AR attack rate // $0a -122 2233 3334 4444 1:LPSLNK loop start link 2:KRS key rate scaling 3:DL decay level 4:RR release rate // $0c ---- --12 3333 3333 1:STWINH stack write inhibit 2:SDIR sound direct 3:TL total level // $0e 1111 2222 2233 3333 1:MDL modulation level 2:MDXSL modulation input x 3:MDYSL modulation input y // $10 -111 1-22 2222 2222 1:OCT octave 2:FNS frequency number switch // $12 1222 2233 4445 5666 1:LFORE 2:LFOF 3:PLFOWS 4:PLFOS 5:ALFOWS 6:ALFOS // $14 ---- ---- -111 1222 1:ISEL input select 2:OMXL input mix level // $16 1112 2222 3334 4444 1:DISDL 2:DIPAN 3:EFSDL 4:EFPAN // //-------------------------------------------------------------- #include #include #include #include #include "c68k/c68k.h" #include "cs2.h" #include "debug.h" #include "error.h" #include "memory.h" #include "m68kcore.h" #include "scu.h" #include "yabause.h" #include "scsp.h" #if 0 #include "windows/aviout.h" #endif #ifdef PSP # include "psp/common.h" /* Macro to write a variable's value through the cache to main memory */ # define WRITE_THROUGH(var) (*(u32 *)((u32)&(var) | 0x40000000) = (var)) /* Macro to flush SCSP state so it can be read from the ME */ # define FLUSH_SCSP() sceKernelDcacheWritebackRange(&scsp, sizeof(scsp)) #else // !PSP # define WRITE_THROUGH(var) /*nothing*/ # define FLUSH_SCSP() /*nothing*/ #endif //////////////////////////////////////////////////////////////// #ifndef PI #define PI 3.14159265358979323846 #endif #define SCSP_FREQ 44100 // SCSP frequency #define SCSP_RAM_SIZE 0x080000 // SCSP RAM size #define SCSP_RAM_MASK (SCSP_RAM_SIZE - 1) #define SCSP_MIDI_IN_EMP 0x01 // MIDI flags #define SCSP_MIDI_IN_FUL 0x02 #define SCSP_MIDI_IN_OVF 0x04 #define SCSP_MIDI_OUT_EMP 0x08 #define SCSP_MIDI_OUT_FUL 0x10 #define SCSP_ENV_RELEASE 3 // Envelope phase #define SCSP_ENV_SUSTAIN 2 #define SCSP_ENV_DECAY 1 #define SCSP_ENV_ATTACK 0 #define SCSP_FREQ_HB 19 // Freq counter int part #define SCSP_FREQ_LB 10 // Freq counter float part #define SCSP_ENV_HB 10 // Env counter int part #define SCSP_ENV_LB 10 // Env counter float part #define SCSP_LFO_HB 10 // LFO counter int part #define SCSP_LFO_LB 10 // LFO counter float part #define SCSP_ENV_LEN (1 << SCSP_ENV_HB) // Env table len #define SCSP_ENV_MASK (SCSP_ENV_LEN - 1) // Env table mask #define SCSP_FREQ_LEN (1 << SCSP_FREQ_HB) // Freq table len #define SCSP_FREQ_MASK (SCSP_FREQ_LEN - 1) // Freq table mask #define SCSP_LFO_LEN (1 << SCSP_LFO_HB) // LFO table len #define SCSP_LFO_MASK (SCSP_LFO_LEN - 1) // LFO table mask #define SCSP_ENV_AS 0 // Env Attack Start #define SCSP_ENV_DS (SCSP_ENV_LEN << SCSP_ENV_LB) // Env Decay Start #define SCSP_ENV_AE (SCSP_ENV_DS - 1) // Env Attack End #define SCSP_ENV_DE (((2 * SCSP_ENV_LEN) << SCSP_ENV_LB) - 1) // Env Decay End #define SCSP_ATTACK_R (u32) (8 * 44100) #define SCSP_DECAY_R (u32) (12 * SCSP_ATTACK_R) //////////////////////////////////////////////////////////////// typedef struct slot_t { u8 swe; // stack write enable u8 sdir; // sound direct u8 pcm8b; // PCM sound format u8 sbctl; // source bit control u8 ssctl; // sound source control u8 lpctl; // loop control u8 key; // KEY_ state u8 keyx; // still playing regardless the KEY_ state (hold, decay) s8 *buf8; // sample buffer 8 bits s16 *buf16; // sample buffer 16 bits u32 fcnt; // phase counter u32 finc; // phase step adder u32 finct; // non adjusted phase step s32 ecnt; // envelope counter s32 *einc; // envelope current step adder s32 einca; // envelope step adder for attack s32 eincd; // envelope step adder for decay 1 s32 eincs; // envelope step adder for decay 2 s32 eincr; // envelope step adder for release s32 ecmp; // envelope compare to raise next phase u32 ecurp; // envelope current phase (attack / decay / release ...) s32 env; // envelope multiplier (at time of last update) void (*enxt)(struct slot_t *); // envelope function pointer for next phase event u32 lfocnt; // lfo counter s32 lfoinc; // lfo step adder u32 sa; // start address u32 lsa; // loop start address u32 lea; // loop end address s32 tl; // total level s32 sl; // sustain level s32 ar; // attack rate s32 dr; // decay rate s32 sr; // sustain rate s32 rr; // release rate s32 *arp; // attack rate table pointer s32 *drp; // decay rate table pointer s32 *srp; // sustain rate table pointer s32 *rrp; // release rate table pointer u32 krs; // key rate scale s32 *lfofmw; // lfo frequency modulation waveform pointer s32 *lfoemw; // lfo envelope modulation waveform pointer u8 lfofms; // lfo frequency modulation sensitivity u8 lfoems; // lfo envelope modulation sensitivity u8 fsft; // frequency shift (used for freq lfo) u8 mdl; // modulation level u8 mdx; // modulation source X u8 mdy; // modulation source Y u8 imxl; // input sound level u8 disll; // direct sound level left u8 dislr; // direct sound level right u8 efsll; // effect sound level left u8 efslr; // effect sound level right u8 eghold; // eg type envelope hold u8 lslnk; // loop start link (start D1R when start loop adr is reached) // NOTE: Previously there were u8 pads here to maintain 4-byte alignment. // There are current 22 u8's in this struct and 1 u16. This makes 24 // bytes, so there are no pads at the moment. // // I'm not sure this is at all necessary either, but keeping this note // in case. } slot_t; typedef struct scsp_t { u32 mem4b; // 4mbit memory u32 mvol; // master volume u32 rbl; // ring buffer lenght u32 rbp; // ring buffer address (pointer) u32 mslc; // monitor slot u32 ca; // call address u32 sgc; // phase u32 eg; // envelope u32 dmea; // dma memory address start u32 drga; // dma register address start u32 dmfl; // dma flags (direction / gate 0 ...) u32 dmlen; // dma transfer len u8 midinbuf[4]; // midi in buffer u8 midoutbuf[4]; // midi out buffer u8 midincnt; // midi in buffer size u8 midoutcnt; // midi out buffer size u8 midflag; // midi flag (empty, full, overflow ...) u8 midflag2; // midi flag 2 (here only for alignement) s32 timacnt; // timer A counter u32 timasd; // timer A step diviser s32 timbcnt; // timer B counter u32 timbsd; // timer B step diviser s32 timccnt; // timer C counter u32 timcsd; // timer C step diviser u32 scieb; // allow sound cpu interrupt u32 scipd; // pending sound cpu interrupt u32 scilv0; // IL0 M68000 interrupt pin state u32 scilv1; // IL1 M68000 interrupt pin state u32 scilv2; // IL2 M68000 interrupt pin state u32 mcieb; // allow main cpu interrupt u32 mcipd; // pending main cpu interrupt u8 *scsp_ram; // scsp ram pointer void (*mintf)(void); // main cpu interupt function pointer void (*sintf)(u32); // sound cpu interrupt function pointer s32 stack[32 * 2]; // two last generation slot output (SCSP STACK) slot_t slot[32]; // 32 slots } scsp_t; //////////////////////////////////////////////////////////////// static s32 scsp_env_table[SCSP_ENV_LEN * 2]; // envelope curve table (attack & decay) static s32 scsp_lfo_sawt_e[SCSP_LFO_LEN]; // lfo sawtooth waveform for envelope static s32 scsp_lfo_squa_e[SCSP_LFO_LEN]; // lfo square waveform for envelope static s32 scsp_lfo_tri_e[SCSP_LFO_LEN]; // lfo triangle waveform for envelope static s32 scsp_lfo_noi_e[SCSP_LFO_LEN]; // lfo noise waveform for envelope static s32 scsp_lfo_sawt_f[SCSP_LFO_LEN]; // lfo sawtooth waveform for frequency static s32 scsp_lfo_squa_f[SCSP_LFO_LEN]; // lfo square waveform for frequency static s32 scsp_lfo_tri_f[SCSP_LFO_LEN]; // lfo triangle waveform for frequency static s32 scsp_lfo_noi_f[SCSP_LFO_LEN]; // lfo noise waveform frequency static s32 scsp_attack_rate[0x40 + 0x20]; // envelope step for attack static s32 scsp_decay_rate[0x40 + 0x20]; // envelope step for decay static s32 scsp_null_rate[0x20]; // null envelope step static s32 scsp_lfo_step[32]; // directly give the lfo counter step static s32 scsp_tl_table[256]; // table of values for total level attentuation static u8 scsp_reg[0x1000]; static u8 *scsp_isr; static u8 *scsp_ccr; static u8 *scsp_dcr; static s32 *scsp_bufL; static s32 *scsp_bufR; static u32 scsp_buf_len; static u32 scsp_buf_pos; static scsp_t scsp; // SCSP structure #define CDDA_NUM_BUFFERS 2*75 static union { u8 data[CDDA_NUM_BUFFERS*2352]; } cddabuf; static unsigned int cdda_next_in=0; // Next sector buffer offset to receive into static u32 cdda_out_left; // Bytes of CDDA left to output //////////////////////////////////////////////////////////////// static void scsp_env_null_next(slot_t *slot); static void scsp_release_next(slot_t *slot); static void scsp_sustain_next(slot_t *slot); static void scsp_decay_next(slot_t *slot); static void scsp_attack_next(slot_t *slot); static void scsp_slot_update_keyon(slot_t *slot); ////////////////////////////////////////////////////////////////////////////// static int scsp_mute_flags = 0; static int scsp_volume = 100; //////////////////////////////////////////////////////////////// // Misc static int scsp_round (double val) { return (int)(val + 0.5); } //////////////////////////////////////////////////////////////// // Interrupts static INLINE void scsp_trigger_main_interrupt (u32 id) { SCSPLOG ("scsp main interrupt accepted %.4X\n", id); scsp.mintf (); } static INLINE void scsp_trigger_sound_interrupt (u32 id) { u32 level; level = 0; if (id > 0x80) id = 0x80; if (scsp.scilv0 & id) level |= 1; if (scsp.scilv1 & id) level |= 2; if (scsp.scilv2 & id) level |= 4; #ifdef SCSP_DEBUG if (id == 0x8) SCSPLOG ("scsp sound interrupt accepted %.2X lev=%d\n", id, level); #endif scsp.sintf (level); } static void scsp_main_interrupt (u32 id) { // if (scsp.mcipd & id) return; // if (id != 0x400) SCSPLOG("scsp main interrupt %.4X\n", id); scsp.mcipd |= id; WRITE_THROUGH (scsp.mcipd); if (scsp.mcieb & id) scsp_trigger_main_interrupt (id); } static void scsp_sound_interrupt (u32 id) { // if (scsp.scipd & id) return; // SCSPLOG ("scsp sound interrupt %.4X\n", id); scsp.scipd |= id; WRITE_THROUGH (scsp.scipd); if (scsp.scieb & id) scsp_trigger_sound_interrupt (id); } //////////////////////////////////////////////////////////////// // Direct Memory Access static void scsp_dma (void) { if (scsp.dmfl & 0x20) { // dsp -> scsp_ram SCSPLOG ("scsp dma: scsp_ram(%08lx) <- reg(%08lx) * %08lx\n", scsp.dmea, scsp.drga, scsp.dmlen); } else { // scsp_ram -> dsp SCSPLOG ("scsp dma: scsp_ram(%08lx) -> reg(%08lx) * %08lx\n", scsp.dmea, scsp.drga, scsp.dmlen); } scsp_ccr[0x16 ^ 3] &= 0xE0; scsp_sound_interrupt (0x10); scsp_main_interrupt (0x10); } //////////////////////////////////////////////////////////////// // Key ON/OFF event handler static void scsp_slot_keyon (slot_t *slot) { // key need to be released before being pressed ;) if (slot->ecurp == SCSP_ENV_RELEASE) { SCSPLOG ("key on slot %d. 68K PC = %08X slot->sa = %08X slot->lsa = %08X " "slot->lea = %08X\n", slot - &(scsp.slot[0]), M68K->GetPC(), slot->sa, slot->lsa, slot->lea >> SCSP_FREQ_LB); // set buffer, loop start/end address of the slot if (slot->pcm8b) { slot->buf8 = (s8*) &(scsp.scsp_ram[slot->sa]); if ((slot->sa + (slot->lea >> SCSP_FREQ_LB)) > SCSP_RAM_MASK) slot->lea = (SCSP_RAM_MASK - slot->sa) << SCSP_FREQ_LB; } else { slot->buf16 = (s16*) &(scsp.scsp_ram[slot->sa & ~1]); if ((slot->sa + (slot->lea >> (SCSP_FREQ_LB - 1))) > SCSP_RAM_MASK) slot->lea = (SCSP_RAM_MASK - slot->sa) << (SCSP_FREQ_LB - 1); } slot->fcnt = 0; // reset frequency counter slot->ecnt = SCSP_ENV_AS; // reset envelope counter (probably wrong, // should convert decay to attack?) slot->env = 0; // reset envelope slot->einc = &slot->einca; // envelope counter step is attack step slot->ecurp = SCSP_ENV_ATTACK; // current envelope phase is attack slot->ecmp = SCSP_ENV_AE; // limit reach to next event (Attack End) slot->enxt = scsp_attack_next; // function pointer to next event } } static void scsp_slot_keyoff (slot_t *slot) { // key need to be pressed before being released ;) if (slot->ecurp != SCSP_ENV_RELEASE) { SCSPLOG ("key off slot %d\n", slot - &(scsp.slot[0])); // if we still are in attack phase at release time, convert attack to decay if (slot->ecurp == SCSP_ENV_ATTACK) slot->ecnt = SCSP_ENV_DE - slot->ecnt; slot->einc = &slot->eincr; slot->ecmp = SCSP_ENV_DE; slot->ecurp = SCSP_ENV_RELEASE; slot->enxt = scsp_release_next; } } static void scsp_slot_keyonoff (void) { slot_t *slot; for(slot = &(scsp.slot[0]); slot < &(scsp.slot[32]); slot++) { if (slot->key) scsp_slot_keyon (slot); else scsp_slot_keyoff (slot); } } /* Envelope Events Handler Max EG level = 0x3FF /|\ / | \ / | \_____ Min EG level = 0x000 __/ | | |\___ A D1 D2 R */ static void scsp_env_null_next (UNUSED slot_t *slot) { // only to prevent null call pointer... } static void scsp_release_next (slot_t *slot) { // end of release happened, update to process the next phase... slot->ecnt = SCSP_ENV_DE; slot->einc = NULL; slot->ecmp = SCSP_ENV_DE + 1; slot->enxt = scsp_env_null_next; } static void scsp_sustain_next (slot_t *slot) { // end of sustain happened, update to process the next phase... slot->ecnt = SCSP_ENV_DE; slot->einc = NULL; slot->ecmp = SCSP_ENV_DE + 1; slot->enxt = scsp_env_null_next; } static void scsp_decay_next (slot_t *slot) { // end of decay happened, update to process the next phase... slot->ecnt = slot->sl; slot->einc = &slot->eincs; slot->ecmp = SCSP_ENV_DE; slot->ecurp = SCSP_ENV_SUSTAIN; slot->enxt = scsp_sustain_next; } static void scsp_attack_next (slot_t *slot) { // end of attack happened, update to process the next phase... slot->ecnt = SCSP_ENV_DS; slot->einc = &slot->eincd; slot->ecmp = slot->sl; slot->ecurp = SCSP_ENV_DECAY; slot->enxt = scsp_decay_next; } //////////////////////////////////////////////////////////////// // Slot Access static void scsp_slot_refresh_einc (slot_t *slot, u32 adsr_bitmask) { if (slot->arp && (adsr_bitmask & 0x1)) slot->einca = slot->arp[(14 - slot->fsft) >> slot->krs]; if (slot->drp && (adsr_bitmask & 0x2)) slot->eincd = slot->drp[(14 - slot->fsft) >> slot->krs]; if (slot->srp && (adsr_bitmask & 0x4)) slot->eincs = slot->srp[(14 - slot->fsft) >> slot->krs]; if (slot->rrp && (adsr_bitmask & 0x8)) slot->eincr = slot->rrp[(14 - slot->fsft) >> slot->krs]; } static void scsp_slot_set_b (u32 s, u32 a, u8 d) { slot_t *slot = &(scsp.slot[s]); SCSPLOG("slot %d : reg %.2X = %.2X\n", s, a & 0x1F, d); scsp_isr[a ^ 3] = d; switch (a & 0x1F) { case 0x00: // KX/KB/SBCTL/SSCTL(high bit) slot->key = (d >> 3) & 1; slot->sbctl = (d >> 1) & 3; slot->ssctl = (slot->ssctl & 1) + ((d & 1) << 1); if (d & 0x10) scsp_slot_keyonoff (); return; case 0x01: // SSCTL(low bit)/LPCTL/8B/SA(highest 4 bits) slot->ssctl = (slot->ssctl & 2) + ((d >> 7) & 1); slot->lpctl = (d >> 5) & 3; slot->pcm8b = d & 0x10; slot->sa = (slot->sa & 0x0FFFF) + ((d & 0xF) << 16); slot->sa &= SCSP_RAM_MASK; if (slot->ecnt < SCSP_ENV_DE) scsp_slot_update_keyon (slot); return; case 0x02: // SA(next highest byte) slot->sa = (slot->sa & 0xF00FF) + (d << 8); slot->sa &= SCSP_RAM_MASK; if (slot->ecnt < SCSP_ENV_DE) scsp_slot_update_keyon (slot); return; case 0x03: // SA(low byte) slot->sa = (slot->sa & 0xFFF00) + d; slot->sa &= SCSP_RAM_MASK; if (slot->ecnt < SCSP_ENV_DE) scsp_slot_update_keyon (slot); return; case 0x04: // LSA(high byte) slot->lsa = (slot->lsa & (0x00FF << SCSP_FREQ_LB)) + (d << (8 + SCSP_FREQ_LB)); return; case 0x05: // LSA(low byte) slot->lsa = (slot->lsa & (0xFF00 << SCSP_FREQ_LB)) + (d << SCSP_FREQ_LB); return; case 0x06: // LEA(high byte) slot->lea = (slot->lea & (0x00FF << SCSP_FREQ_LB)) + (d << (8 + SCSP_FREQ_LB)); return; case 0x07: // LEA(low byte) slot->lea = (slot->lea & (0xFF00 << SCSP_FREQ_LB)) + (d << SCSP_FREQ_LB); return; case 0x08: // D2R/D1R(highest 3 bits) slot->sr = (d >> 3) & 0x1F; slot->dr = (slot->dr & 0x03) + ((d & 7) << 2); if (slot->sr) slot->srp = &scsp_decay_rate[slot->sr << 1]; else slot->srp = &scsp_null_rate[0]; if (slot->dr) slot->drp = &scsp_decay_rate[slot->dr << 1]; else slot->drp = &scsp_null_rate[0]; scsp_slot_refresh_einc (slot, 0x2 | 0x4); return; case 0x09: // D1R(lowest 2 bits)/EGHOLD/AR slot->dr = (slot->dr & 0x1C) + ((d >> 6) & 3); slot->eghold = d & 0x20; slot->ar = d & 0x1F; if (slot->dr) slot->drp = &scsp_decay_rate[slot->dr << 1]; else slot->drp = &scsp_null_rate[0]; if (slot->ar) slot->arp = &scsp_attack_rate[slot->ar << 1]; else slot->arp = &scsp_null_rate[0]; scsp_slot_refresh_einc (slot, 0x1 | 0x2); return; case 0x0A: // LPSLNK/KRS/DL(highest 2 bits) slot->lslnk = d & 0x40; slot->krs = (d >> 2) & 0xF; if (slot->krs == 0xF) slot->krs = 4; else slot->krs >>= 2; slot->sl &= 0xE0 << SCSP_ENV_LB; slot->sl += (d & 3) << (8 + SCSP_ENV_LB); slot->sl += SCSP_ENV_DS; // adjusted for envelope compare (ecmp) scsp_slot_refresh_einc (slot, 0xF); return; case 0x0B: // DL(lowest 3 bits)/RR slot->sl &= 0x300 << SCSP_ENV_LB; slot->sl += (d & 0xE0) << SCSP_ENV_LB; slot->sl += SCSP_ENV_DS; // adjusted for envelope compare (ecmp) slot->rr = d & 0x1F; if (slot->rr) slot->rrp = &scsp_decay_rate[slot->rr << 1]; else slot->rrp = &scsp_null_rate[0]; scsp_slot_refresh_einc (slot, 0x8); return; case 0x0C: // STWINH/SDIR slot->sdir = d & 2; slot->swe = d & 1; return; case 0x0D: // TL slot->tl = scsp_tl_table[(d & 0xFF)]; return; case 0x0E: // MDL/MDXSL(highest 4 bits) slot->mdl = (d >> 4) & 0xF; // need to adjust for correct shift slot->mdx = (slot->mdx & 3) + ((d & 0xF) << 2); return; case 0x0F: // MDXSL(lowest 2 bits)/MDYSL slot->mdx = (slot->mdx & 0x3C) + ((d >> 6) & 3); slot->mdy = d & 0x3F; return; case 0x10: // OCT/FNS(highest 2 bits) if (d & 0x40) slot->fsft = 23 - ((d >> 3) & 0xF); else slot->fsft = ((d >> 3) & 7) ^ 7; slot->finct = (slot->finct & 0x7F80) + ((d & 3) << (8 + 7)); slot->finc = (0x20000 + slot->finct) >> slot->fsft; scsp_slot_refresh_einc (slot, 0xF); return; case 0x11: // FNS(low byte) slot->finct = (slot->finct & 0x18000) + (d << 7); slot->finc = (0x20000 + slot->finct) >> slot->fsft; return; case 0x12: // LFORE/LFOF/PLFOWS if (d & 0x80) { slot->lfoinc = -1; return; } else if (slot->lfoinc == -1) { slot->lfocnt = 0; } slot->lfoinc = scsp_lfo_step[(d >> 2) & 0x1F]; switch (d & 3) { case 0: slot->lfofmw = scsp_lfo_sawt_f; return; case 1: slot->lfofmw = scsp_lfo_squa_f; return; case 2: slot->lfofmw = scsp_lfo_tri_f; return; case 3: slot->lfofmw = scsp_lfo_noi_f; return; } case 0x13: // PLFOS/ALFOWS/ALFOS if ((d >> 5) & 7) slot->lfofms = ((d >> 5) & 7) + 7; else slot->lfofms = 31; if (d & 7) slot->lfoems = ((d & 7) ^ 7) + 4; else slot->lfoems = 31; switch ((d >> 3) & 3) { case 0: slot->lfoemw = scsp_lfo_sawt_e; return; case 1: slot->lfoemw = scsp_lfo_squa_e; return; case 2: slot->lfoemw = scsp_lfo_tri_e; return; case 3: slot->lfoemw = scsp_lfo_noi_e; } return; case 0x15: // ISEL/OMXL if (d & 7) slot->imxl = ((d & 7) ^ 7) + SCSP_ENV_HB; else slot->imxl = 31; return; case 0x16: // DISDL/DIPAN if (d & 0xE0) { // adjusted for envelope calculation // some inaccuracy in panning though... slot->dislr = slot->disll = (((d >> 5) & 7) ^ 7) + SCSP_ENV_HB; if (d & 0x10) { // Panning Left if ((d & 0xF) == 0xF) slot->dislr = 31; else slot->dislr += (d >> 1) & 7; } else { // Panning Right if ((d & 0xF) == 0xF) slot->disll = 31; else slot->disll += (d >> 1) & 7; } } else { slot->dislr = slot->disll = 31; // muted } return; case 0x17: // EFSDL/EFPAN if (d & 0xE0) { slot->efslr = slot->efsll = (((d >> 5) & 7) ^ 7) + SCSP_ENV_HB; if (d & 0x10) { // Panning Left if ((d & 0xF) == 0xF) slot->efslr = 31; else slot->efslr += (d >> 1) & 7; } else { // Panning Right if ((d & 0xF) == 0xF) slot->efsll = 31; else slot->efsll += (d >> 1) & 7; } } else { slot->efslr = slot->efsll = 31; // muted } return; } } static void scsp_slot_set_w (u32 s, s32 a, u16 d) { slot_t *slot = &(scsp.slot[s]); SCSPLOG ("slot %d : reg %.2X = %.4X\n", s, a & 0x1E, d); *(u16 *)&scsp_isr[a ^ 2] = d; switch (a & 0x1E) { case 0x00: // KYONEX/KYONB/SBCTL/SSCTL/LPCTL/PCM8B/SA(highest 4 bits) slot->key = (d >> 11) & 1; slot->sbctl = (d >> 9) & 3; slot->ssctl = (d >> 7) & 3; slot->lpctl = (d >> 5) & 3; slot->pcm8b = d & 0x10; slot->sa = (slot->sa & 0x0FFFF) | ((d & 0xF) << 16); slot->sa &= SCSP_RAM_MASK; if (slot->ecnt < SCSP_ENV_DE) scsp_slot_update_keyon(slot); if (d & 0x1000) scsp_slot_keyonoff(); return; case 0x02: // SA(low word) slot->sa = (slot->sa & 0xF0000) | d; slot->sa &= SCSP_RAM_MASK; if (slot->ecnt < SCSP_ENV_DE) scsp_slot_update_keyon(slot); return; case 0x04: // LSA slot->lsa = d << SCSP_FREQ_LB; return; case 0x06: // LEA slot->lea = d << SCSP_FREQ_LB; return; case 0x08: // D2R/D1R/EGHOLD/AR slot->sr = (d >> 11) & 0x1F; slot->dr = (d >> 6) & 0x1F; slot->eghold = d & 0x20; slot->ar = d & 0x1F; if (slot->sr) slot->srp = &scsp_decay_rate[slot->sr << 1]; else slot->srp = &scsp_null_rate[0]; if (slot->dr) slot->drp = &scsp_decay_rate[slot->dr << 1]; else slot->drp = &scsp_null_rate[0]; if (slot->ar) slot->arp = &scsp_attack_rate[slot->ar << 1]; else slot->arp = &scsp_null_rate[0]; scsp_slot_refresh_einc (slot, 0x1 | 0x2 | 0x4); return; case 0x0A: // LPSLNK/KRS/DL/RR slot->lslnk = (d >> 8) & 0x40; slot->krs = (d >> 10) & 0xF; if (slot->krs == 0xF) slot->krs = 4; else slot->krs >>= 2; slot->sl = ((d & 0x3E0) << SCSP_ENV_LB) + SCSP_ENV_DS; // adjusted for envelope compare (ecmp) slot->rr = d & 0x1F; if (slot->rr) slot->rrp = &scsp_decay_rate[slot->rr << 1]; else slot->rrp = &scsp_null_rate[0]; scsp_slot_refresh_einc (slot, 0xF); return; case 0x0C: // STWINH/SDIR slot->sdir = (d >> 8) & 2; slot->swe = (d >> 8) & 1; slot->tl = scsp_tl_table[(d & 0xFF)]; return; case 0x0E: // MDL/MDXSL/MDYSL slot->mdl = (d >> 12) & 0xF; // need to adjust for correct shift slot->mdx = (d >> 6) & 0x3F; slot->mdy = d & 0x3F; return; case 0x10: // OCT/FNS if (d & 0x4000) slot->fsft = 23 - ((d >> 11) & 0xF); else slot->fsft = (((d >> 11) & 7) ^ 7); slot->finc = ((0x400 + (d & 0x3FF)) << 7) >> slot->fsft; scsp_slot_refresh_einc (slot, 0xF); return; case 0x12: // LFORE/LFOF/PLFOWS/PLFOS/ALFOWS/ALFOS if (d & 0x8000) { slot->lfoinc = -1; return; } else if (slot->lfoinc == -1) { slot->lfocnt = 0; } slot->lfoinc = scsp_lfo_step[(d >> 10) & 0x1F]; if ((d >> 5) & 7) slot->lfofms = ((d >> 5) & 7) + 7; else slot->lfofms = 31; if (d & 7) slot->lfoems = ((d & 7) ^ 7) + 4; else slot->lfoems = 31; switch ((d >> 8) & 3) { case 0: slot->lfofmw = scsp_lfo_sawt_f; break; case 1: slot->lfofmw = scsp_lfo_squa_f; break; case 2: slot->lfofmw = scsp_lfo_tri_f; break; case 3: slot->lfofmw = scsp_lfo_noi_f; break; } switch ((d >> 3) & 3) { case 0: slot->lfoemw = scsp_lfo_sawt_e; return; case 1: slot->lfoemw = scsp_lfo_squa_e; return; case 2: slot->lfoemw = scsp_lfo_tri_e; return; case 3: slot->lfoemw = scsp_lfo_noi_e; } return; case 0x14: // ISEL/OMXL if (d & 7) slot->imxl = ((d & 7) ^ 7) + SCSP_ENV_HB; else slot->imxl = 31; return; case 0x16: // DISDL/DIPAN/EFSDL/EFPAN if (d & 0xE000) { // adjusted fr enveloppe calculation // some accuracy lose for panning here... slot->dislr = slot->disll = (((d >> 13) & 7) ^ 7) + SCSP_ENV_HB; if (d & 0x1000) { // Panning Left if ((d & 0xF00) == 0xF00) slot->dislr = 31; else slot->dislr += (d >> 9) & 7; } else { // Panning Right if ((d & 0xF00) == 0xF00) slot->disll = 31; else slot->disll += (d >> 9) & 7; } } else { slot->dislr = slot->disll = 31; // muted } if (d & 0xE0) { slot->efslr = slot->efsll = (((d >> 5) & 7) ^ 7) + SCSP_ENV_HB; if (d & 0x10) { // Panning Left if ((d & 0xF) == 0xF) slot->efslr = 31; else slot->efslr += (d >> 1) & 7; } else { // Panning Right if ((d & 0xF) == 0xF) slot->efsll = 31; else slot->efsll += (d >> 1) & 7; } } else { slot->efslr = slot->efsll = 31; // muted } return; } } static u8 scsp_slot_get_b (u32 s, u32 a) { u8 val = scsp_isr[a ^ 3]; // Mask out keyonx if ((a & 0x1F) == 0x00) val &= 0xEF; SCSPLOG ("r_b slot %d (%.2X) : reg %.2X = %.2X\n", s, a, a & 0x1F, val); return val; } static u16 scsp_slot_get_w(u32 s, u32 a) { u16 val = *(u16 *)&scsp_isr[a ^ 2]; if ((a & 0x1E) == 0x00) return val &= 0xEFFF; SCSPLOG ("r_w slot %d (%.2X) : reg %.2X = %.4X\n", s, a, a & 0x1E, val); return val; } //////////////////////////////////////////////////////////////// // SCSP Access static void scsp_set_b (u32 a, u8 d) { if ((a != 0x408) && (a != 0x41D)) { SCSPLOG("scsp : reg %.2X = %.2X\n", a & 0x3F, d); } scsp_ccr[a ^ 3] = d; switch (a & 0x3F) { case 0x00: // MEM4MB/DAC18B scsp.mem4b = (d >> 1) & 0x1; if (scsp.mem4b) { M68K->SetFetch(0x000000, 0x080000, (pointer)SoundRam); } else { M68K->SetFetch(0x000000, 0x040000, (pointer)SoundRam); M68K->SetFetch(0x040000, 0x080000, (pointer)SoundRam); M68K->SetFetch(0x080000, 0x0C0000, (pointer)SoundRam); M68K->SetFetch(0x0C0000, 0x100000, (pointer)SoundRam); } return; case 0x01: // VER/MVOL scsp.mvol = d & 0xF; return; case 0x02: // RBL(high bit) scsp.rbl = (scsp.rbl & 1) + ((d & 1) << 1); return; case 0x03: // RBL(low bit)/RBP scsp.rbl = (scsp.rbl & 2) + ((d >> 7) & 1); scsp.rbp = (d & 0x7F) * (4 * 1024 * 2); return; case 0x07: // MOBUF scsp_midi_out_send(d); return; case 0x08: // MSLC scsp.mslc = (d >> 3) & 0x1F; scsp_update_monitor (); return; case 0x12: // DMEAL(high byte) scsp.dmea = (scsp.dmea & 0x700FE) + (d << 8); return; case 0x13: // DMEAL(low byte) scsp.dmea = (scsp.dmea & 0x7FF00) + (d & 0xFE); return; case 0x14: // DMEAH(high byte) scsp.dmea = (scsp.dmea & 0xFFFE) + ((d & 0x70) << 12); scsp.drga = (scsp.drga & 0xFE) + ((d & 0xF) << 8); return; case 0x15: // DMEAH(low byte) scsp.drga = (scsp.drga & 0xF00) + (d & 0xFE); return; case 0x16: // DGATE/DDIR/DEXE/DTLG(upper 4 bits) scsp.dmlen = (scsp.dmlen & 0xFE) + ((d & 0xF) << 8); if ((scsp.dmfl = d & 0xF0) & 0x10) scsp_dma (); return; case 0x17: // DTLG(lower byte) scsp.dmlen = (scsp.dmlen & 0xF00) + (d & 0xFE); return; case 0x18: // TACTL scsp.timasd = d & 7; return; case 0x19: // TIMA scsp.timacnt = d << 8; return; case 0x1A: // TBCTL scsp.timbsd = d & 7; return; case 0x1B: // TIMB scsp.timbcnt = d << 8; return; case 0x1C: // TCCTL scsp.timcsd = d & 7; return; case 0x1D: // TIMC scsp.timccnt = d << 8; return; case 0x1E: // SCIEB(high byte) { int i; scsp.scieb = (scsp.scieb & 0xFF) + (d << 8); for (i = 0; i < 3; i++) { if (scsp.scieb & (1 << i) && scsp.scipd & (1 << i)) scsp_trigger_sound_interrupt ((1 << (i+8))); } return; } case 0x1F: // SCIEB(low byte) { int i; scsp.scieb = (scsp.scieb & 0x700) + d; for (i = 0; i < 8; i++) { if (scsp.scieb & (1 << i) && scsp.scipd & (1 << i)) scsp_trigger_sound_interrupt ((1 << i)); } return; } case 0x21: // SCIPD(low byte) if (d & 0x20) scsp_sound_interrupt (0x20); return; case 0x22: // SCIRE(high byte) scsp.scipd &= ~(d << 8); return; case 0x23: // SCIRE(low byte) scsp.scipd &= ~(u32)d; return; case 0x25: // SCILV0 scsp.scilv0 = d; return; case 0x27: // SCILV1 scsp.scilv1 = d; return; case 0x29: // SCILV2 scsp.scilv2 = d; return; case 0x2A: // MCIEB(high byte) scsp.mcieb = (scsp.mcieb & 0xFF) + (d << 8); return; case 0x2B: // MCIEB(low byte) scsp.mcieb = (scsp.mcieb & 0x700) + d; return; case 0x2D: // MCIPD(low byte) if (d & 0x20) scsp_main_interrupt(0x20); return; case 0x2E: // MCIRE(high byte) scsp.mcipd &= ~(d << 8); return; case 0x2F: // MCIRE(low byte) scsp.mcipd &= ~(u32)d; return; } } static void scsp_set_w (u32 a, u16 d) { if ((a != 0x418) && (a != 0x41A) && (a != 0x422)) { SCSPLOG("scsp : reg %.2X = %.4X\n", a & 0x3E, d); } *(u16 *)&scsp_ccr[a ^ 2] = d; switch (a & 0x3E) { case 0x00: // MEM4MB/DAC18B/VER/MVOL scsp.mem4b = (d >> 9) & 0x1; scsp.mvol = d & 0xF; if (scsp.mem4b) { M68K->SetFetch(0x000000, 0x080000, (pointer)SoundRam); } else { M68K->SetFetch(0x000000, 0x040000, (pointer)SoundRam); M68K->SetFetch(0x040000, 0x080000, (pointer)SoundRam); M68K->SetFetch(0x080000, 0x0C0000, (pointer)SoundRam); M68K->SetFetch(0x0C0000, 0x100000, (pointer)SoundRam); } return; case 0x02: // RBL/RBP scsp.rbl = (d >> 7) & 3; scsp.rbp = (d & 0x7F) * (4 * 1024 * 2); return; case 0x06: // MOBUF scsp_midi_out_send(d & 0xFF); return; case 0x08: // MSLC scsp.mslc = (d >> 11) & 0x1F; scsp_update_monitor(); return; case 0x12: // DMEAL scsp.dmea = (scsp.dmea & 0x70000) + (d & 0xFFFE); return; case 0x14: // DMEAH/DRGA scsp.dmea = (scsp.dmea & 0xFFFE) + ((d & 0x7000) << 4); scsp.drga = d & 0xFFE; return; case 0x16: // DGATE/DDIR/DEXE/DTLG scsp.dmlen = d & 0xFFE; if ((scsp.dmfl = ((d >> 8) & 0xF0)) & 0x10) scsp_dma (); return; case 0x18: // TACTL/TIMA scsp.timasd = (d >> 8) & 7; scsp.timacnt = (d & 0xFF) << 8; return; case 0x1A: // TBCTL/TIMB scsp.timbsd = (d >> 8) & 7; scsp.timbcnt = (d & 0xFF) << 8; return; case 0x1C: // TCCTL/TIMC scsp.timcsd = (d >> 8) & 7; scsp.timccnt = (d & 0xFF) << 8; return; case 0x1E: // SCIEB { int i; scsp.scieb = d; for (i = 0; i < 11; i++) { if (scsp.scieb & (1 << i) && scsp.scipd & (1 << i)) scsp_trigger_sound_interrupt ((1 << i)); } return; } case 0x20: // SCIPD if (d & 0x20) scsp_sound_interrupt (0x20); return; case 0x22: // SCIRE scsp.scipd &= ~d; return; case 0x24: // SCILV0 scsp.scilv0 = d; return; case 0x26: // SCILV1 scsp.scilv1 = d; return; case 0x28: // SCILV2 scsp.scilv2 = d; return; case 0x2A: // MCIEB { int i; scsp.mcieb = d; for (i = 0; i < 11; i++) { if (scsp.mcieb & (1 << i) && scsp.mcipd & (1 << i)) scsp_trigger_main_interrupt ((1 << i)); } return; } case 0x2C: // MCIPD if (d & 0x20) scsp_main_interrupt (0x20); return; case 0x2E: // MCIRE scsp.mcipd &= ~d; return; } } static u8 scsp_get_b (u32 a) { a &= 0x3F; if ((a != 0x09) && (a != 0x21)) { SCSPLOG("r_b scsp : reg %.2X\n", a); } // if (a == 0x09) SCSPLOG("r_b scsp 09 = %.2X\n", ((scsp.slot[scsp.mslc].fcnt >> (SCSP_FREQ_LB + 12)) & 0x1) << 7); switch (a) { case 0x01: // VER/MVOL scsp_ccr[a ^ 3] &= 0x0F; break; case 0x04: // Midi flags register return scsp.midflag; case 0x05: // MIBUF return scsp_midi_in_read(); case 0x07: // MOBUF return scsp_midi_out_read(); case 0x08: // CA(highest 3 bits) return (scsp.ca >> 8); case 0x09: // CA(lowest bit)/SGC/EG return (scsp.ca & 0xE0) | (scsp.sgc << 5) | scsp.eg; case 0x1E: // SCIEB(high byte) return (scsp.scieb >> 8); case 0x1F: // SCIEB(low byte) return scsp.scieb; case 0x20: // SCIPD(high byte) return (scsp.scipd >> 8); case 0x21: // SCIPD(low byte) return scsp.scipd; case 0x2C: // MCIPD(high byte) return (scsp.mcipd >> 8); case 0x2D: // MCIPD(low byte) return scsp.mcipd; } return scsp_ccr[a ^ 3]; } static u16 scsp_get_w (u32 a) { a &= 0x3E; if ((a != 0x20) && (a != 0x08)) { SCSPLOG("r_w scsp : reg %.2X\n", a * 2); } switch (a) { case 0x00: // MEM4MB/DAC18B/VER/MVOL *(u16 *)&scsp_ccr[a ^ 2] &= 0xFF0F; break; case 0x04: // Midi flags/MIBUF return (scsp.midflag << 8) | scsp_midi_in_read(); case 0x06: // MOBUF return scsp_midi_out_read(); case 0x08: // CA/SGC/EG return (scsp.ca & 0x780) | (scsp.sgc << 5) | scsp.eg; case 0x18: // TACTL return (scsp.timasd << 8); case 0x1A: // TBCTL return (scsp.timbsd << 8); case 0x1C: // TCCTL return (scsp.timcsd << 8); case 0x1E: // SCIEB return scsp.scieb; case 0x20: // SCIPD return scsp.scipd; case 0x2C: // MCIPD return scsp.mcipd; } return *(u16 *)&scsp_ccr[a ^ 2]; } //////////////////////////////////////////////////////////////// // Synth Slot // // SCSPLOG("outL=%.8X bufL=%.8X disll=%d\n", outL, scsp_bufL[scsp_buf_pos], slot->disll); //////////////////////////////////////////////////////////////// #ifdef WORDS_BIGENDIAN #define SCSP_GET_OUT_8B \ out = (s32) slot->buf8[(slot->fcnt >> SCSP_FREQ_LB)]; #else #define SCSP_GET_OUT_8B \ out = (s32) slot->buf8[(slot->fcnt >> SCSP_FREQ_LB) ^ 1]; #endif #define SCSP_GET_OUT_16B \ out = (s32) slot->buf16[slot->fcnt >> SCSP_FREQ_LB]; #define SCSP_GET_ENV \ slot->env = scsp_env_table[slot->ecnt >> SCSP_ENV_LB] * slot->tl / 1024; #define SCSP_GET_ENV_LFO \ slot->env = (scsp_env_table[slot->ecnt >> SCSP_ENV_LB] * slot->tl / 1024) - \ (slot->lfoemw[(slot->lfocnt >> SCSP_LFO_LB) & SCSP_LFO_MASK] >> \ slot->lfoems); #define SCSP_OUT_8B_L \ if ((out) && (slot->env > 0)) \ { \ out *= slot->env; \ scsp_bufL[scsp_buf_pos] += out >> (slot->disll - 8); \ } #define SCSP_OUT_8B_R \ if ((out) && (slot->env > 0)) \ { \ out *= slot->env; \ scsp_bufR[scsp_buf_pos] += out >> (slot->dislr - 8); \ } #define SCSP_OUT_8B_LR \ if ((out) && (slot->env > 0)) \ { \ out *= slot->env; \ scsp_bufL[scsp_buf_pos] += out >> (slot->disll - 8); \ scsp_bufR[scsp_buf_pos] += out >> (slot->dislr - 8); \ } #define SCSP_OUT_16B_L \ if ((out) && (slot->env > 0)) \ { \ out *= slot->env; \ scsp_bufL[scsp_buf_pos] += out >> slot->disll; \ } #define SCSP_OUT_16B_R \ if ((out) && (slot->env > 0)) \ { \ out *= slot->env; \ scsp_bufR[scsp_buf_pos] += out >> slot->dislr; \ } #define SCSP_OUT_16B_LR \ if ((out) && (slot->env > 0)) \ { \ out *= slot->env; \ scsp_bufL[scsp_buf_pos] += out >> slot->disll; \ scsp_bufR[scsp_buf_pos] += out >> slot->dislr; \ } #define SCSP_UPDATE_PHASE \ if ((slot->fcnt += slot->finc) > slot->lea) \ { \ if (slot->lpctl) \ { \ slot->fcnt = slot->lsa; \ } \ else \ { \ slot->ecnt = SCSP_ENV_DE; \ return; \ } \ } #define SCSP_UPDATE_PHASE_LFO \ slot->fcnt += \ ((slot->lfofmw[(slot->lfocnt >> SCSP_LFO_LB) & SCSP_LFO_MASK] << \ (slot->lfofms-7)) >> (slot->fsft+1)); \ if ((slot->fcnt += slot->finc) > slot->lea) \ { \ if (slot->lpctl) \ { \ slot->fcnt = slot->lsa; \ } \ else \ { \ slot->ecnt = SCSP_ENV_DE; \ return; \ } \ } #define SCSP_UPDATE_ENV \ if (slot->einc) slot->ecnt += *slot->einc; \ if (slot->ecnt >= slot->ecmp) \ { \ slot->enxt(slot); \ if (slot->ecnt >= SCSP_ENV_DE) return; \ } #define SCSP_UPDATE_LFO \ slot->lfocnt += slot->lfoinc; //////////////////////////////////////////////////////////////// static void scsp_slot_update_keyon (slot_t *slot) { // set buffer, loop start/end address of the slot if (slot->pcm8b) { slot->buf8 = (s8*)&(scsp.scsp_ram[slot->sa]); if ((slot->sa + (slot->lea >> SCSP_FREQ_LB)) > SCSP_RAM_MASK) slot->lea = (SCSP_RAM_MASK - slot->sa) << SCSP_FREQ_LB; } else { slot->buf16 = (s16*)&(scsp.scsp_ram[slot->sa & ~1]); if ((slot->sa + (slot->lea >> (SCSP_FREQ_LB - 1))) > SCSP_RAM_MASK) slot->lea = (SCSP_RAM_MASK - slot->sa) << (SCSP_FREQ_LB - 1); } SCSP_UPDATE_PHASE } //////////////////////////////////////////////////////////////// static void scsp_slot_update_null (slot_t *slot) { for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_ENV SCSP_UPDATE_PHASE SCSP_UPDATE_ENV } } //////////////////////////////////////////////////////////////// // Normal 8 bits static void scsp_slot_update_8B_L (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { // env = [0..0x3FF] - slot->tl SCSP_GET_OUT_8B SCSP_GET_ENV // don't waste time if no sound... SCSP_OUT_8B_L // calculate new frequency (phase) counter and enveloppe counter SCSP_UPDATE_PHASE SCSP_UPDATE_ENV } } static void scsp_slot_update_8B_R (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV SCSP_OUT_8B_R SCSP_UPDATE_PHASE SCSP_UPDATE_ENV } } static void scsp_slot_update_8B_LR(slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV SCSP_OUT_8B_LR SCSP_UPDATE_PHASE SCSP_UPDATE_ENV } } //////////////////////////////////////////////////////////////// // Envelope LFO modulation 8 bits static void scsp_slot_update_E_8B_L (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV_LFO SCSP_OUT_8B_L SCSP_UPDATE_PHASE SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_E_8B_R (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV_LFO SCSP_OUT_8B_R SCSP_UPDATE_PHASE SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_E_8B_LR (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV_LFO SCSP_OUT_8B_LR SCSP_UPDATE_PHASE SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } //////////////////////////////////////////////////////////////// // Frequency LFO modulation 8 bits static void scsp_slot_update_F_8B_L (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV SCSP_OUT_8B_L SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_F_8B_R (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV SCSP_OUT_8B_R SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_F_8B_LR (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV SCSP_OUT_8B_LR SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } //////////////////////////////////////////////////////////////// // Enveloppe & Frequency LFO modulation 8 bits static void scsp_slot_update_F_E_8B_L (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV_LFO SCSP_OUT_8B_L SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_F_E_8B_R (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV_LFO SCSP_OUT_8B_R SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_F_E_8B_LR(slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_8B SCSP_GET_ENV_LFO SCSP_OUT_8B_LR SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } //////////////////////////////////////////////////////////////// // Normal 16 bits static void scsp_slot_update_16B_L (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV SCSP_OUT_16B_L SCSP_UPDATE_PHASE SCSP_UPDATE_ENV } } static void scsp_slot_update_16B_R (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV SCSP_OUT_16B_R SCSP_UPDATE_PHASE SCSP_UPDATE_ENV } } static void scsp_slot_update_16B_LR (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV SCSP_OUT_16B_LR SCSP_UPDATE_PHASE SCSP_UPDATE_ENV } } //////////////////////////////////////////////////////////////// // Envelope LFO modulation 16 bits static void scsp_slot_update_E_16B_L (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV_LFO SCSP_OUT_16B_L SCSP_UPDATE_PHASE SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_E_16B_R (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV_LFO SCSP_OUT_16B_R SCSP_UPDATE_PHASE SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_E_16B_LR (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV_LFO SCSP_OUT_16B_LR SCSP_UPDATE_PHASE SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } //////////////////////////////////////////////////////////////// // Frequency LFO modulation 16 bits static void scsp_slot_update_F_16B_L (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV SCSP_OUT_16B_L SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_F_16B_R (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV SCSP_OUT_16B_R SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_F_16B_LR (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV SCSP_OUT_16B_LR SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } //////////////////////////////////////////////////////////////// // Envelope & Frequency LFO modulation 16 bits static void scsp_slot_update_F_E_16B_L (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV_LFO SCSP_OUT_16B_L SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_F_E_16B_R (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV_LFO SCSP_OUT_16B_R SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } static void scsp_slot_update_F_E_16B_LR (slot_t *slot) { s32 out; for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { SCSP_GET_OUT_16B SCSP_GET_ENV_LFO SCSP_OUT_16B_LR SCSP_UPDATE_PHASE_LFO SCSP_UPDATE_ENV SCSP_UPDATE_LFO } } //////////////////////////////////////////////////////////////// // Update functions static void (*scsp_slot_update_p[2][2][2][2][2])(slot_t *slot) = { // NO FMS { // NO EMS { // 8 BITS { // NO LEFT { // NO RIGHT scsp_slot_update_null, // RIGHT scsp_slot_update_8B_R }, // LEFT { // NO RIGHT scsp_slot_update_8B_L, // RIGHT scsp_slot_update_8B_LR }, }, // 16 BITS { // NO LEFT { // NO RIGHT scsp_slot_update_null, // RIGHT scsp_slot_update_16B_R }, // LEFT { // NO RIGHT scsp_slot_update_16B_L, // RIGHT scsp_slot_update_16B_LR }, } }, // EMS { // 8 BITS { // NO LEFT { // NO RIGHT scsp_slot_update_null, // RIGHT scsp_slot_update_E_8B_R }, // LEFT { // NO RIGHT scsp_slot_update_E_8B_L, // RIGHT scsp_slot_update_E_8B_LR }, }, // 16 BITS { // NO LEFT { // NO RIGHT scsp_slot_update_null, // RIGHT scsp_slot_update_E_16B_R }, // LEFT { // NO RIGHT scsp_slot_update_E_16B_L, // RIGHT scsp_slot_update_E_16B_LR }, } } }, // FMS { // NO EMS { // 8 BITS { // NO LEFT { // NO RIGHT scsp_slot_update_null, // RIGHT scsp_slot_update_F_8B_R }, // LEFT { // NO RIGHT scsp_slot_update_F_8B_L, // RIGHT scsp_slot_update_F_8B_LR }, }, // 16 BITS { // NO LEFT { // NO RIGHT scsp_slot_update_null, // RIGHT scsp_slot_update_F_16B_R }, // LEFT { // NO RIGHT scsp_slot_update_F_16B_L, // RIGHT scsp_slot_update_F_16B_LR }, } }, // EMS { // 8 BITS { // NO LEFT { // NO RIGHT scsp_slot_update_null, // RIGHT scsp_slot_update_F_E_8B_R }, // LEFT { // NO RIGHT scsp_slot_update_F_E_8B_L, // RIGHT scsp_slot_update_F_E_8B_LR }, }, // 16 BITS { // NO LEFT { // NO RIGHT scsp_slot_update_null, // RIGHT scsp_slot_update_F_E_16B_R }, // LEFT { // NO RIGHT scsp_slot_update_F_E_16B_L, // RIGHT scsp_slot_update_F_E_16B_LR }, } } } }; void scsp_update (s32 *bufL, s32 *bufR, u32 len) { slot_t *slot; scsp_bufL = bufL; scsp_bufR = bufR; for (slot = &(scsp.slot[0]); slot < &(scsp.slot[32]); slot++) { if (slot->ecnt >= SCSP_ENV_DE) continue; // enveloppe null... if (slot->ssctl) { // Still not correct, but at least this fixes games // that rely on Call Address information scsp_buf_len = len; scsp_buf_pos = 0; for(; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++) { if ((slot->fcnt += slot->finc) > slot->lea) { if (slot->lpctl) slot->fcnt = slot->lsa; else { slot->ecnt = SCSP_ENV_DE; break; } } } continue; // not yet supported! } scsp_buf_len = len; scsp_buf_pos = 0; // take effect sound volume if no direct sound volume... if ((slot->disll == 31) && (slot->dislr == 31)) { slot->disll = slot->efsll; slot->dislr = slot->efslr; } // SCSPLOG("update : VL=%d VR=%d CNT=%.8X STEP=%.8X\n", slot->disll, slot->dislr, slot->fcnt, slot->finc); scsp_slot_update_p[(slot->lfofms == 31) ? 0 : 1] [(slot->lfoems == 31) ? 0 : 1] [(slot->pcm8b == 0) ? 1 : 0] [(slot->disll == 31) ? 0 : 1] [(slot->dislr == 31) ? 0 : 1](slot); } if (cdda_out_left > 0) { if (len > cdda_out_left / 4) scsp_buf_len = cdda_out_left / 4; else scsp_buf_len = len; scsp_buf_pos = 0; /* May need to wrap around the buffer, so use nested loops */ while (scsp_buf_pos < scsp_buf_len) { s32 temp = cdda_next_in - cdda_out_left; s32 outpos = (temp < 0) ? temp + sizeof(cddabuf.data) : temp; u8 *buf = &cddabuf.data[outpos]; u32 scsp_buf_target; u32 this_len = scsp_buf_len - scsp_buf_pos; if (this_len > (sizeof(cddabuf.data) - outpos) / 4) this_len = (sizeof(cddabuf.data) - outpos) / 4; scsp_buf_target = scsp_buf_pos + this_len; for (; scsp_buf_pos < scsp_buf_target; scsp_buf_pos++, buf += 4) { s32 out; out = (s32)(s16)((buf[1] << 8) | buf[0]); if (out) scsp_bufL[scsp_buf_pos] += out; out = (s32)(s16)((buf[3] << 8) | buf[2]); if (out) scsp_bufR[scsp_buf_pos] += out; } cdda_out_left -= this_len * 4; } } else if (Cs2Area->isaudio) { SCSPLOG("WARNING: CDDA buffer underrun\n"); } } void scsp_update_monitor(void) { slot_t *slot = &scsp.slot[scsp.mslc]; scsp.ca = ((slot->fcnt >> (SCSP_FREQ_LB + 12)) & 0xF) << 7; scsp.sgc = slot->ecurp; scsp.eg = 0x1f - (slot->env >> (SCSP_ENV_HB - 5)); #ifdef PSP WRITE_THROUGH(scsp.ca); WRITE_THROUGH(scsp.sgc); WRITE_THROUGH(scsp.eg); #endif } void scsp_update_timer (u32 len) { scsp.timacnt += len << (8 - scsp.timasd); if (scsp.timacnt >= 0xFF00) { if (!(scsp.scipd & 0x40)) scsp_sound_interrupt(0x40); if (!(scsp.mcipd & 0x40)) scsp_main_interrupt(0x40); scsp.timacnt -= 0xFF00; } scsp.timbcnt += len << (8 - scsp.timbsd); if (scsp.timbcnt >= 0xFF00) { if (!(scsp.scipd & 0x80)) scsp_sound_interrupt(0x80); if (!(scsp.mcipd & 0x80)) scsp_main_interrupt(0x80); scsp.timbcnt -= 0xFF00; } scsp.timccnt += len << (8 - scsp.timcsd); if (scsp.timccnt >= 0xFF00) { if (!(scsp.scipd & 0x100)) scsp_sound_interrupt(0x100); if (!(scsp.mcipd & 0x100)) scsp_main_interrupt(0x100); scsp.timccnt -= 0xFF00; } // 1F interrupt can't be accurate here... if (len) { if (!(scsp.scipd & 0x400)) scsp_sound_interrupt(0x400); if (!(scsp.mcipd & 0x400)) scsp_main_interrupt(0x400); } } //////////////////////////////////////////////////////////////// // MIDI void scsp_midi_in_send (u8 data) { if (scsp.midflag & SCSP_MIDI_IN_EMP) { scsp_sound_interrupt(0x8); scsp_main_interrupt(0x8); } scsp.midflag &= ~SCSP_MIDI_IN_EMP; if (scsp.midincnt > 3) { scsp.midflag |= SCSP_MIDI_IN_OVF; return; } scsp.midinbuf[scsp.midincnt++] = data; if (scsp.midincnt > 3) scsp.midflag |= SCSP_MIDI_IN_FUL; } void scsp_midi_out_send (u8 data) { scsp.midflag &= ~SCSP_MIDI_OUT_EMP; if (scsp.midoutcnt > 3) return; scsp.midoutbuf[scsp.midoutcnt++] = data; if (scsp.midoutcnt > 3) scsp.midflag |= SCSP_MIDI_OUT_FUL; } u8 scsp_midi_in_read (void) { u8 data; scsp.midflag &= ~(SCSP_MIDI_IN_OVF | SCSP_MIDI_IN_FUL); if (scsp.midincnt > 0) { if (scsp.midincnt > 1) { scsp_sound_interrupt(0x8); scsp_main_interrupt(0x8); } else { scsp.midflag |= SCSP_MIDI_IN_EMP; } data = scsp.midinbuf[0]; switch ((--scsp.midincnt) & 3) { case 1: scsp.midinbuf[0] = scsp.midinbuf[1]; break; case 2: scsp.midinbuf[0] = scsp.midinbuf[1]; scsp.midinbuf[1] = scsp.midinbuf[2]; break; case 3: scsp.midinbuf[0] = scsp.midinbuf[1]; scsp.midinbuf[1] = scsp.midinbuf[2]; scsp.midinbuf[2] = scsp.midinbuf[3]; break; } return data; } return 0xFF; } u8 scsp_midi_out_read (void) { u8 data; scsp.midflag &= ~SCSP_MIDI_OUT_FUL; if (scsp.midoutcnt > 0) { if (scsp.midoutcnt == 1) { scsp.midflag |= SCSP_MIDI_OUT_EMP; scsp_sound_interrupt(0x200); scsp_main_interrupt(0x200); } data = scsp.midoutbuf[0]; switch (--scsp.midoutcnt & 3) { case 1: scsp.midoutbuf[0] = scsp.midoutbuf[1]; break; case 2: scsp.midoutbuf[0] = scsp.midoutbuf[1]; scsp.midoutbuf[1] = scsp.midoutbuf[2]; break; case 3: scsp.midoutbuf[0] = scsp.midoutbuf[1]; scsp.midoutbuf[1] = scsp.midoutbuf[2]; scsp.midoutbuf[2] = scsp.midoutbuf[3]; break; } return data; } return 0xFF; } //////////////////////////////////////////////////////////////// // Access void FASTCALL scsp_w_b (u32 a, u8 d) { a &= 0xFFF; if (a < 0x400) { scsp_slot_set_b (a >> 5, a, d); FLUSH_SCSP (); return; } else if (a < 0x600) { if (a < 0x440) { scsp_set_b (a, d); FLUSH_SCSP (); return; } } else if (a < 0x700) { } else if (a < 0xee4) { a &= 0x3ff; scsp_dcr[a ^ 3] = d; return; } SCSPLOG("WARNING: scsp w_b to %08lx w/ %02x\n", a, d); } //////////////////////////////////////////////////////////////// void FASTCALL scsp_w_w (u32 a, u16 d) { if (a & 1) { SCSPLOG ("ERROR: scsp w_w misaligned : %.8X\n", a); } a &= 0xFFE; if (a < 0x400) { scsp_slot_set_w (a >> 5, a, d); FLUSH_SCSP (); return; } else if (a < 0x600) { if (a < 0x440) { scsp_set_w (a, d); FLUSH_SCSP (); return; } } else if (a < 0x700) { } else if (a < 0xee4) { a &= 0x3ff; *(u16 *)&scsp_dcr[a ^ 2] = d; return; } SCSPLOG ("WARNING: scsp w_w to %08lx w/ %04x\n", a, d); } //////////////////////////////////////////////////////////////// void FASTCALL scsp_w_d (u32 a, u32 d) { if (a & 3) { SCSPLOG ("ERROR: scsp w_d misaligned : %.8X\n", a); } a &= 0xFFC; if (a < 0x400) { scsp_slot_set_w (a >> 5, a + 0, d >> 16); scsp_slot_set_w (a >> 5, a + 2, d & 0xFFFF); FLUSH_SCSP (); return; } else if (a < 0x600) { if (a < 0x440) { scsp_set_w (a + 0, d >> 16); scsp_set_w (a + 2, d & 0xFFFF); FLUSH_SCSP (); return; } } else if (a < 0x700) { } else if (a < 0xee4) { a &= 0x3ff; *(u32 *)&scsp_dcr[a] = d; return; } SCSPLOG ("WARNING: scsp w_d to %08lx w/ %08lx\n", a, d); } //////////////////////////////////////////////////////////////// u8 FASTCALL scsp_r_b (u32 a) { a &= 0xFFF; if (a < 0x400) { return scsp_slot_get_b(a >> 5, a); } else if (a < 0x600) { if (a < 0x440) return scsp_get_b(a); } else if (a < 0x700) { } else if (a < 0xee4) { } SCSPLOG("WARNING: scsp r_b to %08lx\n", a); return 0; } //////////////////////////////////////////////////////////////// u16 FASTCALL scsp_r_w (u32 a) { if (a & 1) { SCSPLOG ("ERROR: scsp r_w misaligned : %.8X\n", a); } a &= 0xFFE; if (a < 0x400) { return scsp_slot_get_w (a >> 5, a); } else if (a < 0x600) { if (a < 0x440) return scsp_get_w (a); } else if (a < 0x700) { } else if (a < 0xee4) { } SCSPLOG ("WARNING: scsp r_w to %08lx\n", a); return 0; } //////////////////////////////////////////////////////////////// u32 FASTCALL scsp_r_d (u32 a) { if (a & 3) { SCSPLOG ("ERROR: scsp r_d misaligned : %.8X\n", a); } a &= 0xFFC; if (a < 0x400) { return (scsp_slot_get_w (a >> 5, a + 0) << 16) | scsp_slot_get_w (a >> 5, a + 2); } else if (a < 0x600) { if (a < 0x440) return (scsp_get_w (a + 0) << 16) | scsp_get_w (a + 2); } else if (a < 0x700) { } else if (a < 0xee4) { } SCSPLOG("WARNING: scsp r_d to %08lx\n", a); return 0; } //////////////////////////////////////////////////////////////// // Interface void scsp_shutdown (void) { } void scsp_reset (void) { slot_t *slot; memset(scsp_reg, 0, 0x1000); scsp.mem4b = 0; scsp.mvol = 0; scsp.rbl = 0; scsp.rbp = 0; scsp.mslc = 0; scsp.ca = 0; scsp.dmea = 0; scsp.drga = 0; scsp.dmfl = 0; scsp.dmlen = 0; scsp.midincnt = 0; scsp.midoutcnt = 0; scsp.midflag = SCSP_MIDI_IN_EMP | SCSP_MIDI_OUT_EMP; scsp.midflag2 = 0; scsp.timacnt = 0xFF00; scsp.timbcnt = 0xFF00; scsp.timccnt = 0xFF00; scsp.timasd = 0; scsp.timbsd = 0; scsp.timcsd = 0; scsp.mcieb = 0; scsp.mcipd = 0; scsp.scieb = 0; scsp.scipd = 0; scsp.scilv0 = 0; scsp.scilv1 = 0; scsp.scilv2 = 0; for(slot = &(scsp.slot[0]); slot < &(scsp.slot[32]); slot++) { memset(slot, 0, sizeof(slot_t)); slot->ecnt = SCSP_ENV_DE; // slot off slot->ecurp = SCSP_ENV_RELEASE; slot->dislr = slot->disll = 31; // direct level sound off slot->efslr = slot->efsll = 31; // effect level sound off // Make sure lfofmw/lfoemw have sane values slot->lfofmw = scsp_lfo_sawt_f; slot->lfoemw = scsp_lfo_sawt_e; } } void scsp_init (u8 *scsp_ram, void (*sint_hand)(u32), void (*mint_hand)(void)) { u32 i, j; double x; scsp_shutdown (); scsp_isr = &scsp_reg[0x0000]; scsp_ccr = &scsp_reg[0x0400]; scsp_dcr = &scsp_reg[0x0700]; scsp.scsp_ram = scsp_ram; scsp.sintf = sint_hand; scsp.mintf = mint_hand; for (i = 0; i < SCSP_ENV_LEN; i++) { // Attack Curve (x^7 ?) x = pow (((double)(SCSP_ENV_MASK - i) / (double)SCSP_ENV_LEN), 7); x *= (double)SCSP_ENV_LEN; scsp_env_table[i] = SCSP_ENV_MASK - (s32)x; // Decay curve (x = linear) x = pow (((double)i / (double)SCSP_ENV_LEN), 1); x *= (double)SCSP_ENV_LEN; scsp_env_table[i + SCSP_ENV_LEN] = SCSP_ENV_MASK - (s32)x; } for (i = 0, j = 0; i < 32; i++) { j += 1 << (i >> 2); // lfo freq x = (SCSP_FREQ / 256.0) / (double)j; // converting lfo freq in lfo step scsp_lfo_step[31 - i] = scsp_round(x * ((double)SCSP_LFO_LEN / (double)SCSP_FREQ) * (double)(1 << SCSP_LFO_LB)); } // Calculate LFO (modulation) values for (i = 0; i < SCSP_LFO_LEN; i++) { // Envelope modulation scsp_lfo_sawt_e[i] = SCSP_LFO_MASK - i; if (i < (SCSP_LFO_LEN / 2)) scsp_lfo_squa_e[i] = SCSP_LFO_MASK; else scsp_lfo_squa_e[i] = 0; if (i < (SCSP_LFO_LEN / 2)) scsp_lfo_tri_e[i] = SCSP_LFO_MASK - (i * 2); else scsp_lfo_tri_e[i] = (i - (SCSP_LFO_LEN / 2)) * 2; scsp_lfo_noi_e[i] = rand() & SCSP_LFO_MASK; // Frequency modulation scsp_lfo_sawt_f[(i + 512) & SCSP_LFO_MASK] = i - (SCSP_LFO_LEN / 2); if (i < (SCSP_LFO_LEN / 2)) scsp_lfo_squa_f[i] = SCSP_LFO_MASK - (SCSP_LFO_LEN / 2) - 128; else scsp_lfo_squa_f[i] = 0 - (SCSP_LFO_LEN / 2) + 128; if (i < (SCSP_LFO_LEN / 2)) scsp_lfo_tri_f[(i + 768) & SCSP_LFO_MASK] = (i * 2) - (SCSP_LFO_LEN / 2); else scsp_lfo_tri_f[(i + 768) & SCSP_LFO_MASK] = (SCSP_LFO_MASK - ((i - (SCSP_LFO_LEN / 2)) * 2)) - (SCSP_LFO_LEN / 2) + 1; scsp_lfo_noi_f[i] = scsp_lfo_noi_e[i] - (SCSP_LFO_LEN / 2); } for(i = 0; i < 4; i++) { scsp_attack_rate[i] = 0; scsp_decay_rate[i] = 0; } for(i = 0; i < 60; i++) { x = 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75 x *= (double)(1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15) x *= (double)(SCSP_ENV_LEN << SCSP_ENV_LB); // adjust for table scsp_env_table scsp_attack_rate[i + 4] = scsp_round(x / (double)SCSP_ATTACK_R); scsp_decay_rate[i + 4] = scsp_round(x / (double)SCSP_DECAY_R); if (scsp_attack_rate[i + 4] == 0) scsp_attack_rate[i + 4] = 1; if (scsp_decay_rate[i + 4] == 0) scsp_decay_rate[i + 4] = 1; } scsp_attack_rate[63] = SCSP_ENV_AE; scsp_decay_rate[61] = scsp_decay_rate[60]; scsp_decay_rate[62] = scsp_decay_rate[60]; scsp_decay_rate[63] = scsp_decay_rate[60]; for(i = 64; i < 96; i++) { scsp_attack_rate[i] = scsp_attack_rate[63]; scsp_decay_rate[i] = scsp_decay_rate[63]; scsp_null_rate[i - 64] = 0; } for(i = 0; i < 96; i++) { SCSPLOG ("attack rate[%d] = %.8X -> %.8X\n", i, scsp_attack_rate[i], scsp_attack_rate[i] >> SCSP_ENV_LB); SCSPLOG ("decay rate[%d] = %.8X -> %.8X\n", i, scsp_decay_rate[i], scsp_decay_rate[i] >> SCSP_ENV_LB); } for(i = 0; i < 256; i++) scsp_tl_table[i] = scsp_round(pow(10, ((double)i * -0.3762) / 20) * 1024.0); scsp_reset(); } ////////////////////////////////////////////////////////////////////////////// // Yabause specific ////////////////////////////////////////////////////////////////////////////// u8 *SoundRam = NULL; ScspInternal *ScspInternalVars; static SoundInterface_struct *SNDCore = NULL; extern SoundInterface_struct *SNDCoreList[]; struct sounddata { u32 *data32; } scspchannel[2]; static u32 scspsoundlen; // Samples to output per frame static u32 scsplines; // Lines per frame static u32 scspsoundbufs; // Number of "scspsoundlen"-sample buffers static u32 scspsoundbufsize; // scspsoundlen * scspsoundbufs static int scspframeaccurate; // True to generate frame-accurate audio static u32 scspsoundgenpos; // Offset of next byte to generate static u32 scspsoundoutleft; // Samples not yet sent to host driver static int scsp_alloc_bufs (void) { if (scspchannel[0].data32) free(scspchannel[0].data32); scspchannel[0].data32 = NULL; if (scspchannel[1].data32) free(scspchannel[1].data32); scspchannel[1].data32 = NULL; scspchannel[0].data32 = (u32 *)calloc(scspsoundbufsize, sizeof(u32)); if (scspchannel[0].data32 == NULL) return -1; scspchannel[1].data32 = (u32 *)calloc(scspsoundbufsize, sizeof(u32)); if (scspchannel[1].data32 == NULL) return -1; return 0; } static u8 IsM68KRunning; static s32 FASTCALL (*m68kexecptr)(s32 cycles); // M68K->Exec or M68KExecBP static s32 savedcycles; // Cycles left over from the last M68KExec() call ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL c68k_byte_read (const u32 adr) { if (adr < 0x100000) return T2ReadByte(SoundRam, adr & 0x7FFFF); else return scsp_r_b(adr); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL c68k_byte_write (const u32 adr, u32 data) { if (adr < 0x100000) T2WriteByte(SoundRam, adr & 0x7FFFF, data); else scsp_w_b(adr, data); } ////////////////////////////////////////////////////////////////////////////// /* exported to m68kd.c */ u32 FASTCALL c68k_word_read (const u32 adr) { if (adr < 0x100000) return T2ReadWord(SoundRam, adr & 0x7FFFF); else return scsp_r_w(adr); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL c68k_word_write (const u32 adr, u32 data) { if (adr < 0x100000) T2WriteWord (SoundRam, adr & 0x7FFFF, data); else scsp_w_w (adr, data); } ////////////////////////////////////////////////////////////////////////////// static void c68k_interrupt_handler (u32 level) { // send interrupt to 68k M68K->SetIRQ ((s32)level); } ////////////////////////////////////////////////////////////////////////////// static void scu_interrupt_handler (void) { // send interrupt to scu ScuSendSoundRequest (); } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL SoundRamReadByte (u32 addr) { addr &= 0xFFFFF; // If mem4b is set, mirror ram every 256k if (scsp.mem4b == 0) addr &= 0x3FFFF; else if (addr > 0x7FFFF) return 0xFF; return T2ReadByte (SoundRam, addr); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL SoundRamWriteByte (u32 addr, u8 val) { addr &= 0xFFFFF; // If mem4b is set, mirror ram every 256k if (scsp.mem4b == 0) addr &= 0x3FFFF; else if (addr > 0x7FFFF) return; T2WriteByte (SoundRam, addr, val); M68K->WriteNotify (addr, 1); } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL SoundRamReadWord (u32 addr) { addr &= 0xFFFFF; if (scsp.mem4b == 0) addr &= 0x3FFFF; else if (addr > 0x7FFFF) return 0xFFFF; return T2ReadWord (SoundRam, addr); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL SoundRamWriteWord (u32 addr, u16 val) { addr &= 0xFFFFF; // If mem4b is set, mirror ram every 256k if (scsp.mem4b == 0) addr &= 0x3FFFF; else if (addr > 0x7FFFF) return; T2WriteWord (SoundRam, addr, val); M68K->WriteNotify (addr, 2); } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL SoundRamReadLong (u32 addr) { addr &= 0xFFFFF; // If mem4b is set, mirror ram every 256k if (scsp.mem4b == 0) addr &= 0x3FFFF; else if (addr > 0x7FFFF) return 0xFFFFFFFF; return T2ReadLong (SoundRam, addr); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL SoundRamWriteLong (u32 addr, u32 val) { addr &= 0xFFFFF; // If mem4b is set, mirror ram every 256k if (scsp.mem4b == 0) addr &= 0x3FFFF; else if (addr > 0x7FFFF) return; T2WriteLong (SoundRam, addr, val); M68K->WriteNotify (addr, 4); } ////////////////////////////////////////////////////////////////////////////// int ScspInit (int coreid) { int i; if ((SoundRam = T2MemoryInit (0x80000)) == NULL) return -1; if ((ScspInternalVars = (ScspInternal *)calloc(1, sizeof(ScspInternal))) == NULL) return -1; if (M68K->Init () != 0) return -1; M68K->SetReadB ((C68K_READ *)c68k_byte_read); M68K->SetReadW ((C68K_READ *)c68k_word_read); M68K->SetWriteB ((C68K_WRITE *)c68k_byte_write); M68K->SetWriteW ((C68K_WRITE *)c68k_word_write); M68K->SetFetch (0x000000, 0x040000, (pointer)SoundRam); M68K->SetFetch (0x040000, 0x080000, (pointer)SoundRam); M68K->SetFetch (0x080000, 0x0C0000, (pointer)SoundRam); M68K->SetFetch (0x0C0000, 0x100000, (pointer)SoundRam); IsM68KRunning = 0; scsp_init (SoundRam, &c68k_interrupt_handler, &scu_interrupt_handler); ScspInternalVars->scsptiming1 = 0; ScspInternalVars->scsptiming2 = 0; for (i = 0; i < MAX_BREAKPOINTS; i++) ScspInternalVars->codebreakpoint[i].addr = 0xFFFFFFFF; ScspInternalVars->numcodebreakpoints = 0; ScspInternalVars->BreakpointCallBack = NULL; ScspInternalVars->inbreakpoint = 0; m68kexecptr = M68K->Exec; // Allocate enough memory for each channel buffer(may have to change) scspsoundlen = 44100 / 60; // assume it's NTSC timing scsplines = 213; scspsoundbufs = 10; // should be enough to prevent skipping scspsoundbufsize = scspsoundlen * scspsoundbufs; if (scsp_alloc_bufs () < 0) return -1; // Reset output pointers scspsoundgenpos = 0; scspsoundoutleft = 0; scspframeaccurate = 0; return ScspChangeSoundCore (coreid); } ////////////////////////////////////////////////////////////////////////////// int ScspChangeSoundCore (int coreid) { int i; // Make sure the old core is freed if (SNDCore) SNDCore->DeInit(); // So which core do we want? if (coreid == SNDCORE_DEFAULT) coreid = 0; // Assume we want the first one // Go through core list and find the id for (i = 0; SNDCoreList[i] != NULL; i++) { if (SNDCoreList[i]->id == coreid) { // Set to current core SNDCore = SNDCoreList[i]; break; } } if (SNDCore == NULL) { SNDCore = &SNDDummy; return -1; } if (SNDCore->Init () == -1) { // Since it failed, instead of it being fatal, we'll just use the dummy // core instead // This might be helpful though. YabSetError (YAB_ERR_CANNOTINIT, (void *)SNDCore->Name); SNDCore = &SNDDummy; } if (SNDCore) { if (scsp_mute_flags) SNDCore->MuteAudio(); else SNDCore->UnMuteAudio(); SNDCore->SetVolume(scsp_volume); } return 0; } ////////////////////////////////////////////////////////////////////////////// void ScspSetFrameAccurate (int on) { scspframeaccurate = (on != 0); } ////////////////////////////////////////////////////////////////////////////// void ScspDeInit (void) { if (scspchannel[0].data32) free(scspchannel[0].data32); scspchannel[0].data32 = NULL; if (scspchannel[1].data32) free(scspchannel[1].data32); scspchannel[1].data32 = NULL; if (SNDCore) SNDCore->DeInit(); SNDCore = NULL; scsp_shutdown(); if (SoundRam) T2MemoryDeInit (SoundRam); SoundRam = NULL; } ////////////////////////////////////////////////////////////////////////////// void M68KStart (void) { M68K->Reset (); savedcycles = 0; IsM68KRunning = 1; } ////////////////////////////////////////////////////////////////////////////// void M68KStop (void) { IsM68KRunning = 0; } ////////////////////////////////////////////////////////////////////////////// void ScspReset (void) { scsp_reset(); } ////////////////////////////////////////////////////////////////////////////// int ScspChangeVideoFormat (int type) { scspsoundlen = 44100 / (type ? 50 : 60); scsplines = type ? 313 : 263; scspsoundbufsize = scspsoundlen * scspsoundbufs; if (scsp_alloc_bufs () < 0) return -1; SNDCore->ChangeVideoFormat (type ? 50 : 60); return 0; } ////////////////////////////////////////////////////////////////////////////// /* Process breakpoints in a separate function to avoid unnecessary register * spillage on the fast path (and to avoid too much block nesting) */ #ifdef __GNUC__ __attribute__((noinline)) #endif static s32 FASTCALL M68KExecBP (s32 cycles); void M68KExec (s32 cycles) { s32 newcycles = savedcycles - cycles; if (LIKELY(IsM68KRunning)) { if (LIKELY(newcycles < 0)) { s32 cyclestoexec = -newcycles; newcycles += (*m68kexecptr)(cyclestoexec); } savedcycles = newcycles; } } //---------------------------------------------------------------------------- static s32 FASTCALL M68KExecBP (s32 cycles) { s32 cyclestoexec=cycles; s32 cyclesexecuted=0; int i; while (cyclesexecuted < cyclestoexec) { // Make sure it isn't one of our breakpoints for (i = 0; i < ScspInternalVars->numcodebreakpoints; i++) { if ((M68K->GetPC () == ScspInternalVars->codebreakpoint[i].addr) && ScspInternalVars->inbreakpoint == 0) { ScspInternalVars->inbreakpoint = 1; if (ScspInternalVars->BreakpointCallBack) ScspInternalVars->BreakpointCallBack (ScspInternalVars->codebreakpoint[i].addr); ScspInternalVars->inbreakpoint = 0; } } // execute instructions individually cyclesexecuted += M68K->Exec(1); } return cyclesexecuted; } ////////////////////////////////////////////////////////////////////////////// void M68KStep (void) { M68K->Exec(1); } ////////////////////////////////////////////////////////////////////////////// // Wait for background execution to finish (used on PSP) void M68KSync (void) { M68K->Sync(); } ////////////////////////////////////////////////////////////////////////////// void ScspConvert32uto16s (s32 *srcL, s32 *srcR, s16 *dst, u32 len) { u32 i; for (i = 0; i < len; i++) { // Left Channel if (*srcL > 0x7FFF) *dst = 0x7FFF; else if (*srcL < -0x8000) *dst = -0x8000; else *dst = *srcL; srcL++; dst++; // Right Channel if (*srcR > 0x7FFF) *dst = 0x7FFF; else if (*srcR < -0x8000) *dst = -0x8000; else *dst = *srcR; srcR++; dst++; } } ////////////////////////////////////////////////////////////////////////////// void ScspReceiveCDDA (const u8 *sector) { // If buffer is half empty or less, boost timing for a bit until we've buffered a few sectors if (cdda_out_left < (sizeof(cddabuf.data) / 2)) { Cs2Area->isaudio = 0; Cs2SetTiming(1); Cs2Area->isaudio = 1; } else if (cdda_out_left > (sizeof(cddabuf.data) * 3 / 4 )) Cs2SetTiming(0); else { Cs2Area->isaudio = 1; Cs2SetTiming(1); } memcpy(cddabuf.data+cdda_next_in, sector, 2352); if (sizeof(cddabuf.data)-cdda_next_in <= 2352) cdda_next_in = 0; else cdda_next_in += 2352; cdda_out_left += 2352; if (cdda_out_left > sizeof(cddabuf.data)) { SCSPLOG ("WARNING: CDDA buffer overrun\n"); cdda_out_left = sizeof(cddabuf.data); } } ////////////////////////////////////////////////////////////////////////////// void ScspExec () { u32 audiosize; ScspInternalVars->scsptiming2 += ((scspsoundlen << 16) + scsplines / 2) / scsplines; scsp_update_timer (ScspInternalVars->scsptiming2 >> 16); // Pass integer part ScspInternalVars->scsptiming2 &= 0xFFFF; // Keep fractional part ScspInternalVars->scsptiming1++; if (ScspInternalVars->scsptiming1 >= scsplines) { s32 *bufL, *bufR; ScspInternalVars->scsptiming1 -= scsplines; ScspInternalVars->scsptiming2 = 0; if (scspframeaccurate) { // Update sound buffers if (scspsoundgenpos + scspsoundlen > scspsoundbufsize) scspsoundgenpos = 0; if (scspsoundoutleft + scspsoundlen > scspsoundbufsize) { u32 overrun = (scspsoundoutleft + scspsoundlen) - scspsoundbufsize; SCSPLOG ("WARNING: Sound buffer overrun, %lu samples\n", (long)overrun); scspsoundoutleft -= overrun; } bufL = (s32 *)&scspchannel[0].data32[scspsoundgenpos]; bufR = (s32 *)&scspchannel[1].data32[scspsoundgenpos]; memset (bufL, 0, sizeof(u32) * scspsoundlen); memset (bufR, 0, sizeof(u32) * scspsoundlen); scsp_update (bufL, bufR, scspsoundlen); scspsoundgenpos += scspsoundlen; scspsoundoutleft += scspsoundlen; } } if (scspframeaccurate) { while (scspsoundoutleft > 0 && (audiosize = SNDCore->GetAudioSpace ()) > 0) { s32 outstart = (s32)scspsoundgenpos - (s32)scspsoundoutleft; if (outstart < 0) outstart += scspsoundbufsize; if (audiosize > scspsoundoutleft) audiosize = scspsoundoutleft; if (audiosize > scspsoundbufsize - outstart) audiosize = scspsoundbufsize - outstart; SNDCore->UpdateAudio (&scspchannel[0].data32[outstart], &scspchannel[1].data32[outstart], audiosize); scspsoundoutleft -= audiosize; #if 0 ScspConvert32uto16s (&scspchannel[0].data32[outstart], &scspchannel[1].data32[outstart], (s16 *)stereodata16, audiosize); DRV_AviSoundUpdate(stereodata16, audiosize); #endif } } else { if ((audiosize = SNDCore->GetAudioSpace ())) { if (audiosize > scspsoundlen) audiosize = scspsoundlen; memset(scspchannel[0].data32, 0, sizeof(u32) * audiosize); memset(scspchannel[1].data32, 0, sizeof(u32) * audiosize); scsp_update ((s32 *)scspchannel[0].data32, (s32 *)scspchannel[1].data32, audiosize); SNDCore->UpdateAudio (scspchannel[0].data32, (u32 *)scspchannel[1].data32, audiosize); #if 0 ScspConvert32uto16s ((s32 *)scspchannel[0].data32, (s32 *)scspchannel[1].data32, (s16 *)stereodata16, audiosize); DRV_AviSoundUpdate (stereodata16, audiosize); #endif } } // if (scspframeaccurate) scsp_update_monitor (); } ////////////////////////////////////////////////////////////////////////////// void M68KWriteNotify (u32 address, u32 size) { M68K->WriteNotify (address, size); } ////////////////////////////////////////////////////////////////////////////// void M68KGetRegisters (m68kregs_struct *regs) { int i; if (regs != NULL) { for (i = 0; i < 8; i++) { regs->D[i] = M68K->GetDReg (i); regs->A[i] = M68K->GetAReg (i); } regs->SR = M68K->GetSR (); regs->PC = M68K->GetPC (); } } ////////////////////////////////////////////////////////////////////////////// void M68KSetRegisters (m68kregs_struct *regs) { int i; if (regs != NULL) { for (i = 0; i < 8; i++) { M68K->SetDReg (i, regs->D[i]); M68K->SetAReg (i, regs->A[i]); } M68K->SetSR (regs->SR); M68K->SetPC (regs->PC); } } ////////////////////////////////////////////////////////////////////////////// void ScspMuteAudio (int flags) { scsp_mute_flags |= flags; if (SNDCore && scsp_mute_flags) SNDCore->MuteAudio (); } ////////////////////////////////////////////////////////////////////////////// void ScspUnMuteAudio (int flags) { scsp_mute_flags &= ~flags; if (SNDCore && (scsp_mute_flags == 0)) SNDCore->UnMuteAudio (); } ////////////////////////////////////////////////////////////////////////////// void ScspSetVolume (int volume) { scsp_volume = volume; if (SNDCore) SNDCore->SetVolume (volume); } ////////////////////////////////////////////////////////////////////////////// void M68KSetBreakpointCallBack (void (*func)(u32)) { ScspInternalVars->BreakpointCallBack = func; } ////////////////////////////////////////////////////////////////////////////// int M68KAddCodeBreakpoint (u32 addr) { int i; if (ScspInternalVars->numcodebreakpoints < MAX_BREAKPOINTS) { // Make sure it isn't already on the list for (i = 0; i < ScspInternalVars->numcodebreakpoints; i++) { if (addr == ScspInternalVars->codebreakpoint[i].addr) return -1; } ScspInternalVars->codebreakpoint[i].addr = addr; ScspInternalVars->numcodebreakpoints++; m68kexecptr = M68KExecBP; return 0; } return -1; } ////////////////////////////////////////////////////////////////////////////// void M68KSortCodeBreakpoints (void) { int i, i2; u32 tmp; for (i = 0; i < (MAX_BREAKPOINTS - 1); i++) { for (i2 = i+1; i2 < MAX_BREAKPOINTS; i2++) { if (ScspInternalVars->codebreakpoint[i].addr == 0xFFFFFFFF && ScspInternalVars->codebreakpoint[i2].addr != 0xFFFFFFFF) { tmp = ScspInternalVars->codebreakpoint[i].addr; ScspInternalVars->codebreakpoint[i].addr = ScspInternalVars->codebreakpoint[i2].addr; ScspInternalVars->codebreakpoint[i2].addr = tmp; } } } } ////////////////////////////////////////////////////////////////////////////// int M68KDelCodeBreakpoint (u32 addr) { int i; if (ScspInternalVars->numcodebreakpoints > 0) { for (i = 0; i < ScspInternalVars->numcodebreakpoints; i++) { if (ScspInternalVars->codebreakpoint[i].addr == addr) { ScspInternalVars->codebreakpoint[i].addr = 0xFFFFFFFF; M68KSortCodeBreakpoints (); ScspInternalVars->numcodebreakpoints--; if (ScspInternalVars->numcodebreakpoints == 0) m68kexecptr = M68K->Exec; return 0; } } } return -1; } ////////////////////////////////////////////////////////////////////////////// m68kcodebreakpoint_struct * M68KGetBreakpointList () { return ScspInternalVars->codebreakpoint; } ////////////////////////////////////////////////////////////////////////////// void M68KClearCodeBreakpoints () { int i; for (i = 0; i < MAX_BREAKPOINTS; i++) ScspInternalVars->codebreakpoint[i].addr = 0xFFFFFFFF; ScspInternalVars->numcodebreakpoints = 0; } ////////////////////////////////////////////////////////////////////////////// int SoundSaveState (FILE *fp) { int i; u32 temp; int offset; u8 nextphase; IOCheck_struct check; offset = StateWriteHeader (fp, "SCSP", 2); // Save 68k registers first ywrite (&check, (void *)&IsM68KRunning, 1, 1, fp); for (i = 0; i < 8; i++) { temp = M68K->GetDReg (i); ywrite (&check, (void *)&temp, 4, 1, fp); } for (i = 0; i < 8; i++) { temp = M68K->GetAReg (i); ywrite (&check, (void *)&temp, 4, 1, fp); } temp = M68K->GetSR (); ywrite (&check, (void *)&temp, 4, 1, fp); temp = M68K->GetPC (); ywrite (&check, (void *)&temp, 4, 1, fp); // Now for the SCSP registers ywrite (&check, (void *)scsp_reg, 0x1000, 1, fp); // Sound RAM is important ywrite (&check, (void *)SoundRam, 0x80000, 1, fp); // Write slot internal variables for (i = 0; i < 32; i++) { s32 einc; ywrite (&check, (void *)&scsp.slot[i].key, 1, 1, fp); ywrite (&check, (void *)&scsp.slot[i].fcnt, 4, 1, fp); ywrite (&check, (void *)&scsp.slot[i].ecnt, 4, 1, fp); if (scsp.slot[i].einc == &scsp.slot[i].einca) einc = 0; else if (scsp.slot[i].einc == &scsp.slot[i].eincd) einc = 1; else if (scsp.slot[i].einc == &scsp.slot[i].eincs) einc = 2; else if (scsp.slot[i].einc == &scsp.slot[i].eincr) einc = 3; else einc = 4; ywrite (&check, (void *)&einc, 4, 1, fp); ywrite (&check, (void *)&scsp.slot[i].ecmp, 4, 1, fp); ywrite (&check, (void *)&scsp.slot[i].ecurp, 4, 1, fp); if (scsp.slot[i].enxt == scsp_env_null_next) nextphase = 0; else if (scsp.slot[i].enxt == scsp_release_next) nextphase = 1; else if (scsp.slot[i].enxt == scsp_sustain_next) nextphase = 2; else if (scsp.slot[i].enxt == scsp_decay_next) nextphase = 3; else if (scsp.slot[i].enxt == scsp_attack_next) nextphase = 4; ywrite (&check, (void *)&nextphase, 1, 1, fp); ywrite (&check, (void *)&scsp.slot[i].lfocnt, 4, 1, fp); ywrite (&check, (void *)&scsp.slot[i].lfoinc, 4, 1, fp); } // Write main internal variables ywrite (&check, (void *)&scsp.mem4b, 4, 1, fp); ywrite (&check, (void *)&scsp.mvol, 4, 1, fp); ywrite (&check, (void *)&scsp.rbl, 4, 1, fp); ywrite (&check, (void *)&scsp.rbp, 4, 1, fp); ywrite (&check, (void *)&scsp.mslc, 4, 1, fp); ywrite (&check, (void *)&scsp.dmea, 4, 1, fp); ywrite (&check, (void *)&scsp.drga, 4, 1, fp); ywrite (&check, (void *)&scsp.dmfl, 4, 1, fp); ywrite (&check, (void *)&scsp.dmlen, 4, 1, fp); ywrite (&check, (void *)scsp.midinbuf, 1, 4, fp); ywrite (&check, (void *)scsp.midoutbuf, 1, 4, fp); ywrite (&check, (void *)&scsp.midincnt, 1, 1, fp); ywrite (&check, (void *)&scsp.midoutcnt, 1, 1, fp); ywrite (&check, (void *)&scsp.midflag, 1, 1, fp); ywrite (&check, (void *)&scsp.timacnt, 4, 1, fp); ywrite (&check, (void *)&scsp.timasd, 4, 1, fp); ywrite (&check, (void *)&scsp.timbcnt, 4, 1, fp); ywrite (&check, (void *)&scsp.timbsd, 4, 1, fp); ywrite (&check, (void *)&scsp.timccnt, 4, 1, fp); ywrite (&check, (void *)&scsp.timcsd, 4, 1, fp); ywrite (&check, (void *)&scsp.scieb, 4, 1, fp); ywrite (&check, (void *)&scsp.scipd, 4, 1, fp); ywrite (&check, (void *)&scsp.scilv0, 4, 1, fp); ywrite (&check, (void *)&scsp.scilv1, 4, 1, fp); ywrite (&check, (void *)&scsp.scilv2, 4, 1, fp); ywrite (&check, (void *)&scsp.mcieb, 4, 1, fp); ywrite (&check, (void *)&scsp.mcipd, 4, 1, fp); ywrite (&check, (void *)scsp.stack, 4, 32 * 2, fp); return StateFinishHeader (fp, offset); } ////////////////////////////////////////////////////////////////////////////// int SoundLoadState (FILE *fp, int version, int size) { int i, i2; u32 temp; u8 nextphase; IOCheck_struct check; // Read 68k registers first yread (&check, (void *)&IsM68KRunning, 1, 1, fp); for (i = 0; i < 8; i++) { yread (&check, (void *)&temp, 4, 1, fp); M68K->SetDReg (i, temp); } for (i = 0; i < 8; i++) { yread (&check, (void *)&temp, 4, 1, fp); M68K->SetAReg (i, temp); } yread (&check, (void *)&temp, 4, 1, fp); M68K->SetSR (temp); yread (&check, (void *)&temp, 4, 1, fp); M68K->SetPC (temp); // Now for the SCSP registers yread (&check, (void *)scsp_reg, 0x1000, 1, fp); // Lastly, sound ram yread (&check, (void *)SoundRam, 0x80000, 1, fp); if (version > 1) { // Internal variables need to be regenerated for(i = 0; i < 32; i++) { for (i2 = 0; i2 < 0x20; i2+=2) scsp_slot_set_w (i, 0x1E - i2, scsp_slot_get_w (i, 0x1E - i2)); } scsp_set_w (0x402, scsp_get_w (0x402)); // Read slot internal variables for (i = 0; i < 32; i++) { s32 einc; yread (&check, (void *)&scsp.slot[i].key, 1, 1, fp); yread (&check, (void *)&scsp.slot[i].fcnt, 4, 1, fp); yread (&check, (void *)&scsp.slot[i].ecnt, 4, 1, fp); yread (&check, (void *)&einc, 4, 1, fp); switch (einc) { case 0: scsp.slot[i].einc = &scsp.slot[i].einca; break; case 1: scsp.slot[i].einc = &scsp.slot[i].eincd; break; case 2: scsp.slot[i].einc = &scsp.slot[i].eincs; break; case 3: scsp.slot[i].einc = &scsp.slot[i].eincr; break; default: scsp.slot[i].einc = NULL; break; } yread (&check, (void *)&scsp.slot[i].ecmp, 4, 1, fp); yread (&check, (void *)&scsp.slot[i].ecurp, 4, 1, fp); yread (&check, (void *)&nextphase, 1, 1, fp); switch (nextphase) { case 0: scsp.slot[i].enxt = scsp_env_null_next; break; case 1: scsp.slot[i].enxt = scsp_release_next; break; case 2: scsp.slot[i].enxt = scsp_sustain_next; break; case 3: scsp.slot[i].enxt = scsp_decay_next; break; case 4: scsp.slot[i].enxt = scsp_attack_next; break; default: break; } yread (&check, (void *)&scsp.slot[i].lfocnt, 4, 1, fp); yread (&check, (void *)&scsp.slot[i].lfoinc, 4, 1, fp); // Rebuild the buf8/buf16 variables if (scsp.slot[i].pcm8b) { scsp.slot[i].buf8 = (s8*)&(scsp.scsp_ram[scsp.slot[i].sa]); if ((scsp.slot[i].sa + (scsp.slot[i].lea >> SCSP_FREQ_LB)) > SCSP_RAM_MASK) scsp.slot[i].lea = (SCSP_RAM_MASK - scsp.slot[i].sa) << SCSP_FREQ_LB; } else { scsp.slot[i].buf16 = (s16*)&(scsp.scsp_ram[scsp.slot[i].sa & ~1]); if ((scsp.slot[i].sa + (scsp.slot[i].lea >> (SCSP_FREQ_LB - 1))) > SCSP_RAM_MASK) scsp.slot[i].lea = (SCSP_RAM_MASK - scsp.slot[i].sa) << (SCSP_FREQ_LB - 1); } } // Read main internal variables yread (&check, (void *)&scsp.mem4b, 4, 1, fp); yread (&check, (void *)&scsp.mvol, 4, 1, fp); yread (&check, (void *)&scsp.rbl, 4, 1, fp); yread (&check, (void *)&scsp.rbp, 4, 1, fp); yread (&check, (void *)&scsp.mslc, 4, 1, fp); yread (&check, (void *)&scsp.dmea, 4, 1, fp); yread (&check, (void *)&scsp.drga, 4, 1, fp); yread (&check, (void *)&scsp.dmfl, 4, 1, fp); yread (&check, (void *)&scsp.dmlen, 4, 1, fp); yread (&check, (void *)scsp.midinbuf, 1, 4, fp); yread (&check, (void *)scsp.midoutbuf, 1, 4, fp); yread (&check, (void *)&scsp.midincnt, 1, 1, fp); yread (&check, (void *)&scsp.midoutcnt, 1, 1, fp); yread (&check, (void *)&scsp.midflag, 1, 1, fp); yread (&check, (void *)&scsp.timacnt, 4, 1, fp); yread (&check, (void *)&scsp.timasd, 4, 1, fp); yread (&check, (void *)&scsp.timbcnt, 4, 1, fp); yread (&check, (void *)&scsp.timbsd, 4, 1, fp); yread (&check, (void *)&scsp.timccnt, 4, 1, fp); yread (&check, (void *)&scsp.timcsd, 4, 1, fp); yread (&check, (void *)&scsp.scieb, 4, 1, fp); yread (&check, (void *)&scsp.scipd, 4, 1, fp); yread (&check, (void *)&scsp.scilv0, 4, 1, fp); yread (&check, (void *)&scsp.scilv1, 4, 1, fp); yread (&check, (void *)&scsp.scilv2, 4, 1, fp); yread (&check, (void *)&scsp.mcieb, 4, 1, fp); yread (&check, (void *)&scsp.mcipd, 4, 1, fp); yread (&check, (void *)scsp.stack, 4, 32 * 2, fp); } return size; } ////////////////////////////////////////////////////////////////////////////// static char * AddSoundLFO (char *outstring, const char *string, u16 level, u16 waveform) { if (level > 0) { switch (waveform) { case 0: AddString(outstring, "%s Sawtooth\r\n", string); break; case 1: AddString(outstring, "%s Square\r\n", string); break; case 2: AddString(outstring, "%s Triangle\r\n", string); break; case 3: AddString(outstring, "%s Noise\r\n", string); break; } } return outstring; } ////////////////////////////////////////////////////////////////////////////// static char * AddSoundPan (char *outstring, u16 pan) { if (pan == 0x0F) { AddString(outstring, "Left = -MAX dB, Right = -0 dB\r\n"); } else if (pan == 0x1F) { AddString(outstring, "Left = -0 dB, Right = -MAX dB\r\n"); } else { AddString(outstring, "Left = -%d dB, Right = -%d dB\r\n", (pan & 0xF) * 3, (pan >> 4) * 3); } return outstring; } ////////////////////////////////////////////////////////////////////////////// static char * AddSoundLevel (char *outstring, u16 level) { if (level == 0) { AddString(outstring, "-MAX dB\r\n"); } else { AddString(outstring, "-%d dB\r\n", (7-level) * 6); } return outstring; } ////////////////////////////////////////////////////////////////////////////// void ScspSlotDebugStats (u8 slotnum, char *outstring) { u32 slotoffset = slotnum * 0x20; AddString (outstring, "Sound Source = "); switch (scsp.slot[slotnum].ssctl) { case 0: AddString (outstring, "External DRAM data\r\n"); break; case 1: AddString (outstring, "Internal(Noise)\r\n"); break; case 2: AddString (outstring, "Internal(0's)\r\n"); break; default: AddString (outstring, "Invalid setting\r\n"); break; } AddString (outstring, "Source bit = "); switch(scsp.slot[slotnum].sbctl) { case 0: AddString (outstring, "No bit reversal\r\n"); break; case 1: AddString (outstring, "Reverse other bits\r\n"); break; case 2: AddString (outstring, "Reverse sign bit\r\n"); break; case 3: AddString (outstring, "Reverse sign and other bits\r\n"); break; } // Loop Control AddString (outstring, "Loop Mode = "); switch (scsp.slot[slotnum].lpctl) { case 0: AddString (outstring, "Off\r\n"); break; case 1: AddString (outstring, "Normal\r\n"); break; case 2: AddString (outstring, "Reverse\r\n"); break; case 3: AddString (outstring, "Alternating\r\n"); break; } // PCM8B // NOTE: Need curly braces here, as AddString is a macro. if (scsp.slot[slotnum].pcm8b) { AddString (outstring, "8-bit samples\r\n"); } else { AddString (outstring, "16-bit samples\r\n"); } AddString (outstring, "Start Address = %05lX\r\n", (unsigned long)scsp.slot[slotnum].sa); AddString (outstring, "Loop Start Address = %04lX\r\n", (unsigned long)scsp.slot[slotnum].lsa >> SCSP_FREQ_LB); AddString (outstring, "Loop End Address = %04lX\r\n", (unsigned long)scsp.slot[slotnum].lea >> SCSP_FREQ_LB); AddString (outstring, "Decay 1 Rate = %ld\r\n", (unsigned long)scsp.slot[slotnum].dr); AddString (outstring, "Decay 2 Rate = %ld\r\n", (unsigned long)scsp.slot[slotnum].sr); if (scsp.slot[slotnum].eghold) AddString (outstring, "EG Hold Enabled\r\n"); AddString (outstring, "Attack Rate = %ld\r\n", (unsigned long)scsp.slot[slotnum].ar); if (scsp.slot[slotnum].lslnk) AddString (outstring, "Loop Start Link Enabled\r\n"); if (scsp.slot[slotnum].krs != 0) AddString (outstring, "Key rate scaling = %ld\r\n", (unsigned long)scsp.slot[slotnum].krs); AddString (outstring, "Decay Level = %d\r\n", (scsp_r_w(slotoffset + 0xA) >> 5) & 0x1F); AddString (outstring, "Release Rate = %ld\r\n", (unsigned long)scsp.slot[slotnum].rr); if (scsp.slot[slotnum].swe) AddString (outstring, "Stack Write Inhibited\r\n"); if (scsp.slot[slotnum].sdir) AddString (outstring, "Sound Direct Enabled\r\n"); AddString (outstring, "Total Level = %ld\r\n", (unsigned long)scsp.slot[slotnum].tl); AddString (outstring, "Modulation Level = %d\r\n", scsp.slot[slotnum].mdl); AddString (outstring, "Modulation Input X = %d\r\n", scsp.slot[slotnum].mdx); AddString (outstring, "Modulation Input Y = %d\r\n", scsp.slot[slotnum].mdy); AddString (outstring, "Octave = %d\r\n", (scsp_r_w(slotoffset + 0x10) >> 11) & 0xF); AddString (outstring, "Frequency Number Switch = %d\r\n", scsp_r_w(slotoffset + 0x10) & 0x3FF); AddString (outstring, "LFO Reset = %s\r\n", ((scsp_r_w(slotoffset + 0x12) >> 15) & 0x1) ? "TRUE" : "FALSE"); AddString (outstring, "LFO Frequency = %d\r\n", (scsp_r_w(slotoffset + 0x12) >> 10) & 0x1F); outstring = AddSoundLFO (outstring, "LFO Frequency modulation waveform = ", (scsp_r_w(slotoffset + 0x12) >> 5) & 0x7, (scsp_r_w(slotoffset + 0x12) >> 8) & 0x3); AddString (outstring, "LFO Frequency modulation level = %d\r\n", (scsp_r_w(slotoffset + 0x12) >> 5) & 0x7); outstring = AddSoundLFO (outstring, "LFO Amplitude modulation waveform = ", scsp_r_w(slotoffset + 0x12) & 0x7, (scsp_r_w(slotoffset + 0x12) >> 3) & 0x3); AddString (outstring, "LFO Amplitude modulation level = %d\r\n", scsp_r_w(slotoffset + 0x12) & 0x7); AddString (outstring, "Input mix level = "); outstring = AddSoundLevel (outstring, scsp_r_w(slotoffset + 0x14) & 0x7); AddString (outstring, "Input Select = %d\r\n", (scsp_r_w(slotoffset + 0x14) >> 3) & 0x1F); AddString (outstring, "Direct data send level = "); outstring = AddSoundLevel (outstring, (scsp_r_w(slotoffset + 0x16) >> 13) & 0x7); AddString (outstring, "Direct data panpot = "); outstring = AddSoundPan (outstring, (scsp_r_w(slotoffset + 0x16) >> 8) & 0x1F); AddString (outstring, "Effect data send level = "); outstring = AddSoundLevel (outstring, (scsp_r_w(slotoffset + 0x16) >> 5) & 0x7); AddString (outstring, "Effect data panpot = "); outstring = AddSoundPan (outstring, scsp_r_w(slotoffset + 0x16) & 0x1F); } ////////////////////////////////////////////////////////////////////////////// void ScspCommonControlRegisterDebugStats (char *outstring) { AddString (outstring, "Memory: %s\r\n", scsp.mem4b ? "4 Mbit" : "2 Mbit"); AddString (outstring, "Master volume: %ld\r\n", (unsigned long)scsp.mvol); AddString (outstring, "Ring buffer length: %ld\r\n", (unsigned long)scsp.rbl); AddString (outstring, "Ring buffer address: %08lX\r\n", (unsigned long)scsp.rbp); AddString (outstring, "\r\n"); AddString (outstring, "Slot Status Registers\r\n"); AddString (outstring, "-----------------\r\n"); AddString (outstring, "Monitor slot: %ld\r\n", (unsigned long)scsp.mslc); AddString (outstring, "Call address: %ld\r\n", (unsigned long)scsp.ca); AddString (outstring, "\r\n"); AddString (outstring, "DMA Registers\r\n"); AddString (outstring, "-----------------\r\n"); AddString (outstring, "DMA memory address start: %08lX\r\n", (unsigned long)scsp.dmea); AddString (outstring, "DMA register address start: %08lX\r\n", (unsigned long)scsp.drga); AddString (outstring, "DMA Flags: %lX\r\n", (unsigned long)scsp.dmlen); AddString (outstring, "\r\n"); AddString (outstring, "Timer Registers\r\n"); AddString (outstring, "-----------------\r\n"); AddString (outstring, "Timer A counter: %02lX\r\n", (unsigned long)scsp.timacnt >> 8); AddString (outstring, "Timer A increment: Every %d sample(s)\r\n", (int)pow(2, (double)scsp.timasd)); AddString (outstring, "Timer B counter: %02lX\r\n", (unsigned long)scsp.timbcnt >> 8); AddString (outstring, "Timer B increment: Every %d sample(s)\r\n", (int)pow(2, (double)scsp.timbsd)); AddString (outstring, "Timer C counter: %02lX\r\n", (unsigned long)scsp.timccnt >> 8); AddString (outstring, "Timer C increment: Every %d sample(s)\r\n", (int)pow(2, (double)scsp.timcsd)); AddString (outstring, "\r\n"); AddString (outstring, "Interrupt Registers\r\n"); AddString (outstring, "-----------------\r\n"); AddString (outstring, "Sound cpu interrupt pending: %04lX\r\n", (unsigned long)scsp.scipd); AddString (outstring, "Sound cpu interrupt enable: %04lX\r\n", (unsigned long)scsp.scieb); AddString (outstring, "Sound cpu interrupt level 0: %04lX\r\n", (unsigned long)scsp.scilv0); AddString (outstring, "Sound cpu interrupt level 1: %04lX\r\n", (unsigned long)scsp.scilv1); AddString (outstring, "Sound cpu interrupt level 2: %04lX\r\n", (unsigned long)scsp.scilv2); AddString (outstring, "Main cpu interrupt pending: %04lX\r\n", (unsigned long)scsp.mcipd); AddString (outstring, "Main cpu interrupt enable: %04lX\r\n", (unsigned long)scsp.mcieb); AddString (outstring, "\r\n"); } ////////////////////////////////////////////////////////////////////////////// int ScspSlotDebugSaveRegisters (u8 slotnum, const char *filename) { FILE *fp; int i; IOCheck_struct check; if ((fp = fopen (filename, "wb")) == NULL) return -1; for (i = (slotnum * 0x20); i < ((slotnum+1) * 0x20); i += 2) { #ifdef WORDS_BIGENDIAN ywrite (&check, (void *)&scsp_isr[i ^ 2], 1, 2, fp); #else ywrite (&check, (void *)&scsp_isr[(i + 1) ^ 2], 1, 1, fp); ywrite (&check, (void *)&scsp_isr[i ^ 2], 1, 1, fp); #endif } fclose (fp); return 0; } ////////////////////////////////////////////////////////////////////////////// static u32 ScspSlotDebugAudio (slot_t *slot, u32 *workbuf, s16 *buf, u32 len) { u32 *bufL, *bufR; bufL = workbuf; bufR = workbuf+len; scsp_bufL = (s32 *)bufL; scsp_bufR = (s32 *)bufR; if (slot->ecnt >= SCSP_ENV_DE) { // envelope null... memset (buf, 0, sizeof(s16) * 2 * len); return 0; } if (slot->ssctl) { memset (buf, 0, sizeof(s16) * 2 * len); return 0; // not yet supported! } scsp_buf_len = len; scsp_buf_pos = 0; // take effect sound volume if no direct sound volume... if ((slot->disll == 31) && (slot->dislr == 31)) { slot->disll = slot->efsll; slot->dislr = slot->efslr; } memset (bufL, 0, sizeof(u32) * len); memset (bufR, 0, sizeof(u32) * len); scsp_slot_update_p[(slot->lfofms == 31)?0:1] [(slot->lfoems == 31)?0:1] [(slot->pcm8b == 0)?1:0] [(slot->disll == 31)?0:1] [(slot->dislr == 31)?0:1](slot); ScspConvert32uto16s ((s32 *)bufL, (s32 *)bufR, (s16 *)buf, len); return len; } ////////////////////////////////////////////////////////////////////////////// typedef struct { char id[4]; u32 size; } chunk_struct; typedef struct { chunk_struct riff; char rifftype[4]; } waveheader_struct; typedef struct { chunk_struct chunk; u16 compress; u16 numchan; u32 rate; u32 bytespersec; u16 blockalign; u16 bitspersample; } fmt_struct; ////////////////////////////////////////////////////////////////////////////// int ScspSlotDebugAudioSaveWav (u8 slotnum, const char *filename) { u32 workbuf[512*2*2]; s16 buf[512*2]; slot_t slot; FILE *fp; u32 counter = 0; waveheader_struct waveheader; fmt_struct fmt; chunk_struct data; long length; IOCheck_struct check; if (scsp.slot[slotnum].lea == 0) return 0; if ((fp = fopen (filename, "wb")) == NULL) return -1; // Do wave header memcpy (waveheader.riff.id, "RIFF", 4); waveheader.riff.size = 0; // we'll fix this after the file is closed memcpy (waveheader.rifftype, "WAVE", 4); ywrite (&check, (void *)&waveheader, 1, sizeof(waveheader_struct), fp); // fmt chunk memcpy (fmt.chunk.id, "fmt ", 4); fmt.chunk.size = 16; // we'll fix this at the end fmt.compress = 1; // PCM fmt.numchan = 2; // Stereo fmt.rate = 44100; fmt.bitspersample = 16; fmt.blockalign = fmt.bitspersample / 8 * fmt.numchan; fmt.bytespersec = fmt.rate * fmt.blockalign; ywrite (&check, (void *)&fmt, 1, sizeof(fmt_struct), fp); // data chunk memcpy (data.id, "data", 4); data.size = 0; // we'll fix this at the end ywrite (&check, (void *)&data, 1, sizeof(chunk_struct), fp); memcpy (&slot, &scsp.slot[slotnum], sizeof(slot_t)); // Clear out the phase counter, etc. slot.fcnt = 0; slot.ecnt = SCSP_ENV_AS; slot.einc = &slot.einca; slot.ecmp = SCSP_ENV_AE; slot.ecurp = SCSP_ENV_ATTACK; slot.enxt = scsp_attack_next; // Mix the audio, and then write it to the file for (;;) { if (ScspSlotDebugAudio (&slot, workbuf, buf, 512) == 0) break; counter += 512; ywrite (&check, (void *)buf, 2, 512 * 2, fp); if (slot.lpctl != 0 && counter >= (44100 * 2 * 5)) break; } length = ftell (fp); // Let's fix the riff chunk size and the data chunk size fseek (fp, sizeof(waveheader_struct)-0x8, SEEK_SET); length -= 0x4; ywrite (&check, (void *)&length, 1, 4, fp); fseek (fp, sizeof(waveheader_struct) + sizeof(fmt_struct) + 0x4, SEEK_SET); length -= sizeof(waveheader_struct) + sizeof(fmt_struct); ywrite (&check, (void *)&length, 1, 4, fp); fclose (fp); return 0; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/sh2trace.h000644 001750 001750 00000002364 12256006161 020065 0ustar00guillaumeguillaume000000 000000 /* src/sh2trace.h: SH-2 tracing header for debugging Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SH2TRACE_H #define SH2TRACE_H #include "core.h" extern FASTCALL u64 sh2_cycle_count(void); extern FASTCALL void sh2_trace_add_cycles(s32 cycles); extern FASTCALL void sh2_trace_writeb(u32 address, u32 value); extern FASTCALL void sh2_trace_writew(u32 address, u32 value); extern FASTCALL void sh2_trace_writel(u32 address, u32 value); extern FASTCALL void sh2_trace(SH2_struct *state, u32 address); #endif // SH2TRACE_H yabause-0.9.13.1/src/sndmac.h000644 001750 001750 00000001611 12256006112 017605 0ustar00guillaumeguillaume000000 000000 /* Copyright 2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SNDMAC_H #define SNDMAC_H #define SNDCORE_MAC 3 extern SoundInterface_struct SNDMac; #endif /* !SNDMAC_H */ yabause-0.9.13.1/src/memory.h000644 001750 001750 00000021654 12256006161 017665 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Guillaume Duhamel Copyright 2005 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MEMORY_H #define MEMORY_H #include #include "core.h" /* Type 1 Memory, faster for byte (8 bits) accesses */ u8 * T1MemoryInit(u32); void T1MemoryDeInit(u8 *); static INLINE u8 T1ReadByte(u8 * mem, u32 addr) { return mem[addr]; } static INLINE u16 T1ReadWord(u8 * mem, u32 addr) { #ifdef WORDS_BIGENDIAN return *((u16 *) (mem + addr)); #else return BSWAP16L(*((u16 *) (mem + addr))); #endif } static INLINE u32 T1ReadLong(u8 * mem, u32 addr) { #ifdef WORDS_BIGENDIAN return *((u32 *) (mem + addr)); #else return BSWAP32(*((u32 *) (mem + addr))); #endif } static INLINE void T1WriteByte(u8 * mem, u32 addr, u8 val) { mem[addr] = val; } static INLINE void T1WriteWord(u8 * mem, u32 addr, u16 val) { #ifdef WORDS_BIGENDIAN *((u16 *) (mem + addr)) = val; #else *((u16 *) (mem + addr)) = BSWAP16L(val); #endif } static INLINE void T1WriteLong(u8 * mem, u32 addr, u32 val) { #ifdef WORDS_BIGENDIAN *((u32 *) (mem + addr)) = val; #else *((u32 *) (mem + addr)) = BSWAP32(val); #endif } /* Type 2 Memory, faster for word (16 bits) accesses */ #define T2MemoryInit(x) (T1MemoryInit(x)) #define T2MemoryDeInit(x) (T1MemoryDeInit(x)) static INLINE u8 T2ReadByte(u8 * mem, u32 addr) { #ifdef WORDS_BIGENDIAN return mem[addr]; #else return mem[addr ^ 1]; #endif } static INLINE u16 T2ReadWord(u8 * mem, u32 addr) { return *((u16 *) (mem + addr)); } static INLINE u32 T2ReadLong(u8 * mem, u32 addr) { #ifdef WORDS_BIGENDIAN return *((u32 *) (mem + addr)); #else return WSWAP32(*((u32 *) (mem + addr))); #endif } static INLINE void T2WriteByte(u8 * mem, u32 addr, u8 val) { #ifdef WORDS_BIGENDIAN mem[addr] = val; #else mem[addr ^ 1] = val; #endif } static INLINE void T2WriteWord(u8 * mem, u32 addr, u16 val) { *((u16 *) (mem + addr)) = val; } static INLINE void T2WriteLong(u8 * mem, u32 addr, u32 val) { #ifdef WORDS_BIGENDIAN *((u32 *) (mem + addr)) = val; #else *((u32 *) (mem + addr)) = WSWAP32(val); #endif } /* Type 3 Memory, faster for long (32 bits) accesses */ typedef struct { u8 * base_mem; u8 * mem; } T3Memory; T3Memory * T3MemoryInit(u32); void T3MemoryDeInit(T3Memory *); static INLINE u8 T3ReadByte(T3Memory * mem, u32 addr) { #ifdef WORDS_BIGENDIAN return mem->mem[addr]; #else return (mem->mem - addr - 1)[0]; #endif } static INLINE u16 T3ReadWord(T3Memory * mem, u32 addr) { #ifdef WORDS_BIGENDIAN return *((u16 *) (mem->mem + addr)); #else return ((u16 *) (mem->mem - addr - 2))[0]; #endif } static INLINE u32 T3ReadLong(T3Memory * mem, u32 addr) { #ifdef WORDS_BIGENDIAN return *((u32 *) (mem->mem + addr)); #else return ((u32 *) (mem->mem - addr - 4))[0]; #endif } static INLINE void T3WriteByte(T3Memory * mem, u32 addr, u8 val) { #ifdef WORDS_BIGENDIAN mem->mem[addr] = val; #else (mem->mem - addr - 1)[0] = val; #endif } static INLINE void T3WriteWord(T3Memory * mem, u32 addr, u16 val) { #ifdef WORDS_BIGENDIAN *((u16 *) (mem->mem + addr)) = val; #else ((u16 *) (mem->mem - addr - 2))[0] = val; #endif } static INLINE void T3WriteLong(T3Memory * mem, u32 addr, u32 val) { #ifdef WORDS_BIGENDIAN *((u32 *) (mem->mem + addr)) = val; #else ((u32 *) (mem->mem - addr - 4))[0] = val; #endif } static INLINE int T123Load(void * mem, u32 size, int type, const char *filename) { FILE *fp; u32 filesize, filesizecheck; u8 *buffer; u32 i; if (!filename) return -1; if ((fp = fopen(filename, "rb")) == NULL) return -1; // Calculate file size fseek(fp, 0, SEEK_END); filesize = ftell(fp); fseek(fp, 0, SEEK_SET); if (filesize > size) return -1; if ((buffer = (u8 *)malloc(filesize)) == NULL) { fclose(fp); return -1; } filesizecheck = (u32)fread((void *)buffer, 1, filesize, fp); fclose(fp); if (filesizecheck != filesize) return -1; switch (type) { case 1: { for (i = 0; i < filesize; i++) T1WriteByte((u8 *) mem, i, buffer[i]); break; } case 2: { for (i = 0; i < filesize; i++) T2WriteByte((u8 *) mem, i, buffer[i]); break; } case 3: { for (i = 0; i < filesize; i++) T3WriteByte((T3Memory *) mem, i, buffer[i]); break; } default: { free(buffer); return -1; } } free(buffer); return 0; } static INLINE int T123Save(void * mem, u32 size, int type, const char *filename) { FILE *fp; u8 *buffer; u32 i; u32 sizecheck; if (filename == NULL) return 0; if (filename[0] == 0x00) return 0; if ((buffer = (u8 *)malloc(size)) == NULL) return -1; switch (type) { case 1: { for (i = 0; i < size; i++) buffer[i] = T1ReadByte((u8 *) mem, i); break; } case 2: { for (i = 0; i < size; i++) buffer[i] = T2ReadByte((u8 *) mem, i); break; } case 3: { for (i = 0; i < size; i++) buffer[i] = T3ReadByte((T3Memory *) mem, i); break; } default: { free(buffer); return -1; } } if ((fp = fopen(filename, "wb")) == NULL) { free(buffer); return -1; } sizecheck = (u32)fwrite((void *)buffer, 1, size, fp); fclose(fp); free(buffer); if (sizecheck != size) return -1; return 0; } /* Dummy memory, always returns 0 */ typedef void Dummy; Dummy * DummyInit(u32); void DummyDeInit(Dummy *); static INLINE u8 DummyReadByte(Dummy UNUSED * d, u32 UNUSED a) { return 0; } static INLINE u16 DummyReadWord(Dummy UNUSED * d, u32 UNUSED a) { return 0; } static INLINE u32 DummyReadLong(Dummy UNUSED * d, u32 UNUSED a) { return 0; } static INLINE void DummyWriteByte(Dummy UNUSED * d, u32 UNUSED a, u8 UNUSED v) {} static INLINE void DummyWriteWord(Dummy UNUSED * d, u32 UNUSED a, u16 UNUSED v) {} static INLINE void DummyWriteLong(Dummy UNUSED * d, u32 UNUSED a, u32 UNUSED v) {} void MappedMemoryInit(void); u8 FASTCALL MappedMemoryReadByte(u32 addr); u16 FASTCALL MappedMemoryReadWord(u32 addr); u32 FASTCALL MappedMemoryReadLong(u32 addr); void FASTCALL MappedMemoryWriteByte(u32 addr, u8 val); void FASTCALL MappedMemoryWriteWord(u32 addr, u16 val); void FASTCALL MappedMemoryWriteLong(u32 addr, u32 val); #ifdef __cplusplus extern "C" { #endif extern u8 *HighWram; #ifdef __cplusplus } #endif extern u8 *LowWram; extern u8 *BiosRom; extern u8 *BupRam; extern u8 BupRamWritten; typedef void (FASTCALL *writebytefunc)(u32, u8); typedef void (FASTCALL *writewordfunc)(u32, u16); typedef void (FASTCALL *writelongfunc)(u32, u32); typedef u8 (FASTCALL *readbytefunc)(u32); typedef u16 (FASTCALL *readwordfunc)(u32); typedef u32 (FASTCALL *readlongfunc)(u32); extern writebytefunc WriteByteList[0x1000]; extern writewordfunc WriteWordList[0x1000]; extern writelongfunc WriteLongList[0x1000]; extern readbytefunc ReadByteList[0x1000]; extern readwordfunc ReadWordList[0x1000]; extern readlongfunc ReadLongList[0x1000]; typedef struct { u32 addr; u32 val; } result_struct; #define SEARCHBYTE 0 #define SEARCHWORD 1 #define SEARCHLONG 2 #define SEARCHEXACT (0 << 2) #define SEARCHLESSTHAN (1 << 2) #define SEARCHGREATERTHAN (2 << 2) #define SEARCHUNSIGNED (0 << 4) #define SEARCHSIGNED (1 << 4) #define SEARCHHEX (2 << 4) #define SEARCHSTRING (3 << 4) #define SEARCHREL8BIT (6 << 4) #define SEARCHREL16BIT (7 << 4) result_struct *MappedMemorySearch(u32 startaddr, u32 endaddr, int searchtype, const char *searchstr, result_struct *prevresults, u32 *maxresults); int MappedMemoryLoad(const char *filename, u32 addr); int MappedMemorySave(const char *filename, u32 addr, u32 size); void MappedMemoryLoadExec(const char *filename, u32 pc); int LoadBios(const char *filename); int LoadBackupRam(const char *filename); void FormatBackupRam(void *mem, u32 size); int YabSaveState(const char *filename); int YabLoadState(const char *filename); int YabSaveStateSlot(const char *dirpath, u8 slot); int YabLoadStateSlot(const char *dirpath, u8 slot); #endif yabause-0.9.13.1/src/sock-dummy.c000644 001750 001750 00000003157 12256006161 020436 0ustar00guillaumeguillaume000000 000000 /* Copyright 2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "core.h" #include "sock.h" ////////////////////////////////////////////////////////////////////////////// int YabSockInit() { return -1; } int YabSockDeInit() { return -1; } int YabSockConnectSocket(const char *ip, int port, YabSock *sock) { return -1; } int YabSockListenSocket(int port, YabSock *sock) { return -1; } int YabSockCloseSocket(YabSock sock) { return -1; } int YabSockSelect(YabSock sock, int check_read, int check_write ) { return -1; } int YabSockIsReadSet(YabSock sock) { return -1; } int YabSockIsWriteSet(YabSock sock) { return -1; } YabSock YabSockAccept(YabSock sock) { return 0; } int YabSockSend(YabSock sock, const void *buf, int len, int flags) { return -1; } int YabSockReceive(YabSock sock, void *buf, int len, int flags) { return -1; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/c68k/gen68k.inc000644 001750 001750 00000153332 12256006174 020557 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2004 Stephane Dallongeville This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #define EA_DREG 0 #define EA_AREG 1 #define EA_AIND 2 #define EA_AINC 3 #define EA_ADEC 4 #define EA_D16A 5 #define EA_D8AX 6 #define EA_A16 7 #define EA_A32 8 #define EA_D16P 9 #define EA_D8PX 10 #define EA_IMM 11 #define EA_AINC7 12 #define EA_ADEC7 13 #define EA_ILLEGAL 15 #define SIZE_BYTE 0 #define SIZE_WORD 1 #define SIZE_LONG 2 #define COND_TR 0 #define COND_FA 1 #define COND_HI 2 #define COND_LS 3 #define COND_CC 4 #define COND_CS 5 #define COND_NE 6 #define COND_EQ 7 #define COND_VC 8 #define COND_VS 9 #define COND_PL 10 #define COND_MI 11 #define COND_GE 12 #define COND_LT 13 #define COND_GT 14 #define COND_LE 15 #define COND_NOT_TR COND_FA #define COND_NOT_FA COND_TR #define COND_NOT_HI COND_LS #define COND_NOT_LS COND_HI #define COND_NOT_CC COND_CS #define COND_NOT_CS COND_CC #define COND_NOT_NE COND_EQ #define COND_NOT_EQ COND_NE #define COND_NOT_VC COND_VS #define COND_NOT_VS COND_VC #define COND_NOT_PL COND_MI #define COND_NOT_MI COND_PL #define COND_NOT_GE COND_LT #define COND_NOT_LT COND_GE #define COND_NOT_GT COND_LE #define COND_NOT_LE COND_GT #define OP_ILLEGAL 0x4AFC static void GenORI(void); static void GenORICCR(void); static void GenORISR(void); static void GenANDI(void); static void GenANDICCR(void); static void GenANDISR(void); static void GenEORI(void); static void GenEORICCR(void); static void GenEORISR(void); static void GenSUBI(void); static void GenADDI(void); static void GenCMPI(void); static void GenBTSTn(void); static void GenBCHGn(void); static void GenBCLRn(void); static void GenBSETn(void); static void GenBTST(void); static void GenBCHG(void); static void GenBCLR(void); static void GenBSET(void); static void GenMOVEPWaD(void); static void GenMOVEPLaD(void); static void GenMOVEPWDa(void); static void GenMOVEPLDa(void); static void GenMOVEB(void); static void GenMOVEL(void); static void GenMOVEW(void); static void GenMOVEAL(void); static void GenMOVEAW(void); static void GenNEGX(void); static void GenCLR(void); static void GenNEG(void); static void GenNOT(void); static void GenMOVESRa(void); static void GenMOVEaSR(void); static void GenMOVEaCCR(void); static void GenNBCD(void); static void GenPEA(void); static void GenSWAP(void); static void GenMOVEMaR(void); static void GenEXT(void); static void GenTST(void); static void GenTAS(void); static void GenILLEGAL(void); static void GenMOVEMRa(void); static void GenTRAP(void); static void GenLINK(void); static void GenLINKA7(void); static void GenULNK(void); static void GenULNKA7(void); static void GenMOVEAUSP(void); static void GenMOVEUSPA(void); static void GenRESET(void); static void GenNOP(void); static void GenSTOP(void); static void GenRTE(void); static void GenRTS(void); static void GenTRAPV(void); static void GenRTR(void); static void GenJSR(void); static void GenJMP(void); static void GenCHK(void); static void GenLEA(void); static void GenSTCC(void); static void GenDBCC(void); static void GenADDQ(void); static void GenSUBQ(void); static void GenBCC(void); static void GenBCC16(void); static void GenBRA(void); static void GenBRA16(void); static void GenBSR(void); static void GenBSR16(void); static void GenMOVEQ(void); static void GenORaD(void); static void GenORDa(void); static void GenSBCD(void); static void GenSBCDM(void); static void GenSBCD7M(void); static void GenSBCDM7(void); static void GenSBCD7M7(void); static void GenDIVU(void); static void GenDIVS(void); static void GenSUBaD(void); static void GenSUBDa(void); static void GenSUBX(void); static void GenSUBXM(void); static void GenSUBX7M(void); static void GenSUBXM7(void); static void GenSUBX7M7(void); static void GenSUBA(void); static void GenCMP(void); static void GenCMPM(void); static void GenCMP7M(void); static void GenCMPM7(void); static void GenCMP7M7(void); static void GenEORDa(void); static void GenCMPA(void); static void GenANDaD(void); static void GenANDDa(void); static void GenABCD(void); static void GenABCDM(void); static void GenABCD7M(void); static void GenABCDM7(void); static void GenABCD7M7(void); static void GenMULU(void); static void GenMULS(void); static void GenEXGDD(void); static void GenEXGAA(void); static void GenEXGAD(void); static void GenADDaD(void); static void GenADDDa(void); static void GenADDX(void); static void GenADDXM(void); static void GenADDX7M(void); static void GenADDXM7(void); static void GenADDX7M7(void); static void GenADDA(void); static void GenASRk(void); static void GenLSRk(void); static void GenROXRk(void); static void GenRORk(void); static void GenASLk(void); static void GenLSLk(void); static void GenROXLk(void); static void GenROLk(void); static void GenASRD(void); static void GenLSRD(void); static void GenROXRD(void); static void GenRORD(void); static void GenASLD(void); static void GenLSLD(void); static void GenROXLD(void); static void GenROLD(void); static void GenASR(void); static void GenLSR(void); static void GenROXR(void); static void GenROR(void); static void GenASL(void); static void GenLSL(void); static void GenROXL(void); static void GenROL(void); static void Gen1010(void); static void Gen1111(void); #ifdef NEOCD_HLE static void Gen0xFABE(void); static void Gen0xFABF(void); static void Gen0xFAC0(void); static void Gen0xFAC1(void); static void Gen0xFAC2(void); static void Gen0xFAC3(void); #endif #ifdef NEOCD_HLE #define OP_INFO_TABLE_LEN (142 + 6) #else #define OP_INFO_TABLE_LEN 144 #endif static c68k_op_info_struc op_info_table[OP_INFO_TABLE_LEN] = { // DAAAAddaaddi DAAAAddaaddi // iid181318m iid181318m // siz siz eam ear eam ear nne6A626Pm nne6A626Pm // opname opbase opmask typ sft 1 1 2 2 dccAX PX dccAX PX GenFunc { "1010", 0xA000, 0xF000, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen1010 }, { "1111", 0xF000, 0xF000, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen1111 }, { "ORI", 0x0000, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenORI }, { "ORICCR", 0x003C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenORICCR }, { "ORISR", 0x007C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenORISR }, { "ANDI", 0x0200, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenANDI }, { "ANDICCR", 0x023C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenANDICCR }, { "ANDISR", 0x027C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenANDISR }, { "EORI", 0x0A00, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenEORI }, { "EORICCR", 0x0A3C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenEORICCR }, { "EORISR", 0x0A7C, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenEORISR }, { "SUBI", 0x0400, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenSUBI }, { "ADDI", 0x0600, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenADDI }, { "CMPI", 0x0C00, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenCMPI }, { "BTSTn", 0x0800, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooooo-", "------------", GenBTSTn }, { "BCHGn", 0x0840, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenBCHGn }, { "BCLRn", 0x0880, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenBCLRn }, { "BSETn", 0x08C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenBSETn }, { "BTST", 0x0100, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenBTST }, { "BCHG", 0x0140, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-ooooooo---", "------------", GenBCHG }, { "BCLR", 0x0180, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-ooooooo---", "------------", GenBCLR }, { "BSET", 0x01C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-ooooooo---", "------------", GenBSET }, { "MOVEPWaD", 0x0108, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenMOVEPWaD }, { "MOVEPLaD", 0x0148, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenMOVEPLaD }, { "MOVEPWDa", 0x0188, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenMOVEPWDa }, { "MOVEPLDa", 0x01C8, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenMOVEPLDa }, { "MOVEB", 0x1000, 0xF000, 0, 0, 3, 0, 6, 9, "oooooooooooo", "o-ooooooo---", GenMOVEB }, { "MOVEL", 0x2000, 0xF000, 0, 0, 3, 0, 6, 9, "oooooooooooo", "o-ooooooo---", GenMOVEL }, { "MOVEW", 0x3000, 0xF000, 0, 0, 3, 0, 6, 9, "oooooooooooo", "o-ooooooo---", GenMOVEW }, { "MOVEAL", 0x2040, 0xF1C0, 0, 0, 3, 0, -1, 9, "oooooooooooo", "------------", GenMOVEAL }, { "MOVEAW", 0x3040, 0xF1C0, 0, 0, 3, 0, -1, 9, "oooooooooooo", "------------", GenMOVEAW }, { "NEGX", 0x4000, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenNEGX }, { "CLR", 0x4200, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenCLR }, { "NEG", 0x4400, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenNEG }, { "NOT", 0x4600, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenNOT }, { "MOVESRa", 0x40C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenMOVESRa }, { "MOVEaCCR", 0x44C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-oooooooooo", "------------", GenMOVEaCCR }, { "MOVEaSR", 0x46C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-oooooooooo", "------------", GenMOVEaSR }, { "NBCD", 0x4800, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenNBCD }, { "PEA", 0x4840, 0xFFC0, 0, 0, 3, 0, -1, -1, "--o--oooooo-", "------------", GenPEA }, { "SWAP", 0x4840, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenSWAP }, { "MOVEMRa", 0x4880, 0xFF80, 1, 6, 3, 0, -1, -1, "--o-ooooo---", "------------", GenMOVEMRa }, { "EXT", 0x4880, 0xFFB8, 1, 6, -1, 0, -1, -1, "------------", "------------", GenEXT }, { "TST", 0x4A00, 0xFF00, 2, 6, 3, 0, -1, -1, "o-ooooooo---", "------------", GenTST }, { "TAS", 0x4AC0, 0xFFC0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenTAS }, { "ILLEGAL", 0x4AFC, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenILLEGAL }, { "MOVEMaR", 0x4C80, 0xFF80, 1, 6, 3, 0, -1, -1, "--oo-oooooo-", "------------", GenMOVEMaR }, { "TRAP", 0x4E40, 0xFFF0, 0, 0, -1, -1, -1, -1, "------------", "------------", GenTRAP }, { "LINK", 0x4E50, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenLINK }, { "LINKA7", 0x4E57, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenLINKA7 }, { "ULNK", 0x4E58, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenULNK }, { "ULNKA7", 0x4E5F, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenULNKA7 }, { "MOVEAUSP", 0x4E60, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenMOVEAUSP }, { "MOVEUSPA", 0x4E68, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenMOVEUSPA }, { "RESET", 0x4E70, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenRESET }, { "NOP", 0x4E71, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenNOP }, { "STOP", 0x4E72, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenSTOP }, { "RTE", 0x4E73, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenRTE }, { "RTS", 0x4E75, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenRTS }, { "TRAPV", 0x4E76, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenTRAPV }, { "RTR", 0x4E77, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenRTR }, { "JSR", 0x4E80, 0xFFC0, 0, 0, 3, 0, -1, -1, "--o--oooooo-", "------------", GenJSR }, { "JMP", 0x4EC0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--o--oooooo-", "------------", GenJMP }, { "CHK", 0x4180, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenCHK }, { "LEA", 0x41C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "--o--oooooo-", "------------", GenLEA }, { "STCC", 0x50C0, 0xF0C0, 0, 0, 3, 0, -1, -1, "o-ooooooo---", "------------", GenSTCC }, { "DBCC", 0x50C8, 0xF0F8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenDBCC }, { "ADDQ", 0x5000, 0xF100, 2, 6, 3, 0, -1, -1, "ooooooooo---", "------------", GenADDQ }, { "SUBQ", 0x5100, 0xF100, 2, 6, 3, 0, -1, -1, "ooooooooo---", "------------", GenSUBQ }, { "BCC", 0x6000, 0xF000, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBCC }, { "BCC16", 0x6000, 0xF0FF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBCC16 }, { "BRA", 0x6000, 0xFF00, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBRA }, { "BRA16", 0x6000, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBRA16 }, { "BSR", 0x6100, 0xFF00, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBSR }, { "BSR16", 0x6100, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenBSR16 }, { "MOVEQ", 0x7000, 0xF100, 0, 0, -1, 9, -1, -1, "------------", "------------", GenMOVEQ }, { "ORaD", 0x8000, 0xF100, 2, 6, 3, 0, -1, 9, "o-oooooooooo", "------------", GenORaD }, { "ORDa", 0x8100, 0xF100, 2, 6, 3, 0, -1, 9, "--ooooooo---", "------------", GenORDa }, { "SBCD", 0x8100, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCD }, { "SBCDM", 0x8108, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCDM }, { "SBCD7M", 0x810F, 0xF1FF, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCD7M }, { "SBCDM7", 0x8F08, 0xFFF8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCDM7 }, { "SBCD7M7", 0x8F0F, 0xFFFF, 0, 0, -1, 0, -1, 9, "------------", "------------", GenSBCD7M7 }, { "DIVU", 0x80C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenDIVU }, { "DIVS", 0x81C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenDIVS }, { "SUBaD", 0x9000, 0xF100, 2, 6, 3, 0, -1, 9, "oooooooooooo", "------------", GenSUBaD }, { "SUBDa", 0x9100, 0xF100, 2, 6, 3, 0, -1, 9, "--ooooooo---", "------------", GenSUBDa }, { "SUBX", 0x9100, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenSUBX }, { "SUBXM", 0x9108, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenSUBXM }, { "SUBX7M", 0x910F, 0xF13F, 2, 6, -1, -1, -1, 9, "------------", "------------", GenSUBX7M }, { "SUBXM7", 0x9F08, 0xFF38, 2, 6, -1, 0, -1, -1, "------------", "------------", GenSUBXM7 }, { "SUBX7M7", 0x9F0F, 0xFF3F, 2, 6, -1, -1, -1, -1, "------------", "------------", GenSUBX7M7 }, { "SUBA", 0x90C0, 0xF0C0, 1, 8, 3, 0, -1, 9, "oooooooooooo", "------------", GenSUBA }, { "CMP", 0xB000, 0xF100, 2, 6, 3, 0, -1, 9, "oooooooooooo", "------------", GenCMP }, { "CMPM", 0xB108, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenCMPM }, { "CMP7M", 0xB10F, 0xF13F, 2, 6, -1, -1, -1, 9, "------------", "------------", GenCMP7M }, { "CMPM7", 0xBF08, 0xFF38, 2, 6, -1, 0, -1, -1, "------------", "------------", GenCMPM7 }, { "CMP7M7", 0xBF0F, 0xFF3F, 2, 6, -1, -1, -1, -1, "------------", "------------", GenCMP7M7 }, { "EORDa", 0xB100, 0xF100, 2, 6, 3, 0, -1, 9, "o-ooooooo---", "------------", GenEORDa }, { "CMPA", 0xB0C0, 0xF0C0, 1, 8, 3, 0, -1, 9, "oooooooooooo", "------------", GenCMPA }, { "ANDaD", 0xC000, 0xF100, 2, 6, 3, 0, -1, 9, "o-oooooooooo", "------------", GenANDaD }, { "ANDDa", 0xC100, 0xF100, 2, 6, 3, 0, -1, 9, "--ooooooo---", "------------", GenANDDa }, { "ABCD", 0xC100, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenABCD }, { "ABCDM", 0xC108, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenABCDM }, { "ABCD7M", 0xC10F, 0xF1FF, 0, 0, -1, -1, -1, 9, "------------", "------------", GenABCD7M }, { "ABCDM7", 0xCF08, 0xFFF8, 0, 0, -1, 0, -1, -1, "------------", "------------", GenABCDM7 }, { "ABCD7M7", 0xCF0F, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", GenABCD7M7 }, { "MULU", 0xC0C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenMULU }, { "MULS", 0xC1C0, 0xF1C0, 0, 0, 3, 0, -1, 9, "o-oooooooooo", "------------", GenMULS }, { "EXGDD", 0xC140, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenEXGDD }, { "EXGAA", 0xC148, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenEXGAA }, { "EXGAD", 0xC188, 0xF1F8, 0, 0, -1, 0, -1, 9, "------------", "------------", GenEXGAD }, { "ADDaD", 0xD000, 0xF100, 2, 6, 3, 0, -1, 9, "oooooooooooo", "------------", GenADDaD }, { "ADDDa", 0xD100, 0xF100, 2, 6, 3, 0, -1, 9, "--ooooooo---", "------------", GenADDDa }, { "ADDX", 0xD100, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenADDX }, { "ADDXM", 0xD108, 0xF138, 2, 6, -1, 0, -1, 9, "------------", "------------", GenADDXM }, { "ADDX7M", 0xD10F, 0xF13F, 2, 6, -1, -1, -1, 9, "------------", "------------", GenADDX7M }, { "ADDXM7", 0xDF08, 0xFF38, 2, 6, -1, 0, -1, -1, "------------", "------------", GenADDXM7 }, { "ADDX7M7", 0xDF0F, 0xFF3F, 2, 6, -1, -1, -1, -1, "------------", "------------", GenADDX7M7 }, { "ADDA", 0xD0C0, 0xF0C0, 1, 8, 3, 0, -1, 9, "oooooooooooo", "------------", GenADDA }, { "ASRk", 0xE000, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenASRk }, { "LSRk", 0xE008, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenLSRk }, { "ROXRk", 0xE010, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenROXRk }, { "RORk", 0xE018, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenRORk }, { "ASLk", 0xE100, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenASLk }, { "LSLk", 0xE108, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenLSLk }, { "ROXLk", 0xE110, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenROXLk }, { "ROLk", 0xE118, 0xF138, 2, 6, -1, 0, -1, -1, "o-----------", "------------", GenROLk }, { "ASRD", 0xE020, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenASRD }, { "LSRD", 0xE028, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenLSRD }, { "ROXRD", 0xE030, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenROXRD }, { "RORD", 0xE038, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenRORD }, { "ASLD", 0xE120, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenASLD }, { "LSLD", 0xE128, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenLSLD }, { "ROXLD", 0xE130, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenROXLD }, { "ROLD", 0xE138, 0xF138, 2, 6, -1, 0, -1, 9, "o-----------", "o-----------", GenROLD }, { "ASR", 0xE0C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenASR }, { "LSR", 0xE2C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenLSR }, { "ROXR", 0xE4C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenROXR }, { "ROR", 0xE6C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenROR }, { "ASL", 0xE1C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenASL }, { "LSL", 0xE3C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenLSL }, { "ROXL", 0xE5C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenROXL }, { "ROL", 0xE7C0, 0xFFC0, 0, 0, 3, 0, -1, -1, "--ooooooo---", "------------", GenROL } #ifdef NEOCD_HLE , { "0xFABE", 0xFABE, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFABE }, { "0xFABF", 0xFABF, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFABF }, { "0xFAC0", 0xFAC0, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFAC0 }, { "0xFAC1", 0xFAC1, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFAC1 }, { "0xFAC2", 0xFAC2, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFAC2 }, { "0xFAC3", 0xFAC3, 0xFFFF, 0, 0, -1, -1, -1, -1, "------------", "------------", Gen0xFAC3 } #endif }; #ifndef C68K_NO_JUMP_TABLE #ifdef C68K_CONST_JUMP_TABLE static u16 op_jump_table[0x10000]; #endif #endif // files where code is generated static FILE* ini_file = NULL; static FILE* opcode_file = NULL; // current generated instruction infos static c68k_op_info_struc *current_op; static u32 current_ea; static u32 current_eam; static u32 current_reg; static u32 current_ea2; static u32 current_eam2; static u32 current_reg2; static u32 current_size; static u32 current_cycle; static u32 current_io_sav; static char current_cond_char[128]; static char szc[20]; static char szcs[20]; static char szcf[20]; static char szcsf[20]; static u32 current_bits_mask; static u8 current_sft_mask; #define EA_DREG 0 #define EA_AREG 1 #define EA_AIND 2 #define EA_AINC 3 #define EA_ADEC 4 #define EA_D16A 5 #define EA_D8AX 6 #define EA_A16 7 #define EA_A32 8 #define EA_D16P 9 #define EA_D8PX 10 #define EA_IMM 11 #define EA_AINC7 12 #define EA_ADEC7 13 #define EA_ILLEGAL 15 static const u32 jmp_jsr_cycle_table[16] = { 0, 0, 4, 0, 0, 6, 10, 6, 8, 6, 10, 0, 0, 0, 0 }; static const u32 lea_pea_cycle_table[16] = { 0, 0, 0, 0, 0, 4, 8, 4, 8, 4, 8, 0, 0, 0, 0 }; static const u32 movem_cycle_table[16] = { 0, 0, 0, 0, 0, 4, 6, 4, 8, 4, 6, 0, 0, 0, 0 }; // general emitter function //////////////////////////// static u32 prepare_generate(void) { char filename[32]; sprintf(filename, "c68k_op%.1X.inc", (current_op->op_base >> 12) & 0xF); if (opcode_file != NULL) { fclose(opcode_file); opcode_file = NULL; } opcode_file = fopen(filename, "at"); if (opcode_file == NULL) { printf("Can't open %s\n", filename); return 1; } return 0; } static void wf_op(char* fmt, ...) { va_list args; if (opcode_file == NULL) return; va_start(args, fmt); vfprintf(opcode_file, fmt, args); va_end(args); } static void gen_jumptable(u32 base, u32 start1, u32 end1, u32 step1, u32 start2, u32 end2, u32 step2, u32 start3, u32 end3, u32 step3, u32 op) { #ifdef C68K_CONST_JUMP_TABLE u32 i, j, k; #endif #ifdef C68K_NO_JUMP_TABLE u32 i, j, k; #endif base &= 0xFFFF; start1 &= 0xFFFF; end1 &= 0xFFFF; step1 &= 0xFFFF; if (end1 < start1) end1 = start1; start2 &= 0xFFFF; end2 &= 0xFFFF; step2 &= 0xFFFF; if (end2 < start2) end2 = start2; op &= 0xFFFF; #ifndef C68K_NO_JUMP_TABLE #ifdef C68K_CONST_JUMP_TABLE if (step1 == 0) step1 = 1; if (step2 == 0) step2 = 1; if (step3 == 0) step3 = 1; for(i = start1; i <= end1; i += step1) for(j = start2; j <= end2; j += step2) for(k = start3; k <= end3; k += step3) op_jump_table[base + i + j + k] = op; #else if (ini_file == NULL) return; if (start1 != end1) { fprintf(ini_file, "\t\tfor(i = 0x%.4X; i <= 0x%.4X; i += 0x%.4X)\n", (int)start1, (int)end1, (int)step1); if (start2 != end2) { fprintf(ini_file, "\t\t\tfor(j = 0x%.4X; j <= 0x%.4X; j += 0x%.4X)\n\t", (int)start2, (int)end2, (int)step2); if (start3 != end3) { fprintf(ini_file, "\t\t\t\tfor(k = 0x%.4X; k <= 0x%.4X; k += 0x%.4X)\n\t", (int)start3, (int)end3, (int)step3); fprintf(ini_file, "\t\t\t\t\tJumpTable[0x%.4X + i + j + k] = &&OP_0x%.4X;\n", (int)base, (int)op); } else fprintf(ini_file, "\t\t\t\tJumpTable[0x%.4X + i + j] = &&OP_0x%.4X;\n", (int)base, (int)op); } else fprintf(ini_file, "\t\t\tJumpTable[0x%.4X + i] = &&OP_0x%.4X;\n", (int)base, (int)op); } else fprintf(ini_file, "\t\tJumpTable[0x%.4X] = &&OP_0x%.4X;\n", (int)base, (int)op); #endif #else if (step1 == 0) step1 = 1; if (step2 == 0) step2 = 1; if (step3 == 0) step3 = 1; for(i = start1; i <= end1; i += step1) for(j = start2; j <= end2; j += step2) for(k = start3; k <= end3; k += step3) { u32 temp=(base + i + j + k); if (temp != op && temp != 0x4E57 && temp != 0x4E5F) wf_op("case 0x%.4X:\n", base + i + j + k); } #endif } static void gen_opjumptable_ext(u32 base, u32 start3, u32 end3, u32 step3, u32 op) { u32 start1, end1, step1, start2, end2, step2; start1 = end1 = step1 = 0; start2 = end2 = step2 = 0; if ((current_op->reg_sft != -1) && (current_ea < 7)) { if ((current_ea == EA_AINC) || (current_ea == EA_ADEC)) end1 = 6 << current_op->reg_sft; else end1 = 7 << current_op->reg_sft; step1 = 1 << current_op->reg_sft; } if ((current_op->reg2_sft != -1) && (current_ea2 < 7)) { if ((current_ea2 == EA_AINC) || (current_ea2 == EA_ADEC)) end2 = 6 << current_op->reg2_sft; else end2 = 7 << current_op->reg2_sft; step2 = 1 << current_op->reg2_sft; } if (start1 != end1) { if (start2 != end2) gen_jumptable(base, start1, end1, step1, start2, end2, step2, start3, end3, step3, op); else gen_jumptable(base, start1, end1, step1, start3, end3, step3, start2, end2, step2, op); } else if (start2 != end2) gen_jumptable(base, start2, end2, step2, start3, end3, step3, start1, end1, step1, op); else gen_jumptable(base, start3, end3, step3, start2, end2, step2, start1, end1, step1, op); } static void gen_opjumptable(u32 op) { gen_opjumptable_ext(op, 0, 0, 0, op); } #define GEN_ADR 1 #define GEN_RES 2 #define GEN_SRC 4 #define GEN_DST 8 #define GEN_ALL 15 static void start_op(u32 op, int v) { current_io_sav = 0; current_cycle = 0; wf_op("\n// %s\n", current_op->op_name); #ifndef C68K_NO_JUMP_TABLE wf_op("OP_0x%.4X:\n", op & 0xFFFF); #else wf_op("case 0x%.4X:\n", op & 0xFFFF); #endif wf_op("{\n"); if (v & GEN_ADR) wf_op("\tu32 adr;\n"); if (v & GEN_RES) wf_op("\tu32 res;\n"); if (v & GEN_DST) wf_op("\tpointer dst;\n"); if (v & GEN_SRC) wf_op("\tpointer src;\n"); } static void add_CCnt(u32 cycle) { if (current_io_sav) wf_op("\tPOST_IO\n"); current_io_sav = 0; wf_op("\tCCnt -= %d;\n", cycle); } static void adds_CCnt(char *str) { if (current_io_sav) wf_op("\tPOST_IO\n"); current_io_sav = 0; wf_op("\tCCnt -= %s;\n", str); } #if 0 // FIXME: warning removal static void sub_CCnt(u32 cycle) { if (current_io_sav) wf_op("\tPOST_IO\n"); current_io_sav = 0; wf_op("\tCCnt += %d;\n", cycle); } static void subs_CCnt(char *str) { if (current_io_sav) wf_op("\tPOST_IO\n"); current_io_sav = 0; wf_op("\tCCnt += %s;\n", str); } static void quick_fterminate_op(u32 cycle) { if (current_io_sav) wf_op("\tPOST_IO\n"); current_io_sav = 0; wf_op("\tCCnt -= %d;\n", current_cycle + cycle); wf_op("\tgoto C68k_Exec_End;\n"); } #endif static void fterminate_op(u32 cycle) { wf_op("}\n"); if (current_io_sav) wf_op("POST_IO\n"); current_io_sav = 0; wf_op("CCnt -= %d;\n", current_cycle + cycle); wf_op("goto C68k_Exec_End;\n"); } static void quick_terminate_op(u32 cycle) { if (current_io_sav) wf_op("\tPOST_IO\n"); current_io_sav = 0; wf_op("\tRET(%d)\n", current_cycle + cycle); } static void terminate_op(u32 cycle) { if (current_io_sav) wf_op("\tPOST_IO\n"); wf_op("}\n"); current_io_sav = 0; wf_op("RET(%d)\n", current_cycle + cycle); } static void do_pre_io(void) { if (!current_io_sav) fprintf(opcode_file, "\tPRE_IO\n"); current_io_sav = 1; } static void mem_op(char* fmt, ...) { va_list args; if (opcode_file == NULL) return; do_pre_io(); va_start(args, fmt); vfprintf(opcode_file, fmt, args); va_end(args); } // flag emitter function ///////////////////////// static void set_logic_flag(void) { wf_op("\tCPU->flag_C = 0;\n"); wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_notZ = res;\n"); switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_N = res;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_N = res >> 8;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static void set_logicl_flag(void) { wf_op("\tCPU->flag_C = 0;\n"); wf_op("\tCPU->flag_V = 0;\n"); switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_N = res;\n"); wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); wf_op("\tCPU->flag_N = res >> 8;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_notZ = res;\n"); wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static void set_add_flag(void) { switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); wf_op("\tCPU->flag_V = (src ^ res) & (dst ^ res);\n"); wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8;\n"); wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_notZ = res;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23;\n"); // wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst) | (~res & (src | dst))) >> 23;\n"); wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24;\n"); wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static void set_addx_flag(void) { switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); wf_op("\tCPU->flag_V = (src ^ res) & (dst ^ res);\n"); wf_op("\tCPU->flag_notZ |= res & 0xFF;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 8;\n"); wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_notZ |= res;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst & 1) + (src >> 1) + (dst >> 1)) >> 23;\n"); // wf_op("\tCPU->flag_X = CPU->flag_C = ((src & dst) | (~res & (src | dst))) >> 23;\n"); wf_op("\tCPU->flag_V = ((src ^ res) & (dst ^ res)) >> 24;\n"); wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static void set_sub_flag(void) { switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n"); wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n"); wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_notZ = res;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); // wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n"); wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static void set_subx_flag(void) { switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n"); wf_op("\tCPU->flag_notZ |= res & 0xFF;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n"); wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_notZ |= res;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); // wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n"); wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static void set_cmp_flag(void) { switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_N = CPU->flag_C = res;\n"); wf_op("\tCPU->flag_V = (src ^ dst) & (res ^ dst);\n"); wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 8;\n"); wf_op("\tCPU->flag_N = CPU->flag_C = res >> 8;\n"); wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_notZ = res;\n"); wf_op("\tCPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); // wf_op("\tCPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); wf_op("\tCPU->flag_V = ((src ^ dst) & (res ^ dst)) >> 24;\n"); wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static void set_negx_flag(void) { switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_V = res & src;\n"); wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); wf_op("\tCPU->flag_notZ |= res & 0xFF;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_V = (res & src) >> 8;\n"); wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); wf_op("\tCPU->flag_notZ |= res & 0xFFFF;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_notZ |= res;\n"); wf_op("\tCPU->flag_V = (res & src) >> 24;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); // wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static void set_neg_flag(void) { switch(current_size) { case SIZE_BYTE: wf_op("\tCPU->flag_V = res & src;\n"); wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res;\n"); wf_op("\tCPU->flag_notZ = res & 0xFF;\n"); break; case SIZE_WORD: wf_op("\tCPU->flag_V = (res & src) >> 8;\n"); wf_op("\tCPU->flag_N = CPU->flag_X = CPU->flag_C = res >> 8;\n"); wf_op("\tCPU->flag_notZ = res & 0xFFFF;\n"); break; case SIZE_LONG: wf_op("\tCPU->flag_notZ = res;\n"); wf_op("\tCPU->flag_V = (res & src) >> 24;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res & 1) + (src >> 1) + (res >> 1)) >> 23;\n"); // wf_op("\tCPU->flag_X = CPU->flag_C = ((src & res) | (~dst & (src | res))) >> 23;\n"); wf_op("\tCPU->flag_N = res >> 24;\n"); break; } } static char* get_cond_as_cond(u32 cond, u32 notvar) { if (notvar) cond ^= 1; switch(cond) { case COND_TR: sprintf(current_cond_char, "(1)"); break; case COND_FA: sprintf(current_cond_char, "(0)"); break; case COND_HI: sprintf(current_cond_char, "(CPU->flag_notZ && (!(CPU->flag_C & 0x100)))"); break; case COND_LS: sprintf(current_cond_char, "((!CPU->flag_notZ) || (CPU->flag_C & 0x100))"); break; case COND_CC: sprintf(current_cond_char, "(!(CPU->flag_C & 0x100))"); break; case COND_CS: sprintf(current_cond_char, "(CPU->flag_C & 0x100)"); break; case COND_NE: sprintf(current_cond_char, "(CPU->flag_notZ)"); break; case COND_EQ: sprintf(current_cond_char, "(!CPU->flag_notZ)"); break; case COND_VC: sprintf(current_cond_char, "(!(CPU->flag_V & 0x80))"); break; case COND_VS: sprintf(current_cond_char, "(CPU->flag_V & 0x80)"); break; case COND_PL: sprintf(current_cond_char, "(!(CPU->flag_N & 0x80))"); break; case COND_MI: sprintf(current_cond_char, "(CPU->flag_N & 0x80)"); break; case COND_GE: sprintf(current_cond_char, "(!((CPU->flag_N ^ CPU->flag_V) & 0x80))"); break; case COND_LT: sprintf(current_cond_char, "((CPU->flag_N ^ CPU->flag_V) & 0x80)"); break; case COND_GT: sprintf(current_cond_char, "(CPU->flag_notZ && (!((CPU->flag_N ^ CPU->flag_V) & 0x80)))"); break; case COND_LE: sprintf(current_cond_char, "((!CPU->flag_notZ) || ((CPU->flag_N ^ CPU->flag_V) & 0x80))"); break; } return current_cond_char; } // effective address related function ////////////////////////////////////// static u32 has_ea(u32 ea) { if (ea == EA_AINC7) return (current_op->ea_supported[EA_AINC] == 'o'); if (ea == EA_ADEC7) return (current_op->ea_supported[EA_ADEC] == 'o'); if (ea <= EA_IMM) return (current_op->ea_supported[ea] == 'o'); return 0; } static u32 has_ea2(u32 ea) { if (ea == EA_AINC7) return (current_op->ea2_supported[EA_AINC] == 'o'); if (ea == EA_ADEC7) return (current_op->ea2_supported[EA_ADEC] == 'o'); if (ea <= EA_IMM) return (current_op->ea2_supported[ea] == 'o'); return 0; } #if 0 // FIXME: warning removal static u32 _eamreg_to_ea(u32 eam, u32 reg) { if ((eam > 7) || (reg > 7)) return EA_ILLEGAL; if ((eam == 3) && (reg == 7)) return EA_AINC7; if ((eam == 4) && (reg == 7)) return EA_ADEC7; if (eam != 7) return eam; if (reg < 5) return (eam + reg); return EA_ILLEGAL; } #endif static u32 _ea_to_eamreg(u32 ea) { if (ea < 7) return (ea << 3) | 0; if (ea == EA_AINC7) return (EA_AINC << 3) | 7; if (ea == EA_ADEC7) return (EA_ADEC << 3) | 7; if (ea <= EA_IMM) return (7 << 3) | (ea - 7); return (7 << 3) | 7; } static u32 is_ea_memory(u32 ea) { if ((ea > EA_AREG) && (ea != EA_IMM)) return 1; else return 0; } static void _ea_calc_free(u32 ea, u32 rsft) { u32 step; step = 0; switch (current_size) { case SIZE_BYTE: if ((ea == EA_AINC7) || (ea == EA_ADEC7)) step = 2; else step = 1; break; case SIZE_WORD: step = 2; break; case SIZE_LONG: step = 4; break; } switch (ea) { case EA_DREG: // wf_op("\tadr = (u32)(&CPU->D[(Opcode >> %d) & 7]);\n", rsft); break; case EA_AREG: // wf_op("\tadr = (u32)(&CPU->A[(Opcode >> %d) & 7]);\n", rsft); break; case EA_AIND: wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); break; case EA_AINC: wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); wf_op("\tCPU->A[(Opcode >> %d) & 7] += %d;\n", rsft, step); break; case EA_AINC7: wf_op("\tadr = CPU->A[7];\n"); wf_op("\tCPU->A[7] += %d;\n", step); break; case EA_ADEC: wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] - %d;\n", rsft, step); wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", rsft); break; case EA_ADEC7: wf_op("\tadr = CPU->A[7] - %d;\n", step); wf_op("\tCPU->A[7] = adr;\n"); break; case EA_D16A: wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] + (s32)(s16)FETCH_WORD;\n", rsft); wf_op("\tPC += 2;\n"); break; case EA_D8AX: wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); wf_op("\tDECODE_EXT_WORD\n"); break; case EA_A16: wf_op("\tadr = (s32)(s16)FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); break; case EA_A32: wf_op("\tadr = (s32)FETCH_LONG;\n"); wf_op("\tPC += 4;\n"); break; case EA_D16P: wf_op("\tadr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); break; case EA_D8PX: wf_op("\tadr = PC - CPU->BasePC;\n"); wf_op("\tDECODE_EXT_WORD\n"); break; } } static void _ea_calc(u32 ea, u32 rsft) { u32 step; u32 cycle_sft; step = 0; cycle_sft = 0; switch (current_size) { case SIZE_BYTE: if ((ea == EA_AINC7) || (ea == EA_ADEC7)) step = 2; else step = 1; break; case SIZE_WORD: step = 2; break; case SIZE_LONG: cycle_sft = 1; step = 4; break; } switch (ea) { case EA_DREG: // wf_op("\tadr = (u32)(&CPU->D[(Opcode >> %d) & 7]);\n", rsft); break; case EA_AREG: // wf_op("\tadr = (u32)(&CPU->A[(Opcode >> %d) & 7]);\n", rsft); break; case EA_IMM: current_cycle += (4 << cycle_sft) + 0; break; case EA_AIND: current_cycle += (4 << cycle_sft) + 0; wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); break; case EA_AINC: current_cycle += (4 << cycle_sft) + 0; wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); wf_op("\tCPU->A[(Opcode >> %d) & 7] += %d;\n", rsft, step); break; case EA_AINC7: current_cycle += (4 << cycle_sft) + 0; wf_op("\tadr = CPU->A[7];\n"); wf_op("\tCPU->A[7] += %d;\n", step); break; case EA_ADEC: current_cycle += (4 << cycle_sft) + 2; wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] - %d;\n", rsft, step); wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", rsft); break; case EA_ADEC7: current_cycle += (4 << cycle_sft) + 2; wf_op("\tadr = CPU->A[7] - %d;\n", step); wf_op("\tCPU->A[7] = adr;\n"); break; case EA_D16A: current_cycle += (4 << cycle_sft) + 4; wf_op("\tadr = CPU->A[(Opcode >> %d) & 7] + (s32)(s16)FETCH_WORD;\n", rsft); wf_op("\tPC += 2;\n"); break; case EA_D8AX: current_cycle += (4 << cycle_sft) + 6; wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", rsft); wf_op("\tDECODE_EXT_WORD\n"); break; case EA_A16: current_cycle += (4 << cycle_sft) + 4; wf_op("\tadr = (s32)(s16)FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); break; case EA_A32: current_cycle += (4 << cycle_sft) + 8; wf_op("\tadr = (s32)FETCH_LONG;\n"); wf_op("\tPC += 4;\n"); break; case EA_D16P: current_cycle += (4 << cycle_sft) + 4; wf_op("\tadr = (PC - CPU->BasePC) + (s32)(s16)FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); break; case EA_D8PX: current_cycle += (4 << cycle_sft) + 6; wf_op("\tadr = PC - CPU->BasePC;\n"); wf_op("\tDECODE_EXT_WORD\n"); break; } } static void _ea_read_(u32 ea, u32 rsft, char dest[4]) { char sz[8]; switch (current_size) { case SIZE_BYTE: strcpy(sz, "BYTE"); break; case SIZE_WORD: strcpy(sz, "WORD"); break; case SIZE_LONG: strcpy(sz, "LONG"); break; } switch (ea) { case EA_DREG: wf_op("\t%s = (%s)CPU->D[(Opcode >> %d) & 7];\n", dest, szc, rsft); break; case EA_AREG: if (current_size == SIZE_BYTE) { wf_op("\t// can't read byte from Ax registers !\n"); wf_op("\tCPU->Status |= C68K_FAULTED;\n"); wf_op("\tCCnt = 0;\n"); wf_op("\tgoto C68k_Exec_Really_End;\n"); } else wf_op("\t%s = (%s)CPU->A[(Opcode >> %d) & 7];\n", dest, szc, rsft); break; /* case EA_DREG: case EA_AREG: wf_op("\t%s = %s(adr);\n", dest, szcf); break; */ case EA_A32: case EA_D8AX: case EA_D8PX: case EA_D16A: case EA_D16P: case EA_A16: case EA_ADEC: case EA_ADEC7: case EA_AIND: case EA_AINC: case EA_AINC7: mem_op("\tREAD_%s_F(adr, %s)\n", sz, dest); break; case EA_IMM: switch (current_size) { case SIZE_BYTE: wf_op("\t%s = FETCH_BYTE;\n", dest); wf_op("\tPC += 2;\n"); break; case SIZE_WORD: wf_op("\t%s = FETCH_WORD;\n", dest); wf_op("\tPC += 2;\n"); break; case SIZE_LONG: wf_op("\t%s = FETCH_LONG;\n", dest); wf_op("\tPC += 4;\n"); break; } break; } } static void _ea_read(u32 ea, u32 rsft) { _ea_read_(ea, rsft, "res"); } static void _ea_read_src(u32 ea, u32 rsft) { _ea_read_(ea, rsft, "src"); } static void _ea_read_dst(u32 ea, u32 rsft) { _ea_read_(ea, rsft, "dst"); } static void _ea_read_sx_(u32 ea, u32 rsft, char dest[4]) { char sz[8]; switch (current_size) { case SIZE_BYTE: strcpy(sz, "BYTE"); break; case SIZE_WORD: strcpy(sz, "WORD"); break; case SIZE_LONG: strcpy(sz, "LONG"); break; } switch (ea) { case EA_DREG: wf_op("\t%s = (s32)(%s)CPU->D[(Opcode >> %d) & 7];\n", dest, szcs, rsft); break; case EA_AREG: if (current_size == SIZE_BYTE) { wf_op("\t// can't read byte from Ax registers !\n"); wf_op("\tCPU->Status |= C68K_FAULTED;\n"); wf_op("\tCCnt = 0;\n"); wf_op("\tgoto C68k_Exec_Really_End;\n"); } else wf_op("\t%s = (s32)(%s)CPU->A[(Opcode >> %d) & 7];\n", dest, szcs, rsft); break; /* case EA_DREG: case EA_AREG: wf_op("\t%s = (s32)(%s(adr));\n", dest, szcsf); break; */ case EA_A32: case EA_D8AX: case EA_D8PX: case EA_D16A: case EA_D16P: case EA_A16: case EA_ADEC: case EA_ADEC7: case EA_AIND: case EA_AINC: case EA_AINC7: mem_op("\tREADSX_%s_F(adr, %s)\n", sz, dest); break; case EA_IMM: switch (current_size) { case SIZE_BYTE: wf_op("\t%s = (s32)(%s(PC)));\n", dest, szcsf); wf_op("\tPC += 2;\n"); break; case SIZE_WORD: wf_op("\t%s = (s32)(%s)FETCH_WORD;\n", dest, szcs); wf_op("\tPC += 2;\n"); break; case SIZE_LONG: wf_op("\t%s = (s32)(%s)FETCH_LONG;\n", dest, szcs); wf_op("\tPC += 4;\n"); break; } break; } } static void _ea_read_sx(u32 ea, u32 rsft) { _ea_read_sx_(ea, rsft, "res"); } static void _ea_read_src_sx(u32 ea, u32 rsft) { _ea_read_sx_(ea, rsft, "src"); } static void _ea_write(u32 ea, u32 rsft) { char sz[8]; switch (current_size) { case SIZE_BYTE: strcpy(sz, "BYTE"); break; case SIZE_WORD: strcpy(sz, "WORD"); break; case SIZE_LONG: strcpy(sz, "LONG"); break; } switch (ea) { case EA_DREG: wf_op("\t%s(&CPU->D[(Opcode >> %d) & 7])) = res;\n", szcf, rsft); break; case EA_AREG: // writes in Ax registers are always 32 bits sized wf_op("\tCPU->A[(Opcode >> %d) & 7] = res;\n", rsft); break; /* case EA_DREG: case EA_AREG: wf_op("\t%s(adr) = res;\n", szcf); break; */ case EA_A32: case EA_D8AX: case EA_D8PX: case EA_D16A: case EA_D16P: case EA_A16: case EA_ADEC: case EA_ADEC7: case EA_AIND: case EA_AINC: case EA_AINC7: mem_op("\tWRITE_%s_F(adr, res)\n", sz); break; } } // misc function ///////////////// static u32 get_current_opcode_base(void) { u32 base; base = current_op->op_base; if (current_op->eam_sft != -1) base += (current_eam & 7) << current_op->eam_sft; if (current_op->reg_sft != -1) base += (current_reg & 7) << current_op->reg_sft; if (current_op->eam2_sft != -1) base += (current_eam2 & 7) << current_op->eam2_sft; if (current_op->reg2_sft != -1) base += (current_reg2 & 7) << current_op->reg2_sft; if (current_op->size_type == 1) base += (current_size - 1) << current_op->size_sft; else if (current_op->size_type == 2) base += (current_size & 3) << current_op->size_sft; return base; } static void start_all(int v) { u32 base; base = get_current_opcode_base(); // generate jump table gen_opjumptable(base); // generate label & declarations start_op(base, v); } static void set_current_size(u32 sz) { current_size = sz; switch(current_size) { case SIZE_BYTE: current_bits_mask = 0xFF; current_sft_mask = 7; strcpy(szc, "u8"); strcpy(szcf, "*(BYTE_OFF + (u8*)"); strcpy(szcs, "s8"); strcpy(szcsf, "*(BYTE_OFF + (s8*)"); break; case SIZE_WORD: current_bits_mask = 0xFFFF; current_sft_mask = 15; strcpy(szc, "u16"); strcpy(szcf, "*(WORD_OFF + (u16*)"); strcpy(szcs, "s16"); strcpy(szcsf, "*(WORD_OFF + (s16*)"); break; case SIZE_LONG: current_bits_mask = 0xFFFFFFFF; current_sft_mask = 31; strcpy(szc, "u32"); strcpy(szcf, "*((u32*)"); strcpy(szcs, "s32"); strcpy(szcsf, "*((s32*)"); break; } } // gen privilege exception (happen when S flag is not set) static void gen_privilege_exception(char *pre) { // swap A7 and USP (because S not set) wf_op("%sres = CPU->USP;\n", pre); wf_op("%sCPU->USP = CPU->A[7];\n", pre); wf_op("%sCPU->A[7] = res;\n", pre); // get vector & add cycle wf_op("%sres = C68K_PRIVILEGE_VIOLATION_EX;\n", pre); adds_CCnt("c68k_exception_cycle_table[res]"); // we will do some mem/io access do_pre_io(); // push PC and SR mem_op("%sPUSH_32_F(PC - CPU->BasePC)\n", pre); mem_op("%sPUSH_16_F(GET_SR)\n", pre); // adjust SR wf_op("%sCPU->flag_S = C68K_SR_S;\n", pre); // fetch new PC mem_op("%sREAD_LONG_F(res * 4, PC)\n", pre); wf_op("%sSET_PC(PC)\n", pre); } static void gen_exception(char *pre, char* exception) { // swap A7 and USP if needed wf_op("%sif (!CPU->flag_S)\n", pre); wf_op("%s{\n", pre); wf_op("%s\tres = CPU->USP;\n", pre); wf_op("%s\tCPU->USP = CPU->A[7];\n", pre); wf_op("%s\tCPU->A[7] = res;\n", pre); wf_op("%s}\n", pre); // get vector & add cycle wf_op("%sres = %s;\n", pre, exception); adds_CCnt("c68k_exception_cycle_table[res]"); // we will do some mem/io access do_pre_io(); // push PC and SR mem_op("%sPUSH_32_F(PC - CPU->BasePC)\n", pre); mem_op("%sPUSH_16_F(GET_SR)\n", pre); // adjust SR wf_op("%sCPU->flag_S = C68K_SR_S;\n", pre); // fetch new PC mem_op("%sREAD_LONG_F(res * 4, PC)\n", pre); wf_op("%sSET_PC(PC)\n", pre); } yabause-0.9.13.1/src/c68k/c68kmac.inc000644 001750 001750 00000025057 12256006174 020713 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2004 Stephane Dallongeville This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // internals core macros ///////////////////////// #define LSL(A, C) ((A) << (C)) #define LSR(A, C) ((A) >> (C)) #define LSR_32(A, C) ((C) < 32 ? (A) >> (C) : 0) #define LSL_32(A, C) ((C) < 32 ? (A) << (C) : 0) #define ROL_8(A, C) (LSL(A, C) | LSR(A, 8-(C))) #define ROL_9(A, C) (LSL(A, C) | LSR(A, 9-(C))) #define ROL_16(A, C) (LSL(A, C) | LSR(A, 16-(C))) #define ROL_17(A, C) (LSL(A, C) | LSR(A, 17-(C))) #define ROL_32(A, C) (LSL_32(A, C) | LSR_32(A, 32-(C))) #define ROL_33(A, C) (LSL_32(A, C) | LSR_32(A, 33-(C))) #define ROR_8(A, C) (LSR(A, C) | LSL(A, 8-(C))) #define ROR_9(A, C) (LSR(A, C) | LSL(A, 9-(C))) #define ROR_16(A, C) (LSR(A, C) | LSL(A, 16-(C))) #define ROR_17(A, C) (LSR(A, C) | LSL(A, 17-(C))) #define ROR_32(A, C) (LSR_32(A, C) | LSL_32(A, 32-(C))) #define ROR_33(A, C) (LSR_32(A, C) | LSL_32(A, 33-(C))) #ifdef TRACE_WITH_Q68 extern void TRACE(int,c68k_struc*,int,int); #else # define TRACE(a,b,c,d) /*nothing*/ #endif #ifndef C68K_NO_JUMP_TABLE #define NEXT \ PRE_IO \ Opcode = FETCH_WORD; \ PC += 2; \ TRACE(PC, CPU, Opcode, CCnt); \ goto *JumpTable[Opcode]; #else #define NEXT \ PRE_IO \ Opcode = FETCH_WORD; \ PC += 2; \ TRACE(PC, CPU, Opcode, CCnt); \ goto SwitchTable; #endif #define RET(A) \ CCnt -= (A); \ if (CCnt <= 0) goto C68k_Exec_End; \ NEXT #define SET_PC(A) \ CPU->BasePC = CPU->Fetch[((A) >> C68K_FETCH_SFT) & C68K_FETCH_MASK]; \ CPU->BasePC -= (A) & 0xFF000000; \ PC = (A) + CPU->BasePC; #define PRE_IO \ CPU->CycleIO = CCnt; #define POST_IO \ CCnt = CPU->CycleIO; #define READ_BYTE_F(A, D) \ D = CPU->Read_Byte(A) & 0xFF; #define READ_WORD_F(A, D) \ D = CPU->Read_Word(A) & 0xFFFF; #ifdef C68K_BIG_ENDIAN #define READ_LONG_F(A, D) \ D = CPU->Read_Word((A)) << 16; \ D |= CPU->Read_Word((A) + 2) & 0xFFFF; #define READ_LONG_DEC_F(A, D) \ D = CPU->Read_Word((A) + 2) & 0xFFFF; \ D |= CPU->Read_Word((A)) << 16; #else #define READ_LONG_F(A, D) \ D = CPU->Read_Word((A)) << 16; \ D |= CPU->Read_Word((A) + 2) & 0xFFFF; #define READ_LONG_DEC_F(A, D) \ D = CPU->Read_Word((A) + 2) & 0xFFFF; \ D |= CPU->Read_Word((A)) << 16; #endif #define READSX_BYTE_F(A, D) \ D = (s32)(s8)CPU->Read_Byte(A); #define READSX_WORD_F(A, D) \ D = (s32)(s16)CPU->Read_Word(A); #ifdef C68K_BIG_ENDIAN #define READSX_LONG_F(A, D) \ D = CPU->Read_Word((A)) << 16; \ D |= CPU->Read_Word((A) + 2) & 0xFFFF; #define READSX_LONG_DEC_F(A, D) \ D = CPU->Read_Word((A) + 2) & 0xFFFF; \ D |= CPU->Read_Word((A)) << 16; #else #define READSX_LONG_F(A, D) \ D = CPU->Read_Word((A)) << 16; \ D |= CPU->Read_Word((A) + 2) & 0xFFFF; #define READSX_LONG_DEC_F(A, D) \ D = CPU->Read_Word((A) + 2) & 0xFFFF; \ D |= CPU->Read_Word((A)) << 16; #endif #define WRITE_BYTE_F(A, D) \ CPU->Write_Byte(A, D); #define WRITE_WORD_F(A, D) \ CPU->Write_Word(A, D); #ifdef C68K_BIG_ENDIAN #define WRITE_LONG_F(A, D) \ CPU->Write_Word((A), (D) >> 16); \ CPU->Write_Word((A) + 2, (D) & 0xFFFF); #define WRITE_LONG_DEC_F(A, D) \ CPU->Write_Word((A) + 2, (D) & 0xFFFF); \ CPU->Write_Word((A), (D) >> 16); #else #define WRITE_LONG_F(A, D) \ CPU->Write_Word((A), (D) >> 16); \ CPU->Write_Word((A) + 2, (D) & 0xFFFF); #define WRITE_LONG_DEC_F(A, D) \ CPU->Write_Word((A) + 2, (D) & 0xFFFF); \ CPU->Write_Word((A), (D) >> 16); #endif #define PUSH_16_F(D) \ CPU->Write_Word(CPU->A[7] -= 2, D); \ #define POP_16_F(D) \ D = (u16)CPU->Read_Word(CPU->A[7]); \ CPU->A[7] += 2; #ifdef C68K_BIG_ENDIAN #define PUSH_32_F(D) \ CPU->A[7] -= 4; \ CPU->Write_Word(CPU->A[7] + 2, (D) & 0xFFFF); \ CPU->Write_Word(CPU->A[7], (D) >> 16); #define POP_32_F(D) \ D = CPU->Read_Word(CPU->A[7]) << 16; \ D |= CPU->Read_Word(CPU->A[7] + 2) & 0xFFFF; \ CPU->A[7] += 4; #else #define PUSH_32_F(D) \ CPU->A[7] -= 4; \ CPU->Write_Word(CPU->A[7] + 2, (D) & 0xFFFF); \ CPU->Write_Word(CPU->A[7], (D) >> 16); #define POP_32_F(D) \ D = CPU->Read_Word(CPU->A[7]) << 16; \ D |= CPU->Read_Word(CPU->A[7] + 2) & 0xFFFF; \ CPU->A[7] += 4; #endif #define FETCH_BYTE \ ((*(u16*)PC) & 0xFF) #define FETCH_WORD \ (*(u16*)PC) #define FETCH_LONG \ (*(u32*)PC) #define DECODE_EXT_WORD \ { \ u32 ext; \ \ ext = (*(u16*)PC); \ PC += 2; \ \ adr += (s32)((s8)(ext)); \ if (ext & 0x0800) adr += (s32) CPU->D[ext >> 12]; \ else adr += (s32)((s16)(CPU->D[ext >> 12])); \ } #ifndef C68K_BIG_ENDIAN #ifdef C68K_BYTE_SWAP_OPT #undef FETCH_LONG #define FETCH_LONG \ ((((u32)(*(u16*)PC)) << 16) | ((u32)(*(u16*)(PC + 2)))) // ((((u32)(*(u8*)(PC + 2))) | (((u32)(*(u8*)(PC + 3))) << 8) | (((u32)(*(u8*)PC)) << 16) | (((u32)(*(u8*)(PC + 1))) << 24))) #else #undef FETCH_BYTE #define FETCH_BYTE \ (*(u16*)PC) >> 8) #undef FETCH_WORD #define FETCH_WORD \ ((((u16)(*(u8*)PC)) << 8) | ((u16)(*(u8*)(PC + 1)))) // ((((u16)(*(u8*)(PC + 1))) | (((u16)(*(u8*)PC)) << 8))) #undef FETCH_LONG #define FETCH_LONG \ ((((u32)(*(u8*)PC)) << 24) | (((u32)(*(u8*)(PC + 1))) << 16) | (((u32)(*(u8*)(PC + 2))) << 8) | ((u32)(*(u8*)(PC + 3)))) // ((((u32)(*(u8*)(PC + 3))) | (((u32)(*(u8*)(PC + 2))) << 8) | (((u32)(*(u8*)(PC + 1))) << 16) | (((u32)(*(u8*)PC)) << 24))) #undef DECODE_EXT_WORD #define DECODE_EXT_WORD \ { \ u32 ext; \ \ ext = (*(u16*)PC); \ PC += 2; \ \ adr += (s32)((s8)(ext >> 8)); \ if (ext & 0x0008) adr += (s32) CPU->D[(ext >> 4) & 0x000F]; \ else adr += (s32)((s16)(CPU->D[(ext >> 4) & 0x000F])); \ } #endif #endif #define GET_CCR \ (((CPU->flag_C >> (C68K_SR_C_SFT - 0)) & 1) | \ ((CPU->flag_V >> (C68K_SR_V_SFT - 1)) & 2) | \ (((!CPU->flag_notZ) & 1) << 2) | \ ((CPU->flag_N >> (C68K_SR_N_SFT - 3)) & 8) | \ ((CPU->flag_X >> (C68K_SR_X_SFT - 4)) & 0x10)) #define GET_SR \ ((CPU->flag_S << 0) | \ (CPU->flag_I << 8) | \ GET_CCR) #define SET_CCR(A) \ CPU->flag_C = (A) << (C68K_SR_C_SFT - 0); \ CPU->flag_V = (A) << (C68K_SR_V_SFT - 1); \ CPU->flag_notZ = ~(A) & 4; \ CPU->flag_N = (A) << (C68K_SR_N_SFT - 3); \ CPU->flag_X = (A) << (C68K_SR_X_SFT - 4); #define SET_SR(A) \ SET_CCR(A) \ CPU->flag_I = ((A) >> 8) & 7; \ CPU->flag_S = (A) & C68K_SR_S; #define CHECK_INT \ { \ s32 line, vect; \ \ line = CPU->IRQLine; \ \ if ((line == 7) || (line > CPU->flag_I)) \ { \ /* get vector */ \ CPU->IRQLine = 0; \ vect = CPU->Interrupt_CallBack(line); \ if (vect == C68K_INT_ACK_AUTOVECTOR) \ vect = C68K_INTERRUPT_AUTOVECTOR_EX + (line & 7); \ \ /* adjust CCnt */ \ CCnt -= c68k_exception_cycle_table[vect]; \ \ /* swap A7 and USP */ \ if (!CPU->flag_S) \ { \ u32 tmpSP; \ \ tmpSP = CPU->USP; \ CPU->USP = CPU->A[7]; \ CPU->A[7] = tmpSP; \ } \ \ PRE_IO \ \ /* push PC and SR */ \ PUSH_32_F(PC - CPU->BasePC) \ PUSH_16_F(GET_SR) \ \ /* adjust SR */ \ CPU->flag_S = C68K_SR_S; \ CPU->flag_I = line; \ \ /* fetch new PC */ \ READ_LONG_F(vect * 4, PC) \ SET_PC(PC) \ \ POST_IO \ } \ } yabause-0.9.13.1/src/c68k/c68k.h000644 001750 001750 00000013500 12256006174 017676 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2004 Stephane Dallongeville Copyright 2004 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /********************************************************************************* * C68K.H : * * C68K include file * ********************************************************************************/ #ifndef _C68K_H_ #define _C68K_H_ #ifdef __cplusplus extern "C" { #endif #include "../core.h" // setting /////////// //#define NEOCD_HLE //#define C68K_GEN #define C68K_BYTE_SWAP_OPT #ifdef WORDS_BIGENDIAN #define C68K_BIG_ENDIAN #endif #ifdef C68K_BIG_ENDIAN #define BYTE_OFF 3 #define WORD_OFF 1 #else #define BYTE_OFF 0 #define WORD_OFF 0 #endif //#define C68K_NO_JUMP_TABLE //#define C68K_DEBUG #define C68K_TAS_CAN_SET_MEMORY //#define C68K_CONST_JUMP_TABLE //#define C68K_AUTOVECTOR_CALLBACK // 68K core types definitions ////////////////////////////// #define C68K_FETCH_BITS 8 // [4-12] default = 8 #define C68K_ADR_BITS 24 #define C68K_FETCH_SFT (C68K_ADR_BITS - C68K_FETCH_BITS) #define C68K_FETCH_BANK (1 << C68K_FETCH_BITS) #define C68K_FETCH_MASK (C68K_FETCH_BANK - 1) #define C68K_SR_C_SFT 8 #define C68K_SR_V_SFT 7 #define C68K_SR_Z_SFT 0 #define C68K_SR_N_SFT 7 #define C68K_SR_X_SFT 8 #define C68K_SR_S_SFT 13 #define C68K_SR_C (1 << C68K_SR_C_SFT) #define C68K_SR_V (1 << C68K_SR_V_SFT) #define C68K_SR_Z 0 #define C68K_SR_N (1 << C68K_SR_N_SFT) #define C68K_SR_X (1 << C68K_SR_X_SFT) #define C68K_SR_S (1 << C68K_SR_S_SFT) #define C68K_CCR_MASK 0x1F #define C68K_SR_MASK (0x2700 | C68K_CCR_MASK) // exception defines taken from musashi core #define C68K_RESET_EX 1 #define C68K_BUS_ERROR_EX 2 #define C68K_ADDRESS_ERROR_EX 3 #define C68K_ILLEGAL_INSTRUCTION_EX 4 #define C68K_ZERO_DIVIDE_EX 5 #define C68K_CHK_EX 6 #define C68K_TRAPV_EX 7 #define C68K_PRIVILEGE_VIOLATION_EX 8 #define C68K_TRACE_EX 9 #define C68K_1010_EX 10 #define C68K_1111_EX 11 #define C68K_FORMAT_ERROR_EX 14 #define C68K_UNINITIALIZED_INTERRUPT_EX 15 #define C68K_SPURIOUS_INTERRUPT_EX 24 #define C68K_INTERRUPT_AUTOVECTOR_EX 24 #define C68K_TRAP_BASE_EX 32 #define C68K_INT_ACK_AUTOVECTOR -1 #define C68K_RUNNING 0x01 #define C68K_HALTED 0x02 #define C68K_WAITING 0x04 #define C68K_DISABLE 0x10 #define C68K_FAULTED 0x40 typedef u32 FASTCALL C68K_READ(const u32 adr); typedef void FASTCALL C68K_WRITE(const u32 adr, u32 data); typedef s32 FASTCALL C68K_INT_CALLBACK(s32 level); typedef void FASTCALL C68K_RESET_CALLBACK(void); typedef struct { u32 D[8]; // 32 bytes aligned u32 A[8]; // 16 bytes aligned u32 flag_C; // 32 bytes aligned u32 flag_V; u32 flag_notZ; u32 flag_N; u32 flag_X; // 16 bytes aligned u32 flag_I; u32 flag_S; u32 USP; pointer PC; // 32 bytes aligned pointer BasePC; u32 Status; s32 IRQLine; s32 CycleToDo; // 16 bytes aligned s32 CycleIO; s32 CycleSup; u32 dirty1; C68K_READ *Read_Byte; // 32 bytes aligned C68K_READ *Read_Word; C68K_WRITE *Write_Byte; C68K_WRITE *Write_Word; C68K_INT_CALLBACK *Interrupt_CallBack; // 16 bytes aligned C68K_RESET_CALLBACK *Reset_CallBack; pointer Fetch[C68K_FETCH_BANK]; // 32 bytes aligned } c68k_struc; // 68K core var declaration //////////////////////////// extern c68k_struc C68K; // 68K core function declaration ///////////////////////////////// void C68k_Init(c68k_struc *cpu, C68K_INT_CALLBACK *int_cb); s32 FASTCALL C68k_Reset(c68k_struc *cpu); // if < 0 --> error (cpu state returned) // if >= 0 --> number of extras cycles done s32 FASTCALL C68k_Exec(c68k_struc *cpu, s32 cycle); void FASTCALL C68k_Set_IRQ(c68k_struc *cpu, s32 level); s32 FASTCALL C68k_Get_CycleToDo(c68k_struc *cpu); s32 FASTCALL C68k_Get_CycleRemaining(c68k_struc *cpu); s32 FASTCALL C68k_Get_CycleDone(c68k_struc *cpu); void FASTCALL C68k_Release_Cycle(c68k_struc *cpu); void FASTCALL C68k_Add_Cycle(c68k_struc *cpu, s32 cycle); void C68k_Set_Fetch(c68k_struc *cpu, u32 low_adr, u32 high_adr, pointer fetch_adr); void C68k_Set_ReadB(c68k_struc *cpu, C68K_READ *Func); void C68k_Set_ReadW(c68k_struc *cpu, C68K_READ *Func); void C68k_Set_WriteB(c68k_struc *cpu, C68K_WRITE *Func); void C68k_Set_WriteW(c68k_struc *cpu, C68K_WRITE *Func); u32 C68k_Get_DReg(c68k_struc *cpu, u32 num); u32 C68k_Get_AReg(c68k_struc *cpu, u32 num); u32 C68k_Get_PC(c68k_struc *cpu); u32 C68k_Get_SR(c68k_struc *cpu); u32 C68k_Get_USP(c68k_struc *cpu); u32 C68k_Get_MSP(c68k_struc *cpu); void C68k_Set_DReg(c68k_struc *cpu, u32 num, u32 val); void C68k_Set_AReg(c68k_struc *cpu, u32 num, u32 val); void C68k_Set_PC(c68k_struc *cpu, u32 val); void C68k_Set_SR(c68k_struc *cpu, u32 val); void C68k_Set_USP(c68k_struc *cpu, u32 val); void C68k_Set_MSP(c68k_struc *cpu, u32 val); #ifdef __cplusplus } #endif #endif // _C68K_H_ yabause-0.9.13.1/src/c68k/gen68k.h000644 001750 001750 00000003141 12256006174 020225 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2004 Stephane Dallongeville Copyright 2004 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /********************************************************************************* * GEN68K.H : * * C68K generator include file * ********************************************************************************/ #ifndef _GEN68K_H_ #define _GEN68K_H_ #ifdef __cplusplus extern "C" { #endif // setting /////////// // structure definition //////////////////////// typedef struct { u32 name; u32 mask; u32 match; } c68k_ea_info_struc; typedef struct __c68k_op_info_struc { s8 op_name[8 + 1]; u16 op_base; u16 op_mask; s8 size_type; s8 size_sft; s8 eam_sft; s8 reg_sft; s8 eam2_sft; s8 reg2_sft; s8 ea_supported[12 + 1]; s8 ea2_supported[12 + 1]; void (*genfunc)(void); } c68k_op_info_struc; #ifdef __cplusplus } #endif #endif // _GEN68K_H_ yabause-0.9.13.1/src/c68k/CMakeLists.txt000644 001750 001750 00000001717 12256006174 021521 0ustar00guillaumeguillaume000000 000000 project(gen68k) cmake_minimum_required(VERSION 2.6) include(CheckCSourceCompiles) # variadic macros check_c_source_compiles("#define MACRO(...) puts(__VA_ARGS__) int main(int argc, char ** argv) { MACRO(\"foo\"); }" VARIADIC_MACROS_OK) if (VARIADIC_MACROS_OK) add_definitions(-DHAVE_C99_VARIADIC_MACROS=1) endif (VARIADIC_MACROS_OK) set(gen68k_SOURCES c68kexec.c c68k.c gen68k.c) add_definitions(-DC68K_GEN) if (MSVC) add_definitions(-DC68K_NO_JUMP_TABLE) endif (MSVC) add_executable(gen68k ${gen68k_SOURCES}) execute_process(COMMAND ${CMAKE_CURRENT_BINARY_DIR}/gen68k) set(GEN68K_INC c68k_ini.inc c68k_op0.inc c68k_op1.inc c68k_op2.inc c68k_op3.inc c68k_op4.inc c68k_op5.inc c68k_op6.inc c68k_op7.inc c68k_op8.inc c68k_op9.inc c68k_opA.inc c68k_opB.inc c68k_opC.inc c68k_opD.inc c68k_opE.inc c68k_opF.inc) add_custom_command(OUTPUT ${GEN68K_INC} COMMAND gen68k DEPENDS gen68k) add_custom_target(c68kinc ALL DEPENDS ${GEN68K_INC}) yabause-0.9.13.1/src/c68k/c68kexec.c000644 001750 001750 00000017620 12256006174 020545 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2004 Stephane Dallongeville This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../core.h" #include "c68k.h" // #define TRACE_WITH_Q68 // Define to use Q68 tracing code to trace insns // (requires Q68 built in, of course) #ifdef NEOCD_HLE void cdrom_load_files(void); void neogeo_cdda_control(void); void neogeo_prio_switch(void); void neogeo_upload(void); #endif // exception cycle table (taken from musashi core) static const s32 c68k_exception_cycle_table[256] = { 4, // 0: Reset - Initial Stack Pointer 4, // 1: Reset - Initial Program Counter 50, // 2: Bus Error 50, // 3: Address Error 34, // 4: Illegal Instruction 38, // 5: Divide by Zero 40, // 6: CHK 34, // 7: TRAPV 34, // 8: Privilege Violation 34, // 9: Trace 4, // 10: 4, // 11: 4, // 12: RESERVED 4, // 13: Coprocessor Protocol Violation 4, // 14: Format Error 44, // 15: Uninitialized Interrupt 4, // 16: RESERVED 4, // 17: RESERVED 4, // 18: RESERVED 4, // 19: RESERVED 4, // 20: RESERVED 4, // 21: RESERVED 4, // 22: RESERVED 4, // 23: RESERVED 44, // 24: Spurious Interrupt 44, // 25: Level 1 Interrupt Autovector 44, // 26: Level 2 Interrupt Autovector 44, // 27: Level 3 Interrupt Autovector 44, // 28: Level 4 Interrupt Autovector 44, // 29: Level 5 Interrupt Autovector 44, // 30: Level 6 Interrupt Autovector 44, // 31: Level 7 Interrupt Autovector 34, // 32: TRAP #0 34, // 33: TRAP #1 34, // 34: TRAP #2 34, // 35: TRAP #3 34, // 36: TRAP #4 34, // 37: TRAP #5 34, // 38: TRAP #6 34, // 39: TRAP #7 34, // 40: TRAP #8 34, // 41: TRAP #9 34, // 42: TRAP #10 34, // 43: TRAP #11 34, // 44: TRAP #12 34, // 45: TRAP #13 34, // 46: TRAP #14 34, // 47: TRAP #15 4, // 48: FP Branch or Set on Unknown Condition 4, // 49: FP Inexact Result 4, // 50: FP Divide by Zero 4, // 51: FP Underflow 4, // 52: FP Operand Error 4, // 53: FP Overflow 4, // 54: FP Signaling NAN 4, // 55: FP Unimplemented Data Type 4, // 56: MMU Configuration Error 4, // 57: MMU Illegal Operation Error 4, // 58: MMU Access Level Violation Error 4, // 59: RESERVED 4, // 60: RESERVED 4, // 61: RESERVED 4, // 62: RESERVED 4, // 63: RESERVED // 64-255: User Defined 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 }; // global variable /////////////////// #ifndef C68K_GEN #ifndef C68K_NO_JUMP_TABLE #ifndef C68K_CONST_JUMP_TABLE static void *JumpTable[0x10000]; #endif #endif static u32 C68k_Initialised = 0; #endif // C68K_GEN #ifdef NEOCD_HLE extern int img_display; #endif // include macro file ////////////////////// #include "c68kmac.inc" #ifndef C68K_GEN # ifdef TRACE_WITH_Q68 #include "../q68/q68.h" static c68k_struc *TRACE_CPU; static uint32_t readw(uint32_t address) { return TRACE_CPU->Read_Word(address); } /* Make our own version of the structure to avoid the overhead of dozens of * function calls every instruction */ static struct { u32 D[8], A[8], PC, SR, USP, SSP, dummy[7]; void *readb, *readw, *writeb, *writew; } state = {.readw = readw}; void TRACE(int PC,c68k_struc *CPU,int Opcode,int CCnt) { static FILE *f; if (!f) f = fopen("c68k.log", "w"); TRACE_CPU = CPU; memcpy(state.D, CPU->D, 16*4); state.PC = PC - 2 - CPU->BasePC; state.SR = GET_SR; if (f) q68_trace((Q68State *)&state, f, CPU->CycleToDo - CCnt, CPU->CycleToDo); } # endif // TRACE_WITH_Q68 #endif // !C68K_GEN // main exec function ////////////////////// s32 FASTCALL C68k_Exec(c68k_struc *cpu, s32 cycle) { #ifndef C68K_GEN #if 0 register c68k_struc *CPU asm ("ebx"); register pointer PC asm ("esi"); register s32 CCnt asm ("edi"); // register u32 Opcode asm ("edi"); // c68k_struc *CPU; // u32 PC; // s32 CCnt; u32 Opcode; #else // register c68k_struc *CPU asm ("r10"); // register u32 PC asm ("r11"); // register s32 CCnt asm ("r12"); // register u32 Opcode asm ("r13"); c68k_struc *CPU; pointer PC; s32 CCnt; u32 Opcode; #endif #endif #ifndef C68K_GEN #ifndef C68K_NO_JUMP_TABLE #ifdef C68K_CONST_JUMP_TABLE #include "c68k_ini.inc" #endif #else C68k_Initialised = 1; #endif CPU = cpu; PC = CPU->PC; if (CPU->Status & (C68K_RUNNING | C68K_DISABLE | C68K_FAULTED)) { #ifndef C68K_NO_JUMP_TABLE #ifndef C68K_CONST_JUMP_TABLE if (!C68k_Initialised) goto C68k_Init; #endif #endif return (CPU->Status | 0x80000000); } if (cycle <= 0) return -cycle; CPU->CycleToDo = CCnt = cycle; #ifndef C68K_DEBUG CHECK_INT #else { s32 line, vect; line = CPU->IRQLine; if ((line == 7) || (line > CPU->flag_I)) { PRE_IO /* get vector */ CPU->IRQLine = 0; vect = CPU->Interrupt_CallBack(line); if (vect == C68K_INT_ACK_AUTOVECTOR) vect = C68K_INTERRUPT_AUTOVECTOR_EX + (line & 7); /* adjust CCnt */ CCnt -= c68k_exception_cycle_table[vect]; /* swap A7 and USP */ if (!CPU->flag_S) { u32 tmpSP; tmpSP = CPU->USP; CPU->USP = CPU->A[7]; CPU->A[7] = tmpSP; } /* push PC and SR */ PUSH_32_F(PC - CPU->BasePC) PUSH_16_F(GET_SR) /* adjust SR */ CPU->flag_S = C68K_SR_S; CPU->flag_I = line; /* fetch new PC */ READ_LONG_F(vect * 4, PC) SET_PC(PC) POST_IO } } #endif if (CPU->Status & (C68K_HALTED | C68K_WAITING)) return CPU->CycleToDo; CPU->CycleSup = 0; CPU->Status |= C68K_RUNNING; #ifndef C68K_DEBUG NEXT #else #ifdef C68K_NO_JUMP_TABLE NEXT #else Opcode = FETCH_WORD; PC += 2; goto *JumpTable[Opcode]; #endif #endif #ifdef C68K_NO_JUMP_TABLE SwitchTable: switch(Opcode) { #endif #include "c68k_op0.inc" #include "c68k_op1.inc" #include "c68k_op2.inc" #include "c68k_op3.inc" #include "c68k_op4.inc" #include "c68k_op5.inc" #include "c68k_op6.inc" #include "c68k_op7.inc" #include "c68k_op8.inc" #include "c68k_op9.inc" #include "c68k_opA.inc" #include "c68k_opB.inc" #include "c68k_opC.inc" #include "c68k_opD.inc" #include "c68k_opE.inc" #include "c68k_opF.inc" #ifdef C68K_NO_JUMP_TABLE } #endif C68k_Exec_End: CHECK_INT if ((CCnt += CPU->CycleSup) > 0) { CPU->CycleSup = 0; NEXT; } C68k_Exec_Really_End: CPU->Status &= ~C68K_RUNNING; CPU->PC = PC; return (CPU->CycleToDo - CCnt); #ifndef C68K_CONST_JUMP_TABLE #ifndef C68K_NO_JUMP_TABLE C68k_Init: { u32 i, j; #include "c68k_ini.inc" C68k_Initialised = 1; } return 0; #endif #endif #else return 0; #endif } yabause-0.9.13.1/src/c68k/gen68k.c000644 001750 001750 00000302671 12256006174 020232 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2004 Stephane Dallongeville This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /********************************************************************************* * GEN68K.C : * * C68K generator source file * ********************************************************************************/ #include #ifdef __WIN32__ #include #endif #include "../core.h" #include "c68k.h" #include "gen68k.h" #ifdef C68K_GEN #include "gen68k.inc" // to do : // need accurate cycles calculations in MUL and DIV instruction // some bugs to fix // opcode generation function ////////////////////////////// static void GenLogicI(char op) { // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES | GEN_SRC); else start_all(GEN_ADR | GEN_RES | GEN_SRC); if (current_ea != EA_DREG) current_cycle += 4; switch (current_size) { case SIZE_BYTE: wf_op("\tsrc = FETCH_BYTE;\n"); wf_op("\tPC += 2;\n"); break; case SIZE_WORD: wf_op("\tsrc = FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); break; case SIZE_LONG: wf_op("\tsrc = FETCH_LONG;\n"); wf_op("\tPC += 4;\n"); current_cycle += 8; break; } // read _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // op wf_op("\tres %c= src;\n", op); // flag calculation set_logic_flag(); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenLogicICCR(char op) { // generate jump table & opcode declaration start_all(GEN_RES); wf_op("\tres = FETCH_BYTE & C68K_CCR_MASK;\n"); wf_op("\tPC += 2;\n"); wf_op("\tres %c= GET_CCR;\n", op); wf_op("\tSET_CCR(res)\n"); terminate_op(20); } static void GenLogicISR(char op) { // generate jump table & opcode declaration start_all(GEN_RES); wf_op("\tif (CPU->flag_S)\n"); wf_op("\t{\n"); wf_op("\t\tres = FETCH_WORD & C68K_SR_MASK;\n"); wf_op("\t\tPC += 2;\n"); wf_op("\t\tres %c= GET_SR;\n", op); wf_op("\t\tSET_SR(res)\n"); if (op != '|') { wf_op("\t\tif (!CPU->flag_S)\n"); wf_op("\t\t{\n"); wf_op("\t\t\tres = CPU->A[7];\n"); wf_op("\t\t\tCPU->A[7] = CPU->USP;\n"); wf_op("\t\t\tCPU->USP = res;\n"); wf_op("\t\t}\n"); } wf_op("\t}\n"); wf_op("\telse\n"); wf_op("\t{\n"); gen_privilege_exception("\t\t"); wf_op("\t}\n"); // check for interrupt fterminate_op(20); } static void GenORI() { GenLogicI('|'); } static void GenORICCR() { GenLogicICCR('|'); } static void GenORISR() { GenLogicISR('|'); } static void GenANDI() { GenLogicI('&'); } static void GenANDICCR() { GenLogicICCR('&'); } static void GenANDISR() { GenLogicISR('&'); } static void GenEORI() { GenLogicI('^'); } static void GenEORICCR() { GenLogicICCR('^'); } static void GenEORISR() { GenLogicISR('^'); } static void GenArithI(char op) { // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES | GEN_SRC | GEN_DST); else start_all(GEN_ALL); if ((op != ' ') && (current_ea != EA_DREG)) current_cycle += 4; switch (current_size) { case SIZE_BYTE: wf_op("\tsrc = FETCH_BYTE;\n"); wf_op("\tPC += 2;\n"); break; case SIZE_WORD: wf_op("\tsrc = FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); break; case SIZE_LONG: wf_op("\tsrc = FETCH_LONG;\n"); wf_op("\tPC += 4;\n"); if (op == ' ') { if (current_ea == EA_DREG) current_cycle += 6; else current_cycle += 4; } else current_cycle += 8; break; } // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_dst(current_ea, current_op->reg_sft); if (op == ' ') { // op wf_op("\tres = dst - src;\n"); // flag calculation set_cmp_flag(); } else { // op wf_op("\tres = dst %c src;\n", op); // flag calculation if (op == '+') set_add_flag(); else set_sub_flag(); // write _ea_write(current_ea, current_op->reg_sft); } terminate_op(8); } static void GenSUBI() { GenArithI('-'); } static void GenADDI() { GenArithI('+'); } static void GenCMPI() { GenArithI(' '); } static void GenBitsOp(char op, u32 dyn) { // generate jump table & opcode declaration if (dyn) current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES | GEN_SRC); else start_all(GEN_ADR | GEN_RES | GEN_SRC); if (current_ea == EA_DREG) { set_current_size(SIZE_LONG); if ((op == 'c') || (op == ' ')) current_cycle += 2; } else set_current_size(SIZE_BYTE); // get shift value in src if (dyn) { _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_src(current_ea2, current_op->reg2_sft); } else { wf_op("\tsrc = FETCH_BYTE;\n"); wf_op("\tPC += 2;\n"); current_cycle += 4; } wf_op("\tsrc = 1 << (src & %d);\n", current_sft_mask); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // flag calculation wf_op("\tCPU->flag_notZ = res & src;\n"); // op switch(op) { case 'c': wf_op("\tres &= ~src;\n"); break; case 'g': wf_op("\tres ^= src;\n"); break; case 's': wf_op("\tres |= src;\n"); break; } // write if (op != ' ') { _ea_write(current_ea, current_op->reg_sft); current_cycle += 4; } terminate_op(4); } static void GenBTSTn() { GenBitsOp(' ', 0); } static void GenBCHGn() { GenBitsOp('g', 0); } static void GenBCLRn() { GenBitsOp('c', 0); } static void GenBSETn() { GenBitsOp('s', 0); } static void GenBTST() { GenBitsOp(' ', 1); } static void GenBCHG() { GenBitsOp('g', 1); } static void GenBCLR() { GenBitsOp('c', 1); } static void GenBSET() { GenBitsOp('s', 1); } static void GenMOVEPWaD() { // generate jump table & opcode declaration current_ea = EA_D16A; current_ea2 = EA_DREG; start_all(GEN_ADR | GEN_RES | GEN_SRC); // read set_current_size(SIZE_BYTE); _ea_calc(current_ea, current_op->reg_sft); mem_op("\tREAD_BYTE_F(adr + 0, res)\n"); mem_op("\tREAD_BYTE_F(adr + 2, src)\n"); // write wf_op("\t*(u16*)(&CPU->D[(Opcode >> %d) & 7]) = (res << 8) | src;\n", current_op->reg2_sft); terminate_op(16); } static void GenMOVEPLaD() { // generate jump table & opcode declaration current_ea = EA_D16A; current_ea2 = EA_DREG; start_all(GEN_ADR | GEN_RES | GEN_SRC); // read set_current_size(SIZE_BYTE); _ea_calc(EA_D16A, current_op->reg_sft); mem_op("\tREAD_BYTE_F(adr, res)\n"); wf_op("\tres <<= 24;\n"); wf_op("\tadr += 2;\n"); mem_op("\tREAD_BYTE_F(adr, src)\n"); wf_op("\tres |= src << 16;\n"); wf_op("\tadr += 2;\n"); mem_op("\tREAD_BYTE_F(adr, src)\n"); wf_op("\tres |= src << 8;\n"); wf_op("\tadr += 2;\n"); mem_op("\tREAD_BYTE_F(adr, src)\n"); // write wf_op("\tCPU->D[(Opcode >> %d) & 7] = res | src;\n", current_op->reg2_sft); terminate_op(24); } static void GenMOVEPWDa() { // generate jump table & opcode declaration current_ea = EA_D16A; current_ea2 = EA_DREG; start_all(GEN_ADR | GEN_RES); // read set_current_size(SIZE_LONG); _ea_calc(current_ea2, current_op->reg2_sft); _ea_read(current_ea2, current_op->reg2_sft); // write set_current_size(SIZE_BYTE); _ea_calc(current_ea, current_op->reg_sft); mem_op("\tWRITE_BYTE_F(adr + 0, res >> 8)\n"); mem_op("\tWRITE_BYTE_F(adr + 2, res >> 0)\n"); terminate_op(16); } static void GenMOVEPLDa() { // generate jump table & opcode declaration current_ea = EA_D16A; current_ea2 = EA_DREG; start_all(GEN_ADR | GEN_RES); // read set_current_size(SIZE_LONG); _ea_calc(current_ea2, current_op->reg2_sft); _ea_read(current_ea2, current_op->reg2_sft); // write set_current_size(SIZE_BYTE); _ea_calc(current_ea, current_op->reg_sft); mem_op("\tWRITE_BYTE_F(adr, res >> 24)\n"); wf_op("\tadr += 2;\n"); mem_op("\tWRITE_BYTE_F(adr, res >> 16)\n"); wf_op("\tadr += 2;\n"); mem_op("\tWRITE_BYTE_F(adr, res >> 8)\n"); wf_op("\tadr += 2;\n"); mem_op("\tWRITE_BYTE_F(adr, res >> 0)\n"); terminate_op(24); } static void GenMOVE(u32 size) { set_current_size(size); // generate jump table & opcode declaration if (((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) && ((current_ea2 == EA_AREG) || (current_ea2 == EA_DREG) || (current_ea2 == EA_IMM))) start_all(GEN_RES); else start_all(GEN_ADR | GEN_RES); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // flag calculation set_logic_flag(); if ((current_ea2 == EA_ADEC) || (current_ea2 == EA_ADEC7)) current_cycle -= 2; // write _ea_calc(current_ea2, current_op->reg2_sft); _ea_write(current_ea2, current_op->reg2_sft); terminate_op(4); } static void GenMOVEB() { GenMOVE(SIZE_BYTE); } static void GenMOVEW() { GenMOVE(SIZE_WORD); } static void GenMOVEL() { GenMOVE(SIZE_LONG); } static void GenMOVEA(u32 size) { set_current_size(size); // generate jump table & opcode declaration current_ea2 = EA_AREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES); else start_all(GEN_ADR | GEN_RES); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_sx(current_ea, current_op->reg_sft); // write (dst = Ax) _ea_calc(current_ea2, current_op->reg2_sft); _ea_write(current_ea2, current_op->reg2_sft); terminate_op(4); } static void GenMOVEAW() { GenMOVEA(SIZE_WORD); } static void GenMOVEAL() { GenMOVEA(SIZE_LONG); } static void GenMOVEQ() { u32 base = get_current_opcode_base(); // generate jump table current_ea = EA_DREG; gen_opjumptable_ext(base, 0x00, 0xFF, 1, base); // generate label & declarations start_op(base, GEN_RES); // read set_current_size(SIZE_BYTE); wf_op("\tres = (s32)(s8)Opcode;\n"); // fast flag calculation for moveQ wf_op("\tCPU->flag_C = CPU->flag_V = 0;\n"); wf_op("\tCPU->flag_N = CPU->flag_notZ = res;\n"); // write set_current_size(SIZE_LONG); _ea_calc(current_ea, current_op->reg_sft); _ea_write(current_ea, current_op->reg_sft); terminate_op(4); } static void GenSingle(char op) { // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) { if (op == 'c') start_all(GEN_RES); else start_all(GEN_RES | GEN_SRC); } else { if (op == 'c') start_all(GEN_ADR | GEN_RES); else start_all(GEN_ADR | GEN_RES | GEN_SRC); } if (current_size == SIZE_LONG) current_cycle = 6; else current_cycle= 4; if (is_ea_memory(current_ea)) current_cycle *= 2; // read _ea_calc(current_ea, current_op->reg_sft); if (op != 'c') _ea_read_src(current_ea, current_op->reg_sft); // op switch (op) { case 'x': // negx wf_op("\tres = -src - ((CPU->flag_X >> 8) & 1);\n"); break; case 'g': // neg wf_op("\tres = -src;\n"); break; case 'n': // not wf_op("\tres = ~src;\n"); break; case 'c': // clr wf_op("\tres = 0;\n"); break; } // flag calculation switch (op) { case 'x': // negx set_negx_flag(); break; case 'g': // neg set_neg_flag(); break; case 'n': // not set_logicl_flag(); break; case 'c': // clr wf_op("\tCPU->flag_N = CPU->flag_notZ = CPU->flag_V = CPU->flag_C = 0;\n"); break; } // write _ea_write(current_ea, current_op->reg_sft); terminate_op(0); } static void GenCLR() { GenSingle('c'); } static void GenNEGX() { GenSingle('x'); } static void GenNEG() { GenSingle('g'); } static void GenNOT() { GenSingle('n'); } static void GenMOVESRa() { // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES); else start_all(GEN_ADR | GEN_RES); // read wf_op("\tres = GET_SR;\n"); // write set_current_size(SIZE_WORD); if (is_ea_memory(current_ea)) current_cycle += 2; _ea_calc(current_ea, current_op->reg_sft); _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenMOVEaSR() { // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES); else start_all(GEN_ADR | GEN_RES); wf_op("\tif (CPU->flag_S)\n"); wf_op("\t{\n"); // read set_current_size(SIZE_WORD); _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); wf_op("\t\tSET_SR(res)\n"); wf_op("\t\tif (!CPU->flag_S)\n"); wf_op("\t\t{\n"); wf_op("\t\t\tres = CPU->A[7];\n"); wf_op("\t\t\tCPU->A[7] = CPU->USP;\n"); wf_op("\t\t\tCPU->USP = res;\n"); wf_op("\t\t}\n"); wf_op("\t}\n"); wf_op("\telse\n"); wf_op("\t{\n"); gen_privilege_exception("\t\t"); wf_op("\t}\n"); // force terminaison to check for interrupt fterminate_op(12); } static void GenMOVEaCCR() { // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES); else start_all(GEN_ADR | GEN_RES); // read set_current_size(SIZE_WORD); _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // write wf_op("\tSET_CCR(res)\n"); terminate_op(12); } static void GenMOVEAUSP() { current_ea = EA_AREG; // generate jump table & opcode declaration start_all(GEN_RES); wf_op("\tif (!CPU->flag_S)\n"); wf_op("\t{\n"); gen_privilege_exception("\t\t"); quick_terminate_op(4); wf_op("\t}\n"); // read set_current_size(SIZE_LONG); _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // write wf_op("\tCPU->USP = res;\n"); terminate_op(4); } static void GenMOVEUSPA() { current_ea = EA_AREG; // generate jump table & opcode declaration start_all(GEN_RES); wf_op("\tif (!CPU->flag_S)\n"); wf_op("\t{\n"); gen_privilege_exception("\t\t"); quick_terminate_op(4); wf_op("\t}\n"); // read wf_op("\tres = CPU->USP;\n"); // write set_current_size(SIZE_LONG); _ea_calc(current_ea, current_op->reg_sft); _ea_write(current_ea, current_op->reg_sft); terminate_op(4); } static void GenPEA() { set_current_size(SIZE_LONG); // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(0); else start_all(GEN_ADR); _ea_calc_free(current_ea, current_op->reg_sft); mem_op("\tPUSH_32_F(adr)\n"); terminate_op(lea_pea_cycle_table[current_ea] + 12); } static void GenSWAP() { current_ea = EA_DREG; set_current_size(SIZE_LONG); // generate jump table & opcode declaration start_all(GEN_RES); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // op wf_op("\tres = (res >> 16) | (res << 16);\n"); // flag calculation set_logic_flag(); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(4); } static void GenMOVEMaR() { // generate jump table & opcode declaration start_all(GEN_ALL); // get register mask wf_op("\tres = FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); // get adr if (current_ea == EA_AINC) wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", current_op->reg_sft); else if (current_ea == EA_AINC7) wf_op("\tadr = CPU->A[7];\n"); else _ea_calc(current_ea, current_op->reg_sft); wf_op("\tsrc = (pointer)(&CPU->D[0]);\n"); wf_op("\tdst = adr;\n"); do_pre_io(); wf_op("\tdo\n"); wf_op("\t{\n"); wf_op("\t\tif (res & 1)\n"); wf_op("\t\t{\n"); if (current_size == SIZE_WORD) { wf_op("\t\t\tREADSX_WORD_F(adr, *(s32*)src)\n"); wf_op("\t\t\tadr += 2;\n"); } else { wf_op("\t\t\tREAD_LONG_F(adr, *(u32*)src)\n"); wf_op("\t\t\tadr += 4;\n"); } wf_op("\t\t}\n"); wf_op("\t\tsrc += 4;\n"); wf_op("\t} while (res >>= 1);\n"); if (current_ea == EA_AINC) wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", current_op->reg_sft); else if (current_ea == EA_AINC7) wf_op("\tCPU->A[7] = adr;\n"); adds_CCnt("(adr - dst) * 2"); terminate_op(movem_cycle_table[current_ea] + 12); } static void GenMOVEMRa() { // generate jump table & opcode declaration start_all(GEN_ALL); // get register mask wf_op("\tres = FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); // get adr if (current_ea == EA_ADEC) wf_op("\tadr = CPU->A[(Opcode >> %d) & 7];\n", current_op->reg_sft); else if (current_ea == EA_ADEC7) wf_op("\tadr = CPU->A[7];\n"); else _ea_calc(current_ea, current_op->reg_sft); if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) wf_op("\tsrc = (pointer)(&CPU->A[7]);\n"); else wf_op("\tsrc = (pointer)(&CPU->D[0]);\n"); wf_op("\tdst = adr;\n"); do_pre_io(); wf_op("\tdo\n"); wf_op("\t{\n"); wf_op("\t\tif (res & 1)\n"); wf_op("\t\t{\n"); if (current_size == SIZE_WORD) { if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) wf_op("\t\t\tadr -= 2;\n"); wf_op("\t\t\tWRITE_WORD_F(adr, *(u16*)src)\n"); if (!((current_ea == EA_ADEC) || (current_ea == EA_ADEC7))) wf_op("\t\t\tadr += 2;\n"); } else { if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) { wf_op("\t\t\tadr -= 4;\n"); wf_op("\t\t\tWRITE_LONG_DEC_F(adr, *(u32*)src)\n"); } else { wf_op("\t\t\tWRITE_LONG_F(adr, *(u32*)src)\n"); wf_op("\t\t\tadr += 4;\n"); } } wf_op("\t\t}\n"); if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) wf_op("\t\tsrc -= 4;\n"); else wf_op("\t\tsrc += 4;\n"); wf_op("\t} while (res >>= 1);\n"); if (current_ea == EA_ADEC) wf_op("\tCPU->A[(Opcode >> %d) & 7] = adr;\n", current_op->reg_sft); else if (current_ea == EA_ADEC7) wf_op("\tCPU->A[7] = adr;\n"); if ((current_ea == EA_ADEC) || (current_ea == EA_ADEC7)) adds_CCnt("(dst - adr) * 2"); else adds_CCnt("(adr - dst) * 2"); terminate_op(movem_cycle_table[current_ea] + 8); } static void GenEXT() { current_ea = EA_DREG; // generate jump table & opcode declaration start_all(GEN_RES); // read set_current_size(current_size - 1); _ea_calc(current_ea, current_op->reg_sft); _ea_read_sx(current_ea, current_op->reg_sft); // flag calculation set_logic_flag(); // write set_current_size(current_size + 1); _ea_write(current_ea, current_op->reg_sft); terminate_op(4); } static void GenTST() { // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES); else start_all(GEN_ADR | GEN_RES); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // flag calculation set_logic_flag(); terminate_op(4); } static void GenTAS() { set_current_size(SIZE_BYTE); if (is_ea_memory(current_ea)) current_cycle += 6; // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES); else start_all(GEN_ADR | GEN_RES); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // flag calculation set_logic_flag(); #ifndef C68K_TAS_CAN_SET_MEMORY if (current_ea < EA_AIND) { #endif // flag calculation wf_op("\tres |= 0x80;\n"); // write _ea_write(current_ea, current_op->reg_sft); #ifndef C68K_TAS_CAN_SET_MEMORY } #endif terminate_op(4); } static void GenTRAP() { u32 base; base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, (0 << 0), (15 << 0), (1 << 0), base); // generate label & declarations start_op(base, GEN_RES); gen_exception("\t", "C68K_TRAP_BASE_EX + (Opcode & 0xF)"); terminate_op(4); } static void GenTRAPV() { // generate label & declarations start_all(GEN_RES); wf_op("\tif %s\n", get_cond_as_cond(COND_VS, 0)); wf_op("\t{\n"); gen_exception("\t\t", "C68K_TRAPV_EX"); wf_op("\t}\n"); terminate_op(4); } static void GenLINK() { current_ea = EA_AREG; set_current_size(SIZE_LONG); // generate jump table & opcode declaration start_all(GEN_RES); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // push mem_op("\tPUSH_32_F(res)\n"); wf_op("\tres = CPU->A[7];\n"); // write _ea_write(current_ea, current_op->reg_sft); // update SP wf_op("\tCPU->A[7] += (s32)(s16)FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); terminate_op(16); } static void GenLINKA7() { current_ea = EA_AREG; set_current_size(SIZE_LONG); // generate jump table & opcode declaration start_all(0); // push A7 wf_op("\tCPU->A[7] -= 4;\n"); mem_op("\tWRITE_LONG_DEC_F(CPU->A[7], CPU->A[7])\n"); // update A7 wf_op("\tCPU->A[7] += (s32)(s16)FETCH_WORD;\n"); wf_op("\tPC += 2;\n"); terminate_op(16); } static void GenULNK() { current_ea = EA_AREG; set_current_size(SIZE_LONG); // generate jump table & opcode declaration start_all(GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // pop wf_op("\tCPU->A[7] = src + 4;\n"); mem_op("\tREAD_LONG_F(src, res)\n"); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(12); } static void GenULNKA7() { current_ea = EA_AREG; set_current_size(SIZE_LONG); // generate jump table & opcode declaration start_all(0); mem_op("\tREAD_LONG_F(CPU->A[7], CPU->A[7])\n"); terminate_op(12); } static void GenRESET() { // generate jump table & opcode declaration start_all(GEN_RES); wf_op("\tif (!CPU->flag_S)\n"); wf_op("\t{\n"); gen_privilege_exception("\t\t"); quick_terminate_op(4); wf_op("\t}\n"); // Reset callback function mem_op("\tCPU->Reset_CallBack();\n"); terminate_op(132); } static void GenLEA() { current_ea2 = EA_AREG; set_current_size(SIZE_LONG); // generate jump table & opcode declaration start_all(GEN_ADR | GEN_RES); _ea_calc_free(current_ea, current_op->reg_sft); wf_op("\tres = adr;\n"); current_cycle = lea_pea_cycle_table[current_ea]; _ea_calc(current_ea2, current_op->reg2_sft); _ea_write(current_ea2, current_op->reg2_sft); terminate_op(4); } static void GenNOP() { start_all(0); terminate_op(4); } static void GenILLEGAL() { start_all(GEN_RES); gen_exception("\t\t", "C68K_ILLEGAL_INSTRUCTION_EX"); terminate_op(4); } static void GenCHK() { current_ea2 = EA_DREG; set_current_size(SIZE_WORD); // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES | GEN_SRC); else start_all(GEN_ADR | GEN_RES | GEN_SRC); // read Src _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read Dx _ea_calc(current_ea2, current_op->reg2_sft); _ea_read(current_ea2, current_op->reg2_sft); wf_op("\tif (((s32)res < 0) || (res > src))\n"); wf_op("\t{\n"); wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); gen_exception("\t\t", "C68K_CHK_EX"); wf_op("\t}\n"); terminate_op(10); } static void GenSTOP() { // generate jump table & opcode declaration start_all(GEN_RES); wf_op("\tif (!CPU->flag_S)\n"); wf_op("\t{\n"); wf_op("\t\tPC += 2;\n"); gen_privilege_exception("\t\t"); quick_terminate_op(4); wf_op("\t}\n"); // read & set SR wf_op("\tres = FETCH_WORD & C68K_SR_MASK;\n"); wf_op("\tPC += 2;\n"); wf_op("\tSET_SR(res)\n"); // if S flag not set --> we swap stack pointer wf_op("\tif (!CPU->flag_S)\n"); wf_op("\t{\n"); wf_op("\t\tres = CPU->A[7];\n"); wf_op("\t\tCPU->A[7] = CPU->USP;\n"); wf_op("\t\tCPU->USP = res;\n"); wf_op("\t}\n"); wf_op("\tCPU->Status |= C68K_HALTED;\n"); wf_op("\tCCnt = 0;\n"); // force end execution fterminate_op(4); } static void GenRTE() { start_all(GEN_RES); wf_op("\tif (!CPU->flag_S)\n"); wf_op("\t{\n"); gen_privilege_exception("\t\t"); quick_terminate_op(4); wf_op("\t}\n"); // restore SR and PC mem_op("\tPOP_16_F(res)\n"); wf_op("\tSET_SR(res)\n"); mem_op("\tPOP_32_F(res)\n"); wf_op("\tSET_PC(res)\n"); // if S flag not set --> we swap stack pointer wf_op("\tif (!CPU->flag_S)\n"); wf_op("\t{\n"); wf_op("\t\tres = CPU->A[7];\n"); wf_op("\t\tCPU->A[7] = CPU->USP;\n"); wf_op("\t\tCPU->USP = res;\n"); wf_op("\t}\n"); // check for interrupt fterminate_op(20); } static void GenRTS() { start_all(GEN_RES); mem_op("\tPOP_32_F(res)\n"); wf_op("\tSET_PC(res)\n"); terminate_op(16); } static void GenRTR() { start_all(GEN_RES); mem_op("\tPOP_16_F(res)\n"); wf_op("\tSET_CCR(res)\n"); mem_op("\tPOP_32_F(res)\n"); wf_op("\tSET_PC(res)\n"); terminate_op(20); } static void GenJSR() { start_all(GEN_ADR); // get adr _ea_calc_free(current_ea, current_op->reg_sft); mem_op("\tPUSH_32_F(PC - CPU->BasePC)\n"); wf_op("\tSET_PC(adr)\n"); terminate_op(jmp_jsr_cycle_table[current_ea] + 12); } static void GenJMP() { start_all(GEN_ADR); // get adr _ea_calc_free(current_ea, current_op->reg_sft); wf_op("\tSET_PC(adr)\n"); terminate_op(jmp_jsr_cycle_table[current_ea] + 4); } static void GenSTCC() { u32 base, cond; base = get_current_opcode_base(); for(cond = 0; cond < 0x10; cond++) { // generate jump table gen_opjumptable(base + (cond << 8)); // generate label & declarations if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_op(base + (cond << 8), GEN_RES); else start_op(base + (cond << 8), GEN_ADR | GEN_RES); set_current_size(SIZE_BYTE); if (is_ea_memory(current_ea)) current_cycle += 4; // op _ea_calc(current_ea, current_op->reg_sft); if ((cond != COND_TR) && (cond != COND_FA)) { wf_op("\tif %s\n", get_cond_as_cond(cond, 0)); wf_op("\t{\n"); } if (cond != COND_FA) { wf_op("\tres = 0xFF;\n"); // write _ea_write(current_ea, current_op->reg_sft); if (!is_ea_memory(current_ea)) quick_terminate_op(6); else quick_terminate_op(4); } if ((cond != COND_TR) && (cond != COND_FA)) { wf_op("\t}\n"); } if (cond != COND_TR) { wf_op("\tres = 0;\n"); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(4); } wf_op("}\n"); } } static void GenDBCC() { u32 base, cond; base = get_current_opcode_base(); current_ea = EA_DREG; set_current_size(SIZE_WORD); for(cond = 0; cond < 0x10; cond++) { // generate jump table gen_opjumptable(base + (cond << 8)); // generate label & declarations start_op(base + (cond << 8), cond==COND_TR ? 0 : GEN_RES); if (cond != COND_TR) { if (cond != COND_FA) { wf_op("\tif %s\n", get_cond_as_cond(cond, 1)); wf_op("\t{\n"); } // read Dx _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // dec Dx wf_op("\tres--;\n"); // write Dx _ea_write(current_ea, current_op->reg_sft); wf_op("\tif ((s32)res != -1)\n"); wf_op("\t{\n"); wf_op("\t\tPC += (s32)(s16)FETCH_WORD;\n"); // unbase PC wf_op("\t\tPC -= CPU->BasePC;\n"); // rebase PC wf_op("\t\tSET_PC(PC);\n"); quick_terminate_op(10); wf_op("\t}\n"); if (cond != COND_FA) { wf_op("\t}\n"); wf_op("\telse\n"); wf_op("\t{\n"); wf_op("\t\tPC += 2;\n"); quick_terminate_op(12); wf_op("\t}\n"); } } wf_op("\tPC += 2;\n"); if (cond == COND_TR) terminate_op(12); else terminate_op(14); } } static void GenBCC() { u32 base, cond; base = get_current_opcode_base(); for(cond = 2; cond < 0x10; cond++) { // generate jump table gen_opjumptable_ext(base + (cond << 8), 0x01, 0xFF, 1, base + (cond << 8) + 0x01); // generate label & declarations start_op(base + (cond << 8) + 0x01, 0); // op wf_op("\tif %s\n", get_cond_as_cond(cond, 0)); wf_op("\t{\n"); wf_op("\t\tPC += (s32)(s8)Opcode;\n"); // no rebase needed for 8 bits deplacement add_CCnt(2); wf_op("\t}\n"); terminate_op(8); } } static void GenBCC16() { u32 base, cond; base = get_current_opcode_base(); for(cond = 2; cond < 0x10; cond++) { // generate jump table gen_opjumptable(base + (cond << 8)); // generate label & declarations start_op(base + (cond << 8), 0); // op wf_op("\tif %s\n", get_cond_as_cond(cond, 0)); wf_op("\t{\n"); wf_op("\t\tPC += (s32)(s16)FETCH_WORD;\n"); // unbase PC wf_op("\t\tPC -= CPU->BasePC;\n"); // rebase PC wf_op("\t\tSET_PC(PC);\n"); quick_terminate_op(10); wf_op("\t}\n"); wf_op("\tPC += 2;\n"); terminate_op(12); } } static void GenBRA() { u32 base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x01, 0xFF, 1, base + 0x01); // generate label & declarations start_op(base + 0x01, 0); wf_op("\tPC += (s32)(s8)Opcode;\n"); // no rebase needed for 8 bits deplacement terminate_op(10); } static void GenBRA16() { u32 base = get_current_opcode_base(); // generate jump table gen_opjumptable(base + 0x00); // generate label & declarations start_op(base + 0x00, 0); wf_op("\tPC += (s32)(s16)FETCH_WORD;\n"); // unbase PC wf_op("\tPC -= CPU->BasePC;\n"); // rebase PC wf_op("\tSET_PC(PC);\n"); terminate_op(10); } static void GenBSR() { u32 base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x01, 0xFF, 1, base + 0x01); // generate label & declarations start_op(base + 0x01, 0); mem_op("\tPUSH_32_F(PC - CPU->BasePC)\n"); wf_op("\tPC += (s32)(s8)Opcode;\n"); // no rebase needed for 8 bits deplacement terminate_op(18); } static void GenBSR16() { u32 base = get_current_opcode_base(); // generate jump table gen_opjumptable(base + 0x00); // generate label & declarations start_op(base + 0x00, GEN_RES); wf_op("\tres = (s32)(s16)FETCH_WORD;\n"); // unbase PC wf_op("\tPC -= CPU->BasePC;\n"); mem_op("\tPUSH_32_F(PC + 2)\n"); wf_op("\tPC += (s32) res;\n"); // rebase PC for 16 bits deplacement wf_op("\tSET_PC(PC);\n"); terminate_op(18); } static void GenArithQ(char op) { u32 base; if ((current_ea == EA_AREG) && (current_size == SIZE_BYTE)) return; base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, (0 << 9), (7 << 9), (1 << 9), base); // generate label & declarations if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_op(base, GEN_DST | GEN_RES | GEN_SRC); else start_op(base, GEN_ALL); if (current_ea == EA_AREG) set_current_size(SIZE_LONG); if (is_ea_memory(current_ea)) current_cycle += 4; if (current_size == SIZE_LONG) current_cycle += 4; // read src wf_op("\tsrc = (((Opcode >> 9) - 1) & 7) + 1;\n"); // read dst _ea_calc(current_ea, current_op->reg_sft); _ea_read_dst(current_ea, current_op->reg_sft); // op wf_op("\tres = dst %c src;\n", op); // flag calculation if (current_ea != EA_AREG) { if (op == '+') set_add_flag(); else set_sub_flag(); } // write dst _ea_write(current_ea, current_op->reg_sft); terminate_op(4); } static void GenADDQ() { GenArithQ('+'); } static void GenSUBQ() { GenArithQ('-'); } static void GenLogicaD(char op) { // generate jump table & opcode declaration current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES | GEN_SRC); else start_all(GEN_ADR | GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) { if (!is_ea_memory(current_ea)) current_cycle += 2; else current_cycle += 4; } // read src _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read(current_ea2, current_op->reg2_sft); // op wf_op("\tres %c= src;\n", op); // flag calculation set_logic_flag(); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); terminate_op(4); } static void GenLogicDa(char op) { // generate jump table & opcode declaration current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES | GEN_SRC); else start_all(GEN_ADR | GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 4; // read src (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_src(current_ea2, current_op->reg2_sft); // read dst _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // op wf_op("\tres %c= src;\n", op); // flag calculation set_logic_flag(); // write dst _ea_write(current_ea, current_op->reg_sft); if (current_ea == EA_DREG) terminate_op(4); else terminate_op(8); } static void GenANDaD() { GenLogicaD('&'); } static void GenANDDa() { GenLogicDa('&'); } static void GenORaD() { GenLogicaD('|'); } static void GenORDa() { GenLogicDa('|'); } static void GenEORDa() { GenLogicDa('^'); } static void GenNBCD() { set_current_size(SIZE_BYTE); // generate jump table & opcode declaration if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES); else start_all(GEN_ADR | GEN_RES); if (is_ea_memory(current_ea)) current_cycle += 2; // read _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // op wf_op("\tres = 0x9a - res - ((CPU->flag_X >> C68K_SR_X_SFT) & 1);\n"); wf_op("\n"); wf_op("\tif (res != 0x9a)\n"); wf_op("\t{\n"); wf_op("\t\tif ((res & 0x0f) == 0xa) res = (res & 0xf0) + 0x10;\n"); wf_op("\t\tres &= 0xFF;\n"); // write _ea_write(current_ea, current_op->reg_sft); // flag calculation wf_op("\t\tCPU->flag_notZ |= res;\n"); wf_op("\t\tCPU->flag_X = CPU->flag_C = C68K_SR_C;\n"); wf_op("\t}\n"); wf_op("\telse CPU->flag_X = CPU->flag_C = 0;\n"); wf_op("\tCPU->flag_N = res;\n"); terminate_op(6); } static void GenBCD(char op) { // op wf_op("\tres = (dst & 0xF) %c (src & 0xF) %c ((CPU->flag_X >> C68K_SR_X_SFT) & 1);\n", op, op); wf_op("\tif (res > 9) res %c= 6;\n", op); wf_op("\tres += (dst & 0xF0) %c (src & 0xF0);\n", op, op); // flag calculation wf_op("\tif (res > 0x99)\n"); wf_op("\t{\n"); switch (op) { case '+': wf_op("\t\tres -= 0xA0;\n"); break; case '-': wf_op("\t\tres += 0xA0;\n"); break; } wf_op("\t\tCPU->flag_X = CPU->flag_C = C68K_SR_C;\n"); wf_op("\t}\n"); wf_op("\telse CPU->flag_X = CPU->flag_C = 0;\n"); wf_op("\tCPU->flag_notZ |= res & 0xFF;\n"); wf_op("\tCPU->flag_N = res;\n"); } static void GenxBCD(char op) { set_current_size(SIZE_BYTE); // generate jump table & opcode declaration current_ea = EA_DREG; current_ea2 = EA_DREG; start_all(GEN_DST | GEN_RES | GEN_SRC); // read src (Dx) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op & flag calculation GenBCD(op); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); terminate_op(6); } static void GenxBCDM(char op) { set_current_size(SIZE_BYTE); // generate jump table & opcode declaration current_ea = EA_ADEC; current_ea2 = EA_ADEC; start_all(GEN_ALL); // read src (ADEC) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (ADEC) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op & flag calculation GenBCD(op); // write dst (ADEC) _ea_write(current_ea2, current_op->reg2_sft); terminate_op(6); } static void GenxBCD7M(char op) { set_current_size(SIZE_BYTE); // generate jump table & opcode declaration current_ea = EA_ADEC7; current_ea2 = EA_ADEC; start_all(GEN_ALL); // read src (ADEC7) _ea_calc(current_ea, 0); _ea_read_src(current_ea, 0); // read dst (ADEC) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op & flag calculation GenBCD(op); // write dst (ADEC) _ea_write(current_ea2, current_op->reg2_sft); terminate_op(6); } static void GenxBCDM7(char op) { set_current_size(SIZE_BYTE); // generate jump table & opcode declaration current_ea = EA_ADEC; current_ea2 = EA_ADEC7; start_all(GEN_ALL); // read src (ADEC) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (ADEC7) _ea_calc(current_ea2, 0); _ea_read_dst(current_ea2, 0); // op & flag calculation GenBCD(op); // write dst (ADEC7) _ea_write(current_ea2, 0); terminate_op(6); } static void GenxBCD7M7(char op) { set_current_size(SIZE_BYTE); // generate jump table & opcode declaration current_ea = EA_ADEC7; current_ea2 = EA_ADEC7; start_all(GEN_ALL); // read src (ADEC7) _ea_calc(current_ea, 0); _ea_read_src(current_ea, 0); // read dst (ADEC7) _ea_calc(current_ea2, 0); _ea_read_dst(current_ea2, 0); // op & flag calculation GenBCD(op); // write dst (ADEC7) _ea_write(current_ea2, 0); terminate_op(6); } static void GenABCD() { GenxBCD('+'); } static void GenABCDM() { GenxBCDM('+'); } static void GenABCD7M() { GenxBCD7M('+'); } static void GenABCDM7() { GenxBCDM7('+'); } static void GenABCD7M7() { GenxBCD7M7('+'); } static void GenSBCD() { GenxBCD('-'); } static void GenSBCDM() { GenxBCDM('-'); } static void GenSBCD7M() { GenxBCD7M('-'); } static void GenSBCDM7() { GenxBCDM7('-'); } static void GenSBCD7M7() { GenxBCD7M7('-'); } static void GenDIVU() { // generate jump table & opcode declaration current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_DST | GEN_RES | GEN_SRC); else start_all(GEN_ALL); set_current_size(SIZE_WORD); // read src _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // division by zero wf_op("\tif (src == 0)\n"); wf_op("\t{\n"); gen_exception("\t\t", "C68K_ZERO_DIVIDE_EX"); quick_terminate_op(10); wf_op("\t}\n"); set_current_size(SIZE_LONG); // read dst (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); wf_op("\t{\n"); wf_op("\t\tu32 q, r;\n"); wf_op("\n"); wf_op("\t\tq = dst / src;\n"); wf_op("\t\tr = dst %% src;\n"); wf_op("\n"); wf_op("\t\tif (q & 0xFFFF0000)\n"); wf_op("\t\t{\n"); // overflow occured wf_op("\t\t\tCPU->flag_V = C68K_SR_V;\n"); quick_terminate_op(70); wf_op("\t\t}\n"); // quotient size = word set_current_size(SIZE_WORD); wf_op("\t\tq &= 0x%.8X;\n", current_bits_mask); wf_op("\t\tCPU->flag_notZ = q;\n"); wf_op("\t\tCPU->flag_N = q >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\t\tCPU->flag_V = CPU->flag_C = 0;\n"); wf_op("\t\tres = q | (r << 16);\n"); set_current_size(SIZE_LONG); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); wf_op("\t}\n"); // max cycle = 140 terminate_op(140 - 50); } static void GenDIVS() { // generate jump table & opcode declaration current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_DST | GEN_RES | GEN_SRC); else start_all(GEN_ALL); set_current_size(SIZE_WORD); // read src _ea_calc(current_ea, current_op->reg_sft); _ea_read_src_sx(current_ea, current_op->reg_sft); // division by zero wf_op("\tif (src == 0)\n"); wf_op("\t{\n"); gen_exception("\t\t", "C68K_ZERO_DIVIDE_EX"); quick_terminate_op(10); wf_op("\t}\n"); set_current_size(SIZE_LONG); // read dst (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // division by zero wf_op("\tif ((dst == 0x80000000) && (src == -1))\n"); wf_op("\t{\n"); wf_op("\t\tCPU->flag_notZ = CPU->flag_N = 0;\n"); wf_op("\t\tCPU->flag_V = CPU->flag_C = 0;\n"); wf_op("\t\tres = 0;\n"); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); quick_terminate_op(50); wf_op("\t}\n"); wf_op("\t{\n"); wf_op("\t\ts32 q, r;\n"); wf_op("\n"); wf_op("\t\tq = (s32)dst / (s32)src;\n"); wf_op("\t\tr = (s32)dst %% (s32)src;\n"); wf_op("\n"); wf_op("\t\tif ((q > 0x7FFF) || (q < -0x8000))\n"); wf_op("\t\t{\n"); // overflow occured wf_op("\t\t\tCPU->flag_V = C68K_SR_V;\n"); quick_terminate_op(80); wf_op("\t\t}\n"); // quotient size = word set_current_size(SIZE_WORD); wf_op("\t\tq &= 0x%.8X;\n", current_bits_mask); wf_op("\t\tCPU->flag_notZ = q;\n"); wf_op("\t\tCPU->flag_N = q >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\t\tCPU->flag_V = CPU->flag_C = 0;\n"); wf_op("\t\tres = q | (r << 16);\n"); set_current_size(SIZE_LONG); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); wf_op("\t}\n"); // max cycle = 158 terminate_op(158 - 50); } static void GenMULU() { // generate jump table & opcode declaration current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES | GEN_SRC); else start_all(GEN_ADR | GEN_RES | GEN_SRC); set_current_size(SIZE_WORD); // read src _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read(current_ea2, current_op->reg2_sft); set_current_size(SIZE_LONG); // op wf_op("\tres *= src;\n"); // flag calculation wf_op("\tCPU->flag_N = res >> 24;\n"); wf_op("\tCPU->flag_notZ = res;\n"); wf_op("\tCPU->flag_V = CPU->flag_C = 0;\n"); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); // min cycle = 38; max cycle = 70 terminate_op(38 + (2 * 6)); } static void GenMULS() { // generate jump table & opcode declaration current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_RES | GEN_SRC); else start_all(GEN_ADR | GEN_RES | GEN_SRC); set_current_size(SIZE_WORD); // read src signed _ea_calc(current_ea, current_op->reg_sft); _ea_read_src_sx(current_ea, current_op->reg_sft); // read dst signed (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_sx(current_ea2, current_op->reg2_sft); set_current_size(SIZE_LONG); // op //wf_op("\t(s32)res *= (s32)src;\n"); wf_op("\tres *= (s32)src;\n"); // antime fix // flag calculation wf_op("\tCPU->flag_N = res >> 24;\n"); wf_op("\tCPU->flag_notZ = res;\n"); wf_op("\tCPU->flag_V = CPU->flag_C = 0;\n"); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); // min cycle = 38; max cycle = 70 terminate_op(38 + (2 * 6)); } static void GenArithaD(char op) { // generate jump table & opcode declaration current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_DST | GEN_RES | GEN_SRC); else start_all(GEN_ALL); if (current_size == SIZE_LONG) { if (!is_ea_memory(current_ea)) current_cycle += 2; else current_cycle += 4; } // read src _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); if (op == ' ') { // op wf_op("\tres = dst - src;\n"); // flag calculation set_cmp_flag(); } else { // op wf_op("\tres = dst %c src;\n", op); // flag calculation if (op == '+') set_add_flag(); else set_sub_flag(); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); } terminate_op(4); } static void GenArithDa(char op) { // generate jump table & opcode declaration current_ea2 = EA_DREG; start_all(GEN_ALL); if (current_size == SIZE_LONG) current_cycle += 4; // read src (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_src(current_ea2, current_op->reg2_sft); // read dst _ea_calc(current_ea, current_op->reg_sft); _ea_read_dst(current_ea, current_op->reg_sft); // op wf_op("\tres = dst %c src;\n", op); // flag calculation if (op == '+') set_add_flag(); else set_sub_flag(); // write dst _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenArithA(char op) { // generate jump table & opcode declaration current_ea2 = EA_AREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_DST | GEN_RES | GEN_SRC); else start_all(GEN_ALL); if ((op != ' ') && ((current_size == SIZE_WORD) || (is_ea_memory(current_ea)))) current_cycle += 2; // read src _ea_calc(current_ea, current_op->reg_sft); _ea_read_src_sx(current_ea, current_op->reg_sft); // read dst (Ax) set_current_size(SIZE_LONG); _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op if (op == ' ') { // op wf_op("\tres = dst - src;\n"); // flag calculation set_cmp_flag(); } else { // op wf_op("\tres = dst %c src;\n", op); // write dst (Ax) _ea_write(current_ea2, current_op->reg2_sft); } terminate_op(6); } static void GenArithX(char op) { // generate jump table & opcode declaration current_ea = EA_DREG; current_ea2 = EA_DREG; if ((current_ea == EA_AREG) || (current_ea == EA_DREG) || (current_ea == EA_IMM)) start_all(GEN_DST | GEN_RES | GEN_SRC); else start_all(GEN_ALL); if (current_size == SIZE_LONG) current_cycle += 4; // read src (Dx) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (Dx) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); // flag calculation if (op == '+') set_addx_flag(); else set_subx_flag(); // write dst (Dx) _ea_write(current_ea2, current_op->reg2_sft); terminate_op(4); } static void GenArithXM(char op) { // generate jump table & opcode declaration current_ea = EA_ADEC; current_ea2 = EA_ADEC; start_all(GEN_ALL); if (current_size == SIZE_LONG) current_cycle += 4; // read src (ADEC) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (ADEC) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); // flag calculation if (op == '+') set_addx_flag(); else set_subx_flag(); // write dst (ADEC) _ea_write(current_ea2, current_op->reg2_sft); terminate_op(6); } static void GenArithX7M(char op) { // generate jump table & opcode declaration current_ea = EA_ADEC7; current_ea2 = EA_ADEC; start_all(GEN_ALL); if (current_size == SIZE_LONG) current_cycle += 4; // read src (ADEC7) _ea_calc(current_ea, 0); _ea_read_src(current_ea, 0); // read dst (ADEC) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); // flag calculation if (op == '+') set_addx_flag(); else set_subx_flag(); // write dst (ADEC) _ea_write(current_ea2, current_op->reg2_sft); terminate_op(6); } static void GenArithXM7(char op) { // generate jump table & opcode declaration current_ea = EA_ADEC; current_ea2 = EA_ADEC7; start_all(GEN_ALL); if (current_size == SIZE_LONG) current_cycle += 4; // read src (ADEC) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (ADEC7) _ea_calc(current_ea2, 0); _ea_read_dst(current_ea2, 0); // op wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); // flag calculation if (op == '+') set_addx_flag(); else set_subx_flag(); // write dst (ADEC7) _ea_write(current_ea2, 0); terminate_op(6); } static void GenArithX7M7(char op) { // generate jump table & opcode declaration current_ea = EA_ADEC7; current_ea2 = EA_ADEC7; start_all(GEN_ALL); if (current_size == SIZE_LONG) current_cycle += 4; // read src (ADEC7) _ea_calc(current_ea, 0); _ea_read_src(current_ea, 0); // read dst (ADEC7) _ea_calc(current_ea2, 0); _ea_read_dst(current_ea2, 0); // op wf_op("\tres = dst %c src %c ((CPU->flag_X >> 8) & 1);\n", op, op); // flag calculation if (op == '+') set_addx_flag(); else set_subx_flag(); // write dst (ADEC7) _ea_write(current_ea2, 0); terminate_op(6); } static void GenADDaD() { GenArithaD('+'); } static void GenADDDa() { GenArithDa('+'); } static void GenADDA() { GenArithA('+'); } static void GenADDX() { GenArithX('+'); } static void GenADDXM() { GenArithXM('+'); } static void GenADDX7M() { GenArithX7M('+'); } static void GenADDXM7() { GenArithXM7('+'); } static void GenADDX7M7() { GenArithX7M7('+'); } static void GenSUBaD() { GenArithaD('-'); } static void GenSUBDa() { GenArithDa('-'); } static void GenSUBA() { GenArithA('-'); } static void GenSUBX() { GenArithX('-'); } static void GenSUBXM() { GenArithXM('-'); } static void GenSUBX7M() { GenArithX7M('-'); } static void GenSUBXM7() { GenArithXM7('-'); } static void GenSUBX7M7() { GenArithX7M7('-'); } static void GenCMP() { GenArithaD(' '); } static void GenCMPA() { GenArithA(' '); } static void GenCMPM() { // generate jump table & opcode declaration current_ea = EA_AINC; current_ea2 = EA_AINC; start_all(GEN_ALL); // read src (ADEC) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (ADEC) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op wf_op("\tres = dst - src;\n"); // flag calculation set_cmp_flag(); terminate_op(4); } static void GenCMP7M() { // generate jump table & opcode declaration current_ea = EA_AINC7; current_ea2 = EA_AINC; start_all(GEN_ALL); // read src (ADEC) _ea_calc(current_ea, 0); _ea_read_src(current_ea, 0); // read dst (ADEC) _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_dst(current_ea2, current_op->reg2_sft); // op wf_op("\tres = dst - src;\n"); // flag calculation set_cmp_flag(); terminate_op(4); } static void GenCMPM7() { // generate jump table & opcode declaration current_ea = EA_AINC; current_ea2 = EA_AINC7; start_all(GEN_ALL); // read src (ADEC) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // read dst (ADEC) _ea_calc(current_ea2, 0); _ea_read_dst(current_ea2, 0); // op wf_op("\tres = dst - src;\n"); // flag calculation set_cmp_flag(); terminate_op(4); } static void GenCMP7M7() { // generate jump table & opcode declaration current_ea = EA_AINC7; current_ea2 = EA_AINC7; start_all(GEN_ALL); // read src (ADEC) _ea_calc(current_ea, 0); _ea_read_src(current_ea, 0); // read dst (ADEC) _ea_calc(current_ea2, 0); _ea_read_dst(current_ea2, 0); // op wf_op("\tres = dst - src;\n"); // flag calculation set_cmp_flag(); terminate_op(4); } static void GenEXGDD() { // generate jump table & opcode declaration set_current_size(SIZE_LONG); current_ea = EA_DREG; current_ea2 = EA_DREG; start_all(GEN_RES | GEN_SRC); // read R1 _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // read R2 _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_src(current_ea2, current_op->reg2_sft); // write R1 _ea_write(current_ea2, current_op->reg2_sft); wf_op("\tres = src;\n"); // write R2 _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenEXGAA() { // generate jump table & opcode declaration set_current_size(SIZE_LONG); current_ea = EA_AREG; current_ea2 = EA_AREG; start_all(GEN_RES | GEN_SRC); // read R1 _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // read R2 _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_src(current_ea2, current_op->reg2_sft); // write R1 _ea_write(current_ea2, current_op->reg2_sft); wf_op("\tres = src;\n"); // write R2 _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenEXGAD() { // generate jump table & opcode declaration set_current_size(SIZE_LONG); current_ea = EA_AREG; current_ea2 = EA_DREG; start_all(GEN_RES | GEN_SRC); // read R1 _ea_calc(current_ea, current_op->reg_sft); _ea_read(current_ea, current_op->reg_sft); // read R2 _ea_calc(current_ea2, current_op->reg2_sft); _ea_read_src(current_ea2, current_op->reg2_sft); // write R1 _ea_write(current_ea2, current_op->reg2_sft); wf_op("\tres = src;\n"); // write R2 _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenASRk() { u32 base; current_ea = EA_DREG; // dst = Dx base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); // generate label & declarations start_op(base, GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); adds_CCnt("sft * 2"); // read (sign extend) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src_sx(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); wf_op("\tres = ((s32)src) >> sft;\n"); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res;\n"); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenLSRk() { u32 base; current_ea = EA_DREG; // dst = Dx base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); // generate label & declarations start_op(base, GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); adds_CCnt("sft * 2"); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_N = CPU->flag_V = 0;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); wf_op("\tres = src >> sft;\n"); wf_op("\tCPU->flag_notZ = res;\n"); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenROXRk() { u32 base; current_ea = EA_DREG; // dst = Dx base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); // generate label & declarations start_op(base, GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); adds_CCnt("sft * 2"); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & C / X flags calculation if (current_size != SIZE_LONG) { wf_op("\tsrc |= (CPU->flag_X & C68K_SR_X) << %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); wf_op("\tres = (src >> sft) | (src << (%d - sft));\n", current_sft_mask + 2); wf_op("\tCPU->flag_X = CPU->flag_C = res >> %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); } else { wf_op("\tCPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); wf_op("\tif (sft == 1) res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1)));\n"); wf_op("\telse res = (src >> sft) | (src << (33 - sft)) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + sft)));\n"); wf_op("\tCPU->flag_X = CPU->flag_C;\n"); } // V / N / Z flags calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); if (current_size == SIZE_LONG) wf_op("\tCPU->flag_notZ = res;\n"); else wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenRORk() { u32 base; current_ea = EA_DREG; // dst = Dx base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); // generate label & declarations start_op(base, GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); adds_CCnt("sft * 2"); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); wf_op("\tres = (src >> sft) | (src << (%d - sft));\n", current_sft_mask + 1); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); if (current_size == SIZE_LONG) wf_op("\tCPU->flag_notZ = res;\n"); else wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenASLk() { u32 base; current_ea = EA_DREG; // dst = Dx base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); // generate label & declarations start_op(base, GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); adds_CCnt("sft * 2"); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // if (shift < size op) ... only for BYTE here if (current_size == SIZE_BYTE) { wf_op("\tif (sft < %d)\n", current_sft_mask + 1); wf_op("\t{\n"); } // op & flag X, C, N, Z calculation if (((current_sft_mask + 1) - C68K_SR_C_SFT) < 8) wf_op("\t\tCPU->flag_X = CPU->flag_C = src << (%d + sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); else wf_op("\t\tCPU->flag_X = CPU->flag_C = src >> (%d - sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); wf_op("\t\tres = src << sft;\n"); wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\t\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); // we do V flag calculation at end for a better register usage wf_op("\t\tCPU->flag_V = 0;\n"); if (current_size == SIZE_BYTE) { wf_op("\t\tif ((sft > %d) && (src)) CPU->flag_V = C68K_SR_V;\n", current_sft_mask); wf_op("\t\telse\n"); } wf_op("\t\t{\n"); wf_op("\t\t\tu32 msk = (((s32)0x80000000) >> (sft + %d)) & 0x%.8X;\n", 31 - current_sft_mask, current_bits_mask); wf_op("\t\t\tsrc &= msk;\n"); wf_op("\t\t\tif ((src) && (src != msk)) CPU->flag_V = C68K_SR_V;\n"); wf_op("\t\t}\n"); if (current_size == SIZE_BYTE) { quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of shift == size op (sft = 8 for byte operation) wf_op("\tif (src) CPU->flag_V = C68K_SR_V;\n"); wf_op("\telse CPU->flag_V = 0;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT;\n"); // write wf_op("\tres = 0;\n"); _ea_write(current_ea, current_op->reg_sft); // others flags wf_op("\tCPU->flag_N = 0;\n"); wf_op("\tCPU->flag_notZ = 0;\n"); } terminate_op(6); } static void GenLSLk() { u32 base; current_ea = EA_DREG; // dst = Dx base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); // generate label & declarations start_op(base, GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); adds_CCnt("sft * 2"); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); if (((current_sft_mask + 1) - C68K_SR_C_SFT) < 8) wf_op("\tCPU->flag_X = CPU->flag_C = src << (%d + sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); else wf_op("\tCPU->flag_X = CPU->flag_C = src >> (%d - sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); wf_op("\tres = src << sft;\n"); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenROXLk() { u32 base; current_ea = EA_DREG; // dst = Dx base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); // generate label & declarations start_op(base, GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); adds_CCnt("sft * 2"); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & C / X flags calculation if (current_size != SIZE_LONG) { wf_op("\tsrc |= (CPU->flag_X & C68K_SR_X) << %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); wf_op("\tres = (src << sft) | (src >> (%d - sft));\n", current_sft_mask + 2); wf_op("\tCPU->flag_X = CPU->flag_C = res >> %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); } else { wf_op("\tCPU->flag_C = src >> ((32 - C68K_SR_C_SFT) - sft);\n"); wf_op("\tif (sft == 1) res = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> ((C68K_SR_X_SFT + 1) - 1));\n"); wf_op("\telse res = (src << sft) | (src >> (33 - sft)) | ((CPU->flag_X & C68K_SR_X) >> ((C68K_SR_X_SFT + 1) - sft));\n"); wf_op("\tCPU->flag_X = CPU->flag_C;\n"); } // V / N / Z flags calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); if (current_size == SIZE_LONG) wf_op("\tCPU->flag_notZ = res;\n"); else wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenROLk() { u32 base; current_ea = EA_DREG; // dst = Dx base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0E00, 0x0200, base); // generate label & declarations start_op(base, GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = (((Opcode >> 9) - 1) & 7) + 1;\n"); adds_CCnt("sft * 2"); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); if (((current_sft_mask + 1) - C68K_SR_C_SFT) < 8) wf_op("\tCPU->flag_C = src << (%d + sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); else wf_op("\tCPU->flag_C = src >> (%d - sft);\n", current_sft_mask + 1 - C68K_SR_C_SFT); wf_op("\tres = (src << sft) | (src >> (%d - sft));\n", current_sft_mask + 1); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); if (current_size == SIZE_LONG) wf_op("\tCPU->flag_notZ = res;\n"); else wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(6); } static void GenASRD() { // u32 base = get_current_opcode_base(); current_ea = EA_DREG; // dst = Dx start_all(GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); // read (sign extend) _ea_calc(current_ea, current_op->reg_sft); _ea_read_src_sx(current_ea, current_op->reg_sft); // if (shift != 0) wf_op("\tif (sft)\n"); wf_op("\t{\n"); adds_CCnt("sft * 2"); // if (shift < size op) wf_op("\t\tif (sft < %d)\n", current_sft_mask + 1); wf_op("\t\t{\n"); // op & flag calculation wf_op("\t\t\tCPU->flag_V = 0;\n"); if (current_size == SIZE_BYTE) wf_op("\t\t\tCPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); else wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src >> (sft - 1)) << C68K_SR_C_SFT;\n"); wf_op("\t\t\tres = ((s32)src) >> sft;\n", szcs); wf_op("\t\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\t\t\tCPU->flag_notZ = res;\n"); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t\t}\n"); wf_op("\n"); // special case of shift >= size op // if signed wf_op("\t\tif (src & (1 << %d))\n", current_sft_mask); wf_op("\t\t{\n"); // op & flag calculation wf_op("\t\t\tCPU->flag_N = C68K_SR_N;\n"); wf_op("\t\t\tCPU->flag_notZ = 1;\n"); wf_op("\t\t\tCPU->flag_V = 0;\n"); wf_op("\t\t\tCPU->flag_C = C68K_SR_C;\n"); wf_op("\t\t\tCPU->flag_X = C68K_SR_X;\n"); wf_op("\t\t\tres = 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t\t}\n"); wf_op("\n"); // if not signed wf_op("\t\tCPU->flag_N = 0;\n"); wf_op("\t\tCPU->flag_notZ = 0;\n"); wf_op("\t\tCPU->flag_V = 0;\n"); wf_op("\t\tCPU->flag_C = 0;\n"); wf_op("\t\tCPU->flag_X = 0;\n"); wf_op("\t\tres = 0;\n"); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of (shift == 0) wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = 0;\n"); wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = src;\n"); terminate_op(6); } static void GenLSRD() { // u32 base = get_current_opcode_base(); current_ea = EA_DREG; // dst = Dx start_all(GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // if (shift != 0) wf_op("\tif (sft)\n"); wf_op("\t{\n"); adds_CCnt("sft * 2"); // if (shift <= size op) if (current_size == SIZE_LONG) wf_op("\t\tif (sft < 32)\n"); else wf_op("\t\tif (sft <= %d)\n", current_sft_mask + 1); wf_op("\t\t{\n"); // op & flag calculation wf_op("\t\t\tCPU->flag_N = CPU->flag_V = 0;\n"); if (current_size == SIZE_BYTE) wf_op("\t\t\tCPU->flag_X = CPU->flag_C = src << ((C68K_SR_C_SFT + 1) - sft);\n"); else wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src >> (sft - 1)) << C68K_SR_C_SFT;\n"); wf_op("\t\t\tres = src >> sft;\n", szcs); wf_op("\t\t\tCPU->flag_notZ = res;\n"); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t\t}\n"); wf_op("\n"); // special case of shift > size op if (current_size == SIZE_LONG) { wf_op("\t\tif (sft == 32) CPU->flag_C = src >> (31 - C68K_SR_C_SFT);\n"); wf_op("\t\telse CPU->flag_C = 0;\n"); wf_op("\t\tCPU->flag_X = CPU->flag_C;\n"); } else wf_op("\t\tCPU->flag_X = CPU->flag_C = 0;\n"); wf_op("\t\tCPU->flag_N = 0;\n"); wf_op("\t\tCPU->flag_notZ = 0;\n"); wf_op("\t\tCPU->flag_V = 0;\n"); wf_op("\t\tres = 0;\n"); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of (shift == 0) wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = 0;\n"); wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = src;\n"); terminate_op(6); } static void GenROXRD() { // u32 base = get_current_opcode_base(); current_ea = EA_DREG; // dst = Dx start_all(GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // if (shift != 0) wf_op("\tif (sft)\n"); wf_op("\t{\n"); adds_CCnt("sft * 2"); wf_op("\t\tsft %%= %d;\n", current_sft_mask + 2); wf_op("\n"); // op & C / X flag calculation if (current_size != SIZE_LONG) { wf_op("\t\tsrc |= (CPU->flag_X & C68K_SR_X) << %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); wf_op("\t\tres = (src >> sft) | (src << (%d - sft));\n", current_sft_mask + 2); wf_op("\t\tCPU->flag_X = CPU->flag_C = res >> %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); } else { wf_op("\t\tif (sft != 0)\n"); wf_op("\t\t{\n"); wf_op("\t\t\tif (sft == 1) res = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1)));\n"); wf_op("\t\t\telse res = (src >> sft) | (src << (33 - sft)) | (((CPU->flag_X & C68K_SR_X) << (32 - (C68K_SR_X_SFT + 1))) >> (sft - 1));\n"); wf_op("\t\t\tCPU->flag_X = (src >> (32 - sft)) << C68K_SR_X_SFT;\n"); wf_op("\t\t}\n"); wf_op("\t\telse res = src;\n"); wf_op("\t\tCPU->flag_C = CPU->flag_X;\n"); } // V / N / Z flag calculation wf_op("\t\tCPU->flag_V = 0;\n"); wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); if (current_size == SIZE_LONG) wf_op("\t\tCPU->flag_notZ = res;\n"); else wf_op("\t\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of (shift == 0) wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = CPU->flag_X;\n"); wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = src;\n"); terminate_op(6); } static void GenRORD() { // u32 base = get_current_opcode_base(); current_ea = EA_DREG; // dst = Dx start_all(GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // if (shift != 0) wf_op("\tif (sft)\n"); wf_op("\t{\n"); adds_CCnt("sft * 2"); wf_op("\t\tsft &= 0x%.2X;\n", current_sft_mask); wf_op("\t\t\n"); // op & flag calculation if (current_size == SIZE_BYTE) wf_op("\t\tCPU->flag_C = src << (C68K_SR_C_SFT - ((sft - 1) & 7));\n"); else wf_op("\t\tCPU->flag_C = (src >> ((sft - 1) & %d)) << C68K_SR_C_SFT;\n", current_sft_mask); wf_op("\t\tres = (src >> sft) | (src << (%d - sft));\n", current_sft_mask + 1); wf_op("\t\tCPU->flag_V = 0;\n"); wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); if (current_size == SIZE_LONG) wf_op("\t\tCPU->flag_notZ = res;\n"); else wf_op("\t\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of (shift == 0) wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = 0;\n"); wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = src;\n"); terminate_op(6); } static void GenASLD() { // u32 base = get_current_opcode_base(); current_ea = EA_DREG; // dst = Dx start_all(GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // if (shift != 0) wf_op("\tif (sft)\n"); wf_op("\t{\n"); adds_CCnt("sft * 2"); // if (shift < size op) wf_op("\t\tif (sft < %d)\n", current_sft_mask + 1); wf_op("\t\t{\n"); // op & flag calculation if (current_size != SIZE_LONG) { wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src << sft) >> %d;\n", (current_sft_mask + 1) - C68K_SR_C_SFT); wf_op("\t\t\tres = (src << sft) & 0x%.8X;\n", current_bits_mask); } else { wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT;\n"); wf_op("\t\t\tres = src << sft;\n"); } wf_op("\t\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\t\t\tCPU->flag_notZ = res;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); // we do V flag calculation at end for a better register usage wf_op("\t\t\tCPU->flag_V = 0;\n"); wf_op("\t\t\t{\n"); wf_op("\t\t\t\tu32 msk = (((s32)0x80000000) >> (sft + %d)) & 0x%.8X;\n", 31 - current_sft_mask, current_bits_mask); wf_op("\t\t\t\tsrc &= msk;\n"); wf_op("\t\t\t\tif ((src) && (src != msk)) CPU->flag_V = C68K_SR_V;\n"); wf_op("\t\t\t}\n"); quick_terminate_op(6); wf_op("\t\t}\n"); wf_op("\n"); // special case of shift >= size op wf_op("\t\tif (sft == %d) CPU->flag_C = src << C68K_SR_C_SFT;\n", current_bits_mask + 1); wf_op("\t\telse CPU->flag_C = 0;\n"); wf_op("\t\tCPU->flag_X = CPU->flag_C;\n"); wf_op("\t\tif (src) CPU->flag_V = C68K_SR_V;\n"); wf_op("\t\telse CPU->flag_V = 0;\n"); wf_op("\t\tres = 0;\n"); // write _ea_write(current_ea, current_op->reg_sft); // others flags wf_op("\t\tCPU->flag_N = 0;\n"); wf_op("\t\tCPU->flag_notZ = 0;\n"); quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of (shift == 0) wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = 0;\n"); wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = src;\n"); terminate_op(6); } static void GenLSLD() { // u32 base = get_current_opcode_base(); current_ea = EA_DREG; // dst = Dx start_all(GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // if (shift != 0) wf_op("\tif (sft)\n"); wf_op("\t{\n"); adds_CCnt("sft * 2"); // if (shift <= size op) if (current_size == SIZE_LONG) wf_op("\t\tif (sft < 32)\n"); else wf_op("\t\tif (sft <= %d)\n", current_sft_mask + 1); wf_op("\t\t{\n"); // op & flag calculation if (current_size != SIZE_LONG) { wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src << sft) >> %d;\n", (current_sft_mask + 1) - C68K_SR_C_SFT); wf_op("\t\t\tres = (src << sft) & 0x%.8X;\n", current_bits_mask); } else { wf_op("\t\t\tCPU->flag_X = CPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT;\n"); wf_op("\t\t\tres = src << sft;\n"); } wf_op("\t\t\tCPU->flag_V = 0;\n"); wf_op("\t\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\t\t\tCPU->flag_notZ = res;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t\t}\n"); wf_op("\n"); // special case of shift > size op if (current_size == SIZE_LONG) { wf_op("\t\tif (sft == 32) CPU->flag_C = src << C68K_SR_C_SFT;\n"); wf_op("\t\telse CPU->flag_C = 0;\n"); wf_op("\t\tCPU->flag_X = CPU->flag_C;\n"); } else wf_op("\t\tCPU->flag_X = CPU->flag_C = 0;\n"); wf_op("\t\tCPU->flag_N = 0;\n"); wf_op("\t\tCPU->flag_notZ = 0;\n"); wf_op("\t\tCPU->flag_V = 0;\n"); wf_op("\t\tres = 0;\n"); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of (shift == 0) wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = 0;\n"); wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = src;\n"); terminate_op(6); } static void GenROXLD() { // u32 base = get_current_opcode_base(); current_ea = EA_DREG; // dst = Dx start_all(GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // if (shift != 0) wf_op("\tif (sft)\n"); wf_op("\t{\n"); adds_CCnt("sft * 2"); wf_op("\t\tsft %%= %d;\n", current_sft_mask + 2); wf_op("\n"); // op & C/X flags calculation if (current_size != SIZE_LONG) { wf_op("\t\tsrc |= (CPU->flag_X & C68K_SR_X) << %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); wf_op("\t\tres = (src << sft) | (src >> (%d - sft));\n", current_sft_mask + 2); wf_op("\t\tCPU->flag_X = CPU->flag_C = res >> %d;\n", (current_sft_mask + 1) - C68K_SR_X_SFT); } else { wf_op("\t\tif (sft != 0)\n"); wf_op("\t\t{\n"); wf_op("\t\t\tif (sft == 1) res = (src << 1) | ((CPU->flag_X >> ((C68K_SR_X_SFT + 1) - 1)) & 1);\n"); wf_op("\t\t\telse res = (src << sft) | (src >> (33 - sft)) | (((CPU->flag_X >> ((C68K_SR_X_SFT + 1) - 1)) & 1) << (sft - 1));\n"); wf_op("\t\t\tCPU->flag_X = (src >> (32 - sft)) << C68K_SR_X_SFT;\n"); wf_op("\t\t}\n"); wf_op("\t\telse res = src;\n"); wf_op("\t\tCPU->flag_C = CPU->flag_X;\n"); } // V / N / Z flags calculation wf_op("\t\tCPU->flag_V = 0;\n"); wf_op("\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); if (current_size == SIZE_LONG) wf_op("\t\tCPU->flag_notZ = res;\n"); else wf_op("\t\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of (shift == 0) wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = CPU->flag_X;\n"); wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = src;\n"); terminate_op(6); } static void GenROLD() { // u32 base = get_current_opcode_base(); current_ea = EA_DREG; // dst = Dx start_all(GEN_RES | GEN_SRC); if (current_size == SIZE_LONG) current_cycle += 2; wf_op("\tu32 sft;\n"); wf_op("\n"); wf_op("\tsft = CPU->D[(Opcode >> %d) & 7] & 0x3F;\n", current_op->reg2_sft); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // if (shift != 0) wf_op("\tif (sft)\n"); wf_op("\t{\n"); adds_CCnt("sft * 2"); // if ((shift & size op) != 0) wf_op("\t\tif (sft &= 0x%.2X)\n", current_sft_mask); wf_op("\t\t{\n"); // op & flag calculation if (current_size != SIZE_LONG) { wf_op("\t\t\tCPU->flag_C = (src << sft) >> %d;\n", (current_sft_mask + 1) - C68K_SR_C_SFT); wf_op("\t\t\tres = ((src << sft) | (src >> (%d - sft))) & 0x%.8X;\n", current_sft_mask + 1, current_bits_mask); } else { wf_op("\t\t\tCPU->flag_C = (src >> (32 - sft)) << C68K_SR_C_SFT;\n"); wf_op("\t\t\tres = (src << sft) | (src >> (%d - sft));\n", current_sft_mask + 1); } wf_op("\t\t\tCPU->flag_V = 0;\n"); wf_op("\t\t\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\t\t\tCPU->flag_notZ = res;\n"); // write _ea_write(current_ea, current_op->reg_sft); quick_terminate_op(6); wf_op("\t\t}\n"); wf_op("\n"); // special case of ((shift & size op) == 0) wf_op("\t\tCPU->flag_V = 0;\n"); wf_op("\t\tCPU->flag_C = src << C68K_SR_C_SFT;\n"); wf_op("\t\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\t\tCPU->flag_notZ = src;\n"); quick_terminate_op(6); wf_op("\t}\n"); wf_op("\n"); // special case of (shift == 0) wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = 0;\n"); wf_op("\tCPU->flag_N = src >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = src;\n"); terminate_op(6); } static void GenASR() { set_current_size(SIZE_WORD); // dst = mem (word operation) start_all(GEN_ADR | GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT;\n"); wf_op("\tres = (src >> 1) | (src & (1 << %d));\n", current_sft_mask); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res;\n"); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenLSR() { set_current_size(SIZE_WORD); // dst = mem (word operation) start_all(GEN_ADR | GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_N = CPU->flag_V = 0;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = src << C68K_SR_C_SFT;\n"); wf_op("\tres = src >> 1;\n"); wf_op("\tCPU->flag_notZ = res;\n"); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenROXR() { set_current_size(SIZE_WORD); // dst = mem (word operation) start_all(GEN_ADR | GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tres = (src >> 1) | ((CPU->flag_X & C68K_SR_X) << %d);\n", current_sft_mask - C68K_SR_X_SFT); wf_op("\tCPU->flag_C = CPU->flag_X = src << C68K_SR_C_SFT;\n"); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res;\n"); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenROR() { set_current_size(SIZE_WORD); // dst = mem (word operation) start_all(GEN_ADR | GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = src << C68K_SR_C_SFT;\n"); wf_op("\tres = (src >> 1) | (src << %d);\n", current_sft_mask); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenASL() { set_current_size(SIZE_WORD); // dst = mem (word operation) start_all(GEN_ADR | GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_X = CPU->flag_C = src >> %d;\n", current_sft_mask - C68K_SR_C_SFT); wf_op("\tres = src << 1;\n"); wf_op("\tCPU->flag_V = (src ^ res) >> %d;\n", current_sft_mask - C68K_SR_V_SFT); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenLSL() { set_current_size(SIZE_WORD); // dst = mem (word operation) start_all(GEN_ADR | GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_X = CPU->flag_C = src >> %d;\n", current_sft_mask - C68K_SR_C_SFT); wf_op("\tres = src << 1;\n"); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenROXL() { set_current_size(SIZE_WORD); // dst = mem (word operation) start_all(GEN_ADR | GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tres = (src << 1) | ((CPU->flag_X & C68K_SR_X) >> %d);\n", C68K_SR_X_SFT); wf_op("\tCPU->flag_X = CPU->flag_C = src >> %d;\n", current_sft_mask - C68K_SR_C_SFT); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void GenROL() { set_current_size(SIZE_WORD); // dst = mem (word operation) start_all(GEN_ADR | GEN_RES | GEN_SRC); // read _ea_calc(current_ea, current_op->reg_sft); _ea_read_src(current_ea, current_op->reg_sft); // op & flag calculation wf_op("\tCPU->flag_V = 0;\n"); wf_op("\tCPU->flag_C = src >> %d;\n", current_sft_mask - C68K_SR_C_SFT); wf_op("\tres = (src << 1) | (src >> %d);\n", current_sft_mask); wf_op("\tCPU->flag_N = res >> %d;\n", current_sft_mask - C68K_SR_N_SFT); wf_op("\tCPU->flag_notZ = res & 0x%.8X;\n", current_bits_mask); // write _ea_write(current_ea, current_op->reg_sft); terminate_op(8); } static void Gen1010() { u32 base; base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0FFF, 0x1, base); // generate label & declarations start_op(base, GEN_RES); wf_op("\tPC -= 2;\n"); gen_exception("\t", "C68K_1010_EX"); terminate_op(4); } static void Gen1111() { u32 base; base = get_current_opcode_base(); // generate jump table gen_opjumptable_ext(base, 0x0000, 0x0FFF, 0x1, base); // generate label & declarations start_op(base, GEN_RES); wf_op("\tPC -= 2;\n"); gen_exception("\t", "C68K_1111_EX"); terminate_op(4); } #ifdef NEOCD_HLE static void Gen0xFABE() { start_all(GEN_ALL); wf_op("\tneogeo_exit();\n"); terminate_op(0); } static void Gen0xFABF() { start_all(GEN_ALL); wf_op("\timg_display = 1;\n"); wf_op("\tcdrom_load_files();\n"); terminate_op(0); } static void Gen0xFAC0() { start_all(GEN_ALL); wf_op("\timg_display = 0;\n"); wf_op("\tcdrom_load_files();\n"); terminate_op(0); } static void Gen0xFAC1() { start_all(GEN_ALL); wf_op("\tneogeo_upload();\n"); terminate_op(0); } static void Gen0xFAC2() { start_all(GEN_ALL); wf_op("\tneogeo_prio_switch();\n"); terminate_op(0); } static void Gen0xFAC3() { start_all(GEN_ALL); wf_op("\tneogeo_cdda_control();\n"); terminate_op(0); } #endif // main function ///////////////// int main(void) { u32 i; u32 s; u32 smax; // clear opcode files for(i = 0; i < 0x10; i++) { char fn[16]; sprintf(fn, "c68k_op%.1X.inc", (int)i); opcode_file = fopen(fn, "wt"); if (opcode_file != NULL) { fclose(opcode_file); opcode_file = NULL; } } // init opcode jump table ini_file = fopen("c68k_ini.inc", "wt"); #ifndef C68K_NO_JUMP_TABLE #ifdef C68K_CONST_JUMP_TABLE for(i = 0; i < 0x10000; i++) op_jump_table[i] = OP_ILLEGAL; #else // defaut ILLEGAL instruction gen_jumptable(0x0000, 0x0000, 0xFFFF, 1, 0, 0, 0, 0, 0, 0, 0x4AFC); #endif #endif // generate opcode files for(i = 0; i < OP_INFO_TABLE_LEN; i++) { current_op = &(op_info_table[i]); if (prepare_generate()) return 1; // s = size to start current_size = 0; smax = SIZE_LONG; if (current_op->size_type == 0) smax = 0; else if (current_op->size_type == 1) current_size = 1; for(s = current_size; s <= smax; s++) { if (current_op->eam_sft != -1) { for(current_ea = 0; current_ea <= EA_ADEC7; current_ea++) { if (!has_ea(current_ea)) continue; current_eam = _ea_to_eamreg(current_ea) >> 3; current_reg = _ea_to_eamreg(current_ea) & 7; if (op_info_table[i].eam2_sft != -1) { for(current_ea2 = 0; current_ea2 <= EA_ADEC7; current_ea2++) { if (!has_ea2(current_ea2)) continue; current_eam2 = _ea_to_eamreg(current_ea2) >> 3; current_reg2 = _ea_to_eamreg(current_ea2) & 7; set_current_size(s); current_op->genfunc(); } } else { current_reg2 = 0; set_current_size(s); current_op->genfunc(); } } } else { current_reg = 0; set_current_size(s); current_op->genfunc(); } } } // generate jumptable file #ifdef C68K_CONST_JUMP_TABLE if (ini_file != NULL) { fprintf(ini_file, "\tstatic const void *JumpTable[0x10000] =\n"); fprintf(ini_file, "\t{\n"); for(i = 0; i < (0x10000 - 4); i += 4) fprintf(ini_file, "\t\t&&OP_0x%.4X, &&OP_0x%.4X, &&OP_0x%.4X, &&OP_0x%.4X,\n", op_jump_table[i + 0], op_jump_table[i + 1], op_jump_table[i + 2], op_jump_table[i + 3]); fprintf(ini_file, "\t\t&&OP_0x%.4X, &&OP_0x%.4X, &&OP_0x%.4X, &&OP_0x%.4X\n", op_jump_table[0xFFFC], op_jump_table[0xFFFD], op_jump_table[0xFFFE], op_jump_table[0xFFFF]); fprintf(ini_file, "\t};\n\n"); } #endif // close handle if (ini_file != NULL) fclose(ini_file); if (opcode_file != NULL) fclose(opcode_file); return 0; } #endif yabause-0.9.13.1/src/c68k/c68k.c000644 001750 001750 00000015455 12256006174 017704 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2004 Stephane Dallongeville This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /********************************************************************************* * * C68K (68000 CPU emulator) version 0.80 * Compiled with Dev-C++ * Copyright 2003-2004 Stephane Dallongeville * ********************************************************************************/ #include #include #include "c68k.h" // shared global variable ////////////////////////// c68k_struc C68K; // include macro file ////////////////////// #include "c68kmac.inc" // prototype ///////////// u32 FASTCALL C68k_Read_Dummy(const u32 adr); void FASTCALL C68k_Write_Dummy(const u32 adr, u32 data); u32 C68k_Read_Byte(c68k_struc *cpu, u32 adr); u32 C68k_Read_Word(c68k_struc *cpu, u32 adr); u32 C68k_Read_Long(c68k_struc *cpu, u32 adr); void C68k_Write_Byte(c68k_struc *cpu, u32 adr, u32 data); void C68k_Write_Word(c68k_struc *cpu, u32 adr, u32 data); void C68k_Write_Long(c68k_struc *cpu, u32 adr, u32 data); s32 FASTCALL C68k_Interrupt_Ack_Dummy(s32 level); void FASTCALL C68k_Reset_Dummy(void); // core main functions /////////////////////// void C68k_Init(c68k_struc *cpu, C68K_INT_CALLBACK *int_cb) { memset(cpu, 0, sizeof(c68k_struc)); C68k_Set_ReadB(cpu, C68k_Read_Dummy); C68k_Set_ReadW(cpu, C68k_Read_Dummy); C68k_Set_WriteB(cpu, C68k_Write_Dummy); C68k_Set_WriteW(cpu, C68k_Write_Dummy); if (int_cb) cpu->Interrupt_CallBack = int_cb; else cpu->Interrupt_CallBack = C68k_Interrupt_Ack_Dummy; cpu->Reset_CallBack = C68k_Reset_Dummy; // used to init JumpTable cpu->Status |= C68K_DISABLE; C68k_Exec(cpu, 0); cpu->Status &= ~C68K_DISABLE; } s32 FASTCALL C68k_Reset(c68k_struc *cpu) { memset(cpu, 0, ((u8 *)&(cpu->dirty1)) - ((u8 *)&(cpu->D[0]))); cpu->flag_notZ = 1; cpu->flag_I = 7; cpu->flag_S = C68K_SR_S; cpu->A[7] = C68k_Read_Long(cpu, 0); C68k_Set_PC(cpu, C68k_Read_Long(cpu, 4)); return cpu->Status; } ///////////////////////////////// void FASTCALL C68k_Set_IRQ(c68k_struc *cpu, s32 level) { cpu->IRQLine = level; if (cpu->Status & C68K_RUNNING) { cpu->CycleSup = cpu->CycleIO; cpu->CycleIO = 0; } cpu->Status &= ~(C68K_HALTED | C68K_WAITING); } ///////////////////////////////// s32 FASTCALL C68k_Get_CycleToDo(c68k_struc *cpu) { if (!(cpu->Status & C68K_RUNNING)) return -1; return cpu->CycleToDo; } s32 FASTCALL C68k_Get_CycleRemaining(c68k_struc *cpu) { if (!(cpu->Status & C68K_RUNNING)) return -1; return (cpu->CycleIO + cpu->CycleSup); } s32 FASTCALL C68k_Get_CycleDone(c68k_struc *cpu) { if (!(cpu->Status & C68K_RUNNING)) return -1; return (cpu->CycleToDo - (cpu->CycleIO + cpu->CycleSup)); } void FASTCALL C68k_Release_Cycle(c68k_struc *cpu) { if (cpu->Status & C68K_RUNNING) cpu->CycleIO = cpu->CycleSup = 0; } void FASTCALL C68k_Add_Cycle(c68k_struc *cpu, s32 cycle) { if (cpu->Status & C68K_RUNNING) cpu->CycleIO -= cycle; } // Read / Write dummy functions //////////////////////////////// u32 FASTCALL C68k_Read_Dummy(UNUSED const u32 adr) { return 0; } void FASTCALL C68k_Write_Dummy(UNUSED const u32 adr, UNUSED u32 data) { } s32 FASTCALL C68k_Interrupt_Ack_Dummy(s32 level) { // return vector return (C68K_INTERRUPT_AUTOVECTOR_EX + level); } void FASTCALL C68k_Reset_Dummy(void) { } // Read / Write core functions /////////////////////////////// u32 C68k_Read_Byte(c68k_struc *cpu, u32 adr) { return cpu->Read_Byte(adr); } u32 C68k_Read_Word(c68k_struc *cpu, u32 adr) { return cpu->Read_Word(adr); } u32 C68k_Read_Long(c68k_struc *cpu, u32 adr) { #ifdef C68K_BIG_ENDIAN return (cpu->Read_Word(adr) << 16) | (cpu->Read_Word(adr + 2) & 0xFFFF); #else return (cpu->Read_Word(adr) << 16) | (cpu->Read_Word(adr + 2) & 0xFFFF); #endif } void C68k_Write_Byte(c68k_struc *cpu, u32 adr, u32 data) { cpu->Write_Byte(adr, data); } void C68k_Write_Word(c68k_struc *cpu, u32 adr, u32 data) { cpu->Write_Word(adr, data); } void C68k_Write_Long(c68k_struc *cpu, u32 adr, u32 data) { #ifdef C68K_BIG_ENDIAN cpu->Write_Word(adr, data >> 16); cpu->Write_Word(adr + 2, data & 0xFFFF); #else cpu->Write_Word(adr, data >> 16); cpu->Write_Word(adr + 2, data & 0xFFFF); #endif } // setting core functions ////////////////////////// void C68k_Set_Fetch(c68k_struc *cpu, u32 low_adr, u32 high_adr, pointer fetch_adr) { u32 i, j; i = (low_adr >> C68K_FETCH_SFT) & C68K_FETCH_MASK; j = (high_adr >> C68K_FETCH_SFT) & C68K_FETCH_MASK; fetch_adr -= i << C68K_FETCH_SFT; while (i <= j) cpu->Fetch[i++] = fetch_adr; } void C68k_Set_ReadB(c68k_struc *cpu, C68K_READ *Func) { cpu->Read_Byte = Func; } void C68k_Set_ReadW(c68k_struc *cpu, C68K_READ *Func) { cpu->Read_Word = Func; } void C68k_Set_WriteB(c68k_struc *cpu, C68K_WRITE *Func) { cpu->Write_Byte = Func; } void C68k_Set_WriteW(c68k_struc *cpu, C68K_WRITE *Func) { cpu->Write_Word = Func; } // externals main functions //////////////////////////// u32 C68k_Get_DReg(c68k_struc *cpu, u32 num) { return cpu->D[num]; } u32 C68k_Get_AReg(c68k_struc *cpu, u32 num) { return cpu->A[num]; } u32 C68k_Get_PC(c68k_struc *cpu) { return (cpu->PC - cpu->BasePC); } u32 C68k_Get_SR(c68k_struc *cpu) { c68k_struc *CPU = cpu; return GET_SR; } u32 C68k_Get_USP(c68k_struc *cpu) { if (cpu->flag_S) return cpu->USP; else return cpu->A[7]; } u32 C68k_Get_MSP(c68k_struc *cpu) { if (cpu->flag_S) return cpu->A[7]; else return cpu->USP; } void C68k_Set_DReg(c68k_struc *cpu, u32 num, u32 val) { cpu->D[num] = val; } void C68k_Set_AReg(c68k_struc *cpu, u32 num, u32 val) { cpu->A[num] = val; } void C68k_Set_PC(c68k_struc *cpu, u32 val) { cpu->BasePC = cpu->Fetch[(val >> C68K_FETCH_SFT) & C68K_FETCH_MASK]; cpu->PC = val + cpu->BasePC; } void C68k_Set_SR(c68k_struc *cpu, u32 val) { c68k_struc *CPU = cpu; SET_SR(val); } void C68k_Set_USP(c68k_struc *cpu, u32 val) { if (cpu->flag_S) cpu->USP = val; else cpu->A[7] = val; } void C68k_Set_MSP(c68k_struc *cpu, u32 val) { if (cpu->flag_S) cpu->A[7] = val; else cpu->USP = val; } yabause-0.9.13.1/src/android/android.cmake000644 001750 001750 00000000675 12256006052 022245 0ustar00guillaumeguillaume000000 000000 SET(CMAKE_SYSTEM_NAME Linux) SET(CMAKE_SYSTEM_VERSION 1) SET(CMAKE_C_COMPILER arm-linux-androideabi-gcc) SET(CMAKE_CXX_COMPILER arm-linux-androideabi-g++) SET(CMAKE_ASM-ATT_COMPILER arm-linux-androideabi-as) SET(CMAKE_FIND_ROOT_PATH /home/guillaume/projects/android/toolchain/sysroot/usr/) SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) SET(ANDROID ON) yabause-0.9.13.1/src/android/res/values/strings.xml000644 001750 001750 00000000155 12256006053 024120 0ustar00guillaumeguillaume000000 000000 Yabause yabause-0.9.13.1/src/android/res/layout/main.xml000644 001750 001750 00000001053 12256006053 023367 0ustar00guillaumeguillaume000000 000000 yabause-0.9.13.1/src/android/res/menu/emulation.xml000644 001750 001750 00000000774 12256006053 024100 0ustar00guillaumeguillaume000000 000000 yabause-0.9.13.1/src/android/res/drawable-ldpi/icon.png000644 001750 001750 00000004754 12256006053 024564 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDR$$ᘘsRGB®ÎébKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÛ ¶áêp lIDATXÃí˜kpVÅÇ»çþ¾y r5.¢ÈE´FA£ö¦C½´JÕꨭmmµZÇiÛâØKF¬â¨Ã¨ÅŠvÚqt¬e¼b½ÚVQ.*bE¹%$’¼—sÎ{ÎîöCB˜âØo>3çË9ûìþöÿcßZ := œ-d-Òž²Ÿ~ ªü: v‚s@Ïg[N3ˆÜ° t²cúî{¼ÿDƒ\Œ´Æ 먒¨ø€ÀõŸæ,ˤ= Û?x˜t-iô,ÀÛÀ‰{5Ø€l'sBTìåhÙ¾'éßy*íQ V.j-Th÷8-­ŒQÍBëJc),™EX«¦\5÷B!ÜÊŠÑ×’&>Jé½5×;HJTL É=¾oócLˆŠ–†tÀ\×!“ñ²Yªë/´uÑ©'~œ5^ÞHùºÀ,ˆ9SiI«] 뉋o½·LeUÍYär5d2>Žcï1\B= (æ °÷Qða0s´ÚmæŽÉ æ…íå·¿ÞÚšö3E9/››@}ãéÄQŠ”!” IRÒøeŒÞ p3°b7€ØOì?ÑàW\B®r AÆÃ<‚ÀÅó\ Ϫå7 uÒeŒ©Æÿc!¬ñÇκÛ©# c¢¨LÆ„¥ˆ|ïJÊ¥%«ã÷t”û:H“ðil'Äó]‚À%|2YŸªQ‡qÄÑçcŒ©Z÷ñ½?ñ¨9T×N"<‚Œï»ø¾‹eõ‘„Ï¢œºïÀÃ}°èÎâ×^¾YÓÛýžgãùa LÖç˜æKÉæjBÞŒt­BΫÈÕs쉗“Éøo0äŽ#ØÕõƤ»ó¦´ïØÖ¾/Ö-úc½Vjyµo7UøžüwÛJaÛ65õ3–ñ 2>Ùl–Q£ÙÐþ’”ýaàe`ììóæS5j,B d„1£ =J¾¯ `ðÈpbÈ}aŒ2+Š=ÝMÃgí϶»·uç]4o·7y~ß¹°•Lf ¼®çàziº‹õï.D-ZI‡~ûÄ-,LÃhJÏ–ö¸æ–ØXâÌ©?¹¾è1F_G½¬\~g}÷.ÇR«iÂt®›û$ÅbU£§¦®‘R)"MI9%’ðÖ擦%Œ1ç½²ÖßëI þ´þ•çíñ3O-Û–Õ:åg7>µG»ÕÀ1…ü§GKËbü³ÂxÛŽd⤌U‡R•jTªH’”å¯ÞÍæ^¸ øóÁö@)swÏ–TvÔÛvÝvR±`˜¶ç[W¿ù×Ú&43mÆ72!R`Û2‘H!Ìe>þïk´­ùÀZà†ÖãÆX‰ú…âRcD5‚Y&0wÞôÞö6¹þÁÛŽ—–lÞ´úM»vÒäHZÖý“®».ÞÏZ@¤ÿ|êFúz·£”&IRÂ0&Ÿ/Ge’4%M=;:xaéïB„À)7O¯n+ÓæyÁ¯Æ75›X]™­«pǸ¶u‰¬¾yFýeRyaaÇö²*Çd«FûR³äŠn6F_†}<ûøo‰£2a)¦X )äK !QG1KÏ%)‡c.hZ­¥-_¬oš8ú¢_ßä6ŸùmÆO;†º¬GÓÏÉ9¶#´yèæcjgÊ4IÎݱé#ÏËÔ*QÆÚp0?ˆ1oÝòaS*†ò!ù|‰B!¤XŒèëÝÅÖ-«1F¿µ¿×ò2ÉÆmÛ' Ë­];Nä*3d++°=R_žÞŽMôo\GχëÙ±a¥®Nª&ÉŒK¯â±¹×nCë1c9–h Q¢Ë©1`äE¿[ûéÓàýEóÇ¢ôÛý]ÛF~ð¯Ý$ ‡¤ð²9ªÆŽÇQ‰[‘ò` #’(¤\*R.æÉ²…¸Ð¿÷¾ä¸Œmù&ÓÏÿ~,¥|ëïWÿàœ¾¨ðSaÄ%@-„Z³T[ÜÛúN禽Jؼ½1)§K´QÓ¶®]ãlkoi9>äS§´lꎛIÓWgÓØÜ¢…ÒaÒy}]} ?ùäð`þ{ÕÔfñbk}ïÆK´Joµ,»¾ØŸOú¶uøQ¾¨§\èG+ B „ÀöüÊ‘äÆR5q2™Æ#¨=zÁ²g–²fmÛCw\}Í•bêÔògаE¾im•í þ,i»³…㜆íŽs²Ù:ÛÏø–ë#]ËóŽGYÁ¶1ë6y·½—k¶³æ½ò»î˜ Ü~( ÛÃR¶¶jàÁgÈÚ—,ÉemÏüÑ÷„±¸LÛWYqY3#õ¹/ìCi<ùÜsóC[‡ðq³ú ¿ù°?·§‰eR¼OïcÙïëÿ´ LÕàŬýËÛ®/ÚþàñOÝ,ÇáIEND®B`‚yabause-0.9.13.1/src/android/res/drawable-hdpi/icon.png000644 001750 001750 00000013203 12256006053 024545 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRHHUí³GsRGB®ÎébKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÛ  ß…IDATxÚí›y”Å}Ç?UÕ=³;{ïJ«ÝÕ‰´BÒjq‰[˜`°yÁ›‰í<Œí8NÂ{'aN@„sÄvb^& —lF`Œ £“Õ-±+iïkfú¨ÊÝ=Ó³ZI+XùxQ½Wo{z¦«ª¿õ;¾¿ß¯N´íD;Ñ~³-tæ8t¨û5½Ç·Ó;pà§ Þú5€3)ÜŒ±^{G|’ÛŽ@¸ô8ôîqX³ÌÃ&úp~ôÁ*º©¦}ˆõÜôã} ºq¨=Nà| x$ú ’—¡¬¹Ç<ˆïü ß]¿u ðò€ý@5"…ú"B”;Dþ>Üôcñ[w7Æà”†jj*Vñ Ç<ˆöZñ2?Šßz ¸@û­\èà-‡ð2ÏÄmÕ¨›PQ‰yñ[ßÊÇ '#p@¡’—ûFš¼ìòø­î¸I°Fxf Ð,$a¾³ •8Ô“J)Q–$™¼Œ®­ãØÀOéÙ=÷ß_øÏÍþÊíIàŠèV2uV²Ï÷1ÚŒÚø™gÀ å„ ø½PP P¤W ý|¡&Ê)KaY KI”U©¸˜ž£¯/=æ:€Mÿú5ÆVHÌBs œ2€sH‰Jîð×5R~†hÊGIÅ"´–Wàû>¾¯Gaw~ŽöwÇo-„£pahJ¤ŸG¦þDñÈ*%DLØ•¥PJ’¬¹„ÌÐZ<§@L¨ª\¾òžÛ~â" I`d€êBÏâÇŸù˜Ý«ª½Ë.Æ÷|„O€ðñ=ÿ6r/¾ózüÖ/;G˜ïˆí"àÕèwRÍÀ*¾öÐEËBp,Ë A’XJ‘Ú@Û®ïæ€|âÖ¯Ð<}Ê‘æÖ¿°Áh³É’jOÑ õÁä[nì@YÅÆÕÏ ¤Æó`<ÏÇu=¼‘@2iÜ¡ïcL_tg¨úŽ €û¿Ì»ÑKPöÙ‡€cÛÖ0 ”%QJ±gÛƒô÷n`îÔ‰<ù×_CŠÜôY¯ ̳HµjVÅÔ âÚk·ýëùJ¥˜~Êß)|_ç€ñ</g$¼Ì2´·%oˆ»óòH“Y£è–аÎð³+‘rBMÌ©U ËV‡¥,Å”׳qÝã³a×>–½¹Zæü3— -þ[9ég~½¥okùB@ôk(.®Â÷}|gJÉ<ÃÀ±m« [¶bƬkI$ÊÃ1Œž:ÊòÃ!³æÝD"açÆÎÍ3lƒT¸6%ýÓåÀq€sê•a[BÏÑ `t–]A²xJ¸Èü )‘,"‘,å`{ŽÒOÞZ2÷B`i$ñ'Í¼Š†)+ì‘n‰áÊ!ÀH÷=çn‹y-°ú¨œî‰ç;s¦¿ïÇs¥µ;0Ã$)aqÒÌ+¨ª™ó£˜wYôÆ%¥uÌ™ÿ¹‚M±í˜zÇ%'Tm'ó.Ùôšáã-é=Vj~ÙYó/_Q¦5ñè9ðRúy ìOh´í0›DÒæ´³¿7µÀG˜òžxNéôs¾FQqI{8µ(¼Ö~'}]ÿ¯ øìhß÷XTŒÍßY2ëã šž=ëäéµO¿µ­ ÚDûýTÕœêh7­aöAå$ª¼²žþÞ=ôt툆>øçÀÝ´àGÑFN~óNÿCäÕ밮ƠµË¾à¹]ñXó´0Þ[€6?ôç%^0¥¶²=4À/¶ï HLf/E© ”ULÍífÞ8G;mؤºIMl~ïY´ö¢u4O ›veh§H$K¹üS߯N#…Œ¹ò$SÁhßó4}=kãã}xñX„bTmyôž+ŒËrc {µ–sš›ØÖÑÃŽýêÛ·‘qµgQœª,4Ú¡íîÕJJ*°owÎ6ÌžÚÃÏ7_¾vmý÷¸\u…l™1hË£wŸi0Ïžï±é•Ÿ0~úÉ”ÖÉÁª²’‡2Ù¿Èyµ=ì|ÿ‰`±a°¼€p&[a[y»T\œâÒOÜŸrfÈ–ÿH©ÅWßI"‘È«iÎSåÇŽâ>)a˯Äuûã)ŒËâhLÚòз&j#žJ¯ÙüÊ Œ›ÖHeýÄHžÿcN[ækZ뀟GÏíݹ‚΃ër»©”*ØeË.´Qs›?ÎÔégƧþ~ÜËœ±ðz&M>¥æ²y©‘aß³c9=]›âãÝ7<…ñ‘ÚsÿýÅZZÏ ÃÖ7^¦¨¬œ 3gGà¬hoÏÜ$ZZ¢¤Ë" ?úòÝÕÿ‚“íA*‰’2H E`Å9“­° ®ºöN¤<Ô–UÔrùU·å©Â°Ð%„%R)””ôv·²uãŇx¸õ£¤,G¨¿Ä¹/J–íúå; õt1íÌ+ß,lñÙE--q‘Í„É0àd{Y½ê^„ )z‰ð…â<ɶSNšÏÙ\È:®¾n1eåU#HŽ ÁW9ð}/Íš7—b´_Ó5§{ƒØøèÝW #–tîÞÁæWWÐ|å§#»“ÕBž3÷K·þò0ã-þ*úдàYp΃˜)a‘HعkÛ¶ÂX* Z; t^ ÐZSU=×óð\Ç ƒNÇÃq\\×Ãɺ8Ž‹ãx¬\q'»·¿_Ë•Àò PAºcÛ#÷T8†‡ÜtšÖ7WÒ0g^Î(cî˜{óaÁ!ç§¼·ö1&53­q!RJ¤H)P9•Ë{:Û.£¢¢)e˜v"sƒÁhƒe´ÖøR ¥ H¢ úÖ Ï çŸÆœCTÌÁ,&l]õ Ú÷˜Ü|FÄ2ÞÕ÷bÌ ÂŒ Æ^{ñ.Òƒa,™©°¬(LI$‚ž Âßå˜sø¼)Ý­¼³ê_ãó·Æ|c&A›ÿíÞ“Œö¿ е{;Ý{w1yþ¬dQ¨‹ò/θùfwc¿&¡D&ÝÍË?¾ƒk>÷p^G€¾¸”w²F´6ø~ˆŒ…"÷¼ã¦yéù¿Á÷Ýx 㼑õ÷ ÆÕÇ:ßÓ²ÔÝ&„ØîW¶¿Ñ²òð Ñú€eŒfÇ;o!¤¤~NSôõë³o¾íÕcþÕPÌÿ`ßî5¬~ëœ{ñMŒ·°>eÐ&Æh† &ÇŒGªÍýì§÷ÑÛ]o»È•s[@ZÍõŸ3ÆÜ¤]ÎC aP]ußj6ËppÛVÒý½ÔLžF¢8yîo鼨}øùë±o÷/!|ac›¢µFû:gsÇÅq=<×Ï}oBà´6¹g7¾»œëŸ‰Ï÷T<…q×¼Új~Ý:cÌc¡ÚŽó3F|I)µñ®¦ºÅ-Ã~'ƒº’cHïÙóÞ:j¦åª*{gWOÿñ‡Tá³Cw‹Ö>Ï?u+ƒ]¡ é0 ñÂj„ëžÉ ½”ç• íë<˜ÆÐqp'?]þ÷‡Ma|«©þB¤|;—Ü·,ê™wr#§NŸÌ¼ñęLQ[’ Håð°ü­š_÷\Ë‚†TŠiÄÐw tO7BHª&NÔá¹#TF›ª} ¿o?Ï?u;×}þa¤’xa+Ô1|¥s•­ ¾Îçš# \'Ës?º×Ч0rvgqsÝ\cÌó@À´Y³i:ãl,¡Éöv“íî ÝaawwRlIª’6=—Ž´‹èý'”«¿gàFnøîÝæhÝ @qEV"fÌËÑ<Ï=oÛú:kÞþaAí*ê§qɆ]ÇÅsó¥ß÷yí¥aÿ‡×üw<ÐØ˜Ä°,çÌK/gÑ Ÿ§fúLŠÇ×S\SK²¢†DY%v²%R@iB1¡ÄÆ’9ZxÝ·æ×}9¨zùB}÷¾  ›ª¬ÎŸ|¬où¡ð‹Uå«1p\Ç#›u `D=×£·çoÿìûñqßc-z‹¿*àd€9gË‚+?MIÝdR&RT=žDyviVª«¸A@¤ØRR]dÅkuw¶œ]].Af?3ÐGvp PÆ¢œ ’RÙ½c¾™÷˜&WŠÉ±ãa DÒäºp®ë“ɤ1F/øÇ<>‹B˜ÛŠKJ9û“W‘¬¨"YQ€SZŽUœB%‹P‰ÒN¥–¢nIA©‹ Ç«tâfË Nìî,8€Õ÷OúBKfŒ8—Ïøyžx'¥PJìXæ«&Ò½\TRÖrkÛò~ÝÉ=¹ùT”ec´9º ‘#])[2àBx@ä LH÷öæßÄÍ/µþ±{KÆøLÆJE¡í ¤(” lÞ99¯v„”ŽÏ%¹ÛÄI¸ƒýd{:Étw’é<€;ØŸI£Ý,Úu1¾Wxæ.FµŠTN@β€’à(H>_î æ®í´™$S„\×Ck‰¯4JÉÂq®dBž¤qpZÉ$LpV ) ÛÝñ=T²£}¼¡²}=¸ƒxéA¼LQ ´ÆŒµ°Ohé|š€þŽýñ­¹h¬2!@¾–(_â‡ê3y€"Béë#J1¢Z`°•E¦û Æh¼ô2‘cð2iÜþ^²½]¸}¸ƒýhC0~8—É× B> Ê²¹›n:Í@çJkjˆ?6Æü“ÂŒ@€ã¸()ñ•,°?#IPd‹÷ð$…é6\Ï%ÝqßÉb— m;àXŽƒ7Ôp¡ž.|cr=©pãµU‰TiÁ„í[6Òxn-@ó¦G–Ü<6¦äx8~˜)8–“"Ib¶q¤1÷ÀèiÛKizU”BZvP#s¼ôP =ž‡§ ¾A ÿælp-#Meã'Lx u éÞžHäþqów–Ìúˆ¸lŠéœas6ë»çÄ\¿ò¡ ê-àÚ¼]w‘îîd¨}/ƒm»lÛÃ`û>Ò½ÝAœ§MØõ!àdóÇ÷ÖIx UQI"U›ÐgÛÏ_‹xG5Š—6<ºôÔ13ÒŽ‡›õr^*ïÅâ|¨ðÚ=‚Õ%¬Œ*¦²žÆñ5Ù™»ÚàúWÝÓ:T\½ÒžÆÏŸ |Z*Äò þÔ5Î. ¤Úö±ýí7"î2YýÆæ‡ïþ²ii‘UÅÅËÎ XD“‰ æÏã+7Þì\ÿΓëÞÚxpͪ®«~½«écW8Ž6´fq|€â`ŸMÐCpâÒ£ 89G0 }ý¨ØôÈ=/—:Cƒ¬Yö8¾W¸Su'ŸÂŒ…"„ŒÌüzeÛKÝ’¾§æ^Û⌗3€wŠSõœuáýa 6FE¬Zj@ CCm’“R4N)bÆ›iõÅXJÉy­1Zc|£}²ƒý<ñ?#ÝhßøT‚²› FžË׿ruwÆÃ ÅGîø›õí‹€]z%F?°÷½uì\}èÿŸ”Ÿ@ã¹SRUƒP ¡,„²ú¤e½€¥~"°×L?m“X´È;@E©:N_¸!EEŠÊ²ã*4LH2¹>Ť ÅLŸdb}Š¢„Ì!f‚ìFû­<ÐNÏ®VúÛöaŒÏÄÓÒ³¿g—Üö‚e”%å « f?Ü»¾¡×ñrÆYÀ ¯ºýÊ–•x9·±éá{^@ðqc4úã\àZÀÆ…¤~ö<&6Fªj²ÊB*…°l„’Yi%>JµIËÚ/”å e#-ÅÁÞþšÍ;v^R”,¢¤¬’qãOb\u’T*‘\J„T¦X¹ ™Þ.:·n¤óýt¶n¢«u™¾ž‚õÕžÒÌyöM¶¿ý/>tŸc´I@²I%QJ ø_“ñtÜ(ƒoÚVæßXÓÝ[PöÙú»§ûJ¼Tû®Ë¯V<Í@ÇÁ‘kEBP9q ãfÌ¢¶q6E•Õ!PVš^«Ã]«Âû9‰TøŽÃÀ6ö@Ïžít½¿‰ÎÖM l?JKÐôé?bæe¿ñ=^zðÞëZñæ`êè陸~ÅPÉŸ~½µ5;b]lÃÃK>&…y°|Ïeó«+F”¤á­¸¢’ÒÚ*ê'QTYMQy%‰òr¤´–R!•ÄÍf;àºh×%3ØÓ×K¦¯‡L=»¶“‰Í£m5shºöóŒk<%ˆ±|wUMó9ç?ÐØ˜ìKõßl·(X!‘w|óÝÖ½pøðÝ „x HchÛø.»Ö¾ï{ü65!õ§žÉÌË>E]ó™ÏÃø.Úó·í^\ÓtVA6qÓ„& …0 Š€ƒ±Ý²2¯Dê4ªÊjÒ’ßÂüðߢ2ý}ì^¿šƒÛ¶Æó1¿‘V>iÓ.ü8û$EUã0¾‹ñ<´ïõi×} ›5÷Ô57ŽÙFö\Ð÷–4ßü&Ÿ”Êôõ²ûV:¶¿Oz˜<^ÍN•P;÷4jçžÆ”sQ1y:Æ÷оJw@»þwíÜW1ynטKêQØòoKo0ÆÜœC¡,»;émÛGoÛ^zÛ÷ᦇ>z3YDùäiTLžNÍɧ0aÞéTϘx6LÀy´Æx¯¼öºwÑÂs®+í|NÌë7U•yoi‘[êŠ?0_6°H(K÷N¾“e°»“L_/n&›Mãe² E@0EP!E'3’e$Ë«H–WQ\YEYÃdJ&4 -;ø”¹ç H°½õÎn–=ýÏýä>ho搜»­;Ö6~÷ÛS…6×HK]*”u¡°¬’œ{ϹzUèöC>’[΂.èpY¿±‹·V·±êX»~?ƒC]¸ƒÆKJ¿}Ђ'[ §öt„œ+r¶‰ÙÒRS„²Æ ¥j…e©BPTŽõ ºû<ºú=ºû\öwfxç[vô²u{/í†FPù¾ß-€Žf¿v/¼* ®ÿóÛOÛðþö—A0˜N"‹núcþú²ŽòA2~`»/Ïal$¿íwe'úmmÖobR£ûñÒO|ˆd¿÷ÿ pÑþÎ*vBÅŽ­m#ø/¿±ÃíD;Ñ~×Ûÿ@òIÒx IEND®B`‚yabause-0.9.13.1/src/android/res/drawable-mdpi/icon.png000644 001750 001750 00000007044 12256006053 024560 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDR00Wù‡sRGB®ÎébKGDÿÿÿ ½§“ pHYs × ×B(›xtIMEÛ røm ¤IDAThÞíšy]ÅuÆÝ}—·Œ¤iF3ímƒ$±/ ;R ˜˜¸ Ä`lˆIl1” ŽÉ˜ÍU\àÄbC ÁŽMX±„Y”Ä"£Ñha$FÒh´Ìú4óî»Kwç÷ÞhAHBàT¨âÔÜêwëÞ>ý}Ý_Ÿ>Ýwà ûÂ>µÀÆe€“> ÷fû¶ìà®Oá õS‚ $‡ÙöóbGo' ‘ÅÉ|!*ت5ÝÄùG ðƒR'Ž5 BÄM_ ˆ¼šçÇš[Ñr§g9kûÑ…ßî9:û5!«Pþ‰åÛ[Ìa€¿h‰ãÏ>xÐáÒ2ø80ÎÏ À¹À[F· ½òNýXg®ëJŸE_WFç|àIà’òóõÞU“„â(!ÅT £ŒÂP‹@ [»zž}óÝ?ð3§à¤FGÉǶg’uèxEùöÚ²t}Þ[Üü‹Ž^EªÑ5zïžÏsp=×ÍàTÏ¡kû/B\|ïµ—ß|þqÓ§ ˜•ÄŒBî1Žvw[kó7=úôÀwÜaTV_@’¤”ÄQ‚1f¹ö’„ÿ]¾ý=ðØž!ü6pœƒp3×€H—d#ð=Ïwñ<×up=ÅÖÿ|ß\zÚ Üñw—îÙtA ^·°Ä ÛŒU­"NZ§þã-Çÿ0rÜ·pý#‰â„8Lˆ¢˜(ŠÑºL¿Àê­@]IB$¶ƒ¤:'}B|ßÅ÷½×+–I´•µï߉ÀòÄ÷® fN÷3cÄeüàõñW7öñ-K@*«ªO`Ôøkˆ¢„¸< c¢°XcÐÑËèè J!{:°z/E`Μ ,„òÏ&Sq:~ÊÃ÷=|ßÅõÜRéອ럠}ËBôk‡”ÃÓ~ìà[Ž›å˜“î²ÄqL%%ða"‚þuÄÁ”Ê À½û:S °¨Nµº•tf"éL ¾ïâù©TQJEBÕÃhÛ´­C¨~·Ÿ“Jú“§_ŰšiH)JÅ>µ`ô.ò¹_€^®ÛHu0·X ñ‹žÄ- ©:‘T&; #ßwI¥Š¿3™ ¤R~Ú£aÆ%T ;¢ì÷¹}Ü}˜$¥ÃI³þ©XÇ÷dé•déy»º—6”uÁž“öG yáÇYcþè:î”É®x±éúóíxÞ ª†M.I©4žKª¦¦v"k›^ $£åÀz xPGŸp§\€!Šò¶X¬-f¹îf¶l|¢ ãGÀSêäýh~ø®IñG!Äè­Í«Ä„) fTͰdñ»MjWn 5µÇ1hððÝR˜ÜƦ§«•Î-³K‰Ú À‘•U£™sé|”ãîŽÖ–×ÂBŽæ• Ð:x¸ü`KùG$´vá#­¯XCý†·^5&jå8}sN9nÐbMÂÚ÷@㸪4ìÅËOeuîœqÜÀÐ’îϸà¢[ÈV ÅuÄÅqŽ£ø ùQ¢° Î9”\d/+zÈ5Òy¨]÷êbQ{äãú)„#.iøæ¼7KùG”ïßÊê•ã8ņҼp]ßw©1ž¿8w hœ0ýØ ™vìùEðž‹ç:xž3àcóÆ—Ø¹í­òšýW@Ï¡Ø+•¨ û §nZ¹œÊ‘£É«–X¾3å›7/)½²¸xfKëËlÚ0“©3.ÂQEžW^<οè»ÄqFÇXàü¹7àû^qm5–$Ñ(¥PJ’ëù¦÷~^†ñp)]8$âêçŸ'„}©kË&±yårf\øe„ÏL½nÞeû©÷3à¥<þò²9fé´O:í“*—¥ðª”ÂS\œÂˆB!… "Bz»;xî©«é˵4³ÓC7 `¥Ä,ÐIÂú×—0áÄÓ¬@„V©›>¦Þ7€­#–,º•$R"¥D)‰TÇUx¾K:ãC¤«PJ ¼'„@ ÁkKî)ƒJ°Ÿœ2*Ý8³¾ºqf}怚ëÓWX!Žn[õ®ÈTePMRÜÛð´Û:s=[X²èÎÃÚɬz÷96¬[RÖýÅ@qûѵso›^÷|¾?îU±Ù©bÓÇôºŽ;fÔ-¼}FýäÖÞœ ¦mÕ{ ?b"¬‘jáAÚß|`}óïYùösÅäKŒ6$±& c‚|HÆ$‰Fk1c ÛÛ×ðòâe_‹̨ÍÞ1£îY¬øâŠlÖ­ÎúT¦\\%†Zø{0M·Í¨»~`XýÈü“±|¿mÕ{¢§½É³Î5BÊW®wß!tâàH`Ƈ-Ë8rò™ Rƒ”ÅÜÀ‹N4q\Ì6ã(!ŽúwõòËG¯!ßß°8§ñL z¸°nÌXf}iŽ?n,•)lØÇß®€|¢±0÷ìáƒrKwô½)1f.ÀÎ×ãe³HÇ•ñú'P•ÀVD¼ð›F⨂ÔÐj”TxµO*!¬öž»¦×OqâŒ8L¾§[®­/Ǧ?¡œ¯õöl% c¬­ JI„X[”–N4Q”°½}M¹ÞýÀæÆ£Žò·¤RY{öß^-*FC‡’ Ÿ c0N:‹!Ž’ ö•è.$Ž–úŸŒÚßÝ)”ã”¶p6÷ å´ #Œ1¨X•¬µcѺ('c6š=JuœŽ•ã&MFX[_0ZcÁš½|%QB`¬ë ¨Š Åö£ _ÞøŽ=œ¨b-‚”S —»÷µ–Dk’DdÏ b2@Eʧoë&â|?Vk‚ŽíD¹’|Æ>[Ê<%1ÄaËËY¾» ÇÆqœ¹¥$ì° Qi••¥É,ÀZŒµÅè”è=G TQ¤–¨§ƒÜÆõ¸Û°ÖåzÈoo£ÐÓIb-ºtí¹;`wø™ìÈ"ÃŽ–µrÄ”ig¬}øîÙ“¯ýþ¢CÄJéP1x… ÄqRI¤Ø­ñSU8n†$Î÷ÑØÍÝí[¨',q¾°§‹Hmˆ¥Ì=)–Z"x»¢ºÖJY̬7­\ŽŽBc¬}|õÂ;§"Æ$ìêݼWäÉ!A¾P*w§ý}$q`€ÖÎ@wäcú:w’kk%×¶‰þ®N Ú•®¸tR¡­%ÖÆ xE"ÄóR)Q=þâ  é‹¤1¦!—­yøÇ_·O?­YBeA¡H"__ B¢0dê„1\ý7Í[¿}ö»ï¼ñÞõ=ùöÈ©GmÍö|Hè«h Új3pÆ” 5¶¸½_4ÝßX!•¿%ØÕ;øÝßüJS“±§ŸÃˆ™§á(… Ý6‰ôu÷ßWüñùOês¿G‹MÝu’êVkíl„a!°¹ö-¢§½þ®‚ž.t}¬SÐÜL–ìðTM˜DåØ#¨i8–ʱ¢0dÉÒ%tuw-¸ê¼Ù?'†‡Û)ü¢°záÓ„t®R®ó%\¿A¹ž®‡t]¬µ&Žc„r‘®Ê‘^fé¡CQ®p]¤rÊÁ X¿1Ç›+ÚY¶b+‹^üWººVÌ)¹ðg!°§ýéɪ҆3”çMÂ÷Æ)Ç­Ž7TyA§ßYÓršt< z ]»b:{ZÛúYÝÒËÚ–^‚Âné&ÁSÝúKà 6h¢7û탾üYŸ÷oÄÎgéÌ££7!åÈýÿ$€ ÑÑÒÏåôó£^Ëÿ(ñy·ÿŽÏäÒmÝIEND®B`‚yabause-0.9.13.1/src/android/res/xml/preferences.xml000644 001750 001750 00000001770 12256006053 024235 0ustar00guillaumeguillaume000000 000000 yabause-0.9.13.1/src/android/jni/Android.mk.in000644 001750 001750 00000000634 12256006052 022714 0ustar00guillaumeguillaume000000 000000 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := yabause LOCAL_SRC_FILES := yui.c sndaudiotrack.c LOCAL_STATIC_LIBRARIES := yabause-prebuilt LOCAL_LDLIBS := -llog -ljnigraphics -lGLESv1_CM include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := yabause-prebuilt LOCAL_SRC_FILES := libyabause.a LOCAL_EXPORT_C_INCLUDES := @YABAUSE_INCLUDE_DIR@ include $(PREBUILT_STATIC_LIBRARY) yabause-0.9.13.1/src/android/jni/sndaudiotrack.c000644 001750 001750 00000014220 12256006052 023371 0ustar00guillaumeguillaume000000 000000 /* Copyright 2012 Guillaume Duhamel Copyright 2005-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "sndaudiotrack.h" #include "debug.h" static int SNDAudioTrackInit(void); static void SNDAudioTrackDeInit(void); static int SNDAudioTrackReset(void); static int SNDAudioTrackChangeVideoFormat(int vertfreq); static void SNDAudioTrackUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples); static u32 SNDAudioTrackGetAudioSpace(void); static void SNDAudioTrackMuteAudio(void); static void SNDAudioTrackUnMuteAudio(void); static void SNDAudioTrackSetVolume(int volume); SoundInterface_struct SNDAudioTrack = { SNDCORE_AUDIOTRACK, "Audio Track Sound Interface", SNDAudioTrackInit, SNDAudioTrackDeInit, SNDAudioTrackReset, SNDAudioTrackChangeVideoFormat, SNDAudioTrackUpdateAudio, SNDAudioTrackGetAudioSpace, SNDAudioTrackMuteAudio, SNDAudioTrackUnMuteAudio, SNDAudioTrackSetVolume }; extern JavaVM * yvm; jobject gtrack = NULL; jclass cAudioTrack = NULL; jmethodID mWrite = NULL; int mbufferSizeInBytes; static u16 *stereodata16; static u8 soundvolume; static u8 soundmaxvolume; static u8 soundbufsize; static int soundoffset; static int muted; ////////////////////////////////////////////////////////////////////////////// static int SNDAudioTrackInit(void) { int sampleRateInHz = 44100; int channelConfig = 12; //AudioFormat.CHANNEL_OUT_STEREO int audioFormat = 2; //AudioFormat.ENCODING_PCM_16BIT JNIEnv * env; jobject mtrack = NULL; jmethodID mPlay = NULL; jmethodID mGetMinBufferSize = NULL; jmethodID mAudioTrack = NULL; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return -1; cAudioTrack = (*env)->FindClass(env, "android/media/AudioTrack"); mAudioTrack = (*env)->GetMethodID(env, cAudioTrack, "", "(IIIIII)V"); mWrite = (*env)->GetMethodID(env, cAudioTrack, "write", "([BII)I"); mPlay = (*env)->GetMethodID(env, cAudioTrack, "play", "()V"); mGetMinBufferSize = (*env)->GetStaticMethodID(env, cAudioTrack, "getMinBufferSize", "(III)I"); mbufferSizeInBytes = (*env)->CallStaticIntMethod(env, cAudioTrack, mGetMinBufferSize, sampleRateInHz, channelConfig, audioFormat); mtrack = (*env)->NewObject(env, cAudioTrack, mAudioTrack, 3 /* STREAM_MUSIC */, sampleRateInHz, channelConfig, audioFormat, mbufferSizeInBytes, 1 /* MODE_STREAM */); gtrack = (*env)->NewGlobalRef(env, mtrack); (*env)->CallNonvirtualVoidMethod(env, gtrack, cAudioTrack, mPlay); if ((stereodata16 = (u16 *)malloc(2 * mbufferSizeInBytes)) == NULL) return -1; memset(stereodata16, 0, soundbufsize); soundvolume = 100; soundmaxvolume = 100; soundbufsize = 85; soundoffset = 0; muted = 0; return 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDAudioTrackDeInit(void) { JNIEnv * env; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return; free(stereodata16); stereodata16 = NULL; (*env)->DeleteGlobalRef(env, gtrack); } ////////////////////////////////////////////////////////////////////////////// static int SNDAudioTrackReset(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// static int SNDAudioTrackChangeVideoFormat(int vertfreq) { return 0; } ////////////////////////////////////////////////////////////////////////////// static void sdlConvert32uto16s(s32 *srcL, s32 *srcR, s16 *dst, u32 len) { u32 i; for (i = 0; i < len; i++) { // Left Channel *srcL = ( *srcL *soundvolume ) / soundmaxvolume; if (*srcL > 0x7FFF) *dst = 0x7FFF; else if (*srcL < -0x8000) *dst = -0x8000; else *dst = *srcL; srcL++; dst++; // Right Channel *srcR = ( *srcR *soundvolume ) / soundmaxvolume; if (*srcR > 0x7FFF) *dst = 0x7FFF; else if (*srcR < -0x8000) *dst = -0x8000; else *dst = *srcR; srcR++; dst++; } } static void SNDAudioTrackUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples) { u32 copy1size=0; copy1size = (num_samples * sizeof(s16) * 2); sdlConvert32uto16s((s32 *)leftchanbuffer, (s32 *)rightchanbuffer, (s16 *)(((u8 *)stereodata16)+soundoffset), copy1size / sizeof(s16) / 2); soundoffset += copy1size; if (soundoffset > mbufferSizeInBytes) { if (! muted) { JNIEnv * env; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return; jshortArray array = (*env)->NewShortArray(env, soundoffset); if(array) { (*env)->SetShortArrayRegion(env, array, 0, soundoffset, stereodata16); } (*env)->CallNonvirtualIntMethod(env, gtrack, cAudioTrack, mWrite, array, 0, soundoffset); } soundoffset = 0; } } ////////////////////////////////////////////////////////////////////////////// static u32 SNDAudioTrackGetAudioSpace(void) { static int i = 0; i++; if (i == 55) { i = 0; return mbufferSizeInBytes; } else { return 0; } } ////////////////////////////////////////////////////////////////////////////// static void SNDAudioTrackMuteAudio(void) { muted = 1; } ////////////////////////////////////////////////////////////////////////////// static void SNDAudioTrackUnMuteAudio(void) { muted = 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDAudioTrackSetVolume(int volume) { soundvolume = volume; } yabause-0.9.13.1/src/android/jni/yui.c000644 001750 001750 00000033262 12256006052 021353 0ustar00guillaumeguillaume000000 000000 /* Copyright 2011 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "../../config.h" #include "yabause.h" #include "scsp.h" #include "vidsoft.h" #include "peripheral.h" #include "m68kcore.h" #include "sh2core.h" #include "sh2int.h" #include "cdbase.h" #include "cs2.h" #include "debug.h" #include "osdcore.h" #include #include #define _ANDROID_2_2_ #ifdef _ANDROID_2_2_ #include "miniegl.h" #else #include #endif #include #include #include #include "sndaudiotrack.h" JavaVM * yvm; static jobject yabause; static char mpegpath[256] = "\0"; static char cartpath[256] = "\0"; EGLDisplay g_Display = EGL_NO_DISPLAY; EGLSurface g_Surface = EGL_NO_SURFACE; EGLContext g_Context = EGL_NO_CONTEXT; GLuint g_FrameBuffer = 0; GLuint g_VertexBuffer = 0; int g_buf_width = -1; int g_buf_height = -1; pthread_mutex_t g_mtxGlLock = PTHREAD_MUTEX_INITIALIZER; float vertices [] = { 0, 0, 0, 0, 320, 0, 0, 0, 320, 224, 0, 0, 0, 224, 0, 0 }; M68K_struct * M68KCoreList[] = { &M68KDummy, #ifdef HAVE_C68K &M68KC68K, #endif #ifdef HAVE_Q68 &M68KQ68, #endif NULL }; SH2Interface_struct *SH2CoreList[] = { &SH2Interpreter, &SH2DebugInterpreter, #ifdef SH2_DYNAREC &SH2Dynarec, #endif NULL }; PerInterface_struct *PERCoreList[] = { &PERDummy, NULL }; CDInterface *CDCoreList[] = { &DummyCD, &ISOCD, NULL }; SoundInterface_struct *SNDCoreList[] = { &SNDDummy, &SNDAudioTrack, NULL }; VideoInterface_struct *VIDCoreList[] = { &VIDDummy, &VIDSoft, NULL }; #define LOG_TAG "yabause" /* Override printf for debug*/ int printf( const char * fmt, ... ) { va_list ap; va_start(ap, fmt); int result = __android_log_vprint(ANDROID_LOG_INFO, LOG_TAG, fmt, ap); va_end(ap); return result; } const char * GetBiosPath() { jclass yclass; jmethodID getBiosPath; jstring message; jboolean dummy; JNIEnv * env; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return; yclass = (*env)->GetObjectClass(env, yabause); getBiosPath = (*env)->GetMethodID(env, yclass, "getBiosPath", "()Ljava/lang/String;"); message = (*env)->CallObjectMethod(env, yabause, getBiosPath); if ((*env)->GetStringLength(env, message) == 0) return NULL; else return (*env)->GetStringUTFChars(env, message, &dummy); } const char * GetGamePath() { jclass yclass; jmethodID getGamePath; jstring message; jboolean dummy; JNIEnv * env; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return; yclass = (*env)->GetObjectClass(env, yabause); getGamePath = (*env)->GetMethodID(env, yclass, "getGamePath", "()Ljava/lang/String;"); message = (*env)->CallObjectMethod(env, yabause, getGamePath); if ((*env)->GetStringLength(env, message) == 0) return NULL; else return (*env)->GetStringUTFChars(env, message, &dummy); } const char * GetMemoryPath() { jclass yclass; jmethodID getMemoryPath; jstring message; jboolean dummy; JNIEnv * env; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return; yclass = (*env)->GetObjectClass(env, yabause); getMemoryPath = (*env)->GetMethodID(env, yclass, "getMemoryPath", "()Ljava/lang/String;"); message = (*env)->CallObjectMethod(env, yabause, getMemoryPath); if ((*env)->GetStringLength(env, message) == 0) return NULL; else return (*env)->GetStringUTFChars(env, message, &dummy); } int GetCartridgeType() { jclass yclass; jmethodID getCartridgeType; JNIEnv * env; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return; yclass = (*env)->GetObjectClass(env, yabause); getCartridgeType = (*env)->GetMethodID(env, yclass, "getCartridgePath", "()I"); return (*env)->CallIntMethod(env, yabause, getCartridgeType); } const char * GetCartridgePath() { jclass yclass; jmethodID getCartridgePath; jstring message; jboolean dummy; JNIEnv * env; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return; yclass = (*env)->GetObjectClass(env, yabause); getCartridgePath = (*env)->GetMethodID(env, yclass, "getCartridgePath", "()Ljava/lang/String;"); message = (*env)->CallObjectMethod(env, yabause, getCartridgePath); if ((*env)->GetStringLength(env, message) == 0) return NULL; else return (*env)->GetStringUTFChars(env, message, &dummy); } void YuiErrorMsg(const char *string) { jclass yclass; jmethodID errorMsg; jstring message; JNIEnv * env; if ((*yvm)->GetEnv(yvm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return; yclass = (*env)->GetObjectClass(env, yabause); errorMsg = (*env)->GetMethodID(env, yclass, "errorMsg", "(Ljava/lang/String;)V"); message = (*env)->NewStringUTF(env, string); (*env)->CallVoidMethod(env, yabause, errorMsg, message); } void YuiSwapBuffers(void) { int buf_width, buf_height; int error; pthread_mutex_lock(&g_mtxGlLock); if( g_Display == EGL_NO_DISPLAY ) { pthread_mutex_unlock(&g_mtxGlLock); return; } if( eglMakeCurrent(g_Display,g_Surface,g_Surface,g_Context) == EGL_FALSE ) { printf( "eglMakeCurrent fail %04x",eglGetError()); pthread_mutex_unlock(&g_mtxGlLock); return; } glClearColor( 0.0f,0.0f,0.0f,1.0f); glClear(GL_COLOR_BUFFER_BIT); if( g_FrameBuffer == 0 ) { glEnable(GL_TEXTURE_2D); glGenTextures(1,&g_FrameBuffer); glBindTexture(GL_TEXTURE_2D, g_FrameBuffer); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); error = glGetError(); if( error != GL_NO_ERROR ) { printf("gl error %d", error ); return; } }else{ glBindTexture(GL_TEXTURE_2D, g_FrameBuffer); } VIDCore->GetGlSize(&buf_width, &buf_height); glTexSubImage2D(GL_TEXTURE_2D, 0,0,0,buf_width,buf_height,GL_RGBA,GL_UNSIGNED_BYTE,dispbuffer); if( g_VertexBuffer == 0 ) { glGenBuffers(1, &g_VertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, g_VertexBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),vertices,GL_STATIC_DRAW); error = glGetError(); if( error != GL_NO_ERROR ) { printf("gl error %d", error ); return; } }else{ glBindBuffer(GL_ARRAY_BUFFER, g_VertexBuffer); } if( buf_width != g_buf_width || buf_height != g_buf_height ) { vertices[6]=vertices[10]=(float)buf_width/1024.f; vertices[11]=vertices[15]=(float)buf_height/1024.f; glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),vertices,GL_STATIC_DRAW); glVertexPointer(2, GL_FLOAT, sizeof(float)*4, 0); glTexCoordPointer(2, GL_FLOAT, sizeof(float)*4, (void*)(sizeof(float)*2)); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); g_buf_width = buf_width; g_buf_height = buf_height; } glDrawArrays(GL_TRIANGLE_FAN, 0, 4); eglSwapBuffers(g_Display,g_Surface); pthread_mutex_unlock(&g_mtxGlLock); } int Java_org_yabause_android_YabauseRunnable_initViewport( int width, int height) { int swidth; int sheight; int error; char * buf; g_Display = eglGetCurrentDisplay(); g_Surface = eglGetCurrentSurface(EGL_READ); g_Context = eglGetCurrentContext(); eglQuerySurface(g_Display,g_Surface,EGL_WIDTH,&swidth); eglQuerySurface(g_Display,g_Surface,EGL_HEIGHT,&sheight); glViewport(0,0,swidth,sheight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(0, 320, 224, 0, 1, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); printf(glGetString(GL_VENDOR)); printf(glGetString(GL_RENDERER)); printf(glGetString(GL_VERSION)); printf(glGetString(GL_EXTENSIONS)); printf(eglQueryString(g_Display,EGL_EXTENSIONS)); eglSwapInterval(g_Display,0); eglMakeCurrent(g_Display,EGL_NO_SURFACE,EGL_NO_SURFACE,EGL_NO_CONTEXT); return 0; } #ifdef _ANDROID_2_2_ int initEGLFunc() { void * handle; char *error; handle = dlopen("libEGL.so",RTLD_LAZY); if( handle == NULL ) { printf(dlerror()); return -1; } eglGetCurrentDisplay = dlsym(handle, "eglGetCurrentDisplay"); if( eglGetCurrentDisplay == NULL){ printf(dlerror()); return -1; } eglGetCurrentSurface = dlsym(handle, "eglGetCurrentSurface"); if( eglGetCurrentSurface == NULL){ printf(dlerror()); return -1; } eglGetCurrentContext = dlsym(handle, "eglGetCurrentContext"); if( eglGetCurrentContext == NULL){ printf(dlerror()); return -1; } eglQuerySurface = dlsym(handle, "eglQuerySurface"); if( eglQuerySurface == NULL){ printf(dlerror()); return -1; } eglSwapInterval = dlsym(handle, "eglSwapInterval"); if( eglSwapInterval == NULL){ printf(dlerror()); return -1; } eglMakeCurrent = dlsym(handle, "eglMakeCurrent"); if( eglMakeCurrent == NULL){ printf(dlerror()); return -1; } eglSwapBuffers = dlsym(handle, "eglSwapBuffers"); if( eglSwapBuffers == NULL){ printf(dlerror()); return -1; } eglQueryString = dlsym(handle, "eglQueryString"); if( eglQueryString == NULL){ printf(dlerror()); return -1; } eglGetError = dlsym(handle, "eglGetError"); if( eglGetError == NULL){ printf(dlerror()); return -1; } return 0; } #else int initEGLFunc() { return 0; } #endif int Java_org_yabause_android_YabauseRunnable_lockGL() { pthread_mutex_lock(&g_mtxGlLock); } int Java_org_yabause_android_YabauseRunnable_unlockGL() { pthread_mutex_unlock(&g_mtxGlLock); } jint Java_org_yabause_android_YabauseRunnable_init( JNIEnv* env, jobject obj, jobject yab ) { yabauseinit_struct yinit; int res; void * padbits; if( initEGLFunc() == -1 ) return -1; yabause = (*env)->NewGlobalRef(env, yab); yinit.m68kcoretype = M68KCORE_C68K; yinit.percoretype = PERCORE_DUMMY; #ifdef SH2_DYNAREC yinit.sh2coretype = 2; #else yinit.sh2coretype = SH2CORE_DEFAULT; #endif yinit.vidcoretype = VIDCORE_SOFT; yinit.sndcoretype = SNDCORE_AUDIOTRACK; yinit.cdcoretype = CDCORE_ISO; yinit.carttype = CART_NONE; yinit.regionid = 0; yinit.biospath = GetBiosPath(); yinit.cdpath = GetGamePath(); yinit.buppath = GetMemoryPath(); yinit.mpegpath = mpegpath; yinit.cartpath = GetCartridgePath(); yinit.videoformattype = VIDEOFORMATTYPE_NTSC; yinit.frameskip = 0; res = YabauseInit(&yinit); OSDChangeCore(OSDCORE_SOFT); PerPortReset(); padbits = PerPadAdd(&PORTDATA1); PerSetKey(PERPAD_UP, PERPAD_UP, padbits); PerSetKey(PERPAD_RIGHT, PERPAD_RIGHT, padbits); PerSetKey(PERPAD_DOWN, PERPAD_DOWN, padbits); PerSetKey(PERPAD_LEFT, PERPAD_LEFT, padbits); PerSetKey(PERPAD_START, PERPAD_START, padbits); PerSetKey(PERPAD_A, PERPAD_A, padbits); PerSetKey(PERPAD_B, PERPAD_B, padbits); PerSetKey(PERPAD_C, PERPAD_C, padbits); PerSetKey(PERPAD_X, PERPAD_X, padbits); PerSetKey(PERPAD_Y, PERPAD_Y, padbits); PerSetKey(PERPAD_Z, PERPAD_Z, padbits); ScspSetFrameAccurate(1); return res; } void Java_org_yabause_android_YabauseRunnable_deinit( JNIEnv* env ) { YabauseDeInit(); } void Java_org_yabause_android_YabauseRunnable_exec( JNIEnv* env ) { YabauseExec(); } void Java_org_yabause_android_YabauseRunnable_press( JNIEnv* env, jobject obj, jint key ) { PerKeyDown(key); } void Java_org_yabause_android_YabauseRunnable_release( JNIEnv* env, jobject obj, jint key ) { PerKeyUp(key); } void Java_org_yabause_android_YabauseRunnable_enableFPS( JNIEnv* env, jobject obj, jint enable ) { SetOSDToggle(enable); } void Java_org_yabause_android_YabauseRunnable_enableFrameskip( JNIEnv* env, jobject obj, jint enable ) { if (enable) EnableAutoFrameSkip(); else DisableAutoFrameSkip(); } void Java_org_yabause_android_YabauseRunnable_setVolume( JNIEnv* env, jobject obj, jint volume ) { if (0 == volume) ScspMuteAudio(SCSP_MUTE_USER); else { ScspUnMuteAudio(SCSP_MUTE_USER); ScspSetVolume(volume); } } void log_callback(char * message) { __android_log_print(ANDROID_LOG_INFO, "yabause", "%s", message); } jint JNI_OnLoad(JavaVM * vm, void * reserved) { JNIEnv * env; if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) return -1; yvm = vm; LogStart(); LogChangeOutput(DEBUG_CALLBACK, (char *) log_callback); return JNI_VERSION_1_6; } yabause-0.9.13.1/src/android/jni/miniegl.h000644 001750 001750 00000003024 12256006052 022167 0ustar00guillaumeguillaume000000 000000 #ifndef _MINIEGL_ #define _MINIEGL_ typedef int EGLint; typedef unsigned int EGLBoolean; typedef unsigned int EGLenum; typedef void *EGLConfig; typedef void *EGLContext; typedef void *EGLDisplay; typedef void *EGLSurface; typedef void *EGLClientBuffer; /* EGL aliases */ #define EGL_FALSE 0 #define EGL_TRUE 1 /* Out-of-band handle values */ #define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType)0) #define EGL_NO_CONTEXT ((EGLContext)0) #define EGL_NO_DISPLAY ((EGLDisplay)0) #define EGL_NO_SURFACE ((EGLSurface)0) /* GetCurrentSurface targets */ #define EGL_DRAW 0x3059 #define EGL_READ 0x305A /* QuerySurface / SurfaceAttrib / CreatePbufferSurface targets */ #define EGL_HEIGHT 0x3056 #define EGL_WIDTH 0x3057 /* QueryString targets */ #define EGL_VENDOR 0x3053 #define EGL_VERSION 0x3054 #define EGL_EXTENSIONS 0x3055 #define EGL_CLIENT_APIS 0x308D EGLContext (*eglGetCurrentContext)(void); EGLSurface (*eglGetCurrentSurface)(EGLint readdraw); EGLDisplay (*eglGetCurrentDisplay)(void); EGLBoolean (*eglQuerySurface)(EGLDisplay dpy, EGLSurface surface,EGLint attribute, EGLint *value); EGLBoolean (*eglSwapInterval)(EGLDisplay dpy, EGLint interval); EGLBoolean (*eglMakeCurrent)(EGLDisplay dpy, EGLSurface draw,EGLSurface read, EGLContext ctx); EGLBoolean (*eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface); const char * (*eglQueryString)(EGLDisplay dpy, EGLint name); EGLint (*eglGetError)(void); #endif // _MINIEGL_yabause-0.9.13.1/src/android/jni/sndaudiotrack.h000644 001750 001750 00000001647 12256006052 023407 0ustar00guillaumeguillaume000000 000000 /* Copyright 2012 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SNDAUDIOTRACK_H #define SNDAUDIOTRACK_H #define SNDCORE_AUDIOTRACK 4 #include "scsp.h" extern SoundInterface_struct SNDAudioTrack; #endif yabause-0.9.13.1/src/android/CMakeLists.txt000644 001750 001750 00000010721 12256006054 022356 0ustar00guillaumeguillaume000000 000000 find_program(NDK_BUILD ndk-build) if(NOT NDK_BUILD) message(FATAL_ERROR "ndk build not found, bye") endif() find_program(SDK_ANDROID android) if(NOT SDK_ANDROID) message(FATAL_ERROR "sdk android tool not found, bye") endif() find_program(ANT NAMES ant ant.bat) if(NOT ANT) message(FATAL_ERROR "ant not found, bye") endif() if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) set(yabause_android_SHADOW AndroidManifest.xml project.properties jni/yui.c jni/miniegl.h jni/sndaudiotrack.c jni/sndaudiotrack.h src/org/yabause/android/Cartridge.java src/org/yabause/android/Yabause.java src/org/yabause/android/YabauseAudio.java src/org/yabause/android/YabauseView.java src/org/yabause/android/YabausePad.java src/org/yabause/android/YabauseSettings.java src/org/yabause/android/YabauseStorage.java res/drawable-hdpi/icon.png res/drawable-ldpi/icon.png res/drawable-mdpi/icon.png res/layout/main.xml res/menu/emulation.xml res/values/strings.xml res/xml/preferences.xml ) foreach(item IN LISTS yabause_android_SHADOW) message(STATUS ${item}) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${item}" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${item}" "${CMAKE_CURRENT_BINARY_DIR}/${item}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${item}" ) endforeach() endif() set(YABAUSE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/jni/Android.mk.in ${CMAKE_CURRENT_BINARY_DIR}/jni/Android.mk @ONLY ) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/local.properties" COMMAND ${SDK_ANDROID} update project -p "${CMAKE_CURRENT_BINARY_DIR}" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" "${CMAKE_CURRENT_BINARY_DIR}/project.properties" "${CMAKE_CURRENT_BINARY_DIR}/jni/Android.mk" ) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/jni/libyabause.a" COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/../libyabause.a ${CMAKE_CURRENT_BINARY_DIR}/jni/libyabause.a DEPENDS yabause "${CMAKE_CURRENT_BINARY_DIR}/../config.h" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/local.properties" ) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libs/armeabi/libyabause.so" COMMAND "${NDK_BUILD}" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/libyabause.a" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/yui.c" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/miniegl.h" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/sndaudiotrack.c" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/jni/sndaudiotrack.h" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) set(yabause_android_RES "${CMAKE_CURRENT_BINARY_DIR}/res/drawable-hdpi/icon.png" "${CMAKE_CURRENT_BINARY_DIR}/res/drawable-ldpi/icon.png" "${CMAKE_CURRENT_BINARY_DIR}/res/drawable-mdpi/icon.png" "${CMAKE_CURRENT_BINARY_DIR}/res/layout/main.xml" "${CMAKE_CURRENT_BINARY_DIR}/res/menu/emulation.xml" "${CMAKE_CURRENT_BINARY_DIR}/res/values/strings.xml" "${CMAKE_CURRENT_BINARY_DIR}/res/xml/preferences.xml" ) set(yabause_android_SRC "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/Cartridge.java" "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/Yabause.java" "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/YabauseAudio.java" "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/YabauseView.java" "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/YabausePad.java" "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/YabauseSettings.java" "${CMAKE_CURRENT_BINARY_DIR}/src/org/yabause/android/YabauseStorage.java" ) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/bin/Yabause-debug.apk" COMMAND "${ANT}" ARGS "debug" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libs/armeabi/libyabause.so" DEPENDS ${yabause_android_SRC} DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" DEPENDS ${yabause_android_RES} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/bin/Yabause-release-unsigned.apk" COMMAND "${ANT}" ARGS "release" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libs/armeabi/libyabause.so" DEPENDS ${yabause_android_SRC} DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" DEPENDS ${yabause_android_RES} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) if(CMAKE_BUILD_TYPE STREQUAL "Release") add_custom_target(yabause-android ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/bin/Yabause-release-unsigned.apk") else() add_custom_target(yabause-android ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/bin/Yabause-debug.apk") endif() yabause-0.9.13.1/src/android/android-windows.cmake000644 001750 001750 00000001623 12256006053 023730 0ustar00guillaumeguillaume000000 000000 SET(CMAKE_SYSTEM_NAME Linux) SET(CMAKE_SYSTEM_VERSION 1) SET(CMAKE_C_COMPILER "C:/android-ndk-r8e/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/arm-linux-androideabi-gcc.exe") SET(CMAKE_CXX_COMPILER "C:/android-ndk-r8e/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/arm-linux-androideabi-g++.exe") SET(CMAKE_ASM-ATT_COMPILER "C:/android-ndk-r8e/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/arm-linux-androideabi-as.exe") SET(CMAKE_FIND_ROOT_PATH "C:/android-ndk-r8e/platforms/android-8/arch-arm/usr") set(CMAKE_C_FLAGS "--sysroot=C:/android-ndk-r8e/platforms/android-8/arch-arm" CACHE STRING "GCC flags" FORCE) set(CMAKE_CXX_FLAGS "--sysroot=C:/android-ndk-r8e/platforms/android-8/arch-arm" CACHE STRING "G++ flags" FORCE) SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) SET(ANDROID ON) yabause-0.9.13.1/src/android/src/org/yabause/android/YabausePad.java000644 001750 001750 00000014631 12256006053 027131 0ustar00guillaumeguillaume000000 000000 /* Copyright 2013 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.yabause.android; import android.view.MotionEvent; import android.view.View.OnTouchListener; import android.view.View; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.content.Context; import android.util.AttributeSet; import android.util.Log; class PadEvent { final static int BUTTON_UP = 0; final static int BUTTON_RIGHT = 1; final static int BUTTON_DOWN = 2; final static int BUTTON_LEFT = 3; final static int BUTTON_START = 6; final static int BUTTON_A = 7; final static int BUTTON_B = 8; final static int BUTTON_C = 9; final static int BUTTON_X = 10; final static int BUTTON_Y = 11; final static int BUTTON_Z = 12; private int action; private int key; PadEvent(int action, int key) { this.action = action; this.key = key; } public int getAction() { return this.action; } public int getKey() { return this.key; } } interface OnPadListener { public abstract boolean onPad(View v, PadEvent event); } class YabausePad extends View implements OnTouchListener { private Rect up; private Rect right; private Rect down; private Rect left; private Rect start; private Rect a; private Rect b; private Rect c; private Rect x; private Rect y; private Rect z; private OnPadListener listener = null; public YabausePad(Context context) { super(context); init(); } public YabausePad(Context context, AttributeSet attrs) { super(context, attrs); init(); } public YabausePad(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { setOnTouchListener(this); } @Override public void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setARGB(0x80, 0x80, 0x80, 0x80); canvas.drawRect(up, paint); canvas.drawRect(right, paint); canvas.drawRect(down, paint); canvas.drawRect(left, paint); canvas.drawOval(new RectF(start), paint); canvas.drawCircle(a.centerX(), a.centerY(), 30, paint); canvas.drawCircle(b.centerX(), b.centerY(), 30, paint); canvas.drawCircle(c.centerX(), c.centerY(), 30, paint); canvas.drawCircle(x.centerX(), x.centerY(), 20, paint); canvas.drawCircle(y.centerX(), y.centerY(), 20, paint); canvas.drawCircle(z.centerX(), z.centerY(), 20, paint); paint.setARGB(0x80, 0xFF, 0xFF, 0xFF); paint.setTextAlign(Paint.Align.CENTER); paint.setTextSize(40); canvas.drawText("A", a.centerX(), a.centerY() + 15, paint); canvas.drawText("B", b.centerX(), b.centerY() + 15, paint); canvas.drawText("C", c.centerX(), c.centerY() + 15, paint); paint.setTextSize(25); canvas.drawText("X", x.centerX(), x.centerY() + 10, paint); canvas.drawText("Y", y.centerX(), y.centerY() + 10, paint); canvas.drawText("Z", z.centerX(), z.centerY() + 10, paint); } public void setOnPadListener(OnPadListener listener) { this.listener = listener; } public boolean onTouch(View v, MotionEvent event) { int action = event.getActionMasked(); int posx = (int) event.getX(); int posy = (int) event.getY(); PadEvent pe = null; if ((action != event.ACTION_DOWN) && (action != event.ACTION_UP)) return false; if (up.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_UP); if (right.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_RIGHT); if (down.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_DOWN); if (left.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_LEFT); if (start.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_START); if (a.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_A); if (b.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_B); if (c.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_C); if (x.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_X); if (y.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_Y); if (z.contains(posx, posy)) pe = new PadEvent(action, pe.BUTTON_Z); if ((listener != null) && (pe != null)) listener.onPad(v, pe); return true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); up = new Rect(100, getHeight() - 210, 140, getHeight() - 150); right = new Rect(150, getHeight() - 140, 210, getHeight() - 100); down = new Rect(100, getHeight() - 90, 140, getHeight() - 30); left = new Rect(30, getHeight() - 140, 90, getHeight() - 100); start = new Rect(getWidth() / 2 - 40, getHeight() - 60, getWidth() / 2 + 40, getHeight() - 15); a = new Rect(getWidth() - 235, getHeight() - 75, getWidth() - 185, getHeight() - 25); b = new Rect(getWidth() - 165, getHeight() - 125, getWidth() - 115, getHeight() - 75); c = new Rect(getWidth() - 75, getHeight() - 155, getWidth() - 25, getHeight() - 105); x = new Rect(getWidth() - 280, getHeight() - 140, getWidth() - 240, getHeight() - 100); y = new Rect(getWidth() - 210, getHeight() - 190, getWidth() - 170, getHeight() - 150); z = new Rect(getWidth() - 120, getHeight() - 220, getWidth() - 80, getHeight() - 180); setMeasuredDimension(width, height); } } yabause-0.9.13.1/src/android/src/org/yabause/android/Yabause.java000644 001750 001750 00000022076 12256006053 026506 0ustar00guillaumeguillaume000000 000000 /* Copyright 2011-2013 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.yabause.android; import java.lang.Runnable; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MenuInflater; import android.app.Dialog; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.view.View; class InputHandler extends Handler { private YabauseRunnable yr; public InputHandler(YabauseRunnable yr) { this.yr = yr; } public void handleMessage(Message msg) { Log.v("Yabause", "received message: " + msg.arg1 + " " + msg.arg2); if (msg.arg1 == 0) { yr.press(msg.arg2); } else if (msg.arg1 == 1) { yr.release(msg.arg2); } } } class YabauseRunnable implements Runnable { public static native int init(Yabause yabause); public static native void deinit(); public static native void exec(); public static native void press(int key); public static native void release(int key); public static native int initViewport( int width, int hieght); public static native int drawScreen(); public static native int lockGL(); public static native int unlockGL(); public static native void enableFPS(int enable); public static native void enableFrameskip(int enable); public static native void setVolume(int volume); private boolean inited; private boolean paused; public InputHandler handler; public YabauseRunnable(Yabause yabause) { handler = new InputHandler(this); int ok = init(yabause); inited = (ok == 0); } public void pause() { Log.v("Yabause", "pause... should really pause emulation now..."); paused = true; } public void resume() { Log.v("Yabause", "resuming emulation..."); paused = false; handler.post(this); } public void destroy() { Log.v("Yabause", "destroying yabause..."); inited = false; deinit(); } public void run() { if (inited && (! paused)) { exec(); handler.post(this); } } public boolean paused() { return paused; } } class YabauseHandler extends Handler { private Yabause yabause; public YabauseHandler(Yabause yabause) { this.yabause = yabause; } public void handleMessage(Message msg) { yabause.showDialog(msg.what, msg.getData()); } } public class Yabause extends Activity implements OnPadListener { private static final String TAG = "Yabause"; private YabauseRunnable yabauseThread; private YabauseHandler handler; private YabauseAudio audio; private String biospath; private String gamepath; private int carttype; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); audio = new YabauseAudio(this); PreferenceManager.setDefaultValues(this, R.xml.preferences, false); readPreferences(); handler = new YabauseHandler(this); yabauseThread = new YabauseRunnable(this); YabausePad pad = (YabausePad) findViewById(R.id.yabause_pad); pad.setOnPadListener(this); } @Override public void onPause() { super.onPause(); Log.v(TAG, "pause... should pause emulation..."); yabauseThread.pause(); audio.mute(audio.SYSTEM); } @Override public void onResume() { super.onResume(); Log.v(TAG, "resume... should resume emulation..."); readPreferences(); audio.unmute(audio.SYSTEM); yabauseThread.resume(); } @Override public void onDestroy() { super.onDestroy(); Log.v(TAG, "this is the end..."); yabauseThread.destroy(); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.emulation, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.pause: yabauseThread.pause(); return true; case R.id.quit: this.finish(); return true; case R.id.resume: yabauseThread.resume(); return true; case R.id.settings: Intent intent = new Intent(this, YabauseSettings.class); startActivity(intent); return true; default: return super.onOptionsItemSelected(item); } } @Override public boolean onPrepareOptionsMenu(Menu menu) { if (yabauseThread.paused()) { menu.setGroupVisible(R.id.paused, true); menu.setGroupVisible(R.id.running, false); } else { menu.setGroupVisible(R.id.paused, false); menu.setGroupVisible(R.id.running, true); } return true; } @Override public Dialog onCreateDialog(int id, Bundle args) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(args.getString("message")) .setCancelable(false) .setNegativeButton("Exit", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Yabause.this.finish(); } }) .setPositiveButton("Ignore", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = builder.create(); return alert; } @Override public boolean onPad(View v, PadEvent event) { Message message = handler.obtainMessage(); message.arg1 = event.getAction(); message.arg2 = event.getKey(); yabauseThread.handler.sendMessage(message); return true; } private void errorMsg(String msg) { Message message = handler.obtainMessage(); Bundle bundle = new Bundle(); bundle.putString("message", msg); message.setData(bundle); handler.sendMessage(message); } private void readPreferences() { SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this); boolean fps = sharedPref.getBoolean("pref_fps", false); yabauseThread.enableFPS(fps ? 1 : 0); boolean frameskip = sharedPref.getBoolean("pref_frameskip", false); yabauseThread.enableFrameskip(frameskip ? 1 : 0); boolean audioout = sharedPref.getBoolean("pref_audio", true); if (audioout) { audio.unmute(audio.USER); } else { audio.mute(audio.USER); } String bios = sharedPref.getString("pref_bios", ""); if (bios.length() > 0) { YabauseStorage storage = YabauseStorage.getStorage(); biospath = storage.getBiosPath(bios); } else biospath = ""; String game = sharedPref.getString("pref_game", ""); if (game.length() > 0) { YabauseStorage storage = YabauseStorage.getStorage(); gamepath = storage.getGamePath(game); } else gamepath = ""; String cart = sharedPref.getString("pref_cart", ""); if (cart.length() > 0) { Integer i = new Integer(cart); carttype = i.intValue(); } else carttype = -1; } public String getBiosPath() { return biospath; } public String getGamePath() { return gamepath; } public String getMemoryPath() { return YabauseStorage.getStorage().getMemoryPath("memory.ram"); } public int getCartridgeType() { return carttype; } public String getCartridgePath() { return YabauseStorage.getStorage().getCartridgePath(Cartridge.getDefaultFilename(carttype)); } static { System.loadLibrary("yabause"); } } yabause-0.9.13.1/src/android/src/org/yabause/android/Cartridge.java000644 001750 001750 00000004355 12256006053 027021 0ustar00guillaumeguillaume000000 000000 package org.yabause.android; public class Cartridge { final static int CART_NONE = 0; final static int CART_PAR = 1; final static int CART_BACKUPRAM4MBIT = 2; final static int CART_BACKUPRAM8MBIT = 3; final static int CART_BACKUPRAM16MBIT = 4; final static int CART_BACKUPRAM32MBIT = 5; final static int CART_DRAM8MBIT = 6; final static int CART_DRAM32MBIT = 7; final static int CART_NETLINK = 8; final static int CART_ROM16MBIT = 9; static public String getName(int cartridgetype) { switch(cartridgetype) { case CART_NONE: return "None"; case CART_PAR: return "Pro Action Replay"; case CART_BACKUPRAM4MBIT: return "4 Mbit Backup Ram"; case CART_BACKUPRAM8MBIT: return "8 Mbit Backup Ram"; case CART_BACKUPRAM16MBIT: return "16 Mbit Backup Ram"; case CART_BACKUPRAM32MBIT: return "32 Mbit Backup Ram"; case CART_DRAM8MBIT: return "8 Mbit Dram"; case CART_DRAM32MBIT: return "32 Mbit Dram"; case CART_NETLINK: return "Netlink"; case CART_ROM16MBIT: return "16 Mbit ROM"; } return null; } static public String getDefaultFilename(int cartridgetype) { switch(cartridgetype) { case CART_NONE: return "none.ram"; case CART_PAR: return "par.ram"; case CART_BACKUPRAM4MBIT: return "backup4.ram"; case CART_BACKUPRAM8MBIT: return "backup8.ram"; case CART_BACKUPRAM16MBIT: return "backup16.ram"; case CART_BACKUPRAM32MBIT: return "backup32.ram"; case CART_DRAM8MBIT: return "dram8.ram"; case CART_DRAM32MBIT: return "dram32.ram"; case CART_NETLINK: return "netlink.ram"; case CART_ROM16MBIT: return "rom16.ram"; } return null; } static public int getTypeCount() { return 10; } } yabause-0.9.13.1/src/android/src/org/yabause/android/YabauseSettings.java000644 001750 001750 00000012012 12256006053 030214 0ustar00guillaumeguillaume000000 000000 /* Copyright 2013 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.yabause.android; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.preference.ListPreference; import android.preference.PreferenceActivity; import android.content.SharedPreferences; public class YabauseSettings extends PreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.preferences); YabauseStorage storage = YabauseStorage.getStorage(); /* bios */ ListPreference p = (ListPreference) getPreferenceManager().findPreference("pref_bios"); List labels = new ArrayList(); List values = new ArrayList(); CharSequence[] biosfiles = storage.getBiosFiles(); labels.add("built-in bios"); values.add(""); if ((biosfiles != null) && (biosfiles.length > 0)) { for(CharSequence bios : biosfiles) { labels.add(bios); values.add(bios); } CharSequence[] entries = new CharSequence[labels.size()]; labels.toArray(entries); CharSequence[] entryValues = new CharSequence[values.size()]; values.toArray(entryValues); p.setEntries(entries); p.setEntryValues(entryValues); p.setSummary(p.getEntry()); } else { p.setEnabled(false); p.setSummary("built-in bios"); } /* game */ ListPreference game = (ListPreference) getPreferenceManager().findPreference("pref_game"); List gamelabels = new ArrayList(); List gamevalues = new ArrayList(); CharSequence[] gamefiles = storage.getGameFiles(); if ((gamefiles != null) && (gamefiles.length > 0)) { for(CharSequence gamefile : gamefiles) { gamelabels.add(gamefile); gamevalues.add(gamefile); } CharSequence[] gameentries = new CharSequence[gamelabels.size()]; gamelabels.toArray(gameentries); CharSequence[] gameentryValues = new CharSequence[gamevalues.size()]; gamevalues.toArray(gameentryValues); game.setEntries(gameentries); game.setEntryValues(gameentryValues); game.setSummary(game.getEntry()); } else { game.setEnabled(false); game.setSummary("no game found"); } /* cartridge */ ListPreference cart = (ListPreference) getPreferenceManager().findPreference("pref_cart"); List cartlabels = new ArrayList(); List cartvalues = new ArrayList(); for(int carttype = 0;carttype < Cartridge.getTypeCount();carttype++) { cartlabels.add(Cartridge.getName(carttype)); cartvalues.add(Integer.toString(carttype)); } CharSequence[] cartentries = new CharSequence[cartlabels.size()]; cartlabels.toArray(cartentries); CharSequence[] cartentryValues = new CharSequence[cartvalues.size()]; cartvalues.toArray(cartentryValues); cart.setEntries(cartentries); cart.setEntryValues(cartentryValues); cart.setSummary(cart.getEntry()); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (key.equals("pref_bios")) { ListPreference biosPref = (ListPreference) findPreference(key); biosPref.setSummary(biosPref.getEntry()); } else if (key.equals("pref_game")) { ListPreference gamePref = (ListPreference) findPreference(key); gamePref.setSummary(gamePref.getEntry()); } } @Override protected void onResume() { super.onResume(); getPreferenceScreen().getSharedPreferences() .registerOnSharedPreferenceChangeListener(this); } @Override protected void onPause() { super.onPause(); getPreferenceScreen().getSharedPreferences() .unregisterOnSharedPreferenceChangeListener(this); } } yabause-0.9.13.1/src/android/src/org/yabause/android/YabauseView.java000644 001750 001750 00000013420 12256006053 027332 0ustar00guillaumeguillaume000000 000000 /* Copyright 2011-2013 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.yabause.android; import java.lang.Runnable; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.view.View; class YabauseView extends SurfaceView implements Callback { private static String TAG = "YabauseView"; private static final boolean DEBUG = false; private int axisX = 0; private int axisY = 0; public boolean[] pointers = new boolean[256]; public int[] pointerX = new int[256]; public int[] pointerY = new int[256]; private EGLContext mEglContext; private EGLDisplay mEglDisplay; private EGLSurface mEglSurface; private EGLConfig mEglConfig; public YabauseView(Context context, AttributeSet attrs) { super(context,attrs); init(false, 0, 0); } public YabauseView(Context context) { super(context); init(false, 0, 0); } public YabauseView(Context context, boolean translucent, int depth, int stencil) { super(context); init(translucent, depth, stencil); } private void init(boolean translucent, int depth, int stencil) { getHolder().addCallback(this); getHolder().setType(SurfaceHolder.SURFACE_TYPE_GPU); initGLES(); } private boolean initGLES(){ EGL10 egl = (EGL10)EGLContext.getEGL(); mEglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); if( mEglDisplay == EGL10.EGL_NO_DISPLAY ){ Log.e(TAG, "Fail to get Display"); return false; } int[] version = new int[2]; if( !egl.eglInitialize(mEglDisplay, version) ){ Log.e(TAG, "Fail to eglInitialize"); return false; } int[] configSpec = { EGL10.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] numConfigs = new int[1]; if( !egl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, numConfigs) ){ Log.e(TAG, "Fail to Choose Config"); return false; } mEglConfig = configs[0]; mEglContext = egl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, null); if( mEglContext == EGL10.EGL_NO_CONTEXT ){ Log.e(TAG, "Fail to Create OpenGL Context"); return false; } return true; } private boolean createSurface(){ EGL10 egl = (EGL10)EGLContext.getEGL(); mEglSurface = egl.eglCreateWindowSurface(mEglDisplay, mEglConfig, getHolder(), null); if( mEglSurface == EGL10.EGL_NO_SURFACE ){ return false; } return true; } private void endGLES(){ EGL10 egl = (EGL10)EGLContext.getEGL(); if( mEglSurface != null){ //レンダリングコンテキストã¨ã®çµã³ã¤ã‘ã¯è§£é™¤ egl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); egl.eglDestroySurface(mEglDisplay, mEglSurface); mEglSurface = null; } if( mEglContext != null ){ egl.eglDestroyContext(mEglDisplay, mEglContext); mEglContext = null; } if( mEglDisplay != null){ egl.eglTerminate(mEglDisplay); mEglDisplay = null; } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { EGL10 egl = (EGL10)EGLContext.getEGL(); YabauseRunnable.lockGL(); egl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext); YabauseRunnable.initViewport(width, height); YabauseRunnable.unlockGL(); } @Override public void surfaceCreated(SurfaceHolder holder) { if( !createSurface() ){ Log.e(TAG, "Fail to Creat4e Surface"); return ; } } @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int specw = MeasureSpec.getSize(widthMeasureSpec); int spech = MeasureSpec.getSize(heightMeasureSpec); float specratio = (float) specw / spech; int saturnw = 320; int saturnh = 224; float saturnratio = (float) saturnw / saturnh; float revratio = (float) saturnh / saturnw; if (specratio > saturnratio) { setMeasuredDimension((int) (spech * saturnratio), spech); } else { setMeasuredDimension(specw, (int) (specw * revratio)); } } } yabause-0.9.13.1/src/android/src/org/yabause/android/YabauseAudio.java000644 001750 001750 00000005157 12256006053 027471 0ustar00guillaumeguillaume000000 000000 /* Copyright 2013 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package org.yabause.android; import android.app.Activity; import android.media.AudioManager; public class YabauseAudio implements AudioManager.OnAudioFocusChangeListener { public final int SYSTEM = 1; public final int USER = 2; private Activity activity; private int muteFlags; private boolean muted; YabauseAudio(Activity activity) { this.activity = activity; activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); this.muteFlags = 0; this.muted = false; } public void mute(int flags) { muted = true; muteFlags |= flags; AudioManager am = (AudioManager) activity.getSystemService(Activity.AUDIO_SERVICE); am.abandonAudioFocus(this); YabauseRunnable.setVolume(0); } public void unmute(int flags) { muteFlags &= ~flags; if (0 == muteFlags) { muted = false; AudioManager am = (AudioManager) activity.getSystemService(Activity.AUDIO_SERVICE); int result = am.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { YabauseRunnable.setVolume(0); } else { YabauseRunnable.setVolume(100); } } } @Override public void onAudioFocusChange(int focusChange) { if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) { mute(SYSTEM); } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { YabauseRunnable.setVolume(50); } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { if (muted) unmute(SYSTEM); else YabauseRunnable.setVolume(100); } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { mute(SYSTEM); } } } yabause-0.9.13.1/src/android/src/org/yabause/android/YabauseStorage.java000644 001750 001750 00000005125 12256006053 030027 0ustar00guillaumeguillaume000000 000000 package org.yabause.android; import java.io.File; import java.io.FilenameFilter; import android.os.Environment; import android.util.Log; class BiosFilter implements FilenameFilter { public boolean accept(File dir, String filename) { if (filename.endsWith(".bin")) return true; if (filename.endsWith(".rom")) return true; return false; } } class GameFilter implements FilenameFilter { public boolean accept(File dir, String filename) { if (filename.endsWith(".bin")) return true; if (filename.endsWith(".cue")) return true; if (filename.endsWith(".iso")) return true; if (filename.endsWith(".mds")) return true; return false; } } class MemoryFilter implements FilenameFilter { public boolean accept(File dir, String filename) { if (filename.endsWith(".ram")) return true; return false; } } public class YabauseStorage { static private YabauseStorage instance = null; private File bios; private File games; private File memory; private File cartridge; private YabauseStorage() { File yabroot = new File(Environment.getExternalStorageDirectory(), "yabause"); if (! yabroot.exists()) yabroot.mkdir(); bios = new File(yabroot, "bios"); if (! bios.exists()) bios.mkdir(); games = new File(yabroot, "games"); if (! games.exists()) games.mkdir(); memory = new File(yabroot, "memory"); if (! memory.exists()) memory.mkdir(); cartridge = new File(yabroot, "cartridge"); if (! cartridge.exists()) cartridge.mkdir(); } static public YabauseStorage getStorage() { if (instance == null) { instance = new YabauseStorage(); } return instance; } public String[] getBiosFiles() { String[] biosfiles = bios.list(new BiosFilter()); return biosfiles; } public String getBiosPath(String biosfile) { return bios + File.separator + biosfile; } public String[] getGameFiles() { String[] gamefiles = games.list(new GameFilter()); return gamefiles; } public String getGamePath(String gamefile) { return games + File.separator + gamefile; } public String[] getMemoryFiles() { String[] memoryfiles = memory.list(new MemoryFilter()); return memoryfiles; } public String getMemoryPath(String memoryfile) { return memory + File.separator + memoryfile; } public String getCartridgePath(String cartridgefile) { return cartridge + File.separator + cartridgefile; } } yabause-0.9.13.1/src/android/AndroidManifest.xml000644 001750 001750 00000002437 12256006053 023413 0ustar00guillaumeguillaume000000 000000 yabause-0.9.13.1/src/android/build.xml000644 001750 001750 00000006402 12256006054 021440 0ustar00guillaumeguillaume000000 000000 yabause-0.9.13.1/src/android/project.properties000644 001750 001750 00000000550 12256006053 023400 0ustar00guillaumeguillaume000000 000000 # This file is automatically generated by Android Tools. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must be checked in Version Control Systems. # # To customize properties used by the Ant build system use, # "ant.properties", and override values to adapt the script to your # project structure. # Project target. target=android-8 yabause-0.9.13.1/src/sndal.c000644 001750 001750 00000020026 12256006161 017441 0ustar00guillaumeguillaume000000 000000 /* Copyright 2009 Lawrence Sebald Copyright 2005-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_LIBAL #ifdef __APPLE__ #include #include #else #include #include #endif #include #include #include #include #include "error.h" #include "scsp.h" #include "sndal.h" #include "debug.h" int SNDALInit(); void SNDALDeInit(); int SNDALReset(); int SNDALChangeVideoFormat(int vertfreq); void SNDALUpdateAudio(u32 *left, u32 *right, u32 samples); u32 SNDALGetAudioSpace(); void SNDALMuteAudio(); void SNDALUnMuteAudio(); void SNDALSetVolume(int vol); SoundInterface_struct SNDAL = { SNDCORE_AL, "OpenAL Sound Interface", SNDALInit, SNDALDeInit, SNDALReset, SNDALChangeVideoFormat, SNDALUpdateAudio, SNDALGetAudioSpace, SNDALMuteAudio, SNDALUnMuteAudio, SNDALSetVolume }; #define SOUND_BUFFERS 4 #define SOUND_FREQ 44100 static ALCdevice *device = NULL; static ALCcontext *context = NULL; static ALuint source; static ALuint bufs[SOUND_BUFFERS]; static u16 *buffer; static u32 soundbufsize = 0; static u32 soundoffset; static volatile u32 soundpos; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static int thd_done = 0; static pthread_t thd; static int soundvolume = 1; static int soundlen; static void *sound_update_thd(void *ptr __attribute__((unused))) { ALint proc; ALuint buf; u8 data[2048]; u8 *soundbuf = (u8 *)buffer; int i; while(!thd_done) { /* See if the stream needs updating yet. */ alGetSourcei(source, AL_BUFFERS_PROCESSED, &proc); if(alGetError() != AL_NO_ERROR) { continue; } pthread_mutex_lock(&mutex); /* Go through each buffer that needs more data. */ while(proc--) { /* Unqueue the old buffer, so that it can be filled again. */ alSourceUnqueueBuffers(source, 1, &buf); if(alGetError() != AL_NO_ERROR) { continue; } for(i = 0; i < 2048; ++i) { if(soundpos >= soundbufsize) soundpos = 0; data[i] = soundbuf[soundpos]; ++soundpos; } alBufferData(buf, AL_FORMAT_STEREO16, data, 2048, SOUND_FREQ); alSourceQueueBuffers(source, 1, &buf); } pthread_mutex_unlock(&mutex); /* Sleep for 5ms. */ usleep(5 * 1000); } return NULL; } static void sdlConvert32uto16s(s32 *srcL, s32 *srcR, s16 *dst, u32 len) { u32 i; for (i = 0; i < len; i++) { // Left Channel *srcL = ( *srcL *soundvolume ) / 100; if (*srcL > 0x7FFF) *dst = 0x7FFF; else if (*srcL < -0x8000) *dst = -0x8000; else *dst = *srcL; srcL++; dst++; // Right Channel *srcR = ( *srcR *soundvolume ) / 100; if (*srcR > 0x7FFF) *dst = 0x7FFF; else if (*srcR < -0x8000) *dst = -0x8000; else *dst = *srcR; srcR++; dst++; } } void SNDALUpdateAudio(u32 *left, u32 *right, u32 num_samples) { u32 copy1size = 0, copy2size = 0; pthread_mutex_lock(&mutex); if((soundbufsize - soundoffset) < (num_samples * sizeof(s16) * 2)) { copy1size = (soundbufsize - soundoffset); copy2size = (num_samples * sizeof(s16) * 2) - copy1size; } else { copy1size = (num_samples * sizeof(s16) * 2); copy2size = 0; } sdlConvert32uto16s((s32 *)left, (s32 *)right, (s16 *)(((u8 *)buffer)+soundoffset), copy1size / sizeof(s16) / 2); if(copy2size) sdlConvert32uto16s((s32 *)left + (copy1size / sizeof(s16) / 2), (s32 *)right + (copy1size / sizeof(s16) / 2), (s16 *)buffer, copy2size / sizeof(s16) / 2); soundoffset += copy1size + copy2size; soundoffset %= soundbufsize; pthread_mutex_unlock(&mutex); } int SNDALInit() { int rv = 0, i; /* Attempt to grab the preferred device from OpenAL. */ device = alcOpenDevice(NULL); if(!device) { rv = -1; goto err1; } context = alcCreateContext(device, NULL); if(!context) { rv = -2; goto err2; } alcMakeContextCurrent(context); /* Clear any error states. */ alGetError(); /* Create our sound buffers. */ alGenBuffers(SOUND_BUFFERS, bufs); if(alGetError() != AL_NO_ERROR) { rv = -3; goto err3; } /* Create the source for the stream. */ alGenSources(1, &source); if(alGetError() != AL_NO_ERROR) { rv = -4; goto err4; } /* Set up the source for basic playback. */ alSource3f(source, AL_DIRECTION, 0.0f, 0.0f, 0.0f); alSource3f(source, AL_POSITION, 0.0f, 0.0f, 0.0f); alSource3f(source, AL_VELOCITY, 0.0f, 0.0f, 0.0f); alSourcef(source, AL_ROLLOFF_FACTOR, 0.0f); alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE); soundlen = SOUND_FREQ / 60; soundbufsize = soundlen * SOUND_BUFFERS * 2 * 2; soundvolume = 100; if((buffer = (u16 *)malloc(soundbufsize)) == NULL) { rv = -5; goto err5; } memset(buffer, 0, soundbufsize); for(i = 0; i < SOUND_BUFFERS; ++i) { /* Fill the buffer with empty sound. */ alBufferData(bufs[i], AL_FORMAT_STEREO16, buffer + i * 2048, 2048, SOUND_FREQ); alSourceQueueBuffers(source, 1, bufs + i); } /* Start the sound playback. */ alSourcePlay(source); /* Start the update thread. */ pthread_create(&thd, NULL, &sound_update_thd, NULL); return 0; /* Error conditions. Errors cause cascading deinitialization, so hence this chain of labels. */ err5: alDeleteSources(1, &source); err4: alDeleteBuffers(SOUND_BUFFERS, bufs); err3: alcMakeContextCurrent(NULL); alcDestroyContext(context); err2: alcCloseDevice(device); err1: context = NULL; device = NULL; return rv; } void SNDALDeInit() { /* Stop our update thread. */ thd_done = 1; pthread_join(thd, NULL); /* Stop playback. */ alSourceStop(source); /* Clean up our buffers and such. */ alDeleteSources(1, &source); alDeleteBuffers(SOUND_BUFFERS, bufs); /* Release our context and close the sound device. */ alcMakeContextCurrent(NULL); alcDestroyContext(context); alcCloseDevice(device); context = NULL; device = NULL; thd_done = 0; } int SNDALReset() { return 0; } int SNDALChangeVideoFormat(int vertfreq) { pthread_mutex_lock(&mutex); soundlen = SOUND_FREQ / vertfreq; soundbufsize = soundlen * SOUND_BUFFERS * 2 * 2; if(buffer) free(buffer); if((buffer = (u16 *)malloc(soundbufsize)) == NULL) return -1; memset(buffer, 0, soundbufsize); pthread_mutex_unlock(&mutex); return 0; } u32 SNDALGetAudioSpace() { u32 freespace=0; if(soundoffset > soundpos) freespace = soundbufsize - soundoffset + soundpos; else freespace = soundpos - soundoffset; return (freespace / sizeof(s16) / 2); } void SNDALMuteAudio() { alSourcePause(source); } void SNDALUnMuteAudio() { alSourcePlay(source); } void SNDALSetVolume(int vol) { soundvolume = (int)((128.0 / 100.0) * vol); } #endif /* HAVE_LIBAL */ yabause-0.9.13.1/src/perdx.c000644 001750 001750 00000054373 12256006124 017475 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006,2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #ifdef __MINGW32__ #undef HAVE_XINPUT #endif #define COBJMACROS #include #include #include #include #include "debug.h" #include "peripheral.h" #include "perdx.h" #include "vdp1.h" #include "vdp2.h" #include "yui.h" #include "movie.h" #include "error.h" enum { EMUTYPE_NONE=0, EMUTYPE_STANDARDPAD, EMUTYPE_ANALOGPAD, EMUTYPE_STUNNER, EMUTYPE_MOUSE, EMUTYPE_KEYBOARD }; #define DX_PADOFFSET 24 #define DX_STICKOFFSET 8 #define DX_MAKEKEY(p, s, a) ( ((p) << DX_PADOFFSET) | ((s) << DX_STICKOFFSET) | (a) ) #define DX_PerAxisValue(p, s, a, v) PerAxisValue(DX_MAKEKEY(p, s, a), (v)) #define DX_PerKeyUp(p, s, a) PerKeyUp(DX_MAKEKEY(p, s, a) ) #define DX_PerKeyDown(p, s, a) PerKeyDown(DX_MAKEKEY(p, s, a) ) int Check_Skip_Key(); PerInterface_struct PERDIRECTX = { PERCORE_DIRECTX, "DirectX Input Interface", PERDXInit, PERDXDeInit, PERDXHandleEvents, PERDXScan, TRUE, PERDXFlush, }; LPDIRECTINPUT8 lpDI8 = NULL; struct { BOOL is_xinput_device; int user_index; LPDIRECTINPUTDEVICE8 lpDIDevice; } dev_list[256]; // I hope that's enough u32 num_devices=0; static unsigned int PERCORE_INITIALIZED = 0; const char *mouse_names[] = { "A", "B", "C", "Start", NULL }; #define PAD_DIR_AXISLEFT 0 #define PAD_DIR_AXISRIGHT 1 #define PAD_DIR_AXISUP 2 #define PAD_DIR_AXISDOWN 3 #define PAD_DIR_POVUP 0 #define PAD_DIR_POVRIGHT 1 #define PAD_DIR_POVDOWN 2 #define PAD_DIR_POVLEFT 3 HWND DXGetWindow (); #ifdef HAVE_XINPUT #define SAFE_RELEASE(p) { if(p) { (p)->lpVtbl->Release((p)); (p)=NULL; } } #define SAFE_DELETE(p) { if(p) { free (p); (p)=NULL; } } typedef struct { DWORD dwVidPid; struct XINPUT_DEVICE_NODE* pNext; } XINPUT_DEVICE_NODE; XINPUT_DEVICE_NODE *g_pXInputDeviceList = NULL; BOOL bCleanupCOM=FALSE; ////////////////////////////////////////////////////////////////////////////// HRESULT SetupForIsXInputDevice() { IWbemLocator *pIWbemLocator = NULL; IEnumWbemClassObject *pEnumDevices = NULL; IWbemClassObject *pDevices[20] = {0}; IWbemServices *pIWbemServices = NULL; BSTR bstrNamespace = NULL; BSTR bstrDeviceID = NULL; BSTR bstrClassName = NULL; DWORD uReturned = 0; BOOL bIsXinputDevice = FALSE; UINT iDevice = 0; VARIANT var; HRESULT hr; hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); bCleanupCOM = SUCCEEDED(hr); // Create WMI hr = CoCreateInstance( &CLSID_WbemContext, NULL, CLSCTX_INPROC_SERVER, &IID_IWbemContext, (LPVOID*) &pIWbemLocator); if( FAILED(hr) || pIWbemLocator == NULL ) goto bail; bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto bail; bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto bail; bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto bail; // Connect to WMI hr = IWbemLocator_ConnectServer(pIWbemLocator, bstrNamespace, NULL, NULL, NULL, 0L, NULL, NULL, &pIWbemServices ); if( FAILED(hr) || pIWbemServices == NULL ) goto bail; // Switch security level to IMPERSONATE CoSetProxyBlanket( (IUnknown *)pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); hr = IWbemServices_CreateInstanceEnum( pIWbemServices, bstrClassName, 0, NULL, &pEnumDevices ); if( FAILED(hr) || pEnumDevices == NULL ) goto bail; // Loop over all devices for(;;) { // Get 20 at a time hr = IEnumWbemClassObject_Next( pEnumDevices, 10000, 20, pDevices, &uReturned ); if( FAILED(hr) || uReturned == 0 ) break; for( iDevice=0; iDevicedwVidPid = dwVidPid; pNewNode->pNext = (struct XINPUT_DEVICE_NODE *)g_pXInputDeviceList; g_pXInputDeviceList = pNewNode; } } } SAFE_RELEASE( pDevices[iDevice] ); } } bail: if(bstrNamespace) SysFreeString(bstrNamespace); if(bstrDeviceID) SysFreeString(bstrDeviceID); if(bstrClassName) SysFreeString(bstrClassName); for( iDevice=0; iDevice<20; iDevice++ ) SAFE_RELEASE( pDevices[iDevice] ); SAFE_RELEASE( pEnumDevices ); SAFE_RELEASE( pIWbemLocator ); SAFE_RELEASE( pIWbemServices ); return hr; } ////////////////////////////////////////////////////////////////////////////// void CleanupForIsXInputDevice() { // Cleanup linked list XINPUT_DEVICE_NODE* pNode = g_pXInputDeviceList; while( pNode ) { XINPUT_DEVICE_NODE* pDelete = pNode; pNode = (XINPUT_DEVICE_NODE *)pNode->pNext; SAFE_DELETE( pDelete ); } if( bCleanupCOM ) CoUninitialize(); } ////////////////////////////////////////////////////////////////////////////// BOOL IsXInputDevice( const GUID* guidProduct ) { // Check each xinput device to see if this device's vid/pid matches XINPUT_DEVICE_NODE* pNode = g_pXInputDeviceList; while( pNode ) { if( pNode->dwVidPid == guidProduct->Data1 ) return TRUE; pNode = (XINPUT_DEVICE_NODE*)pNode->pNext; } return FALSE; } #endif ////////////////////////////////////////////////////////////////////////////// BOOL CALLBACK EnumPeripheralsCallback (LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) { if (GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_GAMEPAD || GET_DIDEVICE_TYPE(lpddi->dwDevType) == DI8DEVTYPE_JOYSTICK) { #ifdef HAVE_XINPUT if (IsXInputDevice(&lpddi->guidProduct)) { dev_list[num_devices].lpDIDevice = NULL; dev_list[num_devices].is_xinput_device = TRUE; dev_list[num_devices].user_index=((int *)pvRef)[0]; ((int *)pvRef)[0]++; num_devices++; } else #endif { dev_list[num_devices].is_xinput_device = FALSE; if (SUCCEEDED(IDirectInput8_CreateDevice(lpDI8, &lpddi->guidInstance, &dev_list[num_devices].lpDIDevice, NULL) )) num_devices++; } } return DIENUM_CONTINUE; } ////////////////////////////////////////////////////////////////////////////// int PERDXInit(void) { char tempstr[512]; HRESULT ret; int user_index=0; u32 i; if (PERCORE_INITIALIZED) return 0; if (FAILED((ret = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, &IID_IDirectInput8, (LPVOID *)&lpDI8, NULL)) )) { sprintf(tempstr, "Input. DirectInput8Create error: %s - %s", DXGetErrorString8(ret), DXGetErrorDescription8(ret)); YabSetError(YAB_ERR_CANNOTINIT, tempstr); return -1; } #ifdef HAVE_XINPUT SetupForIsXInputDevice(); #endif num_devices = 0; IDirectInput8_EnumDevices(lpDI8, DI8DEVCLASS_ALL, EnumPeripheralsCallback, &user_index, DIEDFL_ATTACHEDONLY); #ifdef HAVE_XINPUT CleanupForIsXInputDevice(); #endif for (i = 0; i < num_devices; i++) { if (!dev_list[i].is_xinput_device) { if( FAILED( ret = IDirectInputDevice8_SetDataFormat(dev_list[i].lpDIDevice, &c_dfDIJoystick2 ) ) ) return -1; // Set the cooperative level to let DInput know how this device should // interact with the system and with other DInput applications. if( FAILED( ret = IDirectInputDevice8_SetCooperativeLevel( dev_list[i].lpDIDevice, DXGetWindow(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND /* DISCL_EXCLUSIVE | DISCL_FOREGROUND */ ) ) ) return -1; } } PerPortReset(); //LoadDefaultPort1A(); PERCORE_INITIALIZED = 1; return 0; } ////////////////////////////////////////////////////////////////////////////// void PERDXDeInit(void) { u32 i; for (i = 0; i < num_devices; i++) { if (dev_list[i].lpDIDevice) { IDirectInputDevice8_Unacquire(dev_list[i].lpDIDevice); IDirectInputDevice8_Release(dev_list[i].lpDIDevice); dev_list[i].lpDIDevice = NULL; } } if (lpDI8) { IDirectInput8_Release(lpDI8); lpDI8 = NULL; } PERCORE_INITIALIZED = 0; } ////////////////////////////////////////////////////////////////////////////// void PollAxisAsButton(u32 pad, int min_id, int max_id, int deadzone, int val) { if ( val < -deadzone ) { DX_PerKeyUp( pad, 0, max_id ); DX_PerKeyDown( pad, 0, min_id ); } else if ( val > deadzone ) { DX_PerKeyUp( pad, 0, min_id ); DX_PerKeyDown( pad, 0, max_id ); } else { DX_PerKeyUp( pad, 0, min_id ); DX_PerKeyUp( pad, 0, max_id ); } } ////////////////////////////////////////////////////////////////////////////// #ifdef HAVE_XINPUT void PollXInputButtons(u32 pad, XINPUT_STATE *state) { int i; // Check buttons for ( i = 0; i < 16; i++ ) { WORD mask = 1 << i; if ( (state->Gamepad.wButtons & mask) == mask ) DX_PerKeyDown( pad, 0, DIJOFS_BUTTON(i) ); else DX_PerKeyUp( pad, 0, DIJOFS_BUTTON(i) ); } } #endif ////////////////////////////////////////////////////////////////////////////// void PollKeys(void) { u32 i, j; DWORD size=8; HRESULT hr; for (i = 0; i < num_devices; i++) { LPDIRECTINPUTDEVICE8 curdevice; DIJOYSTATE2 js; #ifdef HAVE_XINPUT if (dev_list[i].is_xinput_device) { XINPUT_STATE state; ZeroMemory( &state, sizeof(XINPUT_STATE) ); if (XInputGetState(dev_list[i].user_index, &state) != ERROR_DEVICE_NOT_CONNECTED) continue; // Handle axis DX_PerAxisValue(i, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XI_THUMBLX, (u8)((state.Gamepad.sThumbLX) >> 8)); DX_PerAxisValue(i, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XI_THUMBLY, (u8)((state.Gamepad.sThumbLY) >> 8)); DX_PerAxisValue(i, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XI_THUMBRX, (u8)((state.Gamepad.sThumbRX) >> 8)); DX_PerAxisValue(i, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XI_THUMBRY, (u8)((state.Gamepad.sThumbRY) >> 8)); DX_PerAxisValue(i, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XI_TRIGGERL, state.Gamepad.bLeftTrigger); DX_PerAxisValue(i, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XI_TRIGGERR, state.Gamepad.bRightTrigger); // Left Stick PollAxisAsButton(i, XI_THUMBL+PAD_DIR_AXISLEFT, XI_THUMBL+PAD_DIR_AXISRIGHT, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, state.Gamepad.sThumbLX); PollAxisAsButton(i, XI_THUMBL+PAD_DIR_AXISUP, XI_THUMBL+PAD_DIR_AXISDOWN, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, state.Gamepad.sThumbLY); // Right Stick PollAxisAsButton(i, XI_THUMBR+PAD_DIR_AXISLEFT, XI_THUMBR+PAD_DIR_AXISRIGHT, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, state.Gamepad.sThumbRX); PollAxisAsButton(i, XI_THUMBR+PAD_DIR_AXISUP, XI_THUMBR+PAD_DIR_AXISDOWN, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, state.Gamepad.sThumbRY); PollXInputButtons(i, &state); continue; } else if (dev_list[i].lpDIDevice == NULL) continue; #else if (dev_list[i].lpDIDevice == NULL) continue; #endif curdevice=dev_list[i].lpDIDevice; // Poll the device to read the current state hr = IDirectInputDevice8_Poll(curdevice); if( FAILED( hr ) ) { hr = IDirectInputDevice8_Acquire(curdevice); while( hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(curdevice); continue; } // Get the input's device state if( FAILED( hr = IDirectInputDevice8_GetDeviceState( curdevice, sizeof( DIJOYSTATE2 ), &js ) ) ) continue; // Handle axis DX_PerAxisValue(i, 0x3FFF, XI_THUMBLX, (u8)((js.lX) >> 8)); DX_PerAxisValue(i, 0x3FFF, XI_THUMBLY, (u8)((js.lY) >> 8)); DX_PerAxisValue(i, 0x3FFF, XI_THUMBRX, (u8)((js.lRx) >> 8)); DX_PerAxisValue(i, 0x3FFF, XI_THUMBRY, (u8)((js.lRy) >> 8)); // Left Stick PollAxisAsButton(i, XI_THUMBL+PAD_DIR_AXISLEFT, XI_THUMBL+PAD_DIR_AXISRIGHT, 0x3FFF, js.lX-0x7FFF); PollAxisAsButton(i, XI_THUMBL+PAD_DIR_AXISUP, XI_THUMBL+PAD_DIR_AXISDOWN, 0x3FFF, js.lY-0x7FFF); // Right Stick PollAxisAsButton(i, XI_THUMBR+PAD_DIR_AXISLEFT, XI_THUMBR+PAD_DIR_AXISRIGHT, 0x3FFF, js.lRx-0x7FFF); PollAxisAsButton(i, XI_THUMBR+PAD_DIR_AXISUP, XI_THUMBR+PAD_DIR_AXISDOWN, 0x3FFF, js.lRy-0x7FFF); for (j = 0; j < 4; j++) { if (LOWORD(js.rgdwPOV[j]) == 0xFFFF) { DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT); } // POV Up else if (js.rgdwPOV[j] < 4500) { DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT); } // POV Up-right else if (js.rgdwPOV[j] < 9000) { DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP); DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT); } // POV Right else if (js.rgdwPOV[j] < 13500) { DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP); } // POV Right-down else if (js.rgdwPOV[j] < 18000) { DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT); DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN); } // POV Down else if (js.rgdwPOV[j] < 22500) { DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVRIGHT); } // POV Down-left else if (js.rgdwPOV[j] < 27000) { DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN); DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT); } // POV Left else if (js.rgdwPOV[j] < 31500) { DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP); DX_PerKeyUp(i, 0, DIJOFS_POV(j)+PAD_DIR_POVDOWN); } // POV Left-up else if (js.rgdwPOV[j] < 36000) { DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVLEFT); DX_PerKeyDown(i, 0, DIJOFS_POV(j)+PAD_DIR_POVUP); } } for (j = 0; j < 32; j++) { if (js.rgbButtons[j] & 0x80) DX_PerKeyDown(i,0,DIJOFS_BUTTON(j)); else DX_PerKeyUp(i,0,DIJOFS_BUTTON(j)); } } } ////////////////////////////////////////////////////////////////////////////// int PERDXHandleEvents(void) { PollKeys(); if (YabauseExec() != 0) return -1; return 0; } ////////////////////////////////////////////////////////////////////////////// u32 ScanXInputAxis(int pad, LONG axis, LONG deadzone, SHORT stick, int min_id, int max_id) { if (axis < -deadzone) return DX_MAKEKEY(pad, stick, min_id); else if (axis > deadzone) return DX_MAKEKEY(pad, stick, max_id); else return 0; } ////////////////////////////////////////////////////////////////////////////// u32 ScanXInputTrigger(int pad, BYTE value, BYTE deadzone, SHORT stick, int id) { if (value > deadzone) return DX_MAKEKEY(pad, stick, id); else return 0; } ////////////////////////////////////////////////////////////////////////////// u32 PERDXScan(u32 flags) { int i, j; HRESULT hr; for (i = 0; i < num_devices; i++) { LPDIRECTINPUTDEVICE8 curdevice; DIJOYSTATE2 js; u32 scan; #ifdef HAVE_XINPUT if (dev_list[i].is_xinput_device) { XINPUT_STATE state; ZeroMemory( &state, sizeof(XINPUT_STATE) ); if (XInputGetState(dev_list[i].user_index, &state) != ERROR_DEVICE_NOT_CONNECTED) continue; // Handle axis if (flags & PERSF_AXIS) { // L Thumb if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XI_THUMBLX, XI_THUMBLX)) != 0) return scan; if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XI_THUMBLY, XI_THUMBLY)) != 0) return scan; // R Thumb if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XI_THUMBRX, XI_THUMBRX)) != 0) return scan; if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XI_THUMBRY, XI_THUMBRY)) != 0) return scan; // L Trigger if ((scan = ScanXInputTrigger(i, state.Gamepad.bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XI_TRIGGERL)) != 0) return scan; // R Trigger if ((scan = ScanXInputTrigger(i, state.Gamepad.bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XINPUT_GAMEPAD_TRIGGER_THRESHOLD, XI_TRIGGERR)) != 0) return scan; } if (flags & PERSF_HAT) { // L Thumb if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, 0, XI_THUMBL+PAD_DIR_AXISLEFT, XI_THUMBL+PAD_DIR_AXISRIGHT)) != 0) return scan; if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, 0, XI_THUMBL+PAD_DIR_AXISUP, XI_THUMBL+PAD_DIR_AXISDOWN)) != 0) return scan; // R Thumb if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, 0, XI_THUMBR+PAD_DIR_AXISLEFT, XI_THUMBR+PAD_DIR_AXISRIGHT)) != 0) return scan; if ((scan = ScanXInputAxis(i, state.Gamepad.sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, 0, XI_THUMBR+PAD_DIR_AXISUP, XI_THUMBR+PAD_DIR_AXISDOWN)) != 0) return scan; if (state.Gamepad.wButtons & 0xF) { // Return lowest bit for (j = 0; j < 16; j++) { if (state.Gamepad.wButtons & (1 << j)) return DX_MAKEKEY(i, 0, DIJOFS_BUTTON(j)); } } } if (flags & PERSF_BUTTON) { if (state.Gamepad.wButtons & 0xF3F0) { // Return lowest bit for (j = 0; j < 16; j++) { if (state.Gamepad.wButtons & (1 << j)) return DX_MAKEKEY(i, 0, DIJOFS_BUTTON(j)); } } } continue; } else if (dev_list[i].lpDIDevice == NULL) continue; #else if (dev_list[i].lpDIDevice == NULL) continue; #endif curdevice=dev_list[i].lpDIDevice; // Poll the device to read the current state hr = IDirectInputDevice8_Poll(curdevice); if( FAILED( hr ) ) { hr = IDirectInputDevice8_Acquire(curdevice); while( hr == DIERR_INPUTLOST) hr = IDirectInputDevice8_Acquire(curdevice); continue; } // Get the input's device state if( FAILED( hr = IDirectInputDevice8_GetDeviceState( curdevice, sizeof( DIJOYSTATE2 ), &js ) ) ) continue; if (flags & PERSF_AXIS) { // Left Stick if ((scan = ScanXInputAxis(i, js.lX-0x7FFF, 0x3FFF, 0x3FFF, XI_THUMBLX, XI_THUMBLX)) != 0) return scan; if ((scan = ScanXInputAxis(i, js.lY-0x7FFF, 0x3FFF, 0x3FFF, XI_THUMBLY, XI_THUMBLY)) != 0) return scan; // Right Stick if ((scan = ScanXInputAxis(i, js.lRx-0x7FFF, 0x3FFF, 0x3FFF, XI_THUMBRX, XI_THUMBRX)) != 0) return scan; if ((scan = ScanXInputAxis(i, js.lRy-0x7FFF, 0x3FFF, 0x3FFF, XI_THUMBRY, XI_THUMBRY)) != 0) return scan; } if (flags & PERSF_HAT) { // L Thumb if ((scan = ScanXInputAxis(i, js.lX-0x7FFF, 0x3FFF, 0, XI_THUMBL+PAD_DIR_AXISLEFT, XI_THUMBL+PAD_DIR_AXISRIGHT)) != 0) return scan; if ((scan = ScanXInputAxis(i, js.lY-0x7FFF, 0x3FFF, 0, XI_THUMBL+PAD_DIR_AXISUP, XI_THUMBL+PAD_DIR_AXISDOWN)) != 0) return scan; // R Thumb if ((scan = ScanXInputAxis(i, js.lRx-0x7FFF, 0x3FFF, 0, XI_THUMBR+PAD_DIR_AXISLEFT, XI_THUMBR+PAD_DIR_AXISRIGHT)) != 0) return scan; if ((scan = ScanXInputAxis(i, js.lRy-0x7FFF, 0x3FFF, 0, XI_THUMBR+PAD_DIR_AXISUP, XI_THUMBR+PAD_DIR_AXISDOWN)) != 0) return scan; for (j = 0; j < 4; j++) { // POV Up if (js.rgdwPOV[j] < 4500) return DX_MAKEKEY(i,0,DIJOFS_POV(j)+PAD_DIR_POVUP); // POV Right else if (js.rgdwPOV[j] < 13500) return DX_MAKEKEY(i,0,DIJOFS_POV(j)+PAD_DIR_POVRIGHT); // POV Down else if (js.rgdwPOV[j] < 22500) return DX_MAKEKEY(i,0,DIJOFS_POV(j)+PAD_DIR_POVDOWN); // POV Left else if (js.rgdwPOV[j] < 31500) return DX_MAKEKEY(i,0,DIJOFS_POV(j)+PAD_DIR_POVLEFT); } } if (flags & PERSF_BUTTON) { for (j = 0; j < 32; j++) { if (js.rgbButtons[j] & 0x80) return DX_MAKEKEY(i,0,DIJOFS_BUTTON(j)); } } } return 0; } ////////////////////////////////////////////////////////////////////////////// void PERDXFlush(void) { } //////////////////////////////////////////////////////////////////////////////yabause-0.9.13.1/src/vidogl.c000644 001750 001750 00000413512 12256006134 017632 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2006 Guillaume Duhamel Copyright 2004 Lawrence Sebald Copyright 2004-2007 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_LIBGL #include "vidogl.h" #include "vidshared.h" #include "debug.h" #include "vdp2.h" #include "yabause.h" #include "ygl.h" #include "yui.h" #if defined WORDS_BIGENDIAN #define SAT2YAB1(alpha,temp) (alpha | (temp & 0x7C00) << 1 | (temp & 0x3E0) << 14 | (temp & 0x1F) << 27) #else #define SAT2YAB1(alpha,temp) (alpha << 24 | (temp & 0x1F) << 3 | (temp & 0x3E0) << 6 | (temp & 0x7C00) << 9) #endif #if defined WORDS_BIGENDIAN #define SAT2YAB2(alpha,dot1,dot2) ((dot2 & 0xFF << 24) | ((dot2 & 0xFF00) << 8) | ((dot1 & 0xFF) << 8) | alpha) #else #define SAT2YAB2(alpha,dot1,dot2) (alpha << 24 | ((dot1 & 0xFF) << 16) | (dot2 & 0xFF00) | (dot2 & 0xFF)) #endif #define COLOR_ADDt(b) (b>0xFF?0xFF:(b<0?0:b)) #define COLOR_ADDb(b1,b2) COLOR_ADDt((signed) (b1) + (b2)) #ifdef WORDS_BIGENDIAN #define COLOR_ADD(l,r,g,b) (COLOR_ADDb((l >> 24) & 0xFF, r) << 24) | \ (COLOR_ADDb((l >> 16) & 0xFF, g) << 16) | \ (COLOR_ADDb((l >> 8) & 0xFF, b) << 8) | \ (l & 0xFF) #else #define COLOR_ADD(l,r,g,b) COLOR_ADDb((l & 0xFF), r) | \ (COLOR_ADDb((l >> 8 ) & 0xFF, g) << 8) | \ (COLOR_ADDb((l >> 16 ) & 0xFF, b) << 16) | \ (l & 0xFF000000) #endif int VIDOGLInit(void); void VIDOGLDeInit(void); void VIDOGLResize(unsigned int, unsigned int, int); int VIDOGLIsFullscreen(void); int VIDOGLVdp1Reset(void); void VIDOGLVdp1DrawStart(void); void VIDOGLVdp1DrawEnd(void); void VIDOGLVdp1NormalSpriteDraw(void); void VIDOGLVdp1ScaledSpriteDraw(void); void VIDOGLVdp1DistortedSpriteDraw(void); void VIDOGLVdp1PolygonDraw(void); void VIDOGLVdp1PolylineDraw(void); void VIDOGLVdp1LineDraw(void); void VIDOGLVdp1UserClipping(void); void VIDOGLVdp1SystemClipping(void); void VIDOGLVdp1LocalCoordinate(void); int VIDOGLVdp2Reset(void); void VIDOGLVdp2DrawStart(void); void VIDOGLVdp2DrawEnd(void); void VIDOGLVdp2DrawScreens(void); void VIDOGLVdp2SetResolution(u16 TVMD); void YglGetGlSize(int *width, int *height); VideoInterface_struct VIDOGL = { VIDCORE_OGL, "OpenGL Video Interface", VIDOGLInit, VIDOGLDeInit, VIDOGLResize, VIDOGLIsFullscreen, VIDOGLVdp1Reset, VIDOGLVdp1DrawStart, VIDOGLVdp1DrawEnd, VIDOGLVdp1NormalSpriteDraw, VIDOGLVdp1ScaledSpriteDraw, VIDOGLVdp1DistortedSpriteDraw, VIDOGLVdp1PolygonDraw, VIDOGLVdp1PolylineDraw, VIDOGLVdp1LineDraw, VIDOGLVdp1UserClipping, VIDOGLVdp1SystemClipping, VIDOGLVdp1LocalCoordinate, VIDOGLVdp2Reset, VIDOGLVdp2DrawStart, VIDOGLVdp2DrawEnd, VIDOGLVdp2DrawScreens, YglGetGlSize }; float vdp1wratio=1; float vdp1hratio=1; int GlWidth=320; int GlHeight=224; int vdp1cor=0; int vdp1cog=0; int vdp1cob=0; static int vdp2width; static int vdp2height; static int nbg0priority=0; static int nbg1priority=0; static int nbg2priority=0; static int nbg3priority=0; static int rbg0priority=0; static u32 Vdp2ColorRamGetColor(u32 colorindex, int alpha); // Window Parameter static vdp2WindowInfo * m_vWindinfo0 = NULL; static int m_vWindinfo0_size = -1; static int m_b0WindowChg; static vdp2WindowInfo * m_vWindinfo1 = NULL; static int m_vWindinfo1_size = -1; static int m_b1WindowChg; static vdp2Lineinfo lineNBG0[512]; static vdp2Lineinfo lineNBG1[512]; // Rotate Screen static vdp2rotationparameter_struct paraA; static vdp2rotationparameter_struct paraB; vdp2rotationparameter_struct * FASTCALL vdp2rGetKValue2W( vdp2rotationparameter_struct * param, int index ); vdp2rotationparameter_struct * FASTCALL vdp2rGetKValue1W( vdp2rotationparameter_struct * param, int index ); vdp2rotationparameter_struct * FASTCALL vdp2rGetKValue2Wm3( vdp2rotationparameter_struct * param, int index ); vdp2rotationparameter_struct * FASTCALL vdp2rGetKValue1Wm3( vdp2rotationparameter_struct * param, int index ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode00NoK( vdp2draw_struct * info, int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode00WithK( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode01NoK( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode01WithK( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode02NoK( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode02WithKA( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode02WithKB( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode03NoK( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode03WithKA( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode03WithKB( vdp2draw_struct * info,int h, int v ); vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode03WithK( vdp2draw_struct * info,int h, int v ); static void FASTCALL Vdp1ReadPriority(vdp1cmd_struct *cmd, int * priority, int * colorcl ); static void FASTCALL Vdp1ReadTexture(vdp1cmd_struct *cmd, YglSprite *sprite, YglTexture *texture); u32 FASTCALL Vdp2ColorRamGetColorCM01SC0(vdp2draw_struct * info, u32 colorindex, int alpha ); u32 FASTCALL Vdp2ColorRamGetColorCM01SC1(vdp2draw_struct * info, u32 colorindex, int alpha ); u32 FASTCALL Vdp2ColorRamGetColorCM01SC3(vdp2draw_struct * info, u32 colorindex, int alpha ); u32 FASTCALL Vdp2ColorRamGetColorCM2(vdp2draw_struct * info, u32 colorindex, int alpha ); ////////////////////////////////////////////////////////////////////////////// static void FASTCALL Vdp1ReadTexture(vdp1cmd_struct *cmd, YglSprite *sprite, YglTexture *texture) { int shadow; int priority; int colorcl; int ednmode; int endcnt = 0; u32 charAddr = cmd->CMDSRCA * 8; u32 dot; u8 SPD = ((cmd->CMDPMOD & 0x40) != 0); u8 END = ((cmd->CMDPMOD & 0x80) != 0); u8 MSB = ((cmd->CMDPMOD & 0x8000) != 0); u32 alpha = 0xFF; VDP1LOG("Making new sprite %08X\n", charAddr); if( (cmd->CMDPMOD & 0x20) == 0) ednmode = 1; else ednmode = 0; Vdp1ReadPriority(cmd, &priority, &colorcl ); alpha = 0xF8; if( ((Vdp2Regs->CCCTL >> 6) & 0x01) == 0x01 ) { switch( (Vdp2Regs->SPCTL>>12)&0x03 ) { case 0: if( priority <= ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 1: if( priority == ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 2: if( priority >= ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 3: //if( priority <= (Vdp2Regs->SPCTL>>8)&0x07 ) // alpha = 0xF8-((colorcl<<3)&0xF8); break; } } if( (cmd->CMDPMOD & 0x7)==0x03 || (cmd->CMDPMOD & 0x100) ) { alpha = 0x80; } alpha |= priority; switch((cmd->CMDPMOD >> 3) & 0x7) { case 0: { // 4 bpp Bank mode u32 colorBank = cmd->CMDCOLR; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; u16 i; for(i = 0;i < sprite->h;i++) { u16 j; j = 0; while(j < sprite->w) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); // Pixel 1 if (((dot >> 4) == 0) && !SPD) *texture->textdata++ = 0x00; else if( ((dot >> 4) == 0x0F) && !END ) *texture->textdata++ = 0x00; else if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = Vdp2ColorRamGetColor(((dot >> 4) | colorBank) + colorOffset, alpha); j += 1; // Pixel 2 if (((dot & 0xF) == 0) && !SPD) *texture->textdata++ = 0x00; else if( ((dot & 0xF) == 0x0F) && !END ) *texture->textdata++ = 0x00; else if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = Vdp2ColorRamGetColor(((dot & 0xF) | colorBank) + colorOffset, alpha); j += 1; charAddr += 1; } texture->textdata += texture->w; } break; } case 1: { // 4 bpp LUT mode u16 temp; u32 colorLut = cmd->CMDCOLR * 8; u16 i; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; for(i = 0;i < sprite->h;i++) { u16 j; j = 0; endcnt = 0; while(j < sprite->w) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); if( ednmode && endcnt >= 2 ) { *texture->textdata++ = 0x00; }else if (((dot >> 4) == 0) && !SPD) { *texture->textdata++ = 0; }else if (((dot >> 4) == 0x0F) && !END ) // 6. Commandtable end code { *texture->textdata++ = 0x0; endcnt++; }else{ temp = T1ReadWord(Vdp1Ram, ((dot >> 4) * 2 + colorLut) & 0x7FFFF); if (temp & 0x8000) { if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = SAT2YAB1(alpha, temp); }else if( temp != 0x0000) { Vdp1ProcessSpritePixel(Vdp2Regs->SPCTL & 0xF, &temp, &shadow, &priority, &colorcl); if( shadow != 0 ) { *texture->textdata++ = 0x00; }else{ #ifdef WORDS_BIGENDIAN priority = ((u8 *)&Vdp2Regs->PRISA)[priority^1]&0x7; colorcl = ((u8 *)&Vdp2Regs->CCRSA)[colorcl^1]&0x1F; #else priority = ((u8 *)&Vdp2Regs->PRISA)[priority]&0x7; colorcl = ((u8 *)&Vdp2Regs->CCRSA)[colorcl]&0x1F; #endif alpha = 0xF8; if( ((Vdp2Regs->CCCTL >> 6) & 0x01) == 0x01 ) { switch( (Vdp2Regs->SPCTL>>12)&0x03 ) { case 0: if( priority <= ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 1: if( priority == ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 2: if( priority >= ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 3: //if( priority <= (Vdp2Regs->SPCTL>>8)&0x07 ) // alpha = 0xF8-((colorcl<<3)&0xF8); break; } } alpha |= priority; if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = Vdp2ColorRamGetColor(temp+colorOffset, alpha); } }else{ *texture->textdata++ = 0x0; } } j += 1; if( ednmode && endcnt >= 2 ) { *texture->textdata++ = 0x00; }else if (((dot & 0xF) == 0) && !SPD) { *texture->textdata++ = 0x00; }else if (((dot&0x0F) == 0x0F) && !END ) { *texture->textdata++ = 0x0; endcnt++; }else{ temp = T1ReadWord(Vdp1Ram, ((dot & 0xF) * 2 + colorLut) & 0x7FFFF); if (temp & 0x8000) { if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = SAT2YAB1(alpha, temp); }else if( temp != 0x0000) { Vdp1ProcessSpritePixel(Vdp2Regs->SPCTL & 0xF, &temp, &shadow, &priority, &colorcl); if( shadow != 0 ) { *texture->textdata++ = 0x00; }else{ #ifdef WORDS_BIGENDIAN priority = ((u8 *)&Vdp2Regs->PRISA)[priority^1]&0x7; colorcl = ((u8 *)&Vdp2Regs->CCRSA)[colorcl^1]&0x1F; #else priority = ((u8 *)&Vdp2Regs->PRISA)[priority]&0x7; colorcl = ((u8 *)&Vdp2Regs->CCRSA)[colorcl]&0x1F; #endif alpha = 0xF8; if( ((Vdp2Regs->CCCTL >> 6) & 0x01) == 0x01 ) { switch( (Vdp2Regs->SPCTL>>12)&0x03 ) { case 0: if( priority <= ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 1: if( priority == ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 2: if( priority >= ((Vdp2Regs->SPCTL>>8)&0x07) ) alpha = 0xF8-((colorcl<<3)&0xF8); break; case 3: //if( priority <= (Vdp2Regs->SPCTL>>8)&0x07 ) // alpha = 0xF8-((colorcl<<3)&0xF8); break; } } alpha |= priority; if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = Vdp2ColorRamGetColor(temp+colorOffset, alpha); } }else *texture->textdata++ = 0x0; } j += 1; charAddr += 1; } texture->textdata += texture->w; } break; } case 2: { // 8 bpp(64 color) Bank mode u32 colorBank = cmd->CMDCOLR; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; u16 i, j; for(i = 0;i < sprite->h;i++) { for(j = 0;j < sprite->w;j++) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF) & 0x3F; charAddr++; if ((dot == 0) && !SPD) *texture->textdata++ = 0x00; else if( (dot == 0xFF) && !END ) *texture->textdata++ = 0x00; else if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = Vdp2ColorRamGetColor((dot | colorBank) + colorOffset, alpha); } texture->textdata += texture->w; } break; } case 3: { // 8 bpp(128 color) Bank mode u32 colorBank = cmd->CMDCOLR; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; u16 i, j; for(i = 0;i < sprite->h;i++) { for(j = 0;j < sprite->w;j++) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF) & 0x7F; charAddr++; if ((dot == 0) && !SPD) *texture->textdata++ = 0x00; else if( (dot == 0xFF) && !END ) *texture->textdata++ = 0x00; else if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = Vdp2ColorRamGetColor((dot | colorBank) + colorOffset, alpha); } texture->textdata += texture->w; } break; } case 4: { // 8 bpp(256 color) Bank mode u32 colorBank = cmd->CMDCOLR; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; u16 i, j; for(i = 0;i < sprite->h;i++) { for(j = 0;j < sprite->w;j++) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); charAddr++; if ((dot == 0) && !SPD) *texture->textdata++ = 0x00; else if( (dot == 0xFF) && !END ) *texture->textdata++ = 0x0; else if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = Vdp2ColorRamGetColor((dot | colorBank) + colorOffset, alpha); } texture->textdata += texture->w; } break; } case 5: { // 16 bpp Bank mode u16 i, j; for(i = 0;i < sprite->h;i++) { for(j = 0;j < sprite->w;j++) { dot = T1ReadWord(Vdp1Ram, charAddr & 0x7FFFF); charAddr += 2; //if (!(dot & 0x8000) && (Vdp2Regs->SPCTL & 0x20)) printf("mixed mode\n"); if (!(dot & 0x8000) && !SPD) *texture->textdata++ = 0x00; else if( (dot == 0x7FFF) && !END ) *texture->textdata++ = 0x0; else if( MSB ) *texture->textdata++ = (alpha<<24); else *texture->textdata++ = SAT2YAB1(alpha, dot); } texture->textdata += texture->w; } break; } default: VDP1LOG("Unimplemented sprite color mode: %X\n", (cmd->CMDPMOD >> 3) & 0x7); break; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL Vdp1ReadPriority(vdp1cmd_struct *cmd, int * priority, int * colorcl ) { u8 SPCLMD = Vdp2Regs->SPCTL; u8 sprite_register; u8 *sprprilist = (u8 *)&Vdp2Regs->PRISA; u8 *cclist = (u8 *)&Vdp2Regs->CCRSA; u16 lutPri; u16 *reg_src = &cmd->CMDCOLR; int not_lut = 1; // is the sprite is RGB or LUT (in fact, LUT can use bank color, we just hope it won't...) if ((SPCLMD & 0x20) && (cmd->CMDCOLR & 0x8000)) { // RGB data, use register 0 *priority = Vdp2Regs->PRISA & 0x7; return; } if (((cmd->CMDPMOD >> 3) & 0x7) == 1) { u32 charAddr, dot, colorLut; *priority = Vdp2Regs->PRISA & 0x7; charAddr = cmd->CMDSRCA * 8; dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); colorLut = cmd->CMDCOLR * 8; lutPri = T1ReadWord(Vdp1Ram, (dot >> 4) * 2 + colorLut); if (!(lutPri & 0x8000)) { not_lut = 0; reg_src = &lutPri; } else return; } { u8 sprite_type = SPCLMD & 0xF; switch(sprite_type) { case 0: sprite_register = (*reg_src & 0xC000) >> 14; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>11)&0x07)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[(cmd->CMDCOLR>>11)&0x07]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x7FF; break; case 1: sprite_register = (*reg_src & 0xE000) >> 13; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>11)&0x03)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[(cmd->CMDCOLR>>11)&0x03]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x7FF; break; case 2: sprite_register = (*reg_src >> 14) & 0x1; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>11)&0x07)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[(cmd->CMDCOLR>>11)&0x07]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x7FF; break; case 3: sprite_register = (*reg_src & 0x6000) >> 13; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>11)&0x03)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>11)&0x03)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x7FF; break; case 4: sprite_register = (*reg_src & 0x6000) >> 13; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>10)&0x07)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>10)&0x07)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x3FF; break; case 5: sprite_register = (*reg_src & 0x7000) >> 12; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>11)&0x01)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>11)&0x01)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x7FF; break; case 6: sprite_register = (*reg_src & 0x7000) >> 12; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>10)&0x03)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>10)&0x03)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x3FF; break; case 7: sprite_register = (*reg_src & 0x7000) >> 12; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>9)&0x07)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>9)&0x07)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x1FF; break; case 8: sprite_register = (*reg_src & 0x80) >> 7; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; #else *priority = sprprilist[sprite_register] & 0x7; #endif *colorcl = cclist[0]&0x1F; if (not_lut) cmd->CMDCOLR &= 0x7F; break; case 9: sprite_register = (*reg_src & 0x80) >> 7; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>6)&0x01)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>6)&0x01)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x3F; break; case 10: sprite_register = (*reg_src & 0xC0) >> 6; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; #else *priority = sprprilist[sprite_register] & 0x7; #endif *colorcl = cclist[0]&0x1F; if (not_lut) cmd->CMDCOLR &= 0x3F; break; case 11: sprite_register = 0; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>6)&0x03)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>6)&0x03)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0x3F; break; case 12: sprite_register = (*reg_src & 0x80) >> 7; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; #else *priority = sprprilist[sprite_register] & 0x7; #endif *colorcl = cclist[0]&0x1F; if (not_lut) cmd->CMDCOLR &= 0xFF; break; case 13: sprite_register = (*reg_src & 0x80) >> 7; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>6)&0x01)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>6)&0x01)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0xFF; break; case 14: sprite_register = (*reg_src & 0xC0) >> 6; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[0]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0xFF; break; case 15: sprite_register = 0; #ifdef WORDS_BIGENDIAN *priority = sprprilist[sprite_register^1] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>6)&0x03)^1]&0x1F; #else *priority = sprprilist[sprite_register] & 0x7; *colorcl = cclist[((cmd->CMDCOLR>>6)&0x03)]&0x1F; #endif if (not_lut) cmd->CMDCOLR &= 0xFF; break; default: VDP1LOG("sprite type %d not implemented\n", sprite_type); // if we don't know what to do with a sprite, we put it on top *priority = 7; break; } } } ////////////////////////////////////////////////////////////////////////////// static void Vdp1SetTextureRatio(int vdp2widthratio, int vdp2heightratio) { float vdp1w=1; float vdp1h=1; // may need some tweaking // Figure out which vdp1 screen mode to use switch (Vdp1Regs->TVMR & 7) { case 0: case 2: case 3: vdp1w=1; break; case 1: vdp1w=2; break; default: vdp1w=1; vdp1h=1; break; } // Is double-interlace enabled? if (Vdp1Regs->FBCR & 0x8) vdp1h=2; vdp1wratio = (float)vdp2widthratio / vdp1w; vdp1hratio = (float)vdp2heightratio / vdp1h; } ////////////////////////////////////////////////////////////////////////////// static u32 Vdp2ColorRamGetColor(u32 colorindex, int alpha) { switch(Vdp2Internal.ColorMode) { case 0: case 1: { u32 tmp; colorindex <<= 1; tmp = T2ReadWord(Vdp2ColorRam, colorindex & 0xFFF); return SAT2YAB1(alpha, tmp); } case 2: { u32 tmp1, tmp2; colorindex <<= 2; colorindex &= 0xFFF; tmp1 = T2ReadWord(Vdp2ColorRam, colorindex); tmp2 = T2ReadWord(Vdp2ColorRam, colorindex+2); return SAT2YAB2(alpha, tmp1, tmp2); } default: break; } return 0; } u32 FASTCALL Vdp2ColorRamGetColorCM01SC0(vdp2draw_struct * info, u32 colorindex, int alpha ) { u32 tmp; tmp = T2ReadWord(Vdp2ColorRam, (colorindex<<1) & 0xFFF); return SAT2YAB1(alpha,tmp); } u32 FASTCALL Vdp2ColorRamGetColorCM01SC1(vdp2draw_struct * info, u32 colorindex, int alpha ) { u32 tmp; tmp = T2ReadWord(Vdp2ColorRam, (colorindex<<1) & 0xFFF); if( (info->specialcolorfunction & 1) == 0 ) { return SAT2YAB1(0xFF,tmp); } return SAT2YAB1(alpha,tmp); } u32 FASTCALL Vdp2ColorRamGetColorCM01SC3(vdp2draw_struct * info, u32 colorindex, int alpha ) { u32 tmp; colorindex <<= 1; tmp = T2ReadWord(Vdp2ColorRam, colorindex & 0xFFF); if( ((tmp & 0x8000) == 0) ) { return SAT2YAB1(0xFF,tmp); } return SAT2YAB1(alpha,tmp); } u32 FASTCALL Vdp2ColorRamGetColorCM2(vdp2draw_struct * info, u32 colorindex, int alpha ) { u32 tmp1, tmp2; colorindex <<= 2; colorindex &= 0xFFF; tmp1 = T2ReadWord(Vdp2ColorRam, colorindex); tmp2 = T2ReadWord(Vdp2ColorRam, colorindex+2); return SAT2YAB2(alpha, tmp1, tmp2); } static int Vdp2SetGetColor( vdp2draw_struct * info ) { switch(Vdp2Internal.ColorMode) { case 0: case 1: switch( info->specialcolormode ) { case 0: info->Vdp2ColorRamGetColor = (Vdp2ColorRamGetColor_func) Vdp2ColorRamGetColorCM01SC0; break; case 1: info->Vdp2ColorRamGetColor = (Vdp2ColorRamGetColor_func) Vdp2ColorRamGetColorCM01SC1; break; case 2: info->Vdp2ColorRamGetColor = (Vdp2ColorRamGetColor_func) Vdp2ColorRamGetColorCM01SC0; // Not Supported Yet! break; case 3: info->Vdp2ColorRamGetColor = (Vdp2ColorRamGetColor_func) Vdp2ColorRamGetColorCM01SC3; break; default: info->Vdp2ColorRamGetColor = (Vdp2ColorRamGetColor_func) Vdp2ColorRamGetColorCM01SC0; break; } break; case 2: info->Vdp2ColorRamGetColor = (Vdp2ColorRamGetColor_func) Vdp2ColorRamGetColorCM2; break; default: info->Vdp2ColorRamGetColor = (Vdp2ColorRamGetColor_func) Vdp2ColorRamGetColorCM01SC0; break; } return 0; } ////////////////////////////////////////////////////////////////////////////// // Window static void Vdp2GenerateWindowInfo(void) { int i; int HShift; int v = 0; u32 LineWinAddr; // Is there BG uses Window0? if( (Vdp2Regs->WCTLA & 0X2) || (Vdp2Regs->WCTLA & 0X200) || (Vdp2Regs->WCTLB & 0X2) || (Vdp2Regs->WCTLB & 0X200) || (Vdp2Regs->WCTLC & 0X2) || (Vdp2Regs->WCTLC & 0X200) || (Vdp2Regs->WCTLD & 0X2) || (Vdp2Regs->WCTLD & 0X200) || (Vdp2Regs->RPMD == 0X03) ) { // resize to fit resolusion if( m_vWindinfo0_size != vdp2height ) { if(m_vWindinfo0 != NULL) free(m_vWindinfo0); m_vWindinfo0 = (vdp2WindowInfo*)malloc(sizeof(vdp2WindowInfo)*(vdp2height+8)); for( i=0; i=640 ) HShift = 0; else HShift = 1; // Line Table mode if( (Vdp2Regs->LWTA0.part.U & 0x8000) ) { int preHStart = -1; int preHEnd = -1; // start address LineWinAddr = (u32)((( (Vdp2Regs->LWTA0.part.U & 0x07) << 15) | (Vdp2Regs->LWTA0.part.L >> 1) ) << 2); _Ygl->win0_vertexcnt = 0; for( v = 0; v < vdp2height; v++ ) { if( v < Vdp2Regs->WPSY0 || v > Vdp2Regs->WPEY0 ) { if( m_vWindinfo0[v].WinShowLine ) m_b0WindowChg = 1; m_vWindinfo0[v].WinShowLine = 0; }else{ short HStart = Vdp2RamReadWord(LineWinAddr + (v << 2) ); short HEnd = Vdp2RamReadWord(LineWinAddr + (v << 2) + 2); if( HStart < HEnd ) { HStart >>= HShift; HEnd >>= HShift; if( !( m_vWindinfo0[v].WinHStart == HStart && m_vWindinfo0[v].WinHEnd == HEnd ) ) { m_b0WindowChg = 1; } m_vWindinfo0[v].WinHStart = HStart; m_vWindinfo0[v].WinHEnd = HEnd; m_vWindinfo0[v].WinShowLine = 1; if( v == Vdp2Regs->WPSY0 ) { _Ygl->win0v[_Ygl->win0_vertexcnt*2+0]= HStart; _Ygl->win0v[_Ygl->win0_vertexcnt*2+1]= v; _Ygl->win0_vertexcnt++; _Ygl->win0v[_Ygl->win0_vertexcnt*2+0]= HEnd; _Ygl->win0v[_Ygl->win0_vertexcnt*2+1]= v; _Ygl->win0_vertexcnt++; }else if( ( HStart != preHStart || HEnd != preHEnd) || v == (Vdp2Regs->WPEY0-1) ) { if( (v-1) != _Ygl->win0v[(_Ygl->win0_vertexcnt-1)*2+1] ) { _Ygl->win0v[_Ygl->win0_vertexcnt*2+0]= preHStart; _Ygl->win0v[_Ygl->win0_vertexcnt*2+1]= v-1; _Ygl->win0_vertexcnt++; _Ygl->win0v[_Ygl->win0_vertexcnt*2+0]= preHEnd; _Ygl->win0v[_Ygl->win0_vertexcnt*2+1]= v-1; _Ygl->win0_vertexcnt++; } _Ygl->win0v[_Ygl->win0_vertexcnt*2+0]= HStart; _Ygl->win0v[_Ygl->win0_vertexcnt*2+1]= v; _Ygl->win0_vertexcnt++; _Ygl->win0v[_Ygl->win0_vertexcnt*2+0]= HEnd; _Ygl->win0v[_Ygl->win0_vertexcnt*2+1]= v; _Ygl->win0_vertexcnt++; } preHStart = HStart; preHEnd = HEnd; }else{ if( m_vWindinfo0[v].WinShowLine ) m_b0WindowChg = 1; m_vWindinfo0[v].WinHStart = 0; m_vWindinfo0[v].WinHEnd = 0; m_vWindinfo0[v].WinShowLine = 0; } } } // Parameter Mode }else{ // Check Update if( !( m_vWindinfo0[0].WinHStart == (Vdp2Regs->WPSX0>>HShift) && m_vWindinfo0[0].WinHEnd == (Vdp2Regs->WPEX0>>HShift) ) ) { m_b0WindowChg = 1; } for( v = 0; v < vdp2height; v++ ) { m_vWindinfo0[v].WinHStart = Vdp2Regs->WPSX0 >> HShift; m_vWindinfo0[v].WinHEnd = Vdp2Regs->WPEX0 >> HShift; if( v < Vdp2Regs->WPSY0 || v >= Vdp2Regs->WPEY0 ) { if( m_vWindinfo0[v].WinShowLine ) m_b0WindowChg = 1; m_vWindinfo0[v].WinShowLine = 0; }else{ if( m_vWindinfo0[v].WinShowLine == 0) m_b0WindowChg = 1; m_vWindinfo0[v].WinShowLine = 1; } } _Ygl->win0v[0]= Vdp2Regs->WPSX0 >> HShift; _Ygl->win0v[1]= Vdp2Regs->WPSY0; _Ygl->win0v[2]= Vdp2Regs->WPEX0 >> HShift; _Ygl->win0v[3]= Vdp2Regs->WPSY0; _Ygl->win0v[4]= Vdp2Regs->WPSX0 >> HShift; _Ygl->win0v[5]= Vdp2Regs->WPEY0; _Ygl->win0v[6]= Vdp2Regs->WPEX0 >> HShift; _Ygl->win0v[7]= Vdp2Regs->WPEY0; _Ygl->win0_vertexcnt = 4; } // there is no Window BG }else{ if( m_vWindinfo0_size != 0 ) { m_b0WindowChg = 1; } if( m_vWindinfo0 != NULL ) { free(m_vWindinfo0); m_vWindinfo0 = NULL; } m_vWindinfo0_size = 0; _Ygl->win0_vertexcnt = 0; } // Is there BG uses Window1? if( (Vdp2Regs->WCTLA & 0x8) || (Vdp2Regs->WCTLA & 0x800) || (Vdp2Regs->WCTLB & 0x8) || (Vdp2Regs->WCTLB & 0x800) || (Vdp2Regs->WCTLC & 0x8) || (Vdp2Regs->WCTLC & 0x800) || (Vdp2Regs->WCTLD & 0x8) || (Vdp2Regs->WCTLD & 0x800) ) { // resize to fit resolution if( m_vWindinfo1_size != vdp2height ) { if(m_vWindinfo1 != NULL) free(m_vWindinfo1); m_vWindinfo1 = (vdp2WindowInfo*)malloc(sizeof(vdp2WindowInfo)*vdp2height); for( i=0; i=640 ) HShift = 0; else HShift = 1; // LineTable mode if( (Vdp2Regs->LWTA1.part.U & 0x8000) ) { int preHStart = -1; int preHEnd = -1; _Ygl->win1_vertexcnt = 0; // start address for Window table LineWinAddr = (u32)((( (Vdp2Regs->LWTA1.part.U & 0x07) << 15) | (Vdp2Regs->LWTA1.part.L >> 1) ) << 2); for( v = 0; v < vdp2height; v++ ) { if( v < Vdp2Regs->WPSY1 || v > Vdp2Regs->WPEY1 ) { if( m_vWindinfo1[v].WinShowLine ) m_b1WindowChg = 1; m_vWindinfo1[v].WinShowLine = 0; }else{ short HStart = Vdp2RamReadWord(LineWinAddr + (v << 2) ); short HEnd = Vdp2RamReadWord(LineWinAddr + (v << 2) + 2); if( HStart < HEnd ) { HStart >>= HShift; HEnd >>= HShift; if( !( m_vWindinfo1[v].WinHStart == HStart && m_vWindinfo1[v].WinHEnd == HEnd ) ) { m_b1WindowChg = 1; } m_vWindinfo1[v].WinHStart = HStart; m_vWindinfo1[v].WinHEnd = HEnd; m_vWindinfo1[v].WinShowLine = 1; }else{ if( m_vWindinfo1[v].WinShowLine ) m_b1WindowChg = 1; m_vWindinfo1[v].WinShowLine = 0; } if( v == Vdp2Regs->WPSY1 ) { _Ygl->win1v[_Ygl->win1_vertexcnt*2+0]= HStart; _Ygl->win1v[_Ygl->win1_vertexcnt*2+1]= v; _Ygl->win1_vertexcnt++; _Ygl->win1v[_Ygl->win1_vertexcnt*2+0]= HEnd; _Ygl->win1v[_Ygl->win1_vertexcnt*2+1]= v; _Ygl->win1_vertexcnt++; }else if( ( HStart != preHStart || HEnd != preHEnd) || v == (Vdp2Regs->WPEY1-1) ) { if( (v-1) != _Ygl->win1v[(_Ygl->win1_vertexcnt-1)*2+1] ) { _Ygl->win1v[_Ygl->win1_vertexcnt*2+0]= preHStart; _Ygl->win1v[_Ygl->win1_vertexcnt*2+1]= v-1; _Ygl->win1_vertexcnt++; _Ygl->win1v[_Ygl->win1_vertexcnt*2+0]= preHEnd; _Ygl->win1v[_Ygl->win1_vertexcnt*2+1]= v-1; _Ygl->win1_vertexcnt++; } _Ygl->win1v[_Ygl->win1_vertexcnt*2+0]= HStart; _Ygl->win1v[_Ygl->win1_vertexcnt*2+1]= v; _Ygl->win1_vertexcnt++; _Ygl->win1v[_Ygl->win1_vertexcnt*2+0]= HEnd; _Ygl->win1v[_Ygl->win1_vertexcnt*2+1]= v; _Ygl->win1_vertexcnt++; } preHStart = HStart; preHEnd = HEnd; } } // parameter mode }else{ // check update if( !( m_vWindinfo1[0].WinHStart == (Vdp2Regs->WPSX1>>HShift) && m_vWindinfo1[0].WinHEnd == (Vdp2Regs->WPEX1>>HShift) ) ) { m_b1WindowChg = 1; } for( v = 0; v < vdp2height; v++ ) { m_vWindinfo1[v].WinHStart = Vdp2Regs->WPSX1 >> HShift; m_vWindinfo1[v].WinHEnd = Vdp2Regs->WPEX1 >> HShift; if( v < Vdp2Regs->WPSY1 || v > Vdp2Regs->WPEY1 ) { if( m_vWindinfo1[v].WinShowLine ) m_b1WindowChg = 1; m_vWindinfo1[v].WinShowLine = 0; }else{ if( m_vWindinfo1[v].WinShowLine == 0) m_b1WindowChg = 1; m_vWindinfo1[v].WinShowLine = 1; } } _Ygl->win1v[0]= Vdp2Regs->WPSX1 >> HShift; _Ygl->win1v[1]= Vdp2Regs->WPSY1; _Ygl->win1v[2]= Vdp2Regs->WPEX1 >> HShift; _Ygl->win1v[3]= Vdp2Regs->WPSY1; _Ygl->win1v[4]= Vdp2Regs->WPSX1 >> HShift; _Ygl->win1v[5]= Vdp2Regs->WPEY1; _Ygl->win1v[6]= Vdp2Regs->WPEX1 >> HShift; _Ygl->win1v[7]= Vdp2Regs->WPEY1; _Ygl->win1_vertexcnt = 4; } // no BG uses Window1 }else{ if( m_vWindinfo1_size != 0 ) { m_b1WindowChg = 1; } if( m_vWindinfo1 != NULL ) { free(m_vWindinfo1); m_vWindinfo1 = NULL; } m_vWindinfo1_size = 0; _Ygl->win1_vertexcnt = 0; } if( m_b1WindowChg || m_b0WindowChg ) { YglNeedToUpdateWindow(); m_b0WindowChg = 0; m_b1WindowChg = 0; } } // 0 .. outside,1 .. inside static INLINE int Vdp2CheckWindow(vdp2draw_struct *info, int x, int y, int area, vdp2WindowInfo * vWindinfo ) { // inside if( area == 1 ) { if( vWindinfo[y].WinShowLine == 0 ) return 0; if( x > vWindinfo[y].WinHStart && x < vWindinfo[y].WinHEnd ) { return 1; }else{ return 0; } // outside }else{ if( vWindinfo[y].WinShowLine == 0 ) return 1; if( x < vWindinfo[y].WinHStart ) return 1; if( x > vWindinfo[y].WinHEnd ) return 1; return 0; } return 0; } // 0 .. outside,1 .. inside static int FASTCALL Vdp2CheckWindowDot(vdp2draw_struct *info, int x, int y ) { if( info->bEnWin0 != 0 && info->bEnWin1 == 0 ) { return Vdp2CheckWindow(info, x, y, info->WindowArea0, m_vWindinfo0 ); }else if( info->bEnWin0 == 0 && info->bEnWin1 != 0 ) { return Vdp2CheckWindow(info, x, y, info->WindowArea1, m_vWindinfo1 ); }else if( info->bEnWin0 != 0 && info->bEnWin1 != 0 ) { if( info->LogicWin == 0 ) { return (Vdp2CheckWindow(info, x, y, info->WindowArea0, m_vWindinfo0 )& Vdp2CheckWindow(info, x, y, info->WindowArea1, m_vWindinfo1 )); }else{ return (Vdp2CheckWindow(info, x, y, info->WindowArea0, m_vWindinfo0 )| Vdp2CheckWindow(info, x, y, info->WindowArea1, m_vWindinfo1 )); } } return 0; } // 0 .. all outsize, 1~3 .. partly inside, 4.. all inside static int FASTCALL Vdp2CheckWindowRange(vdp2draw_struct *info, int x, int y, int w, int h ) { int rtn=0; if( info->bEnWin0 != 0 && info->bEnWin1 == 0 ) { rtn += Vdp2CheckWindow(info, x, y, info->WindowArea0, m_vWindinfo0 ); rtn += Vdp2CheckWindow(info, x+w, y, info->WindowArea0, m_vWindinfo0 ); rtn += Vdp2CheckWindow(info, x+w, y+h, info->WindowArea0, m_vWindinfo0 ); rtn += Vdp2CheckWindow(info, x, y+h, info->WindowArea0, m_vWindinfo0 ); return rtn; }else if( info->bEnWin0 == 0 && info->bEnWin1 != 0 ) { rtn += Vdp2CheckWindow(info, x, y, info->WindowArea1, m_vWindinfo1 ); rtn += Vdp2CheckWindow(info, x+w, y, info->WindowArea1, m_vWindinfo1 ); rtn += Vdp2CheckWindow(info, x+w, y+h, info->WindowArea1, m_vWindinfo1 ); rtn += Vdp2CheckWindow(info, x, y+h, info->WindowArea1, m_vWindinfo1 ); return rtn; }else if( info->bEnWin0 != 0 && info->bEnWin1 != 0 ) { if( info->LogicWin == 0 ) { rtn += (Vdp2CheckWindow(info, x, y, info->WindowArea0, m_vWindinfo0 ) & Vdp2CheckWindow(info, x, y, info->WindowArea1, m_vWindinfo1 ) ); rtn += (Vdp2CheckWindow(info, x+w, y, info->WindowArea0, m_vWindinfo0 )& Vdp2CheckWindow(info, x+w, y, info->WindowArea1, m_vWindinfo1 ) ); rtn += (Vdp2CheckWindow(info, x+w, y+h, info->WindowArea0, m_vWindinfo0 )& Vdp2CheckWindow(info, x+w, y+h, info->WindowArea1, m_vWindinfo1 ) ); rtn += (Vdp2CheckWindow(info, x, y+h, info->WindowArea0, m_vWindinfo0 ) & Vdp2CheckWindow(info, x, y+h, info->WindowArea1, m_vWindinfo1 ) ); return rtn; }else{ rtn += (Vdp2CheckWindow(info, x, y, info->WindowArea0, m_vWindinfo0 ) | Vdp2CheckWindow(info, x, y, info->WindowArea1, m_vWindinfo1 ) ); rtn += (Vdp2CheckWindow(info, x+w, y, info->WindowArea0, m_vWindinfo0 ) | Vdp2CheckWindow(info, x+w, y, info->WindowArea1, m_vWindinfo1 ) ); rtn += (Vdp2CheckWindow(info, x+w, y+h, info->WindowArea0, m_vWindinfo0 ) | Vdp2CheckWindow(info, x+w, y+h, info->WindowArea1, m_vWindinfo1 ) ); rtn += (Vdp2CheckWindow(info, x, y+h, info->WindowArea0, m_vWindinfo0 ) | Vdp2CheckWindow(info, x, y+h, info->WindowArea1, m_vWindinfo1 ) ); return rtn; } } return 0; } void Vdp2GenLineinfo( vdp2draw_struct *info ) { int bound = 0; int i; u16 val1,val2; int index = 0; if( info->lineinc == 0 || info->islinescroll == 0 ) return; if( VDPLINE_SY(info->islinescroll)) bound += 0x04; if( VDPLINE_SX(info->islinescroll)) bound += 0x04; if( VDPLINE_SZ(info->islinescroll)) bound += 0x04; for( i = 0; i < vdp2height; i += info->lineinc ) { index = 0; if( VDPLINE_SX(info->islinescroll)) { info->lineinfo[i].LineScrollValH = T1ReadWord(Vdp2Ram, info->linescrolltbl+(i/info->lineinc)*bound); if( ( info->lineinfo[i].LineScrollValH & 0x400) ) info->lineinfo[i].LineScrollValH |= 0xF800; else info->lineinfo[i].LineScrollValH &= 0x07FF; index += 4; }else{ info->lineinfo[i].LineScrollValH = 0; } if( VDPLINE_SY(info->islinescroll)) { info->lineinfo[i].LineScrollValV = T1ReadWord(Vdp2Ram, info->linescrolltbl+(i/info->lineinc)*bound+index); if( ( info->lineinfo[i].LineScrollValV & 0x400) ) info->lineinfo[i].LineScrollValV |= 0xF800; else info->lineinfo[i].LineScrollValV &= 0x07FF; index += 4; }else{ info->lineinfo[i].LineScrollValV = 0; } if( VDPLINE_SZ(info->islinescroll)) { val1=T1ReadWord(Vdp2Ram, info->linescrolltbl+(i/info->lineinc)*bound+index); val2=T1ReadWord(Vdp2Ram, info->linescrolltbl+(i/info->lineinc)*bound+index+2); //info->lineinfo[i].CoordinateIncH = (float)( (int)((val1) & 0x07) + (float)( (val2) >> 8) / 255.0f ); info->lineinfo[i].CoordinateIncH = (((int)((val1) & 0x07)<<8) | (int)( (val2) >> 8)); index += 4; }else{ info->lineinfo[i].CoordinateIncH = 0x0100; } } } static void FASTCALL Vdp2DrawCell(vdp2draw_struct *info, YglTexture *texture) { u32 color; int i, j; switch(info->colornumber) { case 0: // 4 BPP for(i = 0;i < info->cellh;i++) { for(j = 0;j < info->cellw;j+=4) { u16 dot = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); info->charaddr += 2; if (!(dot & 0xF000) && info->transparencyenable) color = 0x00000000; else color = info->Vdp2ColorRamGetColor(info,info->coloroffset + ((info->paladdr << 4) | ((dot & 0xF000) >> 12)), info->alpha); *texture->textdata++ = info->PostPixelFetchCalc(info, color); if (!(dot & 0xF00) && info->transparencyenable) color = 0x00000000; else color = info->Vdp2ColorRamGetColor(info,info->coloroffset + ((info->paladdr << 4) | ((dot & 0xF00) >> 8)), info->alpha); *texture->textdata++ = info->PostPixelFetchCalc(info, color); if (!(dot & 0xF0) && info->transparencyenable) color = 0x00000000; else color = info->Vdp2ColorRamGetColor(info,info->coloroffset + ((info->paladdr << 4) | ((dot & 0xF0) >> 4)), info->alpha); *texture->textdata++ = info->PostPixelFetchCalc(info, color); if (!(dot & 0xF) && info->transparencyenable) color = 0x00000000; else color = info->Vdp2ColorRamGetColor(info,info->coloroffset + ((info->paladdr << 4) | (dot & 0xF)), info->alpha); *texture->textdata++ = info->PostPixelFetchCalc(info, color); } texture->textdata += texture->w; } break; case 1: // 8 BPP for(i = 0;i < info->cellh;i++) { for(j = 0;j < info->cellw;j+=2) { u16 dot = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); info->charaddr += 2; if (!(dot & 0xFF00) && info->transparencyenable) color = 0x00000000; else color = info->Vdp2ColorRamGetColor(info,info->coloroffset + ((info->paladdr << 4) | ((dot & 0xFF00) >> 8)), info->alpha); *texture->textdata++ = info->PostPixelFetchCalc(info, color); if (!(dot & 0xFF) && info->transparencyenable) color = 0x00000000; else color = info->Vdp2ColorRamGetColor(info,info->coloroffset + ((info->paladdr << 4) | (dot & 0xFF)), info->alpha); *texture->textdata++ = info->PostPixelFetchCalc(info, color); } texture->textdata += texture->w; } break; case 2: // 16 BPP(palette) for(i = 0;i < info->cellh;i++) { for(j = 0;j < info->cellw;j++) { u16 dot = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); if ((dot == 0) && info->transparencyenable) color = 0x00000000; else color = info->Vdp2ColorRamGetColor(info,info->coloroffset + dot, info->alpha); info->charaddr += 2; *texture->textdata++ = info->PostPixelFetchCalc(info, color); } texture->textdata += texture->w; } break; case 3: // 16 BPP(RGB) if( info->islinescroll ) // Nights Movie { for(i = 0;i < info->cellh;i++) { int sh,sv; u32 baseaddr; vdp2Lineinfo * line; baseaddr = (u32)info->charaddr; line = &(info->lineinfo[i*info->lineinc]); if( VDPLINE_SX(info->islinescroll) ) sh = line->LineScrollValH+info->sh; else sh = info->sh; if( VDPLINE_SY(info->islinescroll) ) sv = line->LineScrollValV; else sv = i+info->sv; sh &= (info->cellw-1); sv &= (info->cellh-1); if( line->LineScrollValH < sh ) sv-=1; baseaddr += ((sh+ sv * info->cellw)<<1); for(j = 0;j < info->cellw;j++) { u16 dot; u32 addr; if( Vdp2CheckWindowDot(info,j,i)==0 ){ *texture->textdata++=0; continue; } addr = baseaddr + (j<<1); dot = T1ReadWord(Vdp2Ram, addr & 0x7FFFF); if (!(dot & 0x8000) && info->transparencyenable) color = 0x00000000; else color = SAT2YAB1(0xFF,dot); *texture->textdata++ = info->PostPixelFetchCalc(info, color); } texture->textdata += texture->w; } }else{ for(i = 0;i < info->cellh;i++) { for(j = 0;j < info->cellw;j++) { u16 dot = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); info->charaddr += 2; if (!(dot & 0x8000) && info->transparencyenable) color = 0x00000000; else color = SAT2YAB1(0xFF, dot); *texture->textdata++ = info->PostPixelFetchCalc(info, color); } texture->textdata += texture->w; } } break; case 4: // 32 BPP if( info->islinescroll ) // Nights Movie { for(i = 0;i < info->cellh;i++) { int sh,sv; u32 baseaddr; vdp2Lineinfo * line; baseaddr = (u32)info->charaddr; line = &(info->lineinfo[i*info->lineinc]); if( VDPLINE_SX(info->islinescroll) ) sh = line->LineScrollValH+info->sh; else sh = info->sh; if( VDPLINE_SY(info->islinescroll) ) sv = line->LineScrollValV; else sv = i+info->sv; sh &= (info->cellw-1); sv &= (info->cellh-1); if( line->LineScrollValH < sh ) sv-=1; baseaddr += ((sh+ sv * info->cellw)<<2); for(j = 0;j < info->cellw;j++) { u16 dot1, dot2; u32 addr; if( Vdp2CheckWindowDot(info,j,i)==0 ){ *texture->textdata++=0; continue; } addr = baseaddr + (j<<2); dot1 = T1ReadWord(Vdp2Ram, addr & 0x7FFFF); dot2 = T1ReadWord(Vdp2Ram, (addr+2) & 0x7FFFF); if (!(dot1 & 0x8000) && info->transparencyenable) color = 0x00000000; else color = SAT2YAB2(info->alpha, dot1, dot2); *texture->textdata++ = info->PostPixelFetchCalc(info, color); } texture->textdata += texture->w; } }else{ for(i = 0;i < info->cellh;i++) { for(j = 0;j < info->cellw;j++) { u16 dot1, dot2; dot1 = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); info->charaddr += 2; dot2 = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); info->charaddr += 2; if (!(dot1 & 0x8000) && info->transparencyenable) color = 0x00000000; else color = SAT2YAB2(info->alpha, dot1, dot2); *texture->textdata++ = info->PostPixelFetchCalc(info, color); } texture->textdata += texture->w; } } break; } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawPattern(vdp2draw_struct *info, YglTexture *texture) { u32 cacheaddr = ((u32) (info->alpha >> 3) << 27) | (info->paladdr << 20) | info->charaddr; YglCache c; YglSprite tile; int winmode=0; tile.dst = 0; tile.uclipmode = 0; tile.blendmode = info->blendmode; tile.w = tile.h = info->patternpixelwh; tile.flip = info->flipfunction; if (info->specialprimode == 1) tile.priority = (info->priority & 0xFFFFFFFE) | info->specialfunction; else tile.priority = info->priority; tile.vertices[0] = info->x * info->coordincx; tile.vertices[1] = info->y * info->coordincy; tile.vertices[2] = (info->x + tile.w) * info->coordincx; tile.vertices[3] = info->y * info->coordincy; tile.vertices[4] = (info->x + tile.w) * info->coordincx; tile.vertices[5] = (info->y + tile.h) * info->coordincy; tile.vertices[6] = info->x * info->coordincx; tile.vertices[7] = (info->y + tile.h) * info->coordincy; // Screen culling if( tile.vertices[0] >= vdp2width || tile.vertices[1] >= vdp2height || tile.vertices[2] < 0 || tile.vertices[5] < 0 ) { info->x += tile.w; info->y += tile.h; return; } if( (info->bEnWin0 != 0 || info->bEnWin1 != 0) && info->coordincy == 1.0f ) { // coordinate inc is not supported yet. winmode=Vdp2CheckWindowRange( info,info->x,info->y,tile.w,tile.h); if( winmode == 0 ) // all outside, no need to draw { info->x += tile.w; info->y += tile.h; return; } } if (1 == YglIsCached(cacheaddr,&c) ) { YglCachedQuad(&tile, &c); info->x += tile.w; info->y += tile.h; return; } YglQuad(&tile, texture,&c); YglCacheAdd(cacheaddr,&c); switch(info->patternwh) { case 1: Vdp2DrawCell(info, texture); break; case 2: texture->w += 8; Vdp2DrawCell(info, texture); texture->textdata -= (texture->w + 8) * 8 - 8; Vdp2DrawCell(info, texture); texture->textdata -= 8; Vdp2DrawCell(info, texture); texture->textdata -= (texture->w + 8) * 8 - 8; Vdp2DrawCell(info, texture); break; } info->x += tile.w; info->y += tile.h; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2PatternAddr(vdp2draw_struct *info) { switch(info->patterndatasize) { case 1: { u16 tmp = T1ReadWord(Vdp2Ram, info->addr); info->addr += 2; info->specialfunction = (info->supplementdata >> 9) & 0x1; info->specialcolorfunction = (info->supplementdata >> 8) & 0x1; switch(info->colornumber) { case 0: // in 16 colors info->paladdr = ((tmp & 0xF000) >> 12) | ((info->supplementdata & 0xE0) >> 1); break; default: // not in 16 colors info->paladdr = (tmp & 0x7000) >> 8; break; } switch(info->auxmode) { case 0: info->flipfunction = (tmp & 0xC00) >> 10; switch(info->patternwh) { case 1: info->charaddr = (tmp & 0x3FF) | ((info->supplementdata & 0x1F) << 10); break; case 2: info->charaddr = ((tmp & 0x3FF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x1C) << 10); break; } break; case 1: info->flipfunction = 0; switch(info->patternwh) { case 1: info->charaddr = (tmp & 0xFFF) | ((info->supplementdata & 0x1C) << 10); break; case 2: info->charaddr = ((tmp & 0xFFF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x10) << 10); break; } break; } break; } case 2: { u16 tmp1 = T1ReadWord(Vdp2Ram, info->addr); u16 tmp2 = T1ReadWord(Vdp2Ram, info->addr+2); info->addr += 4; info->charaddr = tmp2 & 0x7FFF; info->flipfunction = (tmp1 & 0xC000) >> 14; switch(info->colornumber) { case 0: info->paladdr = (tmp1 & 0x7F); break; default: info->paladdr = (tmp1 & 0x70); break; } info->specialfunction = (tmp1 & 0x2000) >> 13; info->specialcolorfunction = (tmp1 & 0x1000) >> 12; break; } } if (!(Vdp2Regs->VRSIZE & 0x8000)) info->charaddr &= 0x3FFF; info->charaddr *= 0x20; // thanks Runik } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawPage(vdp2draw_struct *info, YglTexture *texture) { int X, Y; int i, j; X = info->x; for(i = 0;i < info->pagewh;i++) { Y = info->y; info->x = X; for(j = 0;j < info->pagewh;j++) { info->y = Y; if ((info->x >= -info->patternpixelwh) && (info->y >= -info->patternpixelwh) && (info->x <= info->draww) && (info->y <= info->drawh)) { Vdp2PatternAddr(info); Vdp2DrawPattern(info, texture); } else { info->addr += info->patterndatasize * 2; info->x += info->patternpixelwh; info->y += info->patternpixelwh; } } } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawPlane(vdp2draw_struct *info, YglTexture *texture) { int X, Y; int i, j; X = info->x; for(i = 0;i < info->planeh;i++) { Y = info->y; info->x = X; for(j = 0;j < info->planew;j++) { info->y = Y; Vdp2DrawPage(info, texture); } } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawMap(vdp2draw_struct *info, YglTexture *texture) { int i, j; int X, Y; int xx,yy; X = info->x; info->patternpixelwh = 8 * info->patternwh; info->draww = (int)((float)vdp2width / info->coordincx); info->drawh = (int)((float)vdp2height / info->coordincy); i=0; yy = info->y*info->coordincy; while( yy < vdp2height ) { Y = info->y; j=0; info->x = X; xx = info->x*info->coordincx; while( xx < vdp2width ) { info->y = Y; info->PlaneAddr(info, info->mapwh * i + j); Vdp2DrawPlane(info, texture); j++; j &= (info->mapwh-1); xx += (info->patternpixelwh*info->pagewh*info->planew) * info->coordincx; } i++; i&=(info->mapwh-1); yy += (info->patternpixelwh*info->pagewh*info->planeh) * info->coordincy; } } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL DoNothing(UNUSED void *info, u32 pixel) { return pixel; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL DoColorOffset(void *info, u32 pixel) { return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor, ((vdp2draw_struct *)info)->cog, ((vdp2draw_struct *)info)->cob); } ////////////////////////////////////////////////////////////////////////////// static INLINE void ReadVdp2ColorOffset(vdp2draw_struct *info, int mask) { if (Vdp2Regs->CLOFEN & mask) { // color offset enable if (Vdp2Regs->CLOFSL & mask) { // color offset B info->cor = Vdp2Regs->COBR & 0xFF; if (Vdp2Regs->COBR & 0x100) info->cor |= 0xFFFFFF00; info->cog = Vdp2Regs->COBG & 0xFF; if (Vdp2Regs->COBG & 0x100) info->cog |= 0xFFFFFF00; info->cob = Vdp2Regs->COBB & 0xFF; if (Vdp2Regs->COBB & 0x100) info->cob |= 0xFFFFFF00; } else { // color offset A info->cor = Vdp2Regs->COAR & 0xFF; if (Vdp2Regs->COAR & 0x100) info->cor |= 0xFFFFFF00; info->cog = Vdp2Regs->COAG & 0xFF; if (Vdp2Regs->COAG & 0x100) info->cog |= 0xFFFFFF00; info->cob = Vdp2Regs->COAB & 0xFF; if (Vdp2Regs->COAB & 0x100) info->cob |= 0xFFFFFF00; } info->PostPixelFetchCalc = &DoColorOffset; } else // color offset disable info->PostPixelFetchCalc = &DoNothing; } ////////////////////////////////////////////////////////////////////////////// static INLINE u32 Vdp2RotationFetchPixel(vdp2draw_struct *info, int x, int y, int cellw) { u32 dot; switch(info->colornumber) { case 0: // 4 BPP dot = T1ReadByte(Vdp2Ram, ((info->charaddr + ((y * cellw) + x) / 2) & 0x7FFFF)); if (!(x & 0x1)) dot >>= 4; if (!(dot & 0xF) && info->transparencyenable) return 0x00000000; else return Vdp2ColorRamGetColor(info->coloroffset + ((info->paladdr << 4) | (dot & 0xF)), info->alpha); case 1: // 8 BPP dot = T1ReadByte(Vdp2Ram, ((info->charaddr + (y * cellw) + x) & 0x7FFFF)); if (!(dot & 0xFF) && info->transparencyenable) return 0x00000000; else return Vdp2ColorRamGetColor(info->coloroffset + ((info->paladdr << 4) | (dot & 0xFF)), info->alpha); case 2: // 16 BPP(palette) dot = T1ReadWord(Vdp2Ram, ((info->charaddr + ((y * cellw) + x) * 2) & 0x7FFFF)); if ((dot == 0) && info->transparencyenable) return 0x00000000; else return Vdp2ColorRamGetColor(info->coloroffset + dot, info->alpha); case 3: // 16 BPP(RGB) dot = T1ReadWord(Vdp2Ram, ((info->charaddr + ((y * cellw) + x) * 2) & 0x7FFFF)); if (!(dot & 0x8000) && info->transparencyenable) return 0x00000000; else return SAT2YAB1(0xFF, dot); case 4: // 32 BPP dot = T1ReadLong(Vdp2Ram, ((info->charaddr + ((y * cellw) + x) * 4) & 0x7FFFF)); if (!(dot & 0x80000000) && info->transparencyenable) return 0x00000000; else return SAT2YAB2(info->alpha, (dot >> 16), dot); default: return 0; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL Vdp2DrawRotation(vdp2draw_struct *info, vdp2rotationparameter_struct *dmy, YglTexture *texture) { int useb = 0; int i, j; int x, y; int cellw, cellh; int pagepixelwh; int planepixelwidth; int planepixelheight; int screenwidth; int screenheight; int oldcellx=-1, oldcelly=-1; u32 color; int vres,hres; int h; int v; int pagesize; int patternshift; u32 LineColorRamAdress; vdp2rotationparameter_struct *parameter; if( vdp2height >= 448 ) vres = (vdp2height>>1); else vres = vdp2height; if( vdp2width >= 640 ) hres = (vdp2width>>1); else hres = vdp2width; info->vertices[0] = 0; info->vertices[1] = 0; info->vertices[2] = vdp2width; info->vertices[3] = 0; info->vertices[4] = vdp2width; info->vertices[5] = vdp2height; info->vertices[6] = 0; info->vertices[7] = vdp2height; cellw = info->cellw; cellh = info->cellh; info->cellw = hres; info->cellh = vres; info->flipfunction = 0; YglQuad((YglSprite *)info, texture,NULL); info->cellw = cellw; info->cellh = cellh; if( Vdp2Regs->RPMD != 0 ) useb = 1; if (!info->isbitmap) { pagepixelwh=64*8; planepixelwidth=info->planew*pagepixelwh; planepixelheight=info->planeh*pagepixelwh; screenwidth=4*planepixelwidth; screenheight=4*planepixelheight; oldcellx=-1; oldcelly=-1; pagesize=info->pagewh*info->pagewh; patternshift = (2+info->patternwh); } else { pagepixelwh=0; planepixelwidth=0; planepixelheight=0; screenwidth=0; screenheight=0; oldcellx=0; oldcelly=0; pagesize=0; patternshift = 0; } if( info->LineColorBase !=0 ) { LineColorRamAdress = (T1ReadWord(Vdp2Ram,info->LineColorBase)&0x780) + info->coloroffset; }else{ LineColorRamAdress = 0x00; } paraA.dx = paraA.A * paraA.deltaX + paraA.B * paraA.deltaY; paraA.dy = paraA.D * paraA.deltaX + paraA.E * paraA.deltaY; paraA.Xp = paraA.A * (paraA.Px - paraA.Cx) + paraA.B * (paraA.Py - paraA.Cy) + paraA.C * (paraA.Pz - paraA.Cz) + paraA.Cx + paraA.Mx; paraA.Yp = paraA.D * (paraA.Px - paraA.Cx) + paraA.E * (paraA.Py - paraA.Cy) + paraA.F * (paraA.Pz - paraA.Cz) + paraA.Cy + paraA.My; if(useb) { paraB.dx = paraB.A * paraB.deltaX + paraB.B * paraB.deltaY; paraB.dy = paraB.D * paraB.deltaX + paraB.E * paraB.deltaY; paraB.Xp = paraB.A * (paraB.Px - paraB.Cx) + paraB.B * (paraB.Py - paraB.Cy) + paraB.C * (paraB.Pz - paraB.Cz) + paraB.Cx + paraB.Mx; paraB.Yp = paraB.D * (paraB.Px - paraB.Cx) + paraB.E * (paraB.Py - paraB.Cy) + paraB.F * (paraB.Pz - paraB.Cz) + paraB.Cy + paraB.My; } for (j = 0; j < vres; j++) { paraA.Xsp = paraA.A * ((paraA.Xst + paraA.deltaXst * j) - paraA.Px) + paraA.B * ((paraA.Yst + paraA.deltaYst * j) - paraA.Py) + paraA.C * (paraA.Zst - paraA.Pz); paraA.Ysp = paraA.D * ((paraA.Xst + paraA.deltaXst *j) - paraA.Px) + paraA.E * ((paraA.Yst + paraA.deltaYst * j) - paraA.Py) + paraA.F * (paraA.Zst - paraA.Pz); paraA.KtablV = paraA.deltaKAst* j; if(useb) { paraB.Xsp = paraB.A * ((paraB.Xst + paraB.deltaXst * j) - paraB.Px) + paraB.B * ((paraB.Yst + paraB.deltaYst * j) - paraB.Py) + paraB.C * (paraB.Zst - paraB.Pz); paraB.Ysp = paraB.D * ((paraB.Xst + paraB.deltaXst * j) - paraB.Px) + paraB.E * ((paraB.Yst + paraB.deltaYst * j) - paraB.Py) + paraB.F * (paraB.Zst - paraB.Pz); paraB.KtablV = paraB.deltaKAst * j; } if( (Vdp2Regs->LCTA.part.U & 0x8000) != 0 && info->LineColorBase !=0 ) { LineColorRamAdress = (T1ReadWord(Vdp2Ram,info->LineColorBase+(y<<1))&0x780) + info->coloroffset; } for( i = 0; i < hres; i++ ) { parameter = info->GetRParam(info,i,j); if( parameter == NULL ) { *(texture->textdata++) = 0x000000; continue; } h = (parameter->ky * ( parameter->Xsp + parameter->dx * i ) + parameter->Xp); v = (parameter->ky * ( parameter->Ysp + parameter->dy * i ) + parameter->Yp); if (info->isbitmap) { h &= cellw-1; v &= cellh-1; // Fetch Pixel color = Vdp2RotationFetchPixel(info, h, v, cellw); } else { // Tile int planenum; if( (h < 0 || h >= parameter->MaxH) || (v < 0 || v >= parameter->MaxV) ) { switch( parameter->screenover ) { case OVERMODE_REPEAT: h &= (parameter->MaxH-1); v &= (parameter->MaxH-1); break; case OVERMODE_SELPATNAME: *(texture->textdata++) = 0x000000; // ToDO continue; break; default: *(texture->textdata++) = 0x000000; continue; } } x = h; y = v; if ((x>>patternshift) != oldcellx || (y>>patternshift) != oldcelly) { oldcellx = x>>patternshift; oldcelly = y>>patternshift; // Calculate which plane we're dealing with planenum = (x >> parameter->ShiftPaneX) + ((y >> parameter->ShiftPaneY) << 2); x &= parameter->MskH; y &= parameter->MskV; info->addr = parameter->PlaneAddrv[planenum]; // Figure out which page it's on(if plane size is not 1x1) info->addr += (((y>>9) * pagesize * info->planew) + ((x>>9) * pagesize) + (((y&511)>>patternshift) * info->pagewh) + ((x&511)>>patternshift)) << info->patterndatasize; Vdp2PatternAddr(info); // Heh, this could be optimized } // Figure out which pixel in the tile we want if (info->patternwh == 1) { x &= 8-1; y &= 8-1; // vertical flip if (info->flipfunction & 0x2) y = 8 - 1 - y; // horizontal flip if (info->flipfunction & 0x1) x = 8 - 1 - x; } else { if (info->flipfunction) { y &= 16 - 1; if (info->flipfunction & 0x2) { if (!(y & 8)) y = 8 - 1 - y + 16; else y = 16 - 1 - y; } else if (y & 8) y += 8; if (info->flipfunction & 0x1) { if (!(x & 8)) y += 8; x &= 8-1; x = 8 - 1 - x; } else if (x & 8) { y += 8; x &= 8-1; } else x &= 8-1; } else { y &= 16 - 1; if (y & 8) y += 8; if (x & 8) y += 8; x &= 8-1; } } // Fetch pixel color = Vdp2RotationFetchPixel(info, x, y, 8); } if( parameter->lineaddr != 0xFFFFFFFF && ((Vdp2Regs->CCCTL>>8)&0x01) ) { u32 linecolor = Vdp2ColorRamGetColor(LineColorRamAdress|parameter->lineaddr,0xFF); color = COLOR_ADD(color, (linecolor)&0xFF, (linecolor>>8)&0xFF, (linecolor>>16)&0xFF); } *(texture->textdata++) = info->PostPixelFetchCalc(info, color); } texture->textdata += texture->w; } } ////////////////////////////////////////////////////////////////////////////// static void SetSaturnResolution(int width, int height) { YglChangeResolution(width, height); vdp2width=width; vdp2height=height; } ////////////////////////////////////////////////////////////////////////////// int VIDOGLInit(void) { if (YglInit(2048, 1024, 8) != 0) return -1; SetSaturnResolution(320, 224); vdp1wratio = 1; vdp1hratio = 1; return 0; } ////////////////////////////////////////////////////////////////////////////// void VIDOGLDeInit(void) { YglDeInit(); } ////////////////////////////////////////////////////////////////////////////// int _VIDOGLIsFullscreen; void VIDOGLResize(unsigned int w, unsigned int h, int on) { glDeleteTextures(1, &_Ygl->texture); _VIDOGLIsFullscreen = on; GlHeight=h; GlWidth=w; YglGLInit(2048, 1024); glViewport(0, 0, w, h); YglNeedToUpdateWindow(); SetSaturnResolution(vdp2width, vdp2height); } ////////////////////////////////////////////////////////////////////////////// int VIDOGLIsFullscreen(void) { return _VIDOGLIsFullscreen; } ////////////////////////////////////////////////////////////////////////////// int VIDOGLVdp1Reset(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1DrawStart(void) { int i; int maxpri; int minpri; u8 *sprprilist = (u8 *)&Vdp2Regs->PRISA; YglCacheReset(); maxpri = 0x00; minpri = 0x07; for( i=0; i<8; i++ ) { if( (sprprilist[i]&0x07) < minpri ) minpri = (sprprilist[i]&0x07); if( (sprprilist[i]&0x07) > maxpri ) maxpri = (sprprilist[i]&0x07); } _Ygl->vdp1_maxpri = maxpri; _Ygl->vdp1_minpri = minpri; if (Vdp2Regs->CLOFEN & 0x40) { // color offset enable if (Vdp2Regs->CLOFSL & 0x40) { // color offset B vdp1cor = Vdp2Regs->COBR & 0xFF; if (Vdp2Regs->COBR & 0x100) vdp1cor |= 0xFFFFFF00; vdp1cog = Vdp2Regs->COBG & 0xFF; if (Vdp2Regs->COBG & 0x100) vdp1cog |= 0xFFFFFF00; vdp1cob = Vdp2Regs->COBB & 0xFF; if (Vdp2Regs->COBB & 0x100) vdp1cob |= 0xFFFFFF00; } else { // color offset A vdp1cor = Vdp2Regs->COAR & 0xFF; if (Vdp2Regs->COAR & 0x100) vdp1cor |= 0xFFFFFF00; vdp1cog = Vdp2Regs->COAG & 0xFF; if (Vdp2Regs->COAG & 0x100) vdp1cog |= 0xFFFFFF00; vdp1cob = Vdp2Regs->COAB & 0xFF; if (Vdp2Regs->COAB & 0x100) vdp1cob |= 0xFFFFFF00; } } else // color offset disable vdp1cor = vdp1cog = vdp1cob = 0; } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1DrawEnd(void) { } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1NormalSpriteDraw(void) { vdp1cmd_struct cmd; YglSprite sprite; YglTexture texture; YglCache cash; u32 tmp; s16 x, y; u16 CMDPMOD; u16 color2; float col[4*4]; int i; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); sprite.dst=0; sprite.blendmode=0; x = cmd.CMDXA + Vdp1Regs->localX; y = cmd.CMDYA + Vdp1Regs->localY; sprite.w = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; sprite.h = cmd.CMDSIZE & 0xFF; sprite.flip = (cmd.CMDCTRL & 0x30) >> 4; sprite.vertices[0] = (int)((float)x * vdp1wratio); sprite.vertices[1] = (int)((float)y * vdp1hratio); sprite.vertices[2] = (int)((float)(x + sprite.w) * vdp1wratio); sprite.vertices[3] = (int)((float)y * vdp1hratio); sprite.vertices[4] = (int)((float)(x + sprite.w) * vdp1wratio); sprite.vertices[5] = (int)((float)(y + sprite.h) * vdp1hratio); sprite.vertices[6] = (int)((float)x * vdp1wratio); sprite.vertices[7] = (int)((float)(y + sprite.h) * vdp1hratio); tmp = cmd.CMDSRCA; tmp <<= 16; tmp |= cmd.CMDCOLR; sprite.priority = 8; CMDPMOD = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x4); sprite.uclipmode=(CMDPMOD>>9)&0x03; // Half trans parent to VDP1 Framebuffer if( (CMDPMOD & 0x7)==0x03 || (CMDPMOD & 0x100) ) { tmp |= 0x00010000; sprite.blendmode = 0x80; } if((CMDPMOD & 0x8000) != 0) { tmp |= 0x00020000; } if( (CMDPMOD & 4) ) { for (i=0; i<4; i++) { color2 = T1ReadWord(Vdp1Ram, (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1C) << 3) + (i << 1)); col[(i << 2) + 0] = (float)((color2 & 0x001F))/(float)(0x1F)-0.5f; col[(i << 2) + 1] = (float)((color2 & 0x03E0)>>5)/(float)(0x1F)-0.5f; col[(i << 2) + 2] = (float)((color2 & 0x7C00)>>10)/(float)(0x1F)-0.5f; col[(i << 2) + 3] = 1.0f; } if (sprite.w > 0 && sprite.h > 1) { if (1 == YglIsCached(tmp,&cash) ) { YglCacheQuadGrowShading(&sprite, col,&cash); return; } YglQuadGrowShading(&sprite, &texture,col,&cash); YglCacheAdd(tmp,&cash); Vdp1ReadTexture(&cmd, &sprite, &texture); return; } } else // No Gouraud shading, use same color for all 4 vertices { if (sprite.w > 0 && sprite.h > 1) { if (1 == YglIsCached(tmp,&cash) ) { YglCachedQuad(&sprite, &cash); return; } YglQuad(&sprite, &texture,&cash); YglCacheAdd(tmp,&cash); Vdp1ReadTexture(&cmd, &sprite, &texture); } } } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1ScaledSpriteDraw(void) { vdp1cmd_struct cmd; YglSprite sprite; YglTexture texture; YglCache cash; u32 tmp; s16 rw=0, rh=0; s16 x, y; u16 CMDPMOD; u16 color2; float col[4*4]; int i; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); sprite.dst=0; sprite.blendmode=0; x = cmd.CMDXA + Vdp1Regs->localX; y = cmd.CMDYA + Vdp1Regs->localY; sprite.w = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; sprite.h = cmd.CMDSIZE & 0xFF; sprite.flip = (cmd.CMDCTRL & 0x30) >> 4; // Setup Zoom Point switch ((cmd.CMDCTRL & 0xF00) >> 8) { case 0x0: // Only two coordinates rw = cmd.CMDXC - x + Vdp1Regs->localX + 1; rh = cmd.CMDYC - y + Vdp1Regs->localY + 1; break; case 0x5: // Upper-left rw = cmd.CMDXB + 1; rh = cmd.CMDYB + 1; break; case 0x6: // Upper-Center rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw/2; rw++; rh++; break; case 0x7: // Upper-Right rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw; rw++; rh++; break; case 0x9: // Center-left rw = cmd.CMDXB; rh = cmd.CMDYB; y = y - rh/2; rw++; rh++; break; case 0xA: // Center-center rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw/2; y = y - rh/2; rw++; rh++; break; case 0xB: // Center-right rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw; y = y - rh/2; rw++; rh++; break; case 0xD: // Lower-left rw = cmd.CMDXB; rh = cmd.CMDYB; y = y - rh; rw++; rh++; break; case 0xE: // Lower-center rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw/2; y = y - rh; rw++; rh++; break; case 0xF: // Lower-right rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw; y = y - rh; rw++; rh++; break; default: break; } sprite.vertices[0] = (int)((float)x * vdp1wratio); sprite.vertices[1] = (int)((float)y * vdp1hratio); sprite.vertices[2] = (int)((float)(x + rw) * vdp1wratio); sprite.vertices[3] = (int)((float)y * vdp1hratio); sprite.vertices[4] = (int)((float)(x + rw) * vdp1wratio); sprite.vertices[5] = (int)((float)(y + rh) * vdp1hratio); sprite.vertices[6] = (int)((float)x * vdp1wratio); sprite.vertices[7] = (int)((float)(y + rh) * vdp1hratio); tmp = cmd.CMDSRCA; tmp <<= 16; tmp |= cmd.CMDCOLR; CMDPMOD = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x4); sprite.uclipmode=(CMDPMOD>>9)&0x03; sprite.priority = 8; // Half trans parent to VDP1 Framebuffer if( (CMDPMOD & 0x7)==0x03 || (CMDPMOD & 0x100) ) { tmp |= 0x00010000; sprite.blendmode = 0x80; } // MSB if((CMDPMOD & 0x8000) != 0) { tmp |= 0x00020000; } if ( (CMDPMOD & 4) ) { for (i=0; i<4; i++) { color2 = T1ReadWord(Vdp1Ram, (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1C) << 3) + (i << 1)); col[(i << 2) + 0] = (float)((color2 & 0x001F))/(float)(0x1F)-0.5f; col[(i << 2) + 1] = (float)((color2 & 0x03E0)>>5)/(float)(0x1F)-0.5f; col[(i << 2) + 2] = (float)((color2 & 0x7C00)>>10)/(float)(0x1F)-0.5f; col[(i << 2) + 3] = 1.0f; } if (sprite.w > 0 && sprite.h > 1) { if (1 == YglIsCached(tmp,&cash) ) { YglCacheQuadGrowShading(&sprite, col,&cash); return; } YglQuadGrowShading(&sprite, &texture,col,&cash); YglCacheAdd(tmp,&cash); Vdp1ReadTexture(&cmd, &sprite, &texture); return; } } else // No Gouraud shading, use same color for all 4 vertices { if (sprite.w > 0 && sprite.h > 1) { if (1 == YglIsCached(tmp,&cash) ) { YglCachedQuad(&sprite, &cash); return; } YglQuad(&sprite, &texture,&cash); YglCacheAdd(tmp,&cash); Vdp1ReadTexture(&cmd, &sprite, &texture); } } } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1DistortedSpriteDraw(void) { vdp1cmd_struct cmd; YglSprite sprite; YglTexture texture; YglCache cash; u32 tmp; u16 CMDPMOD; u16 color2; int i; float col[4*4]; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); sprite.blendmode=0; sprite.dst = 1; sprite.w = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; sprite.h = cmd.CMDSIZE & 0xFF; sprite.flip = (cmd.CMDCTRL & 0x30) >> 4; sprite.vertices[0] = (s32)((float)(cmd.CMDXA + Vdp1Regs->localX) * vdp1wratio); sprite.vertices[1] = (s32)((float)(cmd.CMDYA + Vdp1Regs->localY) * vdp1hratio); sprite.vertices[2] = (s32)((float)((cmd.CMDXB) + Vdp1Regs->localX) * vdp1wratio); sprite.vertices[3] = (s32)((float)(cmd.CMDYB + Vdp1Regs->localY) * vdp1hratio); sprite.vertices[4] = (s32)((float)((cmd.CMDXC) + Vdp1Regs->localX) * vdp1wratio); sprite.vertices[5] = (s32)((float)((cmd.CMDYC) + Vdp1Regs->localY) * vdp1hratio); sprite.vertices[6] = (s32)((float)(cmd.CMDXD + Vdp1Regs->localX) * vdp1wratio); sprite.vertices[7] = (s32)((float)((cmd.CMDYD) + Vdp1Regs->localY) * vdp1hratio); tmp = cmd.CMDSRCA; tmp <<= 16; tmp |= cmd.CMDCOLR; CMDPMOD = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x4); sprite.priority = 8; sprite.uclipmode=(CMDPMOD>>9)&0x03; // Half trans parent to VDP1 Framebuffer if( (CMDPMOD & 0x7)==0x03 || (CMDPMOD & 0x100) ) { tmp |= 0x00010000; sprite.blendmode = 0x80; } // MSB if((CMDPMOD & 0x8000) != 0) { tmp |= 0x00020000; } // Check if the Gouraud shading bit is set and the color mode is RGB if ( (CMDPMOD & 4) ) { for (i=0; i<4; i++) { color2 = T1ReadWord(Vdp1Ram, (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1C) << 3) + (i << 1)); col[(i << 2) + 0] = (float)((color2 & 0x001F))/(float)(0x1F)-0.5f; col[(i << 2) + 1] = (float)((color2 & 0x03E0)>>5)/(float)(0x1F)-0.5f; col[(i << 2) + 2] = (float)((color2 & 0x7C00)>>10)/(float)(0x1F)-0.5f; col[(i << 2) + 3] = 1.0f; } if (sprite.w > 0 && sprite.h > 1) { if (1 == YglIsCached(tmp,&cash) ) { YglCacheQuadGrowShading(&sprite, col,&cash); return; } YglQuadGrowShading(&sprite, &texture,col,&cash); //YglQuad(&sprite, &texture,&c); YglCacheAdd(tmp,&cash); Vdp1ReadTexture(&cmd, &sprite, &texture); return; } } else // No Gouraud shading, use same color for all 4 vertices { if (sprite.w > 0 && sprite.h > 1) { if (1 == YglIsCached(tmp,&cash) ) { YglCachedQuad(&sprite, &cash); return; } YglQuad(&sprite, &texture,&cash); YglCacheAdd(tmp,&cash); Vdp1ReadTexture(&cmd, &sprite, &texture); } } return ; } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1PolygonDraw(void) { s16 X[4]; s16 Y[4]; u16 color; u16 CMDPMOD; u8 alpha; YglSprite polygon; YglTexture texture; u16 color2; int i; float col[4*4]; int gouraud=0; int priority; polygon.blendmode=0; polygon.dst = 0; X[0] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); Y[0] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); X[1] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10); Y[1] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12); X[2] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); Y[2] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); X[3] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x18); Y[3] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1A); color = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x6); CMDPMOD = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x4); polygon.uclipmode=(CMDPMOD>>9)&0x03; // Half trans parent to VDP1 Framebuffer if( (CMDPMOD & 0x7)==0x03 || (CMDPMOD & 0x100) ) { polygon.blendmode = 0x80; } // Check if the Gouraud shading bit is set and the color mode is RGB if( (CMDPMOD & 4) ) { for (i=0; i<4; i++) { color2 = T1ReadWord(Vdp1Ram, (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1C) << 3) + (i << 1)); col[(i << 2) + 0] = (float)((color2 & 0x001F))/(float)(0x1F)-0.5f; col[(i << 2) + 1] = (float)((color2 & 0x03E0)>>5)/(float)(0x1F)-0.5f; col[(i << 2) + 2] = (float)((color2 & 0x7C00)>>10)/(float)(0x1F)-0.5f; col[(i << 2) + 3] = 1.0f; } gouraud = 1; } if (color & 0x8000) priority = Vdp2Regs->PRISA & 0x7; else { int shadow, colorcalc; priority = 0; // Avoid compiler warning Vdp1ProcessSpritePixel(Vdp2Regs->SPCTL & 0xF, &color, &shadow, &priority, &colorcalc); #ifdef WORDS_BIGENDIAN priority = ((u8 *)&Vdp2Regs->PRISA)[priority^1] & 0x7; #else priority = ((u8 *)&Vdp2Regs->PRISA)[priority] & 0x7; #endif } polygon.priority = 8; polygon.vertices[0] = (int)((float)X[0] * vdp1wratio); polygon.vertices[1] = (int)((float)Y[0] * vdp1hratio); polygon.vertices[2] = (int)((float)X[1] * vdp1wratio); polygon.vertices[3] = (int)((float)Y[1] * vdp1hratio); polygon.vertices[4] = (int)((float)X[2] * vdp1wratio); polygon.vertices[5] = (int)((float)Y[2] * vdp1hratio); polygon.vertices[6] = (int)((float)X[3] * vdp1wratio); polygon.vertices[7] = (int)((float)Y[3] * vdp1hratio); polygon.w = 1; polygon.h = 1; polygon.flip = 0; if( gouraud == 1 ) { YglQuadGrowShading(&polygon, &texture,col,NULL); }else{ YglQuad(&polygon, &texture,NULL); } if (color == 0) { alpha = 0; priority = 0; }else{ alpha = 0xF8; } if( (CMDPMOD & 0x100) || (CMDPMOD & 0x7) == 0x3) { alpha = 0x80; } alpha |= priority; if (color & 0x8000) *texture.textdata = SAT2YAB1(alpha,color); else *texture.textdata = Vdp2ColorRamGetColor(color, alpha); } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1PolylineDraw(void) { s16 X[4]; s16 Y[4]; u16 color; u16 CMDPMOD; u8 alpha; YglSprite polygon; YglTexture texture; YglCache c; int priority; polygon.blendmode=0; polygon.dst = 0; X[0] = Vdp1Regs->localX + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C) ); Y[0] = Vdp1Regs->localY + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E) ); X[1] = Vdp1Regs->localX + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10) ); Y[1] = Vdp1Regs->localY + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12) ); X[2] = Vdp1Regs->localX + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14) ); Y[2] = Vdp1Regs->localY + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16) ); X[3] = Vdp1Regs->localX + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x18) ); Y[3] = Vdp1Regs->localY + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1A) ); color = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x6); CMDPMOD = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x4); polygon.uclipmode=(CMDPMOD>>9)&0x03; // Half trans parent to VDP1 Framebuffer if( (CMDPMOD & 0x7)==0x03 || (CMDPMOD & 0x100) ) { polygon.blendmode = 0x80; } if (color & 0x8000) priority = Vdp2Regs->PRISA & 0x7; else { int shadow, colorcalc; priority = 0; // Avoid compiler warning Vdp1ProcessSpritePixel(Vdp2Regs->SPCTL & 0xF, &color, &shadow, &priority, &colorcalc); #ifdef WORDS_BIGENDIAN priority = ((u8 *)&Vdp2Regs->PRISA)[priority^1] & 0x7; #else priority = ((u8 *)&Vdp2Regs->PRISA)[priority] & 0x7; #endif } polygon.priority = 8; // A bit of kludge, but eventually we'll have to redo the YGL anyways. polygon.vertices[0] = (int)((float)X[0] * vdp1wratio); polygon.vertices[1] = (int)((float)Y[0] * vdp1hratio); polygon.vertices[2] = (int)((float)X[0] * vdp1wratio)+1; polygon.vertices[3] = (int)((float)Y[0] * vdp1hratio)+1; polygon.vertices[4] = (int)((float)X[1] * vdp1wratio); polygon.vertices[5] = (int)((float)Y[1] * vdp1hratio); polygon.vertices[6] = (int)((float)X[1] * vdp1wratio)+1; polygon.vertices[7] = (int)((float)Y[1] * vdp1hratio)+1; polygon.w = 1; polygon.h = 1; polygon.flip = 0; YglQuad(&polygon, &texture,&c); polygon.vertices[0] = polygon.vertices[4]; polygon.vertices[1] = polygon.vertices[5]; polygon.vertices[2] = polygon.vertices[6]; polygon.vertices[3] = polygon.vertices[7]; polygon.vertices[4] = (int)((float)X[2] * vdp1wratio); polygon.vertices[5] = (int)((float)Y[2] * vdp1hratio); polygon.vertices[6] = (int)((float)X[2] * vdp1wratio)+1; polygon.vertices[7] = (int)((float)Y[2] * vdp1hratio)+1; YglCachedQuad(&polygon, &c); polygon.vertices[0] = polygon.vertices[4]; polygon.vertices[1] = polygon.vertices[5]; polygon.vertices[2] = polygon.vertices[6]; polygon.vertices[3] = polygon.vertices[7]; polygon.vertices[4] = (int)((float)X[3] * vdp1wratio); polygon.vertices[5] = (int)((float)Y[3] * vdp1hratio); polygon.vertices[6] = (int)((float)X[3] * vdp1wratio)+1; polygon.vertices[7] = (int)((float)Y[3] * vdp1hratio)+1; YglCachedQuad(&polygon, &c); polygon.vertices[0] = (int)((float)X[0] * vdp1wratio); polygon.vertices[1] = (int)((float)Y[0] * vdp1hratio); polygon.vertices[2] = (int)((float)X[0] * vdp1wratio)+1; polygon.vertices[3] = (int)((float)Y[0] * vdp1hratio)+1; YglCachedQuad(&polygon, &c); if (color == 0) { alpha = 0; priority = 0; }else{ alpha = 0xF8; if (CMDPMOD & 0x100) { alpha = 0x80; } } alpha |= priority; if (color & 0x8000) *texture.textdata = SAT2YAB1(alpha,color); else *texture.textdata = Vdp2ColorRamGetColor(color, alpha); } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1LineDraw(void) { s16 X[2]; s16 Y[2]; u16 color; u16 CMDPMOD; u8 alpha; YglSprite polygon; YglTexture texture; int priority; polygon.blendmode=0; polygon.dst = 0; X[0] = Vdp1Regs->localX + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C)); Y[0] = Vdp1Regs->localY + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E)); X[1] = Vdp1Regs->localX + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10)); Y[1] = Vdp1Regs->localY + (T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12)); color = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x6); CMDPMOD = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x4); polygon.uclipmode=(CMDPMOD>>9)&0x03; // Half trans parent to VDP1 Framebuffer if( (CMDPMOD & 0x7)==0x03 || (CMDPMOD & 0x100) ) { polygon.blendmode = 0x80; } if (color & 0x8000) priority = Vdp2Regs->PRISA & 0x7; else { int shadow, colorcalc; priority = 0; // Avoid compiler warning Vdp1ProcessSpritePixel(Vdp2Regs->SPCTL & 0xF, &color, &shadow, &priority, &colorcalc); #ifdef WORDS_BIGENDIAN priority = ((u8 *)&Vdp2Regs->PRISA)[priority^1] & 0x7; #else priority = ((u8 *)&Vdp2Regs->PRISA)[priority] & 0x7; #endif } polygon.priority = 8; polygon.vertices[0] = (int)((float)X[0] * vdp1wratio); polygon.vertices[1] = (int)((float)Y[0] * vdp1hratio); polygon.vertices[2] = (int)((float)X[0] * vdp1wratio)+1; polygon.vertices[3] = (int)((float)Y[0] * vdp1hratio)+1; polygon.vertices[4] = (int)((float)X[1] * vdp1wratio); polygon.vertices[5] = (int)((float)Y[1] * vdp1hratio); polygon.vertices[6] = (int)((float)X[1] * vdp1wratio)+1; polygon.vertices[7] = (int)((float)Y[1] * vdp1hratio)+1; polygon.w = 1; polygon.h = 1; polygon.flip = 0; YglQuad(&polygon, &texture,NULL); if (color == 0) { alpha = 0; priority = 0; }else{ alpha = 0xF8; if (CMDPMOD & 0x100) { alpha = 0x80; } } alpha |= priority; if (color & 0x8000) *texture.textdata = SAT2YAB1(alpha,color); else *texture.textdata = Vdp2ColorRamGetColor(color, alpha); } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1UserClipping(void) { Vdp1Regs->userclipX1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); Vdp1Regs->userclipY1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); Vdp1Regs->userclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); Vdp1Regs->userclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1SystemClipping(void) { Vdp1Regs->systemclipX1 = 0; Vdp1Regs->systemclipY1 = 0; Vdp1Regs->systemclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); Vdp1Regs->systemclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp1LocalCoordinate(void) { Vdp1Regs->localX = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); Vdp1Regs->localY = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); } ////////////////////////////////////////////////////////////////////////////// int VIDOGLVdp2Reset(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp2DrawStart(void) { YglReset(); YglCacheReset(); } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp2DrawEnd(void) { YglRender(); /* It would be better to reset manualchange in a Vdp1SwapFrameBuffer function that would be called here and during a manual change */ Vdp1External.manualchange = 0; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawBackScreen(void) { u32 scrAddr; int dot, y; static unsigned char lineColors[512 * 3]; static int line[512*4]; if (Vdp2Regs->VRSIZE & 0x8000) scrAddr = (((Vdp2Regs->BKTAU & 0x7) << 16) | Vdp2Regs->BKTAL) * 2; else scrAddr = (((Vdp2Regs->BKTAU & 0x3) << 16) | Vdp2Regs->BKTAL) * 2; if (Vdp2Regs->BKTAU & 0x8000) { for(y = 0; y < vdp2height; y++) { dot = T1ReadWord(Vdp2Ram, scrAddr); scrAddr += 2; lineColors[3*y+0] = (dot & 0x1F) << 3; lineColors[3*y+1] = (dot & 0x3E0) >> 2; lineColors[3*y+2] = (dot & 0x7C00) >> 7; line[4*y+0] = 0; line[4*y+1] = y; line[4*y+2] = vdp2width; line[4*y+3] = y; } glColorPointer(3, GL_UNSIGNED_BYTE, 0, lineColors); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_INT, 0, line); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_LINES,0,vdp2height*2); glDisableClientState(GL_COLOR_ARRAY); glColor3ub(0xFF, 0xFF, 0xFF); } else { dot = T1ReadWord(Vdp2Ram, scrAddr); glColor3ub((dot & 0x1F) << 3, (dot & 0x3E0) >> 2, (dot & 0x7C00) >> 7); line[0] = 0; line[1] = 0; line[2] = vdp2width; line[3] = 0; line[4] = vdp2width; line[5] = vdp2height; line[6] = 0; line[7] = vdp2height; glDisable(GL_TEXTURE_2D); glVertexPointer(2, GL_INT, 0, line); glEnableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDrawArrays(GL_TRIANGLE_FAN,0,8); glColor3ub(0xFF, 0xFF, 0xFF); glEnableClientState(GL_TEXTURE_COORD_ARRAY); } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawLineColorScreen(void) { } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawNBG0(void) { vdp2draw_struct info; YglTexture texture; YglCache tmpc; vdp2rotationparameter_struct parameter; info.dst=0; info.uclipmode=0; info.coordincx = 1.0f; info.coordincy = 1.0f; if (Vdp2Regs->BGON & 0x20) { // RBG1 mode info.enable = Vdp2Regs->BGON & 0x20; // Read in Parameter B Vdp2ReadRotationTable(1, ¶meter); if((info.isbitmap = Vdp2Regs->CHCTLA & 0x2) != 0) { // Bitmap Mode ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 2, 0x3); info.charaddr = (Vdp2Regs->MPOFR & 0x70) * 0x2000; info.paladdr = (Vdp2Regs->BMPNA & 0x7) << 4; info.flipfunction = 0; info.specialfunction = 0; } else { // Tile Mode info.mapwh = 4; ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 12); ReadPatternData(&info, Vdp2Regs->PNCN0, Vdp2Regs->CHCTLA & 0x1); } info.rotatenum = 1; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; parameter.coefenab = Vdp2Regs->KTCTL & 0x100; info.LineColorBase = 0x00; if (paraB.coefenab) info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode01WithK; else info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode01NoK; } else if (Vdp2Regs->BGON & 0x1) { // NBG0 mode info.enable = Vdp2Regs->BGON & 0x1; if((info.isbitmap = Vdp2Regs->CHCTLA & 0x2) != 0) { // Bitmap Mode ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 2, 0x3); info.x = - ((Vdp2Regs->SCXIN0 & 0x7FF) % info.cellw); info.y = - ((Vdp2Regs->SCYIN0 & 0x7FF) % info.cellh); info.charaddr = (Vdp2Regs->MPOFN & 0x7) * 0x20000; info.paladdr = (Vdp2Regs->BMPNA & 0x7) << 4; info.flipfunction = 0; info.specialfunction = 0; } else { // Tile Mode info.mapwh = 2; ReadPlaneSize(&info, Vdp2Regs->PLSZ); info.x = - ((Vdp2Regs->SCXIN0 & 0x7FF) % (512 * info.planew)); info.y = - ((Vdp2Regs->SCYIN0 & 0x7FF) % (512 * info.planeh)); ReadPatternData(&info, Vdp2Regs->PNCN0, Vdp2Regs->CHCTLA & 0x1); } if( (Vdp2Regs->ZMXN0.all & 0x7FF00) == 0 ) info.coordincx = 1.0f; else info.coordincx = (float) 65536 / (Vdp2Regs->ZMXN0.all & 0x7FF00); switch(Vdp2Regs->ZMCTL&0x03) { case 0: break; case 1: if( info.coordincx < 0.5f ) info.coordincx = 0.5f; break; case 2: case 3: if( info.coordincx < 0.25f ) info.coordincx = 0.25f; break; } if( (Vdp2Regs->ZMYN0.all & 0x7FF00) == 0 ) info.coordincx = 1.0f; else info.coordincy = (float) 65536 / (Vdp2Regs->ZMYN0.all & 0x7FF00); info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG0PlaneAddr; } else // Not enabled return; info.transparencyenable = !(Vdp2Regs->BGON & 0x100); info.specialprimode = Vdp2Regs->SFPRMD & 0x3; info.specialcolormode = Vdp2Regs->SFCCMD & 0x3; info.colornumber = (Vdp2Regs->CHCTLA & 0x70) >> 4; if(Vdp2Regs->CCCTL & 0x1) { info.alpha = ((~Vdp2Regs->CCRNA & 0x1F) << 3) + 0x7; if(Vdp2Regs->CCCTL & 0x100 && info.specialcolormode == 0) info.blendmode=2; else info.blendmode=1; }else{ info.alpha = 0xFF; info.blendmode=0; } info.coloroffset = (Vdp2Regs->CRAOFA & 0x7) << 8; ReadVdp2ColorOffset(&info, 0x1); info.priority = Vdp2Regs->PRINA & 0x7; if (!(info.enable & Vdp2External.disptoggle) || (info.priority == 0)) return; // Window Mode info.bEnWin0 = (Vdp2Regs->WCTLA >> 1) &0x01; info.WindowArea0 = (Vdp2Regs->WCTLA >> 0) & 0x01; info.bEnWin1 = (Vdp2Regs->WCTLA >> 3) &0x01; info.WindowArea1 = (Vdp2Regs->WCTLA >> 2) & 0x01; info.LogicWin = (Vdp2Regs->WCTLA >> 7 ) & 0x01; if( info.bEnWin0 || info.bEnWin1 ) YglStartWindow(&info,info.bEnWin0, info.WindowArea0,info.bEnWin1, info.WindowArea1,info.LogicWin); ReadLineScrollData(&info, Vdp2Regs->SCRCTL & 0xFF, Vdp2Regs->LSTA0.all); info.lineinfo = lineNBG0; Vdp2GenLineinfo( &info ); Vdp2SetGetColor( &info ); if (info.enable == 1) { // NBG0 draw if (info.isbitmap) { int xx,yy; int isCached = 0; if(info.islinescroll) // Nights Movie { info.sh = (Vdp2Regs->SCXIN0 & 0x7FF); info.sv = (Vdp2Regs->SCYIN0 & 0x7FF); info.x = 0; info.y = 0; } yy = info.y; while( yy < vdp2height ) { xx = info.x; while( xx < vdp2width ) { info.vertices[0] = xx * info.coordincx; info.vertices[1] = yy * info.coordincy; info.vertices[2] = (xx + info.cellw) * info.coordincx; info.vertices[3] = yy * info.coordincy; info.vertices[4] = (xx + info.cellw) * info.coordincx; info.vertices[5] = (yy + info.cellh) * info.coordincy; info.vertices[6] = xx * info.coordincx; info.vertices[7] = (yy + info.cellh) * info.coordincy; if( isCached == 0 ) { YglQuad((YglSprite *)&info, &texture,&tmpc); Vdp2DrawCell(&info, &texture); isCached = 1; }else{ YglCachedQuad((YglSprite *)&info, &tmpc); } xx += info.cellw* info.coordincx; } yy += info.cellh* info.coordincy; } } else { Vdp2DrawMap(&info, &texture); } } else { // RBG1 draw Vdp2DrawRotation(&info, ¶meter, &texture); } if( info.bEnWin0 || info.bEnWin1 ) YglEndWindow(&info); } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawNBG1(void) { vdp2draw_struct info; YglTexture texture; YglCache tmpc; info.dst=0; info.uclipmode=0; info.enable = Vdp2Regs->BGON & 0x2; info.transparencyenable = !(Vdp2Regs->BGON & 0x200); info.specialprimode = (Vdp2Regs->SFPRMD >> 2) & 0x3; info.colornumber = (Vdp2Regs->CHCTLA & 0x3000) >> 12; if((info.isbitmap = Vdp2Regs->CHCTLA & 0x200) != 0) { ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 10, 0x3); info.x = -((Vdp2Regs->SCXIN1 & 0x7FF) % info.cellw); info.y = -((Vdp2Regs->SCYIN1 & 0x7FF) % info.cellh); info.charaddr = ((Vdp2Regs->MPOFN & 0x70) >> 4) * 0x20000; info.paladdr = (Vdp2Regs->BMPNA & 0x700) >> 4; info.flipfunction = 0; info.specialfunction = 0; } else { info.mapwh = 2; ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 2); info.x = - ((Vdp2Regs->SCXIN1 & 0x7FF) % (512 * info.planew)); info.y = - ((Vdp2Regs->SCYIN1 & 0x7FF) % (512 * info.planeh)); ReadPatternData(&info, Vdp2Regs->PNCN1, Vdp2Regs->CHCTLA & 0x100); } info.specialcolormode = (Vdp2Regs->SFCCMD>>2) & 0x3; if (Vdp2Regs->CCCTL & 0x2) { info.alpha = ((~Vdp2Regs->CCRNA & 0x1F00) >> 5) + 0x7; if(Vdp2Regs->CCCTL & 0x100 && info.specialcolormode == 0 ) { info.blendmode=2; }else{ info.blendmode=1; } }else{ info.alpha = 0xFF; info.blendmode=0; } info.coloroffset = (Vdp2Regs->CRAOFA & 0x70) << 4; ReadVdp2ColorOffset(&info, 0x2); if( (Vdp2Regs->ZMXN1.all & 0x7FF00) == 0 ) info.coordincx = 1.0f; else info.coordincx = (float) 65536 / (Vdp2Regs->ZMXN1.all & 0x7FF00); switch((Vdp2Regs->ZMCTL>>8)&0x03) { case 0: break; case 1: if( info.coordincx < 0.5f ) info.coordincx = 0.5f; break; case 2: case 3: if( info.coordincx < 0.25f ) info.coordincx = 0.25f; break; } if( (Vdp2Regs->ZMYN1.all & 0x7FF00) == 0 ) info.coordincx = 1.0f; else info.coordincy = (float) 65536 / (Vdp2Regs->ZMYN1.all & 0x7FF00); info.priority = (Vdp2Regs->PRINA >> 8) & 0x7;; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG1PlaneAddr; if (!(info.enable & Vdp2External.disptoggle) || (info.priority == 0) || (Vdp2Regs->BGON & 0x1 && (Vdp2Regs->CHCTLA & 0x70) >> 4 == 4)) // If NBG0 16M mode is enabled, don't draw return; // Window Mode info.bEnWin0 = (Vdp2Regs->WCTLA >> 9) &0x01; info.WindowArea0 = (Vdp2Regs->WCTLA >> 8) & 0x01; info.bEnWin1 = (Vdp2Regs->WCTLA >> 11) &0x01; info.WindowArea1 = (Vdp2Regs->WCTLA >> 10) & 0x01; info.LogicWin = (Vdp2Regs->WCTLA >> 15 ) & 0x01; if( info.bEnWin0 || info.bEnWin1 ) YglStartWindow(&info,info.bEnWin0, info.WindowArea0,info.bEnWin1, info.WindowArea1,info.LogicWin); ReadLineScrollData(&info, Vdp2Regs->SCRCTL >> 8, Vdp2Regs->LSTA1.all); info.lineinfo = lineNBG1; Vdp2GenLineinfo( &info ); Vdp2SetGetColor( &info ); #if 0 if(info->islinescroll) { int y = info->y; if( VDPLINE_SX(info->islinescroll) ) info->x += info->lineinfo[y].LineScrollValH; if( VDPLINE_SY(info->islinescroll) )info->y += info->lineinfo[y].LineScrollValH; } #endif if (info.isbitmap) { int xx,yy; int isCached = 0; if(info.islinescroll) { info.sh = (Vdp2Regs->SCXIN1 & 0x7FF); info.sv = (Vdp2Regs->SCYIN1 & 0x7FF); info.x = 0; info.y = 0; } yy = info.y; while( yy < vdp2height ) { xx = info.x; while( xx < vdp2width ) { info.vertices[0] = xx * info.coordincx; info.vertices[1] = yy * info.coordincy; info.vertices[2] = (xx + info.cellw) * info.coordincx; info.vertices[3] = yy * info.coordincy; info.vertices[4] = (xx + info.cellw) * info.coordincx; info.vertices[5] = (yy + info.cellh) * info.coordincy; info.vertices[6] = xx * info.coordincx; info.vertices[7] = (yy + info.cellh) * info.coordincy; if( isCached == 0 ) { YglQuad((YglSprite *)&info, &texture,&tmpc); Vdp2DrawCell(&info, &texture); isCached = 1; }else{ YglCachedQuad((YglSprite *)&info, &tmpc); } xx += info.cellw* info.coordincx; } yy += info.cellh* info.coordincy; } } else Vdp2DrawMap(&info, &texture); if( info.bEnWin0 || info.bEnWin1 ) YglEndWindow(&info); } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawNBG2(void) { vdp2draw_struct info; YglTexture texture; info.dst=0; info.uclipmode=0; info.enable = Vdp2Regs->BGON & 0x4; info.transparencyenable = !(Vdp2Regs->BGON & 0x400); info.specialprimode = (Vdp2Regs->SFPRMD >> 4) & 0x3; info.colornumber = (Vdp2Regs->CHCTLB & 0x2) >> 1; info.mapwh = 2; ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 4); info.x = - ((Vdp2Regs->SCXN2 & 0x7FF) % (512 * info.planew)); info.y = - ((Vdp2Regs->SCYN2 & 0x7FF) % (512 * info.planeh)); ReadPatternData(&info, Vdp2Regs->PNCN2, Vdp2Regs->CHCTLB & 0x1); info.specialcolormode = (Vdp2Regs->SFCCMD>>4) & 0x3; if (Vdp2Regs->CCCTL & 0x4) { info.alpha = ((~Vdp2Regs->CCRNB & 0x1F) << 3) + 0x7; if(Vdp2Regs->CCCTL & 0x100 && info.specialcolormode == 0 ) { info.blendmode=2; }else{ info.blendmode=1; } }else{ info.alpha = 0xFF; info.blendmode=0; } info.coloroffset = Vdp2Regs->CRAOFA & 0x700; ReadVdp2ColorOffset(&info, 0x4); info.coordincx = info.coordincy = 1; info.priority = Vdp2Regs->PRINB & 0x7;; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG2PlaneAddr; if (!(info.enable & Vdp2External.disptoggle) || (info.priority == 0) || (Vdp2Regs->BGON & 0x1 && (Vdp2Regs->CHCTLA & 0x70) >> 4 >= 2)) // If NBG0 2048/32786/16M mode is enabled, don't draw return; // Window Mode info.bEnWin0 = (Vdp2Regs->WCTLB >> 1) &0x01; info.WindowArea0 = (Vdp2Regs->WCTLB >> 0) & 0x01; info.bEnWin1 = (Vdp2Regs->WCTLB >> 3) &0x01; info.WindowArea1 = (Vdp2Regs->WCTLB >> 2) & 0x01; info.LogicWin = (Vdp2Regs->WCTLB >> 7 ) & 0x01; Vdp2SetGetColor( &info ); if( info.bEnWin0 || info.bEnWin1 ) YglStartWindow(&info,info.bEnWin0, info.WindowArea0,info.bEnWin1, info.WindowArea1,info.LogicWin); info.islinescroll = 0; info.linescrolltbl = 0; info.lineinc = 0; Vdp2DrawMap(&info, &texture); if( info.bEnWin0 || info.bEnWin1 ) YglEndWindow(&info); } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawNBG3(void) { vdp2draw_struct info; YglTexture texture; info.dst=0; info.uclipmode=0; info.enable = Vdp2Regs->BGON & 0x8; info.transparencyenable = !(Vdp2Regs->BGON & 0x800); info.specialprimode = (Vdp2Regs->SFPRMD >> 6) & 0x3; info.colornumber = (Vdp2Regs->CHCTLB & 0x20) >> 5; info.mapwh = 2; ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 6); info.x = - ((Vdp2Regs->SCXN3 & 0x7FF) % (512 * info.planew)); info.y = - ((Vdp2Regs->SCYN3 & 0x7FF) % (512 * info.planeh)); ReadPatternData(&info, Vdp2Regs->PNCN3, Vdp2Regs->CHCTLB & 0x10); info.specialcolormode = (Vdp2Regs->SFCCMD>>6) & 0x03; if (Vdp2Regs->CCCTL & 0x8) { info.alpha = ((~Vdp2Regs->CCRNB & 0x1F00) >> 5) + 0x7; if(Vdp2Regs->CCCTL & 0x100 && info.specialcolormode == 0 ) { info.blendmode=2; }else{ info.blendmode=1; } }else{ info.alpha = 0xFF; info.blendmode=0; } info.coloroffset = (Vdp2Regs->CRAOFA & 0x7000) >> 4; ReadVdp2ColorOffset(&info, 0x8); info.coordincx = info.coordincy = 1; info.priority = (Vdp2Regs->PRINB >> 8) & 0x7; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG3PlaneAddr; if (!(info.enable & Vdp2External.disptoggle) || (info.priority == 0) || (Vdp2Regs->BGON & 0x1 && (Vdp2Regs->CHCTLA & 0x70) >> 4 == 4) || // If NBG0 16M mode is enabled, don't draw (Vdp2Regs->BGON & 0x2 && (Vdp2Regs->CHCTLA & 0x3000) >> 12 >= 2)) // If NBG1 2048/32786 is enabled, don't draw return; // Window Mode info.bEnWin0 = (Vdp2Regs->WCTLB >> 9) &0x01; info.WindowArea0 = (Vdp2Regs->WCTLB >> 8) & 0x01; info.bEnWin1 = (Vdp2Regs->WCTLB >> 11) &0x01; info.WindowArea1 = (Vdp2Regs->WCTLB >> 10) & 0x01; info.LogicWin = (Vdp2Regs->WCTLB >> 15 ) & 0x01; Vdp2SetGetColor( &info ); if( info.bEnWin0 || info.bEnWin1 ) YglStartWindow(&info,info.bEnWin0, info.WindowArea0,info.bEnWin1, info.WindowArea1,info.LogicWin); info.islinescroll = 0; info.linescrolltbl = 0; info.lineinc = 0; Vdp2DrawMap(&info, &texture); if( info.bEnWin0 || info.bEnWin1 ) YglEndWindow(&info); } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawRBG0(void) { vdp2draw_struct info; YglTexture texture; vdp2rotationparameter_struct parameter; info.dst=0; info.uclipmode=0; info.enable = Vdp2Regs->BGON & 0x10; info.priority = Vdp2Regs->PRIR & 0x7; if (!(info.enable & Vdp2External.disptoggle) || (info.priority == 0)) return; info.transparencyenable = !(Vdp2Regs->BGON & 0x1000); info.specialprimode = (Vdp2Regs->SFPRMD >> 8) & 0x3; info.colornumber = (Vdp2Regs->CHCTLB & 0x7000) >> 12; info.bEnWin0 = (Vdp2Regs->WCTLC >> 1) & 0x01; info.WindowArea0 = (Vdp2Regs->WCTLC >> 0) & 0x01; info.bEnWin1 = (Vdp2Regs->WCTLC >> 3) & 0x01; info.WindowArea1 = (Vdp2Regs->WCTLC >> 2) & 0x01; info.LogicWin = (Vdp2Regs->WCTLC >> 7 ) & 0x01; info.islinescroll = 0; info.linescrolltbl = 0; info.lineinc = 0; Vdp2ReadRotationTable(0, ¶A); Vdp2ReadRotationTable(1, ¶B); paraA.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; paraB.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; paraA.charaddr = (Vdp2Regs->MPOFR & 0x7) * 0x20000; paraB.charaddr = (Vdp2Regs->MPOFR & 0x70) * 0x2000; ReadPlaneSizeR(¶A,Vdp2Regs->PLSZ >> 8); ReadPlaneSizeR(¶B,Vdp2Regs->PLSZ >> 12); if( paraA.coefdatasize == 2) { if(paraA.coefmode < 3 ) { info.GetKValueA = vdp2rGetKValue1W; }else{ info.GetKValueA = vdp2rGetKValue1Wm3; } }else{ if(paraA.coefmode < 3 ) { info.GetKValueA = vdp2rGetKValue2W; }else{ info.GetKValueA = vdp2rGetKValue2Wm3; } } if( paraB.coefdatasize == 2) { if(paraB.coefmode < 3 ) { info.GetKValueB = vdp2rGetKValue1W; }else{ info.GetKValueB = vdp2rGetKValue1Wm3; } }else{ if(paraB.coefmode < 3 ) { info.GetKValueB = vdp2rGetKValue2W; }else{ info.GetKValueB = vdp2rGetKValue2Wm3; } } if( Vdp2Regs->RPMD == 0x00 ) { if(!(paraA.coefenab)) { info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode00NoK; }else{ info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode00WithK; } }else if( Vdp2Regs->RPMD == 0x01 ) { if(!(paraB.coefenab)) { info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode01NoK; }else{ info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode01WithK; } }else if( Vdp2Regs->RPMD == 0x02 ) { if(!(paraA.coefenab)) { info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode02NoK; }else{ info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode02WithKA; } }else if( Vdp2Regs->RPMD == 0x03 ) { // Window0 if( ((Vdp2Regs->WCTLD >> 1) & 0x01) == 0x01 ) { info.pWinInfo = m_vWindinfo0; info.WindwAreaMode = (Vdp2Regs->WCTLD & 0x01) ; }else if( ((Vdp2Regs->WCTLD >> 3) & 0x01) == 0x01 ) { info.pWinInfo = m_vWindinfo1; info.WindwAreaMode = ((Vdp2Regs->WCTLD >>2)& 0x01) ; }else{ info.pWinInfo = m_vWindinfo0; info.WindwAreaMode = (Vdp2Regs->WCTLD & 0x01) ; } if( paraA.coefenab == 0 && paraB.coefenab == 0 ) { info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode03NoK; }else if( paraA.coefenab && paraB.coefenab == 0 ) { info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode03WithKA; }else if( paraA.coefenab == 0 && paraB.coefenab ) { info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode03WithKB; }else if( paraA.coefenab && paraB.coefenab ) { info.GetRParam = (Vdp2GetRParam_func) vdp2RGetParamMode03WithK; } } paraA.screenover = (Vdp2Regs->PLSZ >> 10) & 0x03; paraB.screenover = (Vdp2Regs->PLSZ >> 14) & 0x03; // Figure out which Rotation Parameter we're using switch (Vdp2Regs->RPMD & 0x3) { case 0: // Parameter A info.rotatenum = 0; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; break; case 1: // Parameter B info.rotatenum = 1; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; break; case 2: // Parameter A+B switched via coefficients // FIX ME(need to figure out which Parameter is being used) case 3: default: // Parameter A+B switched via rotation parameter window // FIX ME(need to figure out which Parameter is being used) VDP2LOG("Rotation Parameter Mode %d not supported!\n", Vdp2Regs->RPMD & 0x3); info.rotatenum = 0; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; break; } if((info.isbitmap = Vdp2Regs->CHCTLB & 0x200) != 0) { // Bitmap Mode ReadBitmapSize(&info, Vdp2Regs->CHCTLB >> 10, 0x1); if (info.rotatenum == 0) // Parameter A info.charaddr = (Vdp2Regs->MPOFR & 0x7) * 0x20000; else // Parameter B info.charaddr = (Vdp2Regs->MPOFR & 0x70) * 0x2000; info.paladdr = (Vdp2Regs->BMPNB & 0x7) << 4; info.flipfunction = 0; info.specialfunction = 0; } else { int i; // Tile Mode info.mapwh = 4; if (info.rotatenum == 0) // Parameter A ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 8); else // Parameter B ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 12); ReadPatternData(&info, Vdp2Regs->PNCR, Vdp2Regs->CHCTLB & 0x100); paraA.ShiftPaneX = 8 + paraA.planew; paraA.ShiftPaneY = 8 + paraA.planeh; paraB.ShiftPaneX = 8 + paraB.planew; paraB.ShiftPaneY = 8 + paraB.planeh; paraA.MskH = (8 * 64 * paraA.planew)-1; paraA.MskV = (8 * 64 * paraA.planeh)-1; paraB.MskH = (8 * 64 * paraB.planew)-1; paraB.MskV = (8 * 64 * paraB.planeh)-1; paraA.MaxH = 8 * 64 * paraA.planew * 4; paraA.MaxV = 8 * 64 * paraA.planeh * 4; paraB.MaxH = 8 * 64 * paraB.planew * 4; paraB.MaxV = 8 * 64 * paraB.planeh * 4; if( paraA.screenover == OVERMODE_512 ) { paraA.MaxH = 512; paraA.MaxV = 512; } if( paraB.screenover == OVERMODE_512 ) { paraB.MaxH = 512; paraB.MaxV = 512; } for( i=0; i<16; i++ ) { paraA.PlaneAddr(&info,i); paraA.PlaneAddrv[i] = info.addr; paraB.PlaneAddr(&info,i); paraB.PlaneAddrv[i] = info.addr; } } info.specialcolormode = (Vdp2Regs->SFCCMD>>8) & 0x03; info.blendmode=0; if( (Vdp2Regs->LNCLEN & 0x10) == 0x00 ) { if (Vdp2Regs->CCCTL & 0x10) { info.alpha = ((~Vdp2Regs->CCRR & 0x1F) << 3) + 0x7; if(Vdp2Regs->CCCTL & 0x100 && info.specialcolormode == 0 ) { info.blendmode=2; }else{ info.blendmode=1; } }else{ info.alpha = 0xFF; } info.LineColorBase = 0x00; paraA.lineaddr = 0xFFFFFFFF; paraB.lineaddr = 0xFFFFFFFF; }else{ info.alpha = 0xFF; info.LineColorBase = (Vdp2Regs->LCTA.all) << 1; if( info.LineColorBase >= 0x80000 ) info.LineColorBase = 0x00; paraA.lineaddr = 0xFFFFFFFF; paraB.lineaddr = 0xFFFFFFFF; } info.coloroffset = (Vdp2Regs->CRAOFB & 0x7) << 8; ReadVdp2ColorOffset(&info, 0x10); info.coordincx = info.coordincy = 1; // Window Mode info.bEnWin0 = (Vdp2Regs->WCTLC >> 1) &0x01; info.WindowArea0 = (Vdp2Regs->WCTLC >> 0) & 0x01; info.bEnWin1 = (Vdp2Regs->WCTLC >> 3) &0x01; info.WindowArea1 = (Vdp2Regs->WCTLC >> 2) & 0x01; info.LogicWin = (Vdp2Regs->WCTLC >> 7 ) & 0x01; Vdp2SetGetColor( &info ); if( info.bEnWin0 || info.bEnWin1 ) YglStartWindow(&info,info.bEnWin0, info.WindowArea0,info.bEnWin1, info.WindowArea1,info.LogicWin); Vdp2DrawRotation(&info, ¶meter, &texture); if( info.bEnWin0 || info.bEnWin1 ) YglEndWindow(&info); } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp2DrawScreens(void) { VIDOGLVdp2SetResolution(Vdp2Regs->TVMD); Vdp2GenerateWindowInfo(); Vdp2DrawBackScreen(); Vdp2DrawLineColorScreen(); Vdp2DrawNBG3(); Vdp2DrawNBG2(); Vdp2DrawNBG1(); Vdp2DrawNBG0(); Vdp2DrawRBG0(); } ////////////////////////////////////////////////////////////////////////////// void VIDOGLVdp2SetResolution(u16 TVMD) { int width=0, height=0; int wratio=1, hratio=1; // Horizontal Resolution switch (TVMD & 0x7) { case 0: width = 320; wratio = 1; break; case 1: width = 352; wratio = 1; break; case 2: width = 640; wratio = 2; break; case 3: width = 704; wratio = 2; break; case 4: width = 320; wratio = 1; break; case 5: width = 352; wratio = 1; break; case 6: width = 640; wratio = 2; break; case 7: width = 704; wratio = 2; break; } // Vertical Resolution switch ((TVMD >> 4) & 0x3) { case 0: height = 224; break; case 1: height = 240; break; case 2: height = 256; break; default: break; } hratio = 1; // Check for interlace switch ((TVMD >> 6) & 0x3) { case 3: // Double-density Interlace height *= 2; hratio = 2; break; case 2: // Single-density Interlace case 0: // Non-interlace default: break; } SetSaturnResolution(width, height); Vdp1SetTextureRatio(wratio, hratio); } ////////////////////////////////////////////////////////////////////////////// void YglGetGlSize(int *width, int *height) { *width = GlWidth; *height = GlHeight; } vdp2rotationparameter_struct * FASTCALL vdp2rGetKValue2W( vdp2rotationparameter_struct * param, int index ) { float kval; int kdata; kdata = T1ReadLong(Vdp2Ram, (param->coeftbladdr&0x7FFFF) + (index<<2) ); if( kdata & 0x80000000 ) return NULL; kval = (float) (int) ((kdata & 0x00FFFFFF) | (kdata & 0x00800000 ? 0xFF800000 : 0x00000000)) / 65536.0f; switch( param->coefmode ) { case 0: param->kx = kval; param->ky = kval; break; case 1: param->kx = kval; break; case 2: param->ky = kval; break; } param->lineaddr = (kdata >> 24)&0x7F; return param; } vdp2rotationparameter_struct * FASTCALL vdp2rGetKValue1W( vdp2rotationparameter_struct * param, int index ) { float kval; u16 kdata; kdata = T1ReadWord(Vdp2Ram, param->coeftbladdr + (index<<1) ); if( kdata & 0x8000 ) return NULL; kval = (float) (signed) ((kdata & 0x7FFF) | (kdata & 0x4000 ? 0x8000 : 0x0000)) / 1024.0f; switch( param->coefmode ) { case 0: param->kx = kval; param->ky = kval; break; case 1: param->kx = kval; break; case 2: param->ky = kval; break; } return param; } vdp2rotationparameter_struct * FASTCALL vdp2rGetKValue2Wm3( vdp2rotationparameter_struct * param, int index ) { return param; // ToDo: } vdp2rotationparameter_struct * FASTCALL vdp2rGetKValue1Wm3( vdp2rotationparameter_struct * param, int index ) { return param; // ToDo: } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode00NoK( vdp2draw_struct * info, int h, int v ) { return ¶A; } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode00WithK( vdp2draw_struct * info,int h, int v ) { h = paraA.KtablV+(paraA.deltaKAx * h); return info->GetKValueA( ¶A, h ); } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode01NoK( vdp2draw_struct * info,int h, int v ) { return ¶B; } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode01WithK( vdp2draw_struct * info,int h, int v ) { h = (paraB.KtablV+(paraB.deltaKAx * h)); return info->GetKValueB( ¶B, h ); } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode02NoK( vdp2draw_struct * info,int h, int v ) { return ¶A; } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode02WithKA( vdp2draw_struct * info,int h, int v ) { h = (paraA.KtablV+(paraA.deltaKAx * h)); if( info->GetKValueA( ¶A, h ) == NULL ) { return ¶B; } return ¶A; } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode02WithKB( vdp2draw_struct * info,int h, int v ) { return ¶A; } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode03NoK( vdp2draw_struct * info,int h, int v ) { if( info->WindwAreaMode == 0 ) { if( info->pWinInfo[v].WinShowLine == 0 ) { return (¶B); }else{ if( h < info->pWinInfo[v].WinHStart || h >= info->pWinInfo[v].WinHEnd ) { return (¶B); }else{ return (¶A); } } } else { if( info->pWinInfo[v].WinShowLine == 0 ) { return (¶B); }else{ if( h < info->pWinInfo[v].WinHStart || h >= info->pWinInfo[v].WinHEnd ) { return (¶A); }else{ return (¶B); } } } return NULL; } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode03WithKA( vdp2draw_struct * info,int h, int v ) { if( info->WindwAreaMode == 0 ) { if( info->pWinInfo[v].WinShowLine == 0 ) { return (¶B); }else{ if( h < info->pWinInfo[v].WinHStart || h >= info->pWinInfo[v].WinHEnd ) { return (¶B); }else{ h = (paraA.KtablV+(paraA.deltaKAx * h)); return info->GetKValueA( ¶A, h ); } } }else{ if( info->pWinInfo[v].WinShowLine == 0 ) { h = (paraA.KtablV+(paraA.deltaKAx * h)); return info->GetKValueA( ¶A, h ); }else{ if( h < info->pWinInfo[v].WinHStart || h >= info->pWinInfo[v].WinHEnd ) { h = (paraA.KtablV+(paraA.deltaKAx * h)); return info->GetKValueA( ¶A, h ); }else{ return (¶B); } } } return NULL; } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode03WithKB( vdp2draw_struct * info,int h, int v ) { if( info->WindwAreaMode == 0 ) { if( info->pWinInfo[v].WinShowLine == 0 ) { h = (paraB.KtablV+(paraB.deltaKAx * h)); return info->GetKValueB( ¶B, h ); }else{ if( h < info->pWinInfo[v].WinHStart || h >= info->pWinInfo[v].WinHEnd ) { h = (paraB.KtablV+(paraB.deltaKAx * h)); return info->GetKValueB( ¶B, h ); }else{ return ¶A; } } }else{ { if( info->pWinInfo[v].WinShowLine == 0 ) { return ¶A; }else{ if( h < info->pWinInfo[v].WinHStart || h >= info->pWinInfo[v].WinHEnd ) { h = (paraA.KtablV+(paraA.deltaKAx * h)); return info->GetKValueA( ¶A, h ); }else{ h = (paraB.KtablV+(paraB.deltaKAx * h)); return info->GetKValueB( ¶B, h ); } } } } return NULL; } vdp2rotationparameter_struct * FASTCALL vdp2RGetParamMode03WithK( vdp2draw_struct * info,int h, int v ) { if( info->WindwAreaMode == 0 ) { if( info->pWinInfo[v].WinShowLine == 0 ) { h = (paraB.KtablV+(paraB.deltaKAx * h)); return info->GetKValueB( ¶B, h ); }else{ if( h < info->pWinInfo[v].WinHStart || h >= info->pWinInfo[v].WinHEnd ) { h = (paraB.KtablV+(paraB.deltaKAx * h)); return info->GetKValueB( ¶B, h ); }else{ h = (paraA.KtablV+(paraA.deltaKAx * h)); return info->GetKValueA( ¶A, h ); } } }else{ if( info->pWinInfo[v].WinShowLine == 0 ) { h = (paraB.KtablV+(paraB.deltaKAx * h)); return info->GetKValueB( ¶B, h ); }else{ if( h < info->pWinInfo[v].WinHStart || h >= info->pWinInfo[v].WinHEnd ) { h = (paraB.KtablV+(paraB.deltaKAx * h)); return info->GetKValueB( ¶B, h ); }else{ h = (paraA.KtablV+(paraA.deltaKAx * h)); return info->GetKValueA( ¶A, h ); } } } return NULL; } #endif yabause-0.9.13.1/src/smpc.h000644 001750 001750 00000005112 12256006124 017305 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2005 Guillaume Duhamel Copyright 2004-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SMPC_H #define SMPC_H #include "memory.h" #define REGION_AUTODETECT 0 #define REGION_JAPAN 1 #define REGION_ASIANTSC 2 #define REGION_NORTHAMERICA 4 #define REGION_CENTRALSOUTHAMERICANTSC 5 #define REGION_KOREA 6 #define REGION_ASIAPAL 10 #define REGION_EUROPE 12 #define REGION_CENTRALSOUTHAMERICAPAL 13 typedef struct { u8 IREG[7]; u8 padding[8]; u8 COMREG; u8 OREG[32]; u8 SR; u8 SF; u8 padding2[8]; u8 PDR[2]; u8 DDR[2]; u8 IOSEL; u8 EXLE; } Smpc; extern Smpc * SmpcRegs; extern u8 * SmpcRegsT; typedef struct { int offset; int size; u8 data[256]; } PortData_struct; typedef struct { u8 dotsel; // 0 -> 320 | 1 -> 352 u8 mshnmi; u8 sndres; u8 cdres; u8 sysres; u8 resb; u8 ste; u8 resd; u8 intback; u8 intbackIreg0; u8 firstPeri; u8 regionid; u8 regionsetting; u8 SMEM[4]; s32 timing; PortData_struct port1; PortData_struct port2; u8 clocksync; u32 basetime; // Safe until early 2106. After that you're on your own (: } SmpcInternal; extern SmpcInternal * SmpcInternalVars; int SmpcInit(u8 regionid, int clocksync, u32 basetime); void SmpcDeInit(void); void SmpcRecheckRegion(void); void SmpcReset(void); void SmpcResetButton(void); void SmpcExec(s32 t); void SmpcINTBACKEnd(void); void SmpcCKCHG320(void); void SmpcCKCHG352(void); u8 FASTCALL SmpcReadByte(u32); u16 FASTCALL SmpcReadWord(u32); u32 FASTCALL SmpcReadLong(u32); void FASTCALL SmpcWriteByte(u32, u8); void FASTCALL SmpcWriteWord(u32, u16); void FASTCALL SmpcWriteLong(u32, u32); int SmpcSaveState(FILE *fp); int SmpcLoadState(FILE *fp, int version, int size); #endif yabause-0.9.13.1/src/yabause.h000644 001750 001750 00000005306 12256006124 020001 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2005 Guillaume Duhamel Copyright 2004-2006 Theo Berkau Copyright 2006 Anders Montonen This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YABAUSE_H #define YABAUSE_H #include "core.h" typedef struct { int percoretype; int sh2coretype; int vidcoretype; int sndcoretype; int m68kcoretype; int cdcoretype; int carttype; u8 regionid; const char *biospath; const char *cdpath; const char *buppath; const char *mpegpath; const char *cartpath; const char *netlinksetting; int videoformattype; int frameskip; int clocksync; // 1 = sync internal clock to emulation, 0 = realtime clock u32 basetime; // Initial time in clocksync mode (0 = start w/ system time) int usethreads; int osdcoretype; } yabauseinit_struct; #define CLKTYPE_26MHZ 0 #define CLKTYPE_28MHZ 1 #define VIDEOFORMATTYPE_NTSC 0 #define VIDEOFORMATTYPE_PAL 1 #ifndef NO_CLI void print_usage(const char *program_name); #endif void YabauseChangeTiming(int freqtype); int YabauseInit(yabauseinit_struct *init); void YabauseDeInit(void); void YabauseSetDecilineMode(int on); void YabauseResetNoLoad(void); void YabauseReset(void); void YabauseResetButton(void); int YabauseExec(void); void YabauseStartSlave(void); void YabauseStopSlave(void); u64 YabauseGetTicks(void); void YabauseSetVideoFormat(int type); void YabauseSpeedySetup(void); int YabauseQuickLoadGame(void); #define YABSYS_TIMING_BITS 20 #define YABSYS_TIMING_MASK ((1 << YABSYS_TIMING_BITS) - 1) typedef struct { int DecilineMode; int DecilineCount; int LineCount; int VBlankLineCount; int MaxLineCount; u32 DecilineStop; // Fixed point u32 SH2CycleFrac; // Fixed point u32 DecilineUsec; // Fixed point u32 UsecFrac; // Fixed point int CurSH2FreqType; int IsPal; u8 UseThreads; u8 IsSSH2Running; u64 OneFrameTime; u64 tickfreq; int emulatebios; int usequickload; } yabsys_struct; extern yabsys_struct yabsys; int YabauseEmulate(void); #endif yabause-0.9.13.1/src/scu.c000644 001750 001750 00000241321 12256006135 017136 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2006 Guillaume Duhamel Copyright 2005-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "scu.h" #include "debug.h" #include "memory.h" #include "sh2core.h" #include "yabause.h" #ifdef OPTIMIZED_DMA # include "cs2.h" # include "scsp.h" # include "vdp1.h" # include "vdp2.h" #endif Scu * ScuRegs; scudspregs_struct * ScuDsp; scubp_struct * ScuBP; static void ScuTestInterruptMask(void); ////////////////////////////////////////////////////////////////////////////// int ScuInit(void) { int i; if ((ScuRegs = (Scu *) calloc(1, sizeof(Scu))) == NULL) return -1; if ((ScuDsp = (scudspregs_struct *) calloc(1, sizeof(scudspregs_struct))) == NULL) return -1; if ((ScuBP = (scubp_struct *) calloc(1, sizeof(scubp_struct))) == NULL) return -1; for (i = 0; i < MAX_BREAKPOINTS; i++) ScuBP->codebreakpoint[i].addr = 0xFFFFFFFF; ScuBP->numcodebreakpoints = 0; ScuBP->BreakpointCallBack=NULL; ScuBP->inbreakpoint=0; return 0; } ////////////////////////////////////////////////////////////////////////////// void ScuDeInit(void) { if (ScuRegs) free(ScuRegs); ScuRegs = NULL; if (ScuDsp) free(ScuDsp); ScuDsp = NULL; if (ScuBP) free(ScuBP); ScuBP = NULL; } ////////////////////////////////////////////////////////////////////////////// void ScuReset(void) { ScuRegs->D0AD = ScuRegs->D1AD = ScuRegs->D2AD = 0x101; ScuRegs->D0EN = ScuRegs->D1EN = ScuRegs->D2EN = 0x0; ScuRegs->D0MD = ScuRegs->D1MD = ScuRegs->D2MD = 0x7; ScuRegs->DSTP = 0x0; ScuRegs->DSTA = 0x0; ScuDsp->ProgControlPort.all = 0; ScuRegs->PDA = 0x0; ScuRegs->T1MD = 0x0; ScuRegs->IMS = 0xBFFF; ScuRegs->IST = 0x0; ScuRegs->AIACK = 0x0; ScuRegs->ASR0 = ScuRegs->ASR1 = 0x0; ScuRegs->AREF = 0x0; ScuRegs->RSEL = 0x0; ScuRegs->VER = 0x04; // Looks like all consumer saturn's used at least version 4 ScuRegs->timer0 = 0; ScuRegs->timer1 = 0; memset((void *)ScuRegs->interrupts, 0, sizeof(scuinterrupt_struct) * 30); ScuRegs->NumberOfInterrupts = 0; } ////////////////////////////////////////////////////////////////////////////// #ifdef OPTIMIZED_DMA // Table of memory types for DMA optimization, in 512k (1<<19 byte) units: // 0x00 = no special handling // 0x12 = VDP1/2 RAM (8-bit organized, 16-bit copy unit) // 0x22 = M68K RAM (16-bit organized, 16-bit copy unit) // 0x23 = VDP2 color RAM (16-bit organized, 16-bit copy unit) // 0x24 = SH-2 RAM (16-bit organized, 32-bit copy unit) static const u8 DMAMemoryType[0x20000000>>19] = { [0x00200000>>19] = 0x24, [0x00280000>>19] = 0x24, [0x05A00000>>19] = 0x22, [0x05A80000>>19] = 0x22, [0x05C00000>>19] = 0x12, [0x05C00000>>19] = 0x12, [0x05E00000>>19] = 0x12, [0x05E80000>>19] = 0x12, [0x05F00000>>19] = 0x23, [0x06000000>>19] = 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, }; // Function to return the native pointer for an optimized address #ifdef __GNUC__ __attribute__((always_inline)) // Force it inline for better performance #endif static INLINE void *DMAMemoryPointer(u32 address) { u32 page = (address & 0x1FF80000) >> 19; switch (DMAMemoryType[page]) { case 0x12: switch (page) { case 0x05C00000>>19: return &Vdp1Ram[address & 0x7FFFF]; case 0x05E00000>>19: // fall through case 0x05E80000>>19: return &Vdp2Ram[address & 0x7FFFF]; default: return NULL; } case 0x22: return &SoundRam[address & 0x7FFFF]; case 0x23: return &Vdp2ColorRam[address & 0xFFF]; case 0x24: if (page == 0x00200000>>19) { return &LowWram[address & 0xFFFFF]; } else { return &HighWram[address & 0xFFFFF]; } default: return NULL; } } #endif // OPTIMIZED_DMA static void DoDMA(u32 ReadAddress, unsigned int ReadAdd, u32 WriteAddress, unsigned int WriteAdd, u32 TransferSize) { if (ReadAdd == 0) { // DMA fill #ifdef OPTIMIZED_DMA if (ReadAddress == 0x25818000 && WriteAdd == 4) { // Reading from the CD buffer, so optimize if possible. if ((WriteAddress & 0x1E000000) == 0x06000000) { Cs2RapidCopyT2(&HighWram[WriteAddress & 0xFFFFF], TransferSize/4); SH2WriteNotify(WriteAddress, TransferSize); return; } else if ((WriteAddress & 0x1FF00000) == 0x00200000) { Cs2RapidCopyT2(&LowWram[WriteAddress & 0xFFFFF], TransferSize/4); SH2WriteNotify(WriteAddress, TransferSize); return; } } #endif // Is it a constant source or a register whose value can change from // read to read? int constant_source = ((ReadAddress & 0x1FF00000) == 0x00200000) || ((ReadAddress & 0x1E000000) == 0x06000000) || ((ReadAddress & 0x1FF00000) == 0x05A00000) || ((ReadAddress & 0x1DF00000) == 0x05C00000); if ((WriteAddress & 0x1FFFFFFF) >= 0x5A00000 && (WriteAddress & 0x1FFFFFFF) < 0x5FF0000) { // Fill a 32-bit value in 16-bit units. We have to be careful to // avoid misaligned 32-bit accesses, because some hardware (e.g. // PSP) crashes on such accesses. if (constant_source) { u32 counter = 0; u32 val; if (ReadAddress & 2) { // Avoid misaligned access val = MappedMemoryReadWord(ReadAddress) << 16 | MappedMemoryReadWord(ReadAddress+2); } else { val = MappedMemoryReadLong(ReadAddress); } while (counter < TransferSize) { MappedMemoryWriteWord(WriteAddress, (u16)(val >> 16)); WriteAddress += WriteAdd; MappedMemoryWriteWord(WriteAddress, (u16)val); WriteAddress += WriteAdd; counter += 4; } } else { u32 counter = 0; while (counter < TransferSize) { u32 tmp = MappedMemoryReadLong(ReadAddress); MappedMemoryWriteWord(WriteAddress, (u16)(tmp >> 16)); WriteAddress += WriteAdd; MappedMemoryWriteWord(WriteAddress, (u16)tmp); WriteAddress += WriteAdd; ReadAddress += ReadAdd; counter += 4; } } } else { // Fill in 32-bit units (always aligned). u32 start = WriteAddress; if (constant_source) { u32 val = MappedMemoryReadLong(ReadAddress); u32 counter = 0; while (counter < TransferSize) { MappedMemoryWriteLong(WriteAddress, val); ReadAddress += ReadAdd; WriteAddress += WriteAdd; counter += 4; } } else { u32 counter = 0; while (counter < TransferSize) { MappedMemoryWriteLong(WriteAddress, MappedMemoryReadLong(ReadAddress)); ReadAddress += ReadAdd; WriteAddress += WriteAdd; counter += 4; } } // Inform the SH-2 core in case it was a write to main RAM. SH2WriteNotify(start, WriteAddress - start); } } else { // DMA copy #ifdef OPTIMIZED_DMA int source_type = DMAMemoryType[(ReadAddress & 0x1FF80000) >> 19]; int dest_type = DMAMemoryType[(WriteAddress & 0x1FF80000) >> 19]; if (WriteAdd == ((dest_type & 0x2) ? 2 : 4)) { // Writes don't skip any bytes, so use an optimized copy algorithm // if possible. const u8 *source_ptr = DMAMemoryPointer(ReadAddress); u8 *dest_ptr = DMAMemoryPointer(WriteAddress); # ifdef WORDS_BIGENDIAN if ((source_type & 0x30) && (dest_type & 0x30)) { // Source and destination are both directly accessible. memcpy(dest_ptr, source_ptr, TransferSize); if (dest_type == 0x24) { SH2WriteNotify(WriteAddress, TransferSize); } else if (dest_type == 0x22) { M68KWriteNotify(WriteAddress & 0x7FFFF, TransferSize); } return; } # else // !WORDS_BIGENDIAN if (source_type & dest_type & 0x10) { // Source and destination are both 8-bit organized. memcpy(dest_ptr, source_ptr, TransferSize); return; } else if (source_type & dest_type & 0x20) { // Source and destination are both 16-bit organized. memcpy(dest_ptr, source_ptr, TransferSize); if (dest_type == 0x24) { SH2WriteNotify(WriteAddress, TransferSize); } else if (dest_type == 0x22) { M68KWriteNotify(WriteAddress & 0x7FFFF, TransferSize); } return; } else if ((source_type | dest_type) >> 4 == 0x3) { // Need to convert between 8-bit and 16-bit organization. if ((ReadAddress | WriteAddress) & 2) { // Avoid misaligned access const u16 *source_16 = (u16 *)source_ptr; u16 *dest_16 = (u16 *)dest_ptr; u32 counter; for (counter = 0; counter < TransferSize-6; counter += 8, source_16 += 4, dest_16 += 4) { // Use "unsigned int" rather than "u16" because some // compilers try too hard to keep the high bits zero, // thus wasting cycles on every iteration. unsigned int val0 = BSWAP16(source_16[0]); unsigned int val1 = BSWAP16(source_16[1]); unsigned int val2 = BSWAP16(source_16[2]); unsigned int val3 = BSWAP16(source_16[3]); dest_16[0] = val0; dest_16[1] = val1; dest_16[2] = val2; dest_16[3] = val3; } for (; counter < TransferSize; counter += 2, source_16++, dest_16++) { *dest_16 = BSWAP16(*source_16); } } else { // 32-bit aligned accesses possible const u32 *source_32 = (u32 *)source_ptr; u32 *dest_32 = (u32 *)dest_ptr; u32 counter; for (counter = 0; counter < TransferSize-12; counter += 16, source_32 += 4, dest_32 += 4) { u32 val0 = BSWAP16(source_32[0]); u32 val1 = BSWAP16(source_32[1]); u32 val2 = BSWAP16(source_32[2]); u32 val3 = BSWAP16(source_32[3]); dest_32[0] = val0; dest_32[1] = val1; dest_32[2] = val2; dest_32[3] = val3; } for (; counter < TransferSize; counter += 4, source_32++, dest_32++) { *dest_32 = BSWAP16(*source_32); } } return; } # endif // WORDS_BIGENDIAN } #endif // OPTIMIZED_DMA if ((WriteAddress & 0x1FFFFFFF) >= 0x5A00000 && (WriteAddress & 0x1FFFFFFF) < 0x5FF0000) { // Copy in 16-bit units, avoiding misaligned accesses. u32 counter = 0; if (ReadAddress & 2) { // Avoid misaligned access u16 tmp = MappedMemoryReadWord(ReadAddress); MappedMemoryWriteWord(WriteAddress, tmp); WriteAddress += WriteAdd; ReadAddress += 2; counter += 2; } if (TransferSize >= 3) { while (counter < TransferSize-2) { u32 tmp = MappedMemoryReadLong(ReadAddress); MappedMemoryWriteWord(WriteAddress, (u16)(tmp >> 16)); WriteAddress += WriteAdd; MappedMemoryWriteWord(WriteAddress, (u16)tmp); WriteAddress += WriteAdd; ReadAddress += 4; counter += 4; } } if (counter < TransferSize) { u16 tmp = MappedMemoryReadWord(ReadAddress); MappedMemoryWriteWord(WriteAddress, tmp); WriteAddress += WriteAdd; ReadAddress += 2; counter += 2; } } else { u32 counter = 0; u32 start = WriteAddress; while (counter < TransferSize) { MappedMemoryWriteLong(WriteAddress, MappedMemoryReadLong(ReadAddress)); ReadAddress += 4; WriteAddress += WriteAdd; counter += 4; } /* Inform the SH-2 core in case it was a write to main RAM */ SH2WriteNotify(start, WriteAddress - start); } } // Fill / copy } ////////////////////////////////////// static void FASTCALL ScuDMA(scudmainfo_struct *dmainfo) { u8 ReadAdd, WriteAdd; if (dmainfo->AddValue & 0x100) ReadAdd = 4; else ReadAdd = 0; switch(dmainfo->AddValue & 0x7) { case 0x0: WriteAdd = 0; break; case 0x1: WriteAdd = 2; break; case 0x2: WriteAdd = 4; break; case 0x3: WriteAdd = 8; break; case 0x4: WriteAdd = 16; break; case 0x5: WriteAdd = 32; break; case 0x6: WriteAdd = 64; break; case 0x7: WriteAdd = 128; break; default: WriteAdd = 0; break; } if (dmainfo->ModeAddressUpdate & 0x1000000) { // Indirect DMA for (;;) { u32 ThisTransferSize = MappedMemoryReadLong(dmainfo->WriteAddress); u32 ThisWriteAddress = MappedMemoryReadLong(dmainfo->WriteAddress+4); u32 ThisReadAddress = MappedMemoryReadLong(dmainfo->WriteAddress+8); DoDMA(ThisReadAddress & 0x7FFFFFFF, ReadAdd, ThisWriteAddress, WriteAdd, ThisTransferSize); if (ThisReadAddress & 0x80000000) break; dmainfo->WriteAddress+= 0xC; } switch(dmainfo->mode) { case 0: ScuSendLevel0DMAEnd(); break; case 1: ScuSendLevel1DMAEnd(); break; case 2: ScuSendLevel2DMAEnd(); break; } } else { // Direct DMA if (dmainfo->mode > 0) { dmainfo->TransferNumber &= 0xFFF; if (dmainfo->TransferNumber == 0) dmainfo->TransferNumber = 0x1000; } else { if (dmainfo->TransferNumber == 0) dmainfo->TransferNumber = 0x100000; } DoDMA(dmainfo->ReadAddress, ReadAdd, dmainfo->WriteAddress, WriteAdd, dmainfo->TransferNumber); switch(dmainfo->mode) { case 0: ScuSendLevel0DMAEnd(); break; case 1: ScuSendLevel1DMAEnd(); break; case 2: ScuSendLevel2DMAEnd(); break; } } } ////////////////////////////////////////////////////////////////////////////// static u32 readgensrc(u8 num) { u32 val; switch(num) { case 0x0: // M0 return ScuDsp->MD[0][ScuDsp->CT[0]]; case 0x1: // M1 return ScuDsp->MD[1][ScuDsp->CT[1]]; case 0x2: // M2 return ScuDsp->MD[2][ScuDsp->CT[2]]; case 0x3: // M3 return ScuDsp->MD[3][ScuDsp->CT[3]]; case 0x4: // MC0 val = ScuDsp->MD[0][ScuDsp->CT[0]]; ScuDsp->CT[0]++; return val; case 0x5: // MC1 val = ScuDsp->MD[1][ScuDsp->CT[1]]; ScuDsp->CT[1]++; return val; case 0x6: // MC2 val = ScuDsp->MD[2][ScuDsp->CT[2]]; ScuDsp->CT[2]++; return val; case 0x7: // MC3 val = ScuDsp->MD[3][ScuDsp->CT[3]]; ScuDsp->CT[3]++; return val; case 0x9: // ALL return (u32)ScuDsp->ALU.part.L; case 0xA: // ALH return (u32)ScuDsp->ALU.part.H; default: break; } return 0; } ////////////////////////////////////////////////////////////////////////////// static void writed1busdest(u8 num, u32 val) { switch(num) { case 0x0: ScuDsp->MD[0][ScuDsp->CT[0]] = val; ScuDsp->CT[0]++; return; case 0x1: ScuDsp->MD[1][ScuDsp->CT[1]] = val; ScuDsp->CT[1]++; return; case 0x2: ScuDsp->MD[2][ScuDsp->CT[2]] = val; ScuDsp->CT[2]++; return; case 0x3: ScuDsp->MD[3][ScuDsp->CT[3]] = val; ScuDsp->CT[3]++; return; case 0x4: ScuDsp->RX = val; return; case 0x5: ScuDsp->P.all = (signed)val; return; case 0x6: ScuDsp->RA0 = val; return; case 0x7: ScuDsp->WA0 = val; return; case 0xA: ScuDsp->LOP = (u16)val; return; case 0xB: ScuDsp->TOP = (u8)val; return; case 0xC: ScuDsp->CT[0] = (u8)val; return; case 0xD: ScuDsp->CT[1] = (u8)val; return; case 0xE: ScuDsp->CT[2] = (u8)val; return; case 0xF: ScuDsp->CT[3] = (u8)val; return; default: break; } } ////////////////////////////////////////////////////////////////////////////// static void writeloadimdest(u8 num, u32 val) { switch(num) { case 0x0: // MC0 ScuDsp->MD[0][ScuDsp->CT[0]] = val; ScuDsp->CT[0]++; return; case 0x1: // MC1 ScuDsp->MD[1][ScuDsp->CT[1]] = val; ScuDsp->CT[1]++; return; case 0x2: // MC2 ScuDsp->MD[2][ScuDsp->CT[2]] = val; ScuDsp->CT[2]++; return; case 0x3: // MC3 ScuDsp->MD[3][ScuDsp->CT[3]] = val; ScuDsp->CT[3]++; return; case 0x4: // RX ScuDsp->RX = val; return; case 0x5: // PL ScuDsp->P.all = (signed)val; return; case 0x6: // RA0 ScuDsp->RA0 = val; return; case 0x7: // WA0 ScuDsp->WA0 = val; return; case 0xA: // LOP ScuDsp->LOP = (u16)val; return; case 0xC: // PC->TOP, PC ScuDsp->TOP = ScuDsp->PC; ScuDsp->jmpaddr = val; ScuDsp->delayed = 0; return; default: break; } } ////////////////////////////////////////////////////////////////////////////// static u32 readdmasrc(u8 num, u8 add) { u32 val; switch(num) { case 0x0: // M0 val = ScuDsp->MD[0][ScuDsp->CT[0]]; ScuDsp->CT[0]+=add; return val; case 0x1: // M1 val = ScuDsp->MD[1][ScuDsp->CT[1]]; ScuDsp->CT[1]+=add; return val; case 0x2: // M2 val = ScuDsp->MD[2][ScuDsp->CT[2]]; ScuDsp->CT[2]+=add; return val; case 0x3: // M3 val = ScuDsp->MD[3][ScuDsp->CT[3]]; ScuDsp->CT[3]+=add; return val; default: break; } return 0; } ////////////////////////////////////////////////////////////////////////////// static void writedmadest(u8 num, u32 val, u8 add) { switch(num) { case 0x0: // M0 ScuDsp->MD[0][ScuDsp->CT[0]] = val; ScuDsp->CT[0]+=add; return; case 0x1: // M1 ScuDsp->MD[1][ScuDsp->CT[1]] = val; ScuDsp->CT[1]+=add; return; case 0x2: // M2 ScuDsp->MD[2][ScuDsp->CT[2]] = val; ScuDsp->CT[2]+=add; return; case 0x3: // M3 ScuDsp->MD[3][ScuDsp->CT[3]] = val; ScuDsp->CT[3]+=add; return; case 0x4: // Program Ram LOG("scu\t: DMA Program writes not implemented\n"); // ScuDsp->ProgramRam[?] = val; // ?? += add; return; default: break; } } ////////////////////////////////////////////////////////////////////////////// void ScuExec(u32 timing) { int i; // is dsp executing? if (ScuDsp->ProgControlPort.part.EX) { while (timing > 0) { u32 instruction; // Make sure it isn't one of our breakpoints for (i=0; i < ScuBP->numcodebreakpoints; i++) { if ((ScuDsp->PC == ScuBP->codebreakpoint[i].addr) && ScuBP->inbreakpoint == 0) { ScuBP->inbreakpoint = 1; if (ScuBP->BreakpointCallBack) ScuBP->BreakpointCallBack(ScuBP->codebreakpoint[i].addr); ScuBP->inbreakpoint = 0; } } instruction = ScuDsp->ProgramRam[ScuDsp->PC]; // ALU commands switch (instruction >> 26) { case 0x0: // NOP ScuDsp->ALU.all = 0; break; case 0x1: // AND ScuDsp->ALU.part.L = ScuDsp->AC.part.L & ScuDsp->P.part.L; if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; if ((signed)ScuDsp->ALU.part.L < 0) ScuDsp->ProgControlPort.part.S = 1; else ScuDsp->ProgControlPort.part.S = 0; ScuDsp->ProgControlPort.part.C = 0; break; case 0x2: // OR ScuDsp->ALU.part.L = ScuDsp->AC.part.L | ScuDsp->P.part.L; if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; if ((signed)ScuDsp->ALU.part.L < 0) ScuDsp->ProgControlPort.part.S = 1; else ScuDsp->ProgControlPort.part.S = 0; ScuDsp->ProgControlPort.part.C = 0; break; case 0x3: // XOR ScuDsp->ALU.part.L = ScuDsp->AC.part.L ^ ScuDsp->P.part.L; if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; if ((signed)ScuDsp->ALU.part.L < 0) ScuDsp->ProgControlPort.part.S = 1; else ScuDsp->ProgControlPort.part.S = 0; ScuDsp->ProgControlPort.part.C = 0; break; case 0x4: // ADD ScuDsp->ALU.part.L = (unsigned)((signed)ScuDsp->AC.part.L + (signed)ScuDsp->P.part.L); if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; if ((signed)ScuDsp->ALU.part.L < 0) ScuDsp->ProgControlPort.part.S = 1; else ScuDsp->ProgControlPort.part.S = 0; // if (ScuDsp->ALU.part.L ??) // set carry flag // ScuDsp->ProgControlPort.part.C = 1; // else // ScuDsp->ProgControlPort.part.C = 0; // if (ScuDsp->ALU.part.L ??) // set overflow flag // ScuDsp->ProgControlPort.part.V = 1; // else // ScuDsp->ProgControlPort.part.V = 0; break; case 0x5: // SUB ScuDsp->ALU.part.L = (unsigned)((signed)ScuDsp->AC.part.L - (signed)ScuDsp->P.part.L); if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; if ((signed)ScuDsp->ALU.part.L < 0) ScuDsp->ProgControlPort.part.S = 1; else ScuDsp->ProgControlPort.part.S = 0; // if (ScuDsp->ALU.part.L ??) // set carry flag // ScuDsp->ProgControlPort.part.C = 1; // else // ScuDsp->ProgControlPort.part.C = 0; // if (ScuDsp->ALU.part.L ??) // set overflow flag // ScuDsp->ProgControlPort.part.V = 1; // else // ScuDsp->ProgControlPort.part.V = 0; break; case 0x6: // AD2 ScuDsp->ALU.all = (signed)ScuDsp->AC.all + (signed)ScuDsp->P.all; if (ScuDsp->ALU.all == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; if ((signed)ScuDsp->ALU.all < 0) ScuDsp->ProgControlPort.part.S = 1; else ScuDsp->ProgControlPort.part.S = 0; if (ScuDsp->ALU.part.unused != 0) ScuDsp->ProgControlPort.part.V = 1; else ScuDsp->ProgControlPort.part.V = 0; // need carry test break; case 0x8: // SR // LOG("scu\t: SR instruction not implemented\n"); ScuDsp->ProgControlPort.part.C = ScuDsp->AC.part.L & 0x1; ScuDsp->ALU.part.L = (ScuDsp->AC.part.L & 0x80000000) | (ScuDsp->AC.part.L >> 1); if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; ScuDsp->ProgControlPort.part.S = ScuDsp->ALU.part.L >> 31; // LOG("scu\t: SR: ACL = %08X ALL = %08X. C = %d, Z = %d, S = %d\n", ScuDsp->AC.part.L, ScuDsp->ALU.part.L, ScuDsp->ProgControlPort.part.C, ScuDsp->ProgControlPort.part.Z, ScuDsp->ProgControlPort.part.S); break; case 0x9: // RR ScuDsp->ProgControlPort.part.C = ScuDsp->AC.part.L & 0x1; ScuDsp->ALU.part.L = (ScuDsp->ProgControlPort.part.C << 31) | (ScuDsp->AC.part.L >> 1); if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; ScuDsp->ProgControlPort.part.S = ScuDsp->ProgControlPort.part.C; break; case 0xA: // SL ScuDsp->ProgControlPort.part.C = ScuDsp->AC.part.L >> 31; ScuDsp->ALU.part.L = (ScuDsp->AC.part.L << 1); if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; ScuDsp->ProgControlPort.part.S = ScuDsp->ALU.part.L >> 31; break; case 0xB: // RL // LOG("scu\t: RL instruction not implemented\n"); ScuDsp->ProgControlPort.part.C = ScuDsp->AC.part.L >> 31; ScuDsp->ALU.part.L = (ScuDsp->AC.part.L << 1) | ScuDsp->ProgControlPort.part.C; if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; ScuDsp->ProgControlPort.part.S = ScuDsp->ALU.part.L >> 31; // LOG("scu\t: RL: ACL = %08X ALL = %08X. C = %d, Z = %d, S = %d\n", ScuDsp->AC.part.L, ScuDsp->ALU.part.L, ScuDsp->ProgControlPort.part.C, ScuDsp->ProgControlPort.part.Z, ScuDsp->ProgControlPort.part.S); break; case 0xF: // RL8 ScuDsp->ALU.part.L = (ScuDsp->AC.part.L << 8) | ((ScuDsp->AC.part.L >> 24) & 0xFF); ScuDsp->ProgControlPort.part.C = ScuDsp->ALU.part.L & 0x1; if (ScuDsp->ALU.part.L == 0) ScuDsp->ProgControlPort.part.Z = 1; else ScuDsp->ProgControlPort.part.Z = 0; ScuDsp->ProgControlPort.part.S = ScuDsp->ALU.part.L >> 31; break; default: break; } switch (instruction >> 30) { case 0x00: // Operation Commands // X-bus if ((instruction >> 23) & 0x4) { // MOV [s], X ScuDsp->RX = readgensrc((instruction >> 20) & 0x7); } switch ((instruction >> 23) & 0x3) { case 2: // MOV MUL, P ScuDsp->P.all = ScuDsp->MUL.all; break; case 3: // MOV [s], P ScuDsp->P.all = readgensrc((instruction >> 20) & 0x7); break; default: break; } // Y-bus if ((instruction >> 17) & 0x4) { // MOV [s], Y ScuDsp->RY = readgensrc((instruction >> 14) & 0x7); } switch ((instruction >> 17) & 0x3) { case 1: // CLR A ScuDsp->AC.all = 0; break; case 2: // MOV ALU,A ScuDsp->AC.all = ScuDsp->ALU.all; break; case 3: // MOV [s],A ScuDsp->AC.all = (signed)readgensrc((instruction >> 14) & 0x7); break; default: break; } // D1-bus switch ((instruction >> 12) & 0x3) { case 1: // MOV SImm,[d] writed1busdest((instruction >> 8) & 0xF, (u32)(signed char)(instruction & 0xFF)); break; case 3: // MOV [s],[d] writed1busdest((instruction >> 8) & 0xF, readgensrc(instruction & 0xF)); break; default: break; } break; case 0x02: // Load Immediate Commands if ((instruction >> 25) & 1) { switch ((instruction >> 19) & 0x3F) { case 0x01: // MVI Imm,[d]NZ if (!ScuDsp->ProgControlPort.part.Z) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x02: // MVI Imm,[d]NS if (!ScuDsp->ProgControlPort.part.S) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x03: // MVI Imm,[d]NZS if (!ScuDsp->ProgControlPort.part.Z || !ScuDsp->ProgControlPort.part.S) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x04: // MVI Imm,[d]NC if (!ScuDsp->ProgControlPort.part.C) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x08: // MVI Imm,[d]NT0 if (!ScuDsp->ProgControlPort.part.T0) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x21: // MVI Imm,[d]Z if (ScuDsp->ProgControlPort.part.Z) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x22: // MVI Imm,[d]S if (ScuDsp->ProgControlPort.part.S) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x23: // MVI Imm,[d]ZS if (ScuDsp->ProgControlPort.part.Z || ScuDsp->ProgControlPort.part.S) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x24: // MVI Imm,[d]C if (ScuDsp->ProgControlPort.part.C) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; case 0x28: // MVI Imm,[d]T0 if (ScuDsp->ProgControlPort.part.T0) writeloadimdest((instruction >> 26) & 0xF, (instruction & 0x7FFFF) | ((instruction & 0x40000) ? 0xFFF80000 : 0x00000000)); break; default: break; } } else { // MVI Imm,[d] writeloadimdest((instruction >> 26) & 0xF, (instruction & 0xFFFFFF) | ((instruction & 0x1000000) ? 0xFF000000 : 0x00000000)); } break; case 0x03: // Other { u32 i; switch((instruction >> 28) & 0x3) { case 0x00: // DMA Commands { int addressAdd; u32 transferNumber; unsigned char hold=(instruction >> 14) & 0x1; unsigned char direction=(instruction >> 12) & 0x1; if (instruction & 0x2000) { // DMA(H) D0,[RAM],[s]/DMA(H) [RAM],D0,[s] // command format 2 transferNumber = readgensrc(instruction & 0x7); switch((instruction >> 15) & 0x7) { case 0: // Add 0 addressAdd = 0; break; case 1: // Add 1 addressAdd = 4; break; default: // Undocumented mode addressAdd = 4; break; } } else { // DMA(H) D0,[RAM],SImm/DMA(H) [RAM],D0,SImm // command format 1 transferNumber = instruction & 0xFF; switch((instruction >> 15) & 0x7) { case 0: // Add 0 addressAdd = 0; break; case 1: // Add 1 addressAdd = 4; break; case 2: // Add 2 addressAdd = 8; break; case 3: // Add 4 addressAdd = 16; break; case 4: // Add 8 addressAdd = 32; break; case 5: // Add 16 addressAdd = 64; break; case 6: // Add 32 addressAdd = 128; break; case 7: // Add 64 addressAdd = 256; break; default: addressAdd = 0; break; } // LOG("DMA command format 1: addressAdd = %d transferNumber = %d hold = %d dir = %d\n", addressAdd, transferNumber, hold, direction); } if (direction) { u32 WA0temp=ScuDsp->WA0; u32 start; // Looks like some bits are ignored on a real saturn(Grandia takes advantage of this) ScuDsp->WA0 &= 0x01FFFFFF; // DMA(H) [RAM], D0, ?? start = ScuDsp->WA0 << 2; for (i = 0; i < transferNumber; i++) { MappedMemoryWriteLong(ScuDsp->WA0 << 2, readdmasrc((instruction >> 8) & 0x3, 1)); ScuDsp->WA0 += (addressAdd >> 2); } SH2WriteNotify(start, (ScuDsp->WA0 << 2) - start); if (hold) ScuDsp->WA0 = WA0temp; } else { u32 RA0temp=ScuDsp->RA0; // Looks like some bits are ignored on a real saturn(Grandia takes advantage of this) ScuDsp->RA0 &= 0x01FFFFFF; // DMA(H) D0,[RAM], ?? for (i = 0; i < transferNumber; i++) { writedmadest((instruction >> 8) & 0x7, MappedMemoryReadLong(ScuDsp->RA0 << 2), 1); ScuDsp->RA0 += (addressAdd >> 2); } if (hold) ScuDsp->RA0 = RA0temp; } break; } case 0x01: // Jump Commands switch ((instruction >> 19) & 0x7F) { case 0x00: // JMP Imm ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; break; case 0x41: // JMP NZ, Imm if (!ScuDsp->ProgControlPort.part.Z) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } break; case 0x42: // JMP NS, Imm if (!ScuDsp->ProgControlPort.part.S) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } LOG("scu\t: JMP NS: S = %d, jmpaddr = %08X\n", (unsigned int)ScuDsp->ProgControlPort.part.S, (unsigned int)ScuDsp->jmpaddr); break; case 0x43: // JMP NZS, Imm if (!ScuDsp->ProgControlPort.part.Z || !ScuDsp->ProgControlPort.part.S) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } LOG("scu\t: JMP NZS: Z = %d, S = %d, jmpaddr = %08X\n", (unsigned int)ScuDsp->ProgControlPort.part.Z, (unsigned int)ScuDsp->ProgControlPort.part.S, (unsigned int)ScuDsp->jmpaddr); break; case 0x44: // JMP NC, Imm if (!ScuDsp->ProgControlPort.part.C) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } break; case 0x48: // JMP NT0, Imm if (!ScuDsp->ProgControlPort.part.T0) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } LOG("scu\t: JMP NT0: T0 = %d, jmpaddr = %08X\n", (unsigned int)ScuDsp->ProgControlPort.part.T0, (unsigned int)ScuDsp->jmpaddr); break; case 0x61: // JMP Z,Imm if (ScuDsp->ProgControlPort.part.Z) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } break; case 0x62: // JMP S, Imm if (ScuDsp->ProgControlPort.part.S) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } LOG("scu\t: JMP S: S = %d, jmpaddr = %08X\n", (unsigned int)ScuDsp->ProgControlPort.part.S, (unsigned int)ScuDsp->jmpaddr); break; case 0x63: // JMP ZS, Imm if (ScuDsp->ProgControlPort.part.Z || ScuDsp->ProgControlPort.part.S) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } LOG("scu\t: JMP ZS: Z = %d, S = %d, jmpaddr = %08X\n", ScuDsp->ProgControlPort.part.Z, (unsigned int)ScuDsp->ProgControlPort.part.S, (unsigned int)ScuDsp->jmpaddr); break; case 0x64: // JMP C, Imm if (ScuDsp->ProgControlPort.part.C) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } break; case 0x68: // JMP T0,Imm if (ScuDsp->ProgControlPort.part.T0) { ScuDsp->jmpaddr = instruction & 0xFF; ScuDsp->delayed = 0; } break; default: LOG("scu\t: Unknown JMP instruction not implemented\n"); break; } break; case 0x02: // Loop bottom Commands if (instruction & 0x8000000) { // LPS if (ScuDsp->LOP != 0) { ScuDsp->jmpaddr = ScuDsp->PC; ScuDsp->delayed = 0; ScuDsp->LOP--; } } else { // BTM if (ScuDsp->LOP != 0) { ScuDsp->jmpaddr = ScuDsp->TOP; ScuDsp->delayed = 0; ScuDsp->LOP--; } } break; case 0x03: // End Commands ScuDsp->ProgControlPort.part.EX = 0; if (instruction & 0x8000000) { // End with Interrupt ScuDsp->ProgControlPort.part.E = 1; ScuSendDSPEnd(); } LOG("dsp has ended\n"); ScuDsp->ProgControlPort.part.P = ScuDsp->PC+1; timing = 1; break; default: break; } break; } default: LOG("scu\t: Invalid DSP opcode %08X at offset %02X\n", instruction, ScuDsp->PC); break; } // Do RX*RY multiplication ScuDsp->MUL.all = (signed)ScuDsp->RX * (signed)ScuDsp->RY; ScuDsp->PC++; // Handle delayed jumps if (ScuDsp->jmpaddr != 0xFFFFFFFF) { if (ScuDsp->delayed) { ScuDsp->PC = (unsigned char)ScuDsp->jmpaddr; ScuDsp->jmpaddr = 0xFFFFFFFF; } else ScuDsp->delayed = 1; } timing--; } } } ////////////////////////////////////////////////////////////////////////////// static char *disd1bussrc(u8 num) { switch(num) { case 0x0: return "M0"; case 0x1: return "M1"; case 0x2: return "M2"; case 0x3: return "M3"; case 0x4: return "MC0"; case 0x5: return "MC1"; case 0x6: return "MC2"; case 0x7: return "MC3"; case 0x9: return "ALL"; case 0xA: return "ALH"; default: break; } return "??"; } ////////////////////////////////////////////////////////////////////////////// static char *disd1busdest(u8 num) { switch(num) { case 0x0: return "MC0"; case 0x1: return "MC1"; case 0x2: return "MC2"; case 0x3: return "MC3"; case 0x4: return "RX"; case 0x5: return "PL"; case 0x6: return "RA0"; case 0x7: return "WA0"; case 0xA: return "LOP"; case 0xB: return "TOP"; case 0xC: return "CT0"; case 0xD: return "CT1"; case 0xE: return "CT2"; case 0xF: return "CT3"; default: break; } return "??"; } ////////////////////////////////////////////////////////////////////////////// static char *disloadimdest(u8 num) { switch(num) { case 0x0: return "MC0"; case 0x1: return "MC1"; case 0x2: return "MC2"; case 0x3: return "MC3"; case 0x4: return "RX"; case 0x5: return "PL"; case 0x6: return "RA0"; case 0x7: return "WA0"; case 0xA: return "LOP"; case 0xC: return "TOP"; default: break; } return "??"; } ////////////////////////////////////////////////////////////////////////////// static char *disdmaram(u8 num) { switch(num) { case 0x0: // MC0 return "MC0"; case 0x1: // MC1 return "MC1"; case 0x2: // MC2 return "MC2"; case 0x3: // MC3 return "MC3"; case 0x4: // Program Ram return "PRG"; default: break; } return "??"; } ////////////////////////////////////////////////////////////////////////////// void ScuDspDisasm(u8 addr, char *outstring) { u32 instruction; u8 counter=0; u8 filllength=0; instruction = ScuDsp->ProgramRam[addr]; sprintf(outstring, "%02X: ", addr); outstring+=strlen(outstring); if (instruction == 0) { sprintf(outstring, "NOP"); return; } // Handle ALU commands switch (instruction >> 26) { case 0x0: // NOP break; case 0x1: // AND sprintf(outstring, "AND"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0x2: // OR sprintf(outstring, "OR"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0x3: // XOR sprintf(outstring, "XOR"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0x4: // ADD sprintf(outstring, "ADD"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0x5: // SUB sprintf(outstring, "SUB"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0x6: // AD2 sprintf(outstring, "AD2"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0x8: // SR sprintf(outstring, "SR"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0x9: // RR sprintf(outstring, "RR"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0xA: // SL sprintf(outstring, "SL"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0xB: // RL sprintf(outstring, "RL"); counter = strlen(outstring); outstring+=strlen(outstring); break; case 0xF: // RL8 sprintf(outstring, "RL8"); counter = strlen(outstring); outstring+=strlen(outstring); break; default: break; } switch (instruction >> 30) { case 0x00: // Operation Commands filllength = 5 - counter; memset((void *)outstring, 0x20, filllength); counter += filllength; outstring += filllength; if ((instruction >> 23) & 0x4) { sprintf(outstring, "MOV %s, X", disd1bussrc((instruction >> 20) & 0x7)); counter+=strlen(outstring); outstring+=strlen(outstring); } filllength = 16 - counter; memset((void *)outstring, 0x20, filllength); counter += filllength; outstring += filllength; switch ((instruction >> 23) & 0x3) { case 2: sprintf(outstring, "MOV MUL, P"); counter+=strlen(outstring); outstring+=strlen(outstring); break; case 3: sprintf(outstring, "MOV %s, P", disd1bussrc((instruction >> 20) & 0x7)); counter+=strlen(outstring); outstring+=strlen(outstring); break; default: break; } filllength = 27 - counter; memset((void *)outstring, 0x20, filllength); counter += filllength; outstring += filllength; // Y-bus if ((instruction >> 17) & 0x4) { sprintf(outstring, "MOV %s, Y", disd1bussrc((instruction >> 14) & 0x7)); counter+=strlen(outstring); outstring+=strlen(outstring); } filllength = 38 - counter; memset((void *)outstring, 0x20, filllength); counter += filllength; outstring += filllength; switch ((instruction >> 17) & 0x3) { case 1: sprintf(outstring, "CLR A"); counter+=strlen(outstring); outstring+=strlen(outstring); break; case 2: sprintf(outstring, "MOV ALU, A"); counter+=strlen(outstring); outstring+=strlen(outstring); break; case 3: sprintf(outstring, "MOV %s, A", disd1bussrc((instruction >> 14) & 0x7)); counter+=strlen(outstring); outstring+=strlen(outstring); break; default: break; } filllength = 50 - counter; memset((void *)outstring, 0x20, filllength); counter += filllength; outstring += filllength; // D1-bus switch ((instruction >> 12) & 0x3) { case 1: sprintf(outstring, "MOV #$%02X, %s", (unsigned int)instruction & 0xFF, disd1busdest((instruction >> 8) & 0xF)); outstring+=strlen(outstring); break; case 3: sprintf(outstring, "MOV %s, %s", disd1bussrc(instruction & 0xF), disd1busdest((instruction >> 8) & 0xF)); outstring+=strlen(outstring); break; default: outstring[0] = 0x00; break; } break; case 0x02: // Load Immediate Commands if ((instruction >> 25) & 1) { switch ((instruction >> 19) & 0x3F) { case 0x01: sprintf(outstring, "MVI #$%05X,%s,NZ", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x02: sprintf(outstring, "MVI #$%05X,%s,NS", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x03: sprintf(outstring, "MVI #$%05X,%s,NZS", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x04: sprintf(outstring, "MVI #$%05X,%s,NC", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x08: sprintf(outstring, "MVI #$%05X,%s,NT0", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x21: sprintf(outstring, "MVI #$%05X,%s,Z", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x22: sprintf(outstring, "MVI #$%05X,%s,S", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x23: sprintf(outstring, "MVI #$%05X,%s,ZS", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x24: sprintf(outstring, "MVI #$%05X,%s,C", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; case 0x28: sprintf(outstring, "MVI #$%05X,%s,T0", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); break; default: break; } } else { sprintf(outstring, "MVI #$%05X,%s", (unsigned int)instruction & 0x7FFFF, disloadimdest((instruction >> 26) & 0xF)); } break; case 0x03: // Other switch((instruction >> 28) & 0x3) { case 0x00: // DMA Commands { int addressAdd; if (instruction & 0x1000) addressAdd = (instruction >> 15) & 0x7; else addressAdd = (instruction >> 15) & 0x1; switch(addressAdd) { case 0: // Add 0 addressAdd = 0; break; case 1: // Add 1 addressAdd = 1; break; case 2: // Add 2 addressAdd = 2; break; case 3: // Add 4 addressAdd = 4; break; case 4: // Add 8 addressAdd = 8; break; case 5: // Add 16 addressAdd = 16; break; case 6: // Add 32 addressAdd = 32; break; case 7: // Add 64 addressAdd = 64; break; default: addressAdd = 0; break; } LOG("DMA Add = %X, addressAdd = %d", (instruction >> 15) & 0x7, addressAdd); // Write Command name sprintf(outstring, "DMA"); outstring+=strlen(outstring); // Is h bit set? if (instruction & 0x4000) { outstring[0] = 'H'; outstring++; } sprintf(outstring, "%d ", addressAdd); outstring+=strlen(outstring); if (instruction & 0x2000) { // Command Format 2 if (instruction & 0x1000) sprintf(outstring, "%s, D0, %s", disdmaram((instruction >> 8) & 0x7), disd1bussrc(instruction & 0x7)); else sprintf(outstring, "D0, %s, %s", disdmaram((instruction >> 8) & 0x7), disd1bussrc(instruction & 0x7)); } else { // Command Format 1 if (instruction & 0x1000) sprintf(outstring, "%s, D0, #$%02X", disdmaram((instruction >> 8) & 0x7), (int)(instruction & 0xFF)); else sprintf(outstring, "D0, %s, #$%02X", disdmaram((instruction >> 8) & 0x7), (int)(instruction & 0xFF)); } break; } case 0x01: // Jump Commands switch ((instruction >> 19) & 0x7F) { case 0x00: sprintf(outstring, "JMP $%02X", (unsigned int)instruction & 0xFF); break; case 0x41: sprintf(outstring, "JMP NZ,$%02X", (unsigned int)instruction & 0xFF); break; case 0x42: sprintf(outstring, "JMP NS,$%02X", (unsigned int)instruction & 0xFF); break; case 0x43: sprintf(outstring, "JMP NZS,$%02X", (unsigned int)instruction & 0xFF); break; case 0x44: sprintf(outstring, "JMP NC,$%02X", (unsigned int)instruction & 0xFF); break; case 0x48: sprintf(outstring, "JMP NT0,$%02X", (unsigned int)instruction & 0xFF); break; case 0x61: sprintf(outstring, "JMP Z,$%02X", (unsigned int)instruction & 0xFF); break; case 0x62: sprintf(outstring, "JMP S,$%02X", (unsigned int)instruction & 0xFF); break; case 0x63: sprintf(outstring, "JMP ZS,$%02X", (unsigned int)instruction & 0xFF); break; case 0x64: sprintf(outstring, "JMP C,$%02X", (unsigned int)instruction & 0xFF); break; case 0x68: sprintf(outstring, "JMP T0,$%02X", (unsigned int)instruction & 0xFF); break; default: sprintf(outstring, "Unknown JMP"); break; } break; case 0x02: // Loop bottom Commands if (instruction & 0x8000000) sprintf(outstring, "LPS"); else sprintf(outstring, "BTM"); break; case 0x03: // End Commands if (instruction & 0x8000000) sprintf(outstring, "ENDI"); else sprintf(outstring, "END"); break; default: break; } break; default: sprintf(outstring, "Invalid opcode"); break; } } ////////////////////////////////////////////////////////////////////////////// void ScuDspStep(void) { if (ScuDsp) ScuExec(1); } ////////////////////////////////////////////////////////////////////////////// int ScuDspSaveProgram(const char *filename) { FILE *fp; u32 i; u8 *buffer; if (!filename) return -1; if ((fp = fopen(filename, "wb")) == NULL) return -1; if ((buffer = (u8 *)malloc(sizeof(ScuDsp->ProgramRam))) == NULL) { fclose(fp); return -2; } for (i = 0; i < 256; i++) { buffer[i * 4] = (u8)(ScuDsp->ProgramRam[i] >> 24); buffer[(i * 4)+1] = (u8)(ScuDsp->ProgramRam[i] >> 16); buffer[(i * 4)+2] = (u8)(ScuDsp->ProgramRam[i] >> 8); buffer[(i * 4)+3] = (u8)ScuDsp->ProgramRam[i]; } fwrite((void *)buffer, 1, sizeof(ScuDsp->ProgramRam), fp); fclose(fp); free(buffer); return 0; } ////////////////////////////////////////////////////////////////////////////// int ScuDspSaveMD(const char *filename, int num) { FILE *fp; u32 i; u8 *buffer; if (!filename) return -1; if ((fp = fopen(filename, "wb")) == NULL) return -1; if ((buffer = (u8 *)malloc(sizeof(ScuDsp->MD[num]))) == NULL) { fclose(fp); return -2; } for (i = 0; i < 64; i++) { buffer[i * 4] = (u8)(ScuDsp->MD[num][i] >> 24); buffer[(i * 4)+1] = (u8)(ScuDsp->MD[num][i] >> 16); buffer[(i * 4)+2] = (u8)(ScuDsp->MD[num][i] >> 8); buffer[(i * 4)+3] = (u8)ScuDsp->MD[num][i]; } fwrite((void *)buffer, 1, sizeof(ScuDsp->MD[num]), fp); fclose(fp); free(buffer); return 0; } ////////////////////////////////////////////////////////////////////////////// void ScuDspGetRegisters(scudspregs_struct *regs) { if (regs != NULL) { memcpy(regs->ProgramRam, ScuDsp->ProgramRam, sizeof(u32) * 256); memcpy(regs->MD, ScuDsp->MD, sizeof(u32) * 64 * 4); regs->ProgControlPort.all = ScuDsp->ProgControlPort.all; regs->ProgControlPort.part.P = regs->PC = ScuDsp->PC; regs->TOP = ScuDsp->TOP; regs->LOP = ScuDsp->LOP; regs->jmpaddr = ScuDsp->jmpaddr; regs->delayed = ScuDsp->delayed; regs->DataRamPage = ScuDsp->DataRamPage; regs->DataRamReadAddress = ScuDsp->DataRamReadAddress; memcpy(regs->CT, ScuDsp->CT, sizeof(u8) * 4); regs->RX = ScuDsp->RX; regs->RY = ScuDsp->RY; regs->RA0 = ScuDsp->RA0; regs->WA0 = ScuDsp->WA0; regs->AC.all = ScuDsp->AC.all; regs->P.all = ScuDsp->P.all; regs->ALU.all = ScuDsp->ALU.all; regs->MUL.all = ScuDsp->MUL.all; } } ////////////////////////////////////////////////////////////////////////////// void ScuDspSetRegisters(scudspregs_struct *regs) { if (regs != NULL) { memcpy(ScuDsp->ProgramRam, regs->ProgramRam, sizeof(u32) * 256); memcpy(ScuDsp->MD, regs->MD, sizeof(u32) * 64 * 4); ScuDsp->ProgControlPort.all = regs->ProgControlPort.all; ScuDsp->PC = regs->ProgControlPort.part.P; ScuDsp->TOP = regs->TOP; ScuDsp->LOP = regs->LOP; ScuDsp->jmpaddr = regs->jmpaddr; ScuDsp->delayed = regs->delayed; ScuDsp->DataRamPage = regs->DataRamPage; ScuDsp->DataRamReadAddress = regs->DataRamReadAddress; memcpy(ScuDsp->CT, regs->CT, sizeof(u8) * 4); ScuDsp->RX = regs->RX; ScuDsp->RY = regs->RY; ScuDsp->RA0 = regs->RA0; ScuDsp->WA0 = regs->WA0; ScuDsp->AC.all = regs->AC.all; ScuDsp->P.all = regs->P.all; ScuDsp->ALU.all = regs->ALU.all; ScuDsp->MUL.all = regs->MUL.all; } } ////////////////////////////////////////////////////////////////////////////// void ScuDspSetBreakpointCallBack(void (*func)(u32)) { ScuBP->BreakpointCallBack = func; } ////////////////////////////////////////////////////////////////////////////// int ScuDspAddCodeBreakpoint(u32 addr) { int i; if (ScuBP->numcodebreakpoints < MAX_BREAKPOINTS) { // Make sure it isn't already on the list for (i = 0; i < ScuBP->numcodebreakpoints; i++) { if (addr == ScuBP->codebreakpoint[i].addr) return -1; } ScuBP->codebreakpoint[ScuBP->numcodebreakpoints].addr = addr; ScuBP->numcodebreakpoints++; return 0; } return -1; } ////////////////////////////////////////////////////////////////////////////// static void ScuDspSortCodeBreakpoints(void) { int i, i2; u32 tmp; for (i = 0; i < (MAX_BREAKPOINTS-1); i++) { for (i2 = i+1; i2 < MAX_BREAKPOINTS; i2++) { if (ScuBP->codebreakpoint[i].addr == 0xFFFFFFFF && ScuBP->codebreakpoint[i2].addr != 0xFFFFFFFF) { tmp = ScuBP->codebreakpoint[i].addr; ScuBP->codebreakpoint[i].addr = ScuBP->codebreakpoint[i2].addr; ScuBP->codebreakpoint[i2].addr = tmp; } } } } ////////////////////////////////////////////////////////////////////////////// int ScuDspDelCodeBreakpoint(u32 addr) { int i; if (ScuBP->numcodebreakpoints > 0) { for (i = 0; i < ScuBP->numcodebreakpoints; i++) { if (ScuBP->codebreakpoint[i].addr == addr) { ScuBP->codebreakpoint[i].addr = 0xFFFFFFFF; ScuDspSortCodeBreakpoints(); ScuBP->numcodebreakpoints--; return 0; } } } return -1; } ////////////////////////////////////////////////////////////////////////////// scucodebreakpoint_struct *ScuDspGetBreakpointList(void) { return ScuBP->codebreakpoint; } ////////////////////////////////////////////////////////////////////////////// void ScuDspClearCodeBreakpoints(void) { int i; for (i = 0; i < MAX_BREAKPOINTS; i++) ScuBP->codebreakpoint[i].addr = 0xFFFFFFFF; ScuBP->numcodebreakpoints = 0; } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL ScuReadByte(u32 addr) { addr &= 0xFF; switch(addr) { case 0xA7: return (ScuRegs->IST & 0xFF); default: LOG("Unhandled SCU Register byte read %08X\n", addr); return 0; } return 0; } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL ScuReadWord(u32 addr) { addr &= 0xFF; LOG("Unhandled SCU Register word read %08X\n", addr); return 0; } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL ScuReadLong(u32 addr) { addr &= 0xFF; switch(addr) { case 0: return ScuRegs->D0R; case 4: return ScuRegs->D0W; case 8: return ScuRegs->D0C; case 0x20: return ScuRegs->D1R; case 0x24: return ScuRegs->D1W; case 0x28: return ScuRegs->D1C; case 0x40: return ScuRegs->D2R; case 0x44: return ScuRegs->D2W; case 0x48: return ScuRegs->D2C; case 0x7C: return ScuRegs->DSTA; case 0x80: // DSP Program Control Port return (ScuDsp->ProgControlPort.all & 0x00FD00FF); case 0x8C: // DSP Data Ram Data Port if (!ScuDsp->ProgControlPort.part.EX) return ScuDsp->MD[ScuDsp->DataRamPage][ScuDsp->DataRamReadAddress++]; else return 0; case 0xA4: return ScuRegs->IST; case 0xA8: return ScuRegs->AIACK; case 0xC4: return ScuRegs->RSEL; case 0xC8: return ScuRegs->VER; default: LOG("Unhandled SCU Register long read %08X\n", addr); return 0; } } ////////////////////////////////////////////////////////////////////////////// void FASTCALL ScuWriteByte(u32 addr, u8 val) { addr &= 0xFF; switch(addr) { case 0xA7: ScuRegs->IST &= (0xFFFFFF00 | val); // double check this return; default: LOG("Unhandled SCU Register byte write %08X\n", addr); return; } } ////////////////////////////////////////////////////////////////////////////// void FASTCALL ScuWriteWord(u32 addr, UNUSED u16 val) { addr &= 0xFF; LOG("Unhandled SCU Register word write %08X\n", addr); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL ScuWriteLong(u32 addr, u32 val) { addr &= 0xFF; switch(addr) { case 0: ScuRegs->D0R = val; break; case 4: ScuRegs->D0W = val; break; case 8: ScuRegs->D0C = val; break; case 0xC: ScuRegs->D0AD = val; break; case 0x10: if (val & 0x1) { scudmainfo_struct dmainfo; dmainfo.mode = 0; dmainfo.ReadAddress = ScuRegs->D0R; dmainfo.WriteAddress = ScuRegs->D0W; dmainfo.TransferNumber = ScuRegs->D0C; dmainfo.AddValue = ScuRegs->D0AD; dmainfo.ModeAddressUpdate = ScuRegs->D0MD; ScuDMA(&dmainfo); } ScuRegs->D0EN = val; break; case 0x14: if ((val & 0x7) != 7) { LOG("scu\t: DMA mode 0 interrupt start factor not implemented\n"); } ScuRegs->D0MD = val; break; case 0x20: ScuRegs->D1R = val; break; case 0x24: ScuRegs->D1W = val; break; case 0x28: ScuRegs->D1C = val; break; case 0x2C: ScuRegs->D1AD = val; break; case 0x30: if (val & 0x1) { scudmainfo_struct dmainfo; dmainfo.mode = 1; dmainfo.ReadAddress = ScuRegs->D1R; dmainfo.WriteAddress = ScuRegs->D1W; dmainfo.TransferNumber = ScuRegs->D1C; dmainfo.AddValue = ScuRegs->D1AD; dmainfo.ModeAddressUpdate = ScuRegs->D1MD; ScuDMA(&dmainfo); } ScuRegs->D1EN = val; break; case 0x34: if ((val & 0x7) != 7) { LOG("scu\t: DMA mode 1 interrupt start factor not implemented\n"); } ScuRegs->D1MD = val; break; case 0x40: ScuRegs->D2R = val; break; case 0x44: ScuRegs->D2W = val; break; case 0x48: ScuRegs->D2C = val; break; case 0x4C: ScuRegs->D2AD = val; break; case 0x50: if (val & 0x1) { scudmainfo_struct dmainfo; dmainfo.mode = 2; dmainfo.ReadAddress = ScuRegs->D2R; dmainfo.WriteAddress = ScuRegs->D2W; dmainfo.TransferNumber = ScuRegs->D2C; dmainfo.AddValue = ScuRegs->D2AD; dmainfo.ModeAddressUpdate = ScuRegs->D2MD; ScuDMA(&dmainfo); } ScuRegs->D2EN = val; break; case 0x54: if ((val & 0x7) != 7) { LOG("scu\t: DMA mode 2 interrupt start factor not implemented\n"); } ScuRegs->D2MD = val; break; case 0x60: ScuRegs->DSTP = val; break; case 0x80: // DSP Program Control Port LOG("scu\t: wrote %08X to DSP Program Control Port\n", val); ScuDsp->ProgControlPort.all = (ScuDsp->ProgControlPort.all & 0x00FC0000) | (val & 0x060380FF); if (ScuDsp->ProgControlPort.part.LE) { // set pc ScuDsp->PC = (u8)ScuDsp->ProgControlPort.part.P; LOG("scu\t: DSP set pc = %02X\n", ScuDsp->PC); } #if DEBUG if (ScuDsp->ProgControlPort.part.EX) LOG("scu\t: DSP executing: PC = %02X\n", ScuDsp->PC); #endif break; case 0x84: // DSP Program Ram Data Port // LOG("scu\t: wrote %08X to DSP Program ram offset %02X\n", val, ScuDsp->PC); ScuDsp->ProgramRam[ScuDsp->PC] = val; ScuDsp->PC++; ScuDsp->ProgControlPort.part.P = ScuDsp->PC; break; case 0x88: // DSP Data Ram Address Port ScuDsp->DataRamPage = (val >> 6) & 3; ScuDsp->DataRamReadAddress = val & 0x3F; break; case 0x8C: // DSP Data Ram Data Port // LOG("scu\t: wrote %08X to DSP Data Ram Data Port Page %d offset %02X\n", val, ScuDsp->DataRamPage, ScuDsp->DataRamReadAddress); if (!ScuDsp->ProgControlPort.part.EX) { ScuDsp->MD[ScuDsp->DataRamPage][ScuDsp->DataRamReadAddress] = val; ScuDsp->DataRamReadAddress++; } break; case 0x90: ScuRegs->T0C = val; break; case 0x94: ScuRegs->T1S = val; break; case 0x98: ScuRegs->T1MD = val; break; case 0xA0: ScuRegs->IMS = val; ScuTestInterruptMask(); break; case 0xA4: ScuRegs->IST &= val; break; case 0xA8: ScuRegs->AIACK = val; break; case 0xB0: ScuRegs->ASR0 = val; break; case 0xB4: ScuRegs->ASR1 = val; break; case 0xB8: ScuRegs->AREF = val; break; case 0xC4: ScuRegs->RSEL = val; break; default: LOG("Unhandled SCU Register long write %08X\n", addr); break; } } ////////////////////////////////////////////////////////////////////////////// void ScuTestInterruptMask() { unsigned int i, i2; // Handle SCU interrupts for (i = 0; i < ScuRegs->NumberOfInterrupts; i++) { if (!(ScuRegs->IMS & ScuRegs->interrupts[ScuRegs->NumberOfInterrupts-1-i].mask)) { SH2SendInterrupt(MSH2, ScuRegs->interrupts[ScuRegs->NumberOfInterrupts-1-i].vector, ScuRegs->interrupts[ScuRegs->NumberOfInterrupts-1-i].level); ScuRegs->IST &= ~ScuRegs->interrupts[ScuRegs->NumberOfInterrupts-1-i].statusbit; // Shorten list for (i2 = ScuRegs->NumberOfInterrupts-1-i; i2 < (ScuRegs->NumberOfInterrupts-1); i2++) memcpy(&ScuRegs->interrupts[i2], &ScuRegs->interrupts[i2+1], sizeof(scuinterrupt_struct)); ScuRegs->NumberOfInterrupts--; break; } } } ////////////////////////////////////////////////////////////////////////////// static void ScuQueueInterrupt(u8 vector, u8 level, u16 mask, u32 statusbit) { u32 i, i2; scuinterrupt_struct tmp; // Make sure interrupt doesn't already exist for (i = 0; i < ScuRegs->NumberOfInterrupts; i++) { if (ScuRegs->interrupts[i].vector == vector) return; } ScuRegs->interrupts[ScuRegs->NumberOfInterrupts].vector = vector; ScuRegs->interrupts[ScuRegs->NumberOfInterrupts].level = level; ScuRegs->interrupts[ScuRegs->NumberOfInterrupts].mask = mask; ScuRegs->interrupts[ScuRegs->NumberOfInterrupts].statusbit = statusbit; ScuRegs->NumberOfInterrupts++; // Sort interrupts for (i = 0; i < (ScuRegs->NumberOfInterrupts-1); i++) { for (i2 = i+1; i2 < ScuRegs->NumberOfInterrupts; i2++) { if (ScuRegs->interrupts[i].level > ScuRegs->interrupts[i2].level) { memcpy(&tmp, &ScuRegs->interrupts[i], sizeof(scuinterrupt_struct)); memcpy(&ScuRegs->interrupts[i], &ScuRegs->interrupts[i2], sizeof(scuinterrupt_struct)); memcpy(&ScuRegs->interrupts[i2], &tmp, sizeof(scuinterrupt_struct)); } } } } ////////////////////////////////////////////////////////////////////////////// static INLINE void SendInterrupt(u8 vector, u8 level, u16 mask, u32 statusbit) { if (!(ScuRegs->IMS & mask)) SH2SendInterrupt(MSH2, vector, level); else { ScuQueueInterrupt(vector, level, mask, statusbit); ScuRegs->IST |= statusbit; } } ////////////////////////////////////////////////////////////////////////////// void ScuSendVBlankIN(void) { SendInterrupt(0x40, 0xF, 0x0001, 0x0001); } ////////////////////////////////////////////////////////////////////////////// void ScuSendVBlankOUT(void) { SendInterrupt(0x41, 0xE, 0x0002, 0x0002); if (ScuRegs->T1MD & 0x1) { ScuRegs->timer0 = 0; if (ScuRegs->timer0 == ScuRegs->T0C) ScuSendTimer0(); } } ////////////////////////////////////////////////////////////////////////////// void ScuSendHBlankIN(void) { SendInterrupt(0x42, 0xD, 0x0004, 0x0004); if (ScuRegs->T1MD & 0x1) { ScuRegs->timer0++; // if timer0 equals timer 0 compare register, do an interrupt if (ScuRegs->timer0 == ScuRegs->T0C) ScuSendTimer0(); // FIX ME - Should handle timer 1 as well } } ////////////////////////////////////////////////////////////////////////////// void ScuSendTimer0(void) { SendInterrupt(0x43, 0xC, 0x0008, 0x00000008); } ////////////////////////////////////////////////////////////////////////////// void ScuSendTimer1(void) { SendInterrupt(0x44, 0xB, 0x0010, 0x00000010); } ////////////////////////////////////////////////////////////////////////////// void ScuSendDSPEnd(void) { SendInterrupt(0x45, 0xA, 0x0020, 0x00000020); } ////////////////////////////////////////////////////////////////////////////// void ScuSendSoundRequest(void) { SendInterrupt(0x46, 0x9, 0x0040, 0x00000040); } ////////////////////////////////////////////////////////////////////////////// void ScuSendSystemManager(void) { SendInterrupt(0x47, 0x8, 0x0080, 0x00000080); } ////////////////////////////////////////////////////////////////////////////// void ScuSendPadInterrupt(void) { SendInterrupt(0x48, 0x8, 0x0100, 0x00000100); } ////////////////////////////////////////////////////////////////////////////// void ScuSendLevel2DMAEnd(void) { SendInterrupt(0x49, 0x6, 0x0200, 0x00000200); } ////////////////////////////////////////////////////////////////////////////// void ScuSendLevel1DMAEnd(void) { SendInterrupt(0x4A, 0x6, 0x0400, 0x00000400); } ////////////////////////////////////////////////////////////////////////////// void ScuSendLevel0DMAEnd(void) { SendInterrupt(0x4B, 0x5, 0x0800, 0x00000800); } ////////////////////////////////////////////////////////////////////////////// void ScuSendDMAIllegal(void) { SendInterrupt(0x4C, 0x3, 0x1000, 0x00001000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendDrawEnd(void) { SendInterrupt(0x4D, 0x2, 0x2000, 0x00002000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt00(void) { SendInterrupt(0x50, 0x7, 0x8000, 0x00010000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt01(void) { SendInterrupt(0x51, 0x7, 0x8000, 0x00020000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt02(void) { SendInterrupt(0x52, 0x7, 0x8000, 0x00040000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt03(void) { SendInterrupt(0x53, 0x7, 0x8000, 0x00080000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt04(void) { SendInterrupt(0x54, 0x4, 0x8000, 0x00100000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt05(void) { SendInterrupt(0x55, 0x4, 0x8000, 0x00200000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt06(void) { SendInterrupt(0x56, 0x4, 0x8000, 0x00400000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt07(void) { SendInterrupt(0x57, 0x4, 0x8000, 0x00800000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt08(void) { SendInterrupt(0x58, 0x1, 0x8000, 0x01000000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt09(void) { SendInterrupt(0x59, 0x1, 0x8000, 0x02000000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt10(void) { SendInterrupt(0x5A, 0x1, 0x8000, 0x04000000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt11(void) { SendInterrupt(0x5B, 0x1, 0x8000, 0x08000000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt12(void) { SendInterrupt(0x5C, 0x1, 0x8000, 0x10000000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt13(void) { SendInterrupt(0x5D, 0x1, 0x8000, 0x20000000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt14(void) { SendInterrupt(0x5E, 0x1, 0x8000, 0x40000000); } ////////////////////////////////////////////////////////////////////////////// void ScuSendExternalInterrupt15(void) { SendInterrupt(0x5F, 0x1, 0x8000, 0x80000000); } ////////////////////////////////////////////////////////////////////////////// int ScuSaveState(FILE *fp) { int offset; IOCheck_struct check; offset = StateWriteHeader(fp, "SCU ", 1); // Write registers and internal variables ywrite(&check, (void *)ScuRegs, sizeof(Scu), 1, fp); // Write DSP area ywrite(&check, (void *)ScuDsp, sizeof(scudspregs_struct), 1, fp); return StateFinishHeader(fp, offset); } ////////////////////////////////////////////////////////////////////////////// int ScuLoadState(FILE *fp, UNUSED int version, int size) { IOCheck_struct check; // Read registers and internal variables yread(&check, (void *)ScuRegs, sizeof(Scu), 1, fp); // Read DSP area yread(&check, (void *)ScuDsp, sizeof(scudspregs_struct), 1, fp); return size; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/perlinuxjoy.c000644 001750 001750 00000005632 12256006174 020742 0ustar00guillaumeguillaume000000 000000 /* Copyright 2009 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "debug.h" #include "perlinuxjoy.h" #include #include #include #include int PERLinuxJoyInit(void); void PERLinuxJoyDeInit(void); int PERLinuxJoyHandleEvents(void); u32 PERLinuxJoyScan(u32 flags); void PERLinuxJoyFlush(void); void PERLinuxKeyName(u32 key, char * name, int size); PerInterface_struct PERLinuxJoy = { PERCORE_LINUXJOY, "Linux Joystick Interface", PERLinuxJoyInit, PERLinuxJoyDeInit, PERLinuxJoyHandleEvents, PERLinuxJoyScan, 1, PERLinuxJoyFlush, PERLinuxKeyName }; static int hJOY = -1; #define PACKEVENT(evt) ((evt.value < 0 ? 0x10000 : 0) | (evt.type << 8) | (evt.number)) ////////////////////////////////////////////////////////////////////////////// int PERLinuxJoyInit(void) { hJOY = open("/dev/input/js0", O_RDONLY | O_NONBLOCK); if (hJOY == -1) return -1; return 0; } ////////////////////////////////////////////////////////////////////////////// void PERLinuxJoyDeInit(void) { if (hJOY != -1) close(hJOY); } ////////////////////////////////////////////////////////////////////////////// int PERLinuxJoyHandleEvents(void) { struct js_event evt; if (hJOY == -1) return -1; while (read(hJOY, &evt, sizeof(struct js_event)) > 0) { if (evt.value != 0) { PerKeyDown(PACKEVENT(evt)); } else { PerKeyUp(PACKEVENT(evt)); PerKeyUp(0x10000 | PACKEVENT(evt)); } } // execute yabause if ( YabauseExec() != 0 ) { return -1; } // return success return 0; } ////////////////////////////////////////////////////////////////////////////// u32 PERLinuxJoyScan(u32 flags) { struct js_event evt; if (hJOY == -1) return 0; if (read(hJOY, &evt, sizeof(struct js_event)) <= 0) return 0; return PACKEVENT(evt); } ////////////////////////////////////////////////////////////////////////////// void PERLinuxJoyFlush(void) { struct js_event evt; if (hJOY == -1) return; while (read(hJOY, &evt, sizeof(struct js_event)) > 0); } ////////////////////////////////////////////////////////////////////////////// void PERLinuxKeyName(u32 key, char * name, UNUSED int size) { sprintf(name, "%x", (int)key); } yabause-0.9.13.1/src/cheat.h000644 001750 001750 00000003234 12256006135 017434 0ustar00guillaumeguillaume000000 000000 /* Copyright 2007 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CHEAT_H #define CHEAT_H #include "core.h" enum { CHEATTYPE_NONE=0, CHEATTYPE_ENABLE, CHEATTYPE_BYTEWRITE, CHEATTYPE_WORDWRITE, CHEATTYPE_LONGWRITE }; typedef struct { int type; u32 addr; u32 val; char *desc; int enable; } cheatlist_struct; int CheatInit(void); void CheatDeInit(void); int CheatAddCode(int type, u32 addr, u32 val); int CheatAddARCode(const char *code); int CheatChangeDescription(int type, u32 addr, u32 val, char *desc); int CheatChangeDescriptionByIndex(int i, char *desc); int CheatRemoveCode(int type, u32 addr, u32 val); int CheatRemoveCodeByIndex(int i); int CheatRemoveARCode(const char *code); void CheatClearCodes(void); void CheatEnableCode(int index); void CheatDisableCode(int index); void CheatDoPatches(void); cheatlist_struct *CheatGetList(int *cheatnum); int CheatSave(const char *filename); int CheatLoad(const char *filename); #endif yabause-0.9.13.1/src/persdljoy.c000644 001750 001750 00000020221 12256006173 020353 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_LIBSDL #ifdef __APPLE__ #include #else #include "SDL.h" #endif #include "debug.h" #include "persdljoy.h" #define SDL_MAX_AXIS_VALUE 0x110000 #define SDL_MIN_AXIS_VALUE 0x100000 #define SDL_HAT_VALUE 0x200000 #define SDL_MEDIUM_AXIS_VALUE (int)(32768 / 2) #define SDL_BUTTON_PRESSED 1 #define SDL_BUTTON_RELEASED 0 int PERSDLJoyInit(void); void PERSDLJoyDeInit(void); int PERSDLJoyHandleEvents(void); u32 PERSDLJoyScan(u32 flags); void PERSDLJoyFlush(void); void PERSDLKeyName(u32 key, char * name, int size); PerInterface_struct PERSDLJoy = { PERCORE_SDLJOY, "SDL Joystick Interface", PERSDLJoyInit, PERSDLJoyDeInit, PERSDLJoyHandleEvents, PERSDLJoyScan, 1, PERSDLJoyFlush, PERSDLKeyName }; typedef struct { SDL_Joystick* mJoystick; s16* mScanStatus; Uint8* mHatStatus; } PERSDLJoystick; unsigned int SDL_PERCORE_INITIALIZED = 0; unsigned int SDL_PERCORE_JOYSTICKS_INITIALIZED = 0; PERSDLJoystick* SDL_PERCORE_JOYSTICKS = 0; unsigned int SDL_HAT_VALUES[] = { SDL_HAT_UP, SDL_HAT_RIGHT, SDL_HAT_LEFT, SDL_HAT_DOWN }; const unsigned int SDL_HAT_VALUES_NUM = sizeof(SDL_HAT_VALUES) / sizeof(SDL_HAT_VALUES[0]); ////////////////////////////////////////////////////////////////////////////// int PERSDLJoyInit(void) { int i, j; // does not need init if already done if ( SDL_PERCORE_INITIALIZED ) { return 0; } #if defined (_MSC_VER) && SDL_VERSION_ATLEAST(2,0,0) SDL_SetMainReady(); #endif // init joysticks if ( SDL_InitSubSystem( SDL_INIT_JOYSTICK ) == -1 ) { return -1; } // ignore joysticks event in sdl event loop SDL_JoystickEventState( SDL_IGNORE ); // open joysticks SDL_PERCORE_JOYSTICKS_INITIALIZED = SDL_NumJoysticks(); SDL_PERCORE_JOYSTICKS = malloc(sizeof(PERSDLJoystick) * SDL_PERCORE_JOYSTICKS_INITIALIZED); for ( i = 0; i < SDL_PERCORE_JOYSTICKS_INITIALIZED; i++ ) { SDL_Joystick* joy = SDL_JoystickOpen( i ); SDL_JoystickUpdate(); SDL_PERCORE_JOYSTICKS[ i ].mJoystick = joy; SDL_PERCORE_JOYSTICKS[ i ].mScanStatus = joy ? malloc(sizeof(s16) * SDL_JoystickNumAxes( joy )) : 0; SDL_PERCORE_JOYSTICKS[ i ].mHatStatus = joy ? malloc(sizeof(Uint8) * SDL_JoystickNumHats( joy )) : 0; if ( joy ) { for ( j = 0; j < SDL_JoystickNumAxes( joy ); j++ ) { SDL_PERCORE_JOYSTICKS[ i ].mScanStatus[ j ] = SDL_JoystickGetAxis( joy, j ); } for ( j = 0; j < SDL_JoystickNumHats( joy ); j++ ) { SDL_PERCORE_JOYSTICKS[ i ].mHatStatus[ j ] = SDL_JoystickGetHat( joy, j ); } } } // success SDL_PERCORE_INITIALIZED = 1; return 0; } ////////////////////////////////////////////////////////////////////////////// void PERSDLJoyDeInit(void) { // close joysticks if ( SDL_PERCORE_INITIALIZED == 1 ) { int i; for ( i = 0; i < SDL_PERCORE_JOYSTICKS_INITIALIZED; i++ ) { #if SDL_VERSION_ATLEAST(2,0,0) if ( SDL_PERCORE_JOYSTICKS[ i ].mJoystick ) #else if ( SDL_JoystickOpened( i ) ) #endif { SDL_JoystickClose( SDL_PERCORE_JOYSTICKS[ i ].mJoystick ); free( SDL_PERCORE_JOYSTICKS[ i ].mScanStatus ); free( SDL_PERCORE_JOYSTICKS[ i ].mHatStatus ); } } free( SDL_PERCORE_JOYSTICKS ); } SDL_PERCORE_JOYSTICKS_INITIALIZED = 0; SDL_PERCORE_INITIALIZED = 0; // close sdl joysticks SDL_QuitSubSystem( SDL_INIT_JOYSTICK ); } ////////////////////////////////////////////////////////////////////////////// int PERSDLJoyHandleEvents(void) { int joyId; int i; int j; SDL_Joystick* joy; Sint16 cur; Uint8 buttonState; Uint8 newHatState; Uint8 oldHatState; int hatValue; // update joysticks states SDL_JoystickUpdate(); // check each joysticks for ( joyId = 0; joyId < SDL_PERCORE_JOYSTICKS_INITIALIZED; joyId++ ) { joy = SDL_PERCORE_JOYSTICKS[ joyId ].mJoystick; if ( !joy ) { continue; } // check axis for ( i = 0; i < SDL_JoystickNumAxes( joy ); i++ ) { cur = SDL_JoystickGetAxis( joy, i ); PerAxisValue((joyId << 18) | SDL_MEDIUM_AXIS_VALUE | i, (u8)(((int)cur+32768) >> 8)); if ( cur < -SDL_MEDIUM_AXIS_VALUE ) { PerKeyUp( (joyId << 18) | SDL_MAX_AXIS_VALUE | i ); PerKeyDown( (joyId << 18) | SDL_MIN_AXIS_VALUE | i ); } else if ( cur > SDL_MEDIUM_AXIS_VALUE ) { PerKeyUp( (joyId << 18) | SDL_MIN_AXIS_VALUE | i ); PerKeyDown( (joyId << 18) | SDL_MAX_AXIS_VALUE | i ); } else { PerKeyUp( (joyId << 18) | SDL_MIN_AXIS_VALUE | i ); PerKeyUp( (joyId << 18) | SDL_MAX_AXIS_VALUE | i ); } } // check buttons for ( i = 0; i < SDL_JoystickNumButtons( joy ); i++ ) { buttonState = SDL_JoystickGetButton( joy, i ); if ( buttonState == SDL_BUTTON_PRESSED ) { PerKeyDown( (joyId << 18) | (i +1) ); } else if ( buttonState == SDL_BUTTON_RELEASED ) { PerKeyUp( (joyId << 18) | (i +1) ); } } // check hats for ( i = 0; i < SDL_JoystickNumHats( joy ); i++ ) { newHatState = SDL_JoystickGetHat( joy, i ); oldHatState = SDL_PERCORE_JOYSTICKS[ joyId ].mHatStatus[ i ]; for ( j = 0 ; j < SDL_HAT_VALUES_NUM; j++ ) { hatValue = SDL_HAT_VALUES[ j ]; if ( oldHatState & hatValue && ~newHatState & hatValue ) { PerKeyUp( (joyId << 18) | SDL_HAT_VALUE | (hatValue << 4) | i ); } } for ( j = 0 ; j < SDL_HAT_VALUES_NUM; j++ ) { hatValue = SDL_HAT_VALUES[ j ]; if ( ~oldHatState & hatValue && newHatState & hatValue ) { PerKeyDown( (joyId << 18) | SDL_HAT_VALUE | (hatValue << 4) | i); } } SDL_PERCORE_JOYSTICKS[ joyId ].mHatStatus[ i ] = newHatState; } } // execute yabause if ( YabauseExec() != 0 ) { return -1; } // return success return 0; } ////////////////////////////////////////////////////////////////////////////// u32 PERSDLJoyScan( u32 flags ) { // init vars int joyId; int i; SDL_Joystick* joy; Sint16 cur; Uint8 hatState; // update joysticks states SDL_JoystickUpdate(); // check each joysticks for ( joyId = 0; joyId < SDL_PERCORE_JOYSTICKS_INITIALIZED; joyId++ ) { joy = SDL_PERCORE_JOYSTICKS[ joyId ].mJoystick; if ( !joy ) { continue; } // check axis for ( i = 0; i < SDL_JoystickNumAxes( joy ); i++ ) { cur = SDL_JoystickGetAxis( joy, i ); if ( cur != SDL_PERCORE_JOYSTICKS[ joyId ].mScanStatus[ i ] ) { if ( cur < -SDL_MEDIUM_AXIS_VALUE ) { if (flags & PERSF_AXIS) return (joyId << 18) | SDL_MEDIUM_AXIS_VALUE | i; if (flags & PERSF_HAT) return (joyId << 18) | SDL_MIN_AXIS_VALUE | i; } else if ( cur > SDL_MEDIUM_AXIS_VALUE ) { if (flags & PERSF_AXIS) return (joyId << 18) | SDL_MEDIUM_AXIS_VALUE | i; if (flags & PERSF_HAT) return (joyId << 18) | SDL_MAX_AXIS_VALUE | i; } } } if (flags & PERSF_BUTTON) { // check buttons for ( i = 0; i < SDL_JoystickNumButtons( joy ); i++ ) { if ( SDL_JoystickGetButton( joy, i ) == SDL_BUTTON_PRESSED ) { return (joyId << 18) | (i +1); break; } } } if (flags & PERSF_HAT) { // check hats for ( i = 0; i < SDL_JoystickNumHats( joy ); i++ ) { hatState = SDL_JoystickGetHat( joy, i ); switch (hatState) { case SDL_HAT_UP: case SDL_HAT_RIGHT: case SDL_HAT_DOWN: case SDL_HAT_LEFT: return (joyId << 18) | SDL_HAT_VALUE | (hatState << 4) | i; break; default: break; } } } } return 0; } void PERSDLJoyFlush(void) { } void PERSDLKeyName(u32 key, char * name, UNUSED int size) { sprintf(name, "%x", (int)key); } #endif yabause-0.9.13.1/src/cd-freebsd.c000644 001750 001750 00000010252 12256006161 020336 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004-2005 Theo Berkau Copyright 2004-2006 Guillaume Duhamel Copyright 2005 Joost Peters This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "cdbase.h" #include "debug.h" static int FreeBSDCDInit(const char *); static void FreeBSDCDDeInit(void); static s32 FreeBSDCDReadTOC(u32 *); static int FreeBSDCDGetStatus(void); static int FreeBSDCDReadSectorFAD(u32, void *); static void FreeBSDCDReadAheadFAD(u32); CDInterface ArchCD = { CDCORE_ARCH, "FreeBSD CD Drive", FreeBSDCDInit, FreeBSDCDDeInit, FreeBSDCDGetStatus, FreeBSDCDReadTOC, FreeBSDCDReadSectorFAD, FreeBSDCDReadAheadFAD, }; static int hCDROM; static int FreeBSDCDInit(const char * cdrom_name) { int bsize = 2352; if ((hCDROM = open(cdrom_name, O_RDONLY | O_NONBLOCK)) == -1) { LOG("CDInit (%s) failed\n", cdrom_name); return -1; } if (ioctl (hCDROM, CDRIOCSETBLOCKSIZE, &bsize) == -1) { return -1; } LOG("CDInit (%s) OK\n", cdrom_name); return 0; } static void FreeBSDCDDeInit(void) { if (hCDROM != -1) { close(hCDROM); } LOG("CDDeInit OK\n"); } static s32 FreeBSDCDReadTOC(u32 * TOC) { int success; struct ioc_toc_header ctTOC; struct ioc_read_toc_single_entry ctTOCent; int i, j; int add150 = 0; ctTOCent.address_format = CD_LBA_FORMAT; if (hCDROM != -1) { memset(TOC, 0xFF, 0xCC * 2); memset(&ctTOC, 0xFF, sizeof(struct ioc_toc_header)); if (ioctl(hCDROM, CDIOREADTOCHEADER, &ctTOC) == -1) { return 0; } ctTOCent.track = ctTOC.starting_track; ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); if (ctTOCent.entry.addr.lba == 0) add150 = 150; TOC[0] = ((ctTOCent.entry.control << 28) | (ctTOCent.entry.addr_type << 24) | ctTOCent.entry.addr.lba + add150); // convert TOC to saturn format for (i = ctTOC.starting_track + 1; i <= ctTOC.ending_track; i++) { ctTOCent.track = i; ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); TOC[i - 1] = (ctTOCent.entry.control << 28) | (ctTOCent.entry.addr_type << 24) | (ctTOCent.entry.addr.lba + add150); } // Do First, Last, and Lead out sections here ctTOCent.track = ctTOC.starting_track; ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); TOC[99] = (ctTOCent.entry.control << 28) | (ctTOCent.entry.addr_type << 24) | (ctTOC.starting_track << 16); ctTOCent.track = ctTOC.ending_track; ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); TOC[100] = (ctTOCent.entry.control << 28) | (ctTOCent.entry.addr_type << 24) | (ctTOC.starting_track << 16); ctTOCent.track = 0xAA; ioctl(hCDROM, CDIOREADTOCENTRY, &ctTOCent); TOC[101] = (ctTOCent.entry.control << 28) | (ctTOCent.entry.addr_type << 24) | (ctTOCent.entry.addr.lba + add150); return (0xCC * 2); } return 0; } static int FreeBSDCDGetStatus(void) { // 0 - CD Present, disc spinning // 1 - CD Present, disc not spinning // 2 - CD not present // 3 - Tray open // see ../windows/cd.cc for more info return 0; } static int FreeBSDCDReadSectorFAD(u32 FAD, void *buffer) { if (hCDROM != -1) { lseek(hCDROM, (FAD - 150) * 2352, SEEK_SET); read(hCDROM, buffer, 2352); return 1; } return 0; } static void FreeBSDCDReadAheadFAD(UNUSED u32 FAD) { // No-op } yabause-0.9.13.1/src/peripheral.c000644 001750 001750 00000053761 12256006135 020510 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006, 2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "debug.h" #include "peripheral.h" const char * PerPadNames[] = { "Up", "Right", "Down", "Left", "R", "L", "Start", "A", "B", "C", "X", "Y", "Z", NULL }; const char * PerMouseNames[] = { "A", "B", "C", "Start", NULL }; PortData_struct PORTDATA1; PortData_struct PORTDATA2; PerInterface_struct * PERCore = NULL; extern PerInterface_struct * PERCoreList[]; typedef struct { u8 name; void (*Press)(void *); void (*Release)(void *); void (*SetAxisValue)(void *, u32); void (*MoveAxis)(void *, s32, s32); } PerBaseConfig_struct; typedef struct { u32 key; PerBaseConfig_struct * base; void * controller; } PerConfig_struct; #define PERCB(func) ((void (*) (void *)) func) #define PERVALCB(func) ((void (*) (void *, u32)) func) #define PERMOVECB(func) ((void (*) (void *, s32, s32)) func) PerBaseConfig_struct perpadbaseconfig[] = { { PERPAD_UP, PERCB(PerPadUpPressed), PERCB(PerPadUpReleased), NULL, NULL }, { PERPAD_RIGHT, PERCB(PerPadRightPressed), PERCB(PerPadRightReleased), NULL, NULL }, { PERPAD_DOWN, PERCB(PerPadDownPressed), PERCB(PerPadDownReleased), NULL, NULL }, { PERPAD_LEFT, PERCB(PerPadLeftPressed), PERCB(PerPadLeftReleased), NULL, NULL }, { PERPAD_RIGHT_TRIGGER, PERCB(PerPadRTriggerPressed), PERCB(PerPadRTriggerReleased), NULL, NULL }, { PERPAD_LEFT_TRIGGER, PERCB(PerPadLTriggerPressed), PERCB(PerPadLTriggerReleased), NULL, NULL }, { PERPAD_START, PERCB(PerPadStartPressed), PERCB(PerPadStartReleased), NULL, NULL }, { PERPAD_A, PERCB(PerPadAPressed), PERCB(PerPadAReleased), NULL, NULL }, { PERPAD_B, PERCB(PerPadBPressed), PERCB(PerPadBReleased), NULL, NULL }, { PERPAD_C, PERCB(PerPadCPressed), PERCB(PerPadCReleased), NULL, NULL }, { PERPAD_X, PERCB(PerPadXPressed), PERCB(PerPadXReleased), NULL, NULL }, { PERPAD_Y, PERCB(PerPadYPressed), PERCB(PerPadYReleased), NULL, NULL }, { PERPAD_Z, PERCB(PerPadZPressed), PERCB(PerPadZReleased), NULL, NULL }, }; PerBaseConfig_struct permousebaseconfig[] = { { PERMOUSE_LEFT, PERCB(PerMouseLeftPressed), PERCB(PerMouseLeftReleased), NULL, NULL }, { PERMOUSE_MIDDLE, PERCB(PerMouseMiddlePressed), PERCB(PerMouseMiddleReleased), NULL, NULL }, { PERMOUSE_RIGHT, PERCB(PerMouseRightPressed), PERCB(PerMouseRightReleased), NULL, NULL }, { PERMOUSE_START, PERCB(PerMouseStartPressed), PERCB(PerMouseStartReleased), NULL, NULL }, { PERMOUSE_AXIS, NULL, NULL, NULL, PERMOVECB(PerMouseMove) }, }; PerBaseConfig_struct peranalogbaseconfig[] = { { PERANALOG_AXIS1, NULL, NULL, PERVALCB(PerAxis1Value), NULL }, { PERANALOG_AXIS2, NULL, NULL, PERVALCB(PerAxis2Value), NULL }, { PERANALOG_AXIS3, NULL, NULL, PERVALCB(PerAxis3Value), NULL }, { PERANALOG_AXIS4, NULL, NULL, PERVALCB(PerAxis4Value), NULL }, { PERANALOG_AXIS5, NULL, NULL, PERVALCB(PerAxis5Value), NULL }, { PERANALOG_AXIS6, NULL, NULL, PERVALCB(PerAxis6Value), NULL }, { PERANALOG_AXIS7, NULL, NULL, PERVALCB(PerAxis7Value), NULL }, }; static u32 perkeyconfigsize = 0; static PerConfig_struct * perkeyconfig = NULL; static void PerUpdateConfig(PerBaseConfig_struct * baseconfig, int nelems, void * controller); ////////////////////////////////////////////////////////////////////////////// int PerInit(int coreid) { int i; // So which core do we want? if (coreid == PERCORE_DEFAULT) coreid = 0; // Assume we want the first one // Go through core list and find the id for (i = 0; PERCoreList[i] != NULL; i++) { if (PERCoreList[i]->id == coreid) { // Set to current core PERCore = PERCoreList[i]; break; } } if (PERCore == NULL) return -1; if (PERCore->Init() != 0) return -1; return 0; } ////////////////////////////////////////////////////////////////////////////// void PerDeInit(void) { if (PERCore) PERCore->DeInit(); PERCore = NULL; } ////////////////////////////////////////////////////////////////////////////// void PerPadUpPressed(PerPad_struct * pad) { *pad->padbits &= 0xEF; SMPCLOG("Up\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadUpReleased(PerPad_struct * pad) { *pad->padbits |= ~0xEF; } ////////////////////////////////////////////////////////////////////////////// void PerPadDownPressed(PerPad_struct * pad) { *pad->padbits &= 0xDF; SMPCLOG("Down\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadDownReleased(PerPad_struct * pad) { *pad->padbits |= ~0xDF; } ////////////////////////////////////////////////////////////////////////////// void PerPadRightPressed(PerPad_struct * pad) { *pad->padbits &= 0x7F; SMPCLOG("Right\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadRightReleased(PerPad_struct * pad) { *pad->padbits |= ~0x7F; } ////////////////////////////////////////////////////////////////////////////// void PerPadLeftPressed(PerPad_struct * pad) { *pad->padbits &= 0xBF; SMPCLOG("Left\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadLeftReleased(PerPad_struct * pad) { *pad->padbits |= ~0xBF; } ////////////////////////////////////////////////////////////////////////////// void PerPadStartPressed(PerPad_struct * pad) { *pad->padbits &= 0xF7; SMPCLOG("Start\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadStartReleased(PerPad_struct * pad) { *pad->padbits |= ~0xF7; } ////////////////////////////////////////////////////////////////////////////// void PerPadAPressed(PerPad_struct * pad) { *pad->padbits &= 0xFB; SMPCLOG("A\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadAReleased(PerPad_struct * pad) { *pad->padbits |= ~0xFB; } ////////////////////////////////////////////////////////////////////////////// void PerPadBPressed(PerPad_struct * pad) { *pad->padbits &= 0xFE; SMPCLOG("B\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadBReleased(PerPad_struct * pad) { *pad->padbits |= ~0xFE; } ////////////////////////////////////////////////////////////////////////////// void PerPadCPressed(PerPad_struct * pad) { *pad->padbits &= 0xFD; SMPCLOG("C\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadCReleased(PerPad_struct * pad) { *pad->padbits |= ~0xFD; } ////////////////////////////////////////////////////////////////////////////// void PerPadXPressed(PerPad_struct * pad) { *(pad->padbits + 1) &= 0xBF; SMPCLOG("X\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadXReleased(PerPad_struct * pad) { *(pad->padbits + 1) |= ~0xBF; } ////////////////////////////////////////////////////////////////////////////// void PerPadYPressed(PerPad_struct * pad) { *(pad->padbits + 1) &= 0xDF; SMPCLOG("Y\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadYReleased(PerPad_struct * pad) { *(pad->padbits + 1) |= ~0xDF; } ////////////////////////////////////////////////////////////////////////////// void PerPadZPressed(PerPad_struct * pad) { *(pad->padbits + 1) &= 0xEF; SMPCLOG("Z\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadZReleased(PerPad_struct * pad) { *(pad->padbits + 1) |= ~0xEF; } ////////////////////////////////////////////////////////////////////////////// void PerPadRTriggerPressed(PerPad_struct * pad) { *(pad->padbits + 1) &= 0x7F; SMPCLOG("Right Trigger\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadRTriggerReleased(PerPad_struct * pad) { *(pad->padbits + 1) |= ~0x7F; } ////////////////////////////////////////////////////////////////////////////// void PerPadLTriggerPressed(PerPad_struct * pad) { *(pad->padbits + 1) &= 0xF7; SMPCLOG("Left Trigger\n"); } ////////////////////////////////////////////////////////////////////////////// void PerPadLTriggerReleased(PerPad_struct * pad) { *(pad->padbits + 1) |= ~0xF7; } ////////////////////////////////////////////////////////////////////////////// void PerMouseLeftPressed(PerMouse_struct * mouse) { *(mouse->mousebits) |= 1; } ////////////////////////////////////////////////////////////////////////////// void PerMouseLeftReleased(PerMouse_struct * mouse) { *(mouse->mousebits) &= 0xFFFE; } ////////////////////////////////////////////////////////////////////////////// void PerMouseMiddlePressed(PerMouse_struct * mouse) { *(mouse->mousebits) |= 4; } ////////////////////////////////////////////////////////////////////////////// void PerMouseMiddleReleased(PerMouse_struct * mouse) { *(mouse->mousebits) &= 0xFFFB; } ////////////////////////////////////////////////////////////////////////////// void PerMouseRightPressed(PerMouse_struct * mouse) { *(mouse->mousebits) |= 2; } ////////////////////////////////////////////////////////////////////////////// void PerMouseRightReleased(PerMouse_struct * mouse) { *(mouse->mousebits) &= 0xFFFD; } ////////////////////////////////////////////////////////////////////////////// void PerMouseStartPressed(PerMouse_struct * mouse) { *(mouse->mousebits) |= 8; } ////////////////////////////////////////////////////////////////////////////// void PerMouseStartReleased(PerMouse_struct * mouse) { *(mouse->mousebits) &= 0xFFF7; } ////////////////////////////////////////////////////////////////////////////// void PerMouseMove(PerMouse_struct * mouse, s32 dispx, s32 dispy) { int negx, negy, overflowx, overflowy; u8 diffx, diffy; negx = ((mouse->mousebits[0] >> 4) & 1); negy = ((mouse->mousebits[0] >> 5) & 1); overflowx = ((mouse->mousebits[0] >> 6) & 1); overflowy = ((mouse->mousebits[0] >> 7) & 1); if (negx) diffx = ~(mouse->mousebits[1]) & 0xFF; else diffx = mouse->mousebits[1]; if (negy) diffy = ~(mouse->mousebits[2]) & 0xFF; else diffy = mouse->mousebits[2]; if (dispx > 0) { if (negx) { if (dispx - diffx > 0) { diffx = dispx - diffx; negx = 0; } else diffx -= -dispx; } else diffx += dispx; } else { if (negx) diffx += -dispx; else { if (diffx + dispx > 0) diffx += dispx; else { diffx = -dispx - diffx; negx = 1; } } } if (dispy > 0) { if (negy) { if (dispy - diffy > 0) { diffy = dispy - diffy; negy = 0; } else diffy -= -dispy; } else diffy += dispy; } else { if (negy) diffy += -dispy; else { if (diffy + dispy > 0) diffy += dispy; else { diffy = -dispy - diffy; negy = 1; } } } mouse->mousebits[0] = (overflowy << 7) | (overflowx << 6) | (negy << 5) | (negx << 4) | (mouse->mousebits[0] & 0x0F); if (negx) mouse->mousebits[1] = ~(diffx); else mouse->mousebits[1] = diffx; if (negy) mouse->mousebits[2] = ~(diffy); else mouse->mousebits[2] = diffy; } ////////////////////////////////////////////////////////////////////////////// void PerAxis1Value(PerAnalog_struct * analog, u32 val) { analog->analogbits[2] = (u8)val; } ////////////////////////////////////////////////////////////////////////////// void PerAxis2Value(PerAnalog_struct * analog, u32 val) { analog->analogbits[3] = (u8)val; } ////////////////////////////////////////////////////////////////////////////// void PerAxis3Value(PerAnalog_struct * analog, u32 val) { analog->analogbits[4] = (u8)val; } ////////////////////////////////////////////////////////////////////////////// void PerAxis4Value(PerAnalog_struct * analog, u32 val) { analog->analogbits[5] = (u8)val; } ////////////////////////////////////////////////////////////////////////////// void PerAxis5Value(PerAnalog_struct * analog, u32 val) { analog->analogbits[6] = (u8)val; } ////////////////////////////////////////////////////////////////////////////// void PerAxis6Value(PerAnalog_struct * analog, u32 val) { analog->analogbits[7] = (u8)val; } ////////////////////////////////////////////////////////////////////////////// void PerAxis7Value(PerAnalog_struct * analog, u32 val) { analog->analogbits[8] = (u8)val; } ////////////////////////////////////////////////////////////////////////////// void * PerAddPeripheral(PortData_struct *port, int perid) { int pernum = port->data[0] & 0xF; int i; int peroffset=1; u8 size; int current = 1; void * controller; if (pernum == 0xF) return NULL; // if only one peripheral is connected use 0xF0, otherwise use 0x00 or 0x10 if (pernum == 0) { pernum = 1; port->data[0] = 0xF1; } else { if (pernum == 1) { u8 tmp = peroffset; tmp += (port->data[peroffset] & 0xF) + 1; for(i = 0;i < 5;i++) port->data[tmp + i] = 0xFF; } pernum = 6; port->data[0] = 0x16; // figure out where we're at, then add peripheral id + 1 current = 0; size = port->data[peroffset] & 0xF; while ((current < pernum) && (size != 0xF)) { peroffset += size + 1; current++; size = port->data[peroffset] & 0xF; } if (current == pernum) { return NULL; } current++; } port->data[peroffset] = perid; peroffset++; // set peripheral data for peripheral to default values and adjust size // of port data switch (perid) { case PERPAD: port->data[peroffset] = 0xFF; port->data[peroffset+1] = 0xFF; port->size = peroffset+(perid&0xF); break; case PERWHEEL: port->data[peroffset] = 0xFF; port->data[peroffset+1] = 0xFF; port->data[peroffset+2] = 0x7F; port->size = peroffset+(perid&0xF); break; case PER3DPAD: port->data[peroffset] = 0xFF; port->data[peroffset+1] = 0xFF; port->data[peroffset+2] = 0x7F; port->data[peroffset+3] = 0x7F; port->data[peroffset+4] = 0x7F; port->data[peroffset+5] = 0x7F; port->size = peroffset+(perid&0xF); break; case PERKEYBOARD: port->data[peroffset] = 0xFF; port->data[peroffset+1] = 0xF8; port->data[peroffset+2] = 0x06; port->data[peroffset+3] = 0x00; port->size = peroffset+(perid&0xF); break; case PERMOUSE: port->data[peroffset] = 0; port->data[peroffset + 1] = 0; port->data[peroffset + 2] = 0; port->size = peroffset+(perid&0xF); break; default: break; } { u8 tmp = peroffset; tmp += (perid & 0xF); for(i = 0;i < (pernum - current);i++) { port->data[tmp + i] = 0xFF; port->size++; } } controller = (port->data + (peroffset - 1)); switch (perid) { case PERPAD: PerUpdateConfig(perpadbaseconfig, sizeof(perpadbaseconfig)/sizeof(PerBaseConfig_struct), controller); break; case PERWHEEL: case PERMISSIONSTICK: case PER3DPAD: case PERTWINSTICKS: PerUpdateConfig(perpadbaseconfig, sizeof(perpadbaseconfig)/sizeof(PerBaseConfig_struct), controller); PerUpdateConfig(peranalogbaseconfig, sizeof(peranalogbaseconfig)/sizeof(PerBaseConfig_struct), controller); break; case PERMOUSE: PerUpdateConfig(permousebaseconfig, sizeof(permousebaseconfig)/sizeof(PerBaseConfig_struct), controller); break; } return controller; } ////////////////////////////////////////////////////////////////////////////// int PerGetId(void * peripheral) { u8 * id = peripheral; return *id; } ////////////////////////////////////////////////////////////////////////////// void PerFlush(PortData_struct * port) { /* FIXME this function only flush data if there's a mouse connected as * first peripheral */ u8 perid = port->data[1]; if (perid == 0xE3) { PerMouse_struct * mouse = (PerMouse_struct *) (port->data + 1); mouse->mousebits[0] &= 0x0F; mouse->mousebits[1] = 0; mouse->mousebits[2] = 0; } } ////////////////////////////////////////////////////////////////////////////// void PerKeyDown(u32 key) { unsigned int i = 0; while(i < perkeyconfigsize) { if (key == perkeyconfig[i].key) { perkeyconfig[i].base->Press(perkeyconfig[i].controller); } i++; } } ////////////////////////////////////////////////////////////////////////////// void PerKeyUp(u32 key) { unsigned int i = 0; while(i < perkeyconfigsize) { if (key == perkeyconfig[i].key) { perkeyconfig[i].base->Release(perkeyconfig[i].controller); } i++; } } ////////////////////////////////////////////////////////////////////////////// void PerSetKey(u32 key, u8 name, void * controller) { unsigned int i = 0; while(i < perkeyconfigsize) { if ((name == perkeyconfig[i].base->name) && (controller == perkeyconfig[i].controller)) { perkeyconfig[i].key = key; } i++; } } ////////////////////////////////////////////////////////////////////////////// void PerAxisValue(u32 key, u8 val) { unsigned int i = 0; while(i < perkeyconfigsize) { if (key == perkeyconfig[i].key) { if (perkeyconfig[i].base->SetAxisValue) perkeyconfig[i].base->SetAxisValue(perkeyconfig[i].controller, val); } i++; } } ////////////////////////////////////////////////////////////////////////////// void PerAxisMove(u32 key, s32 dispx, s32 dispy) { unsigned int i = 0; while(i < perkeyconfigsize) { if (key == perkeyconfig[i].key) { if (perkeyconfig[i].base->MoveAxis) perkeyconfig[i].base->MoveAxis(perkeyconfig[i].controller, dispx, dispy); } i++; } } ////////////////////////////////////////////////////////////////////////////// void PerPortReset(void) { PORTDATA1.data[0] = 0xF0; PORTDATA1.size = 1; PORTDATA2.data[0] = 0xF0; PORTDATA2.size = 1; perkeyconfigsize = 0; if (perkeyconfig) free(perkeyconfig); perkeyconfig = NULL; } ////////////////////////////////////////////////////////////////////////////// void PerUpdateConfig(PerBaseConfig_struct * baseconfig, int nelems, void * controller) { u32 oldsize = perkeyconfigsize; u32 i, j; perkeyconfigsize += nelems; perkeyconfig = realloc(perkeyconfig, perkeyconfigsize * sizeof(PerConfig_struct)); j = 0; for(i = oldsize;i < perkeyconfigsize;i++) { perkeyconfig[i].base = baseconfig + j; perkeyconfig[i].controller = controller; j++; } } ////////////////////////////////////////////////////////////////////////////// PerPad_struct * PerPadAdd(PortData_struct * port) { return PerAddPeripheral(port, PERPAD); } ////////////////////////////////////////////////////////////////////////////// PerMouse_struct * PerMouseAdd(PortData_struct * port) { return PerAddPeripheral(port, PERMOUSE); } ////////////////////////////////////////////////////////////////////////////// PerAnalog_struct * PerWheelAdd(PortData_struct * port) { return PerAddPeripheral(port, PERWHEEL); } ////////////////////////////////////////////////////////////////////////////// PerAnalog_struct * PerMissionStickAdd(PortData_struct * port) { return PerAddPeripheral(port, PERMISSIONSTICK); } ////////////////////////////////////////////////////////////////////////////// PerAnalog_struct * Per3DPadAdd(PortData_struct * port) { return PerAddPeripheral(port, PER3DPAD); } ////////////////////////////////////////////////////////////////////////////// PerAnalog_struct * PerTwinSticksAdd(PortData_struct * port) { return PerAddPeripheral(port, PERTWINSTICKS); } ////////////////////////////////////////////////////////////////////////////// // Dummy Interface ////////////////////////////////////////////////////////////////////////////// int PERDummyInit(void); void PERDummyDeInit(void); int PERDummyHandleEvents(void); //static PortData_struct port1; //static PortData_struct port2; u32 PERDummyScan(u32 flags); void PERDummyFlush(void); void PERDummyKeyName(u32 key, char * name, int size); PerInterface_struct PERDummy = { PERCORE_DUMMY, "Dummy Input Interface", PERDummyInit, PERDummyDeInit, PERDummyHandleEvents, PERDummyScan, 0, PERDummyFlush, PERDummyKeyName }; ////////////////////////////////////////////////////////////////////////////// int PERDummyInit(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// void PERDummyDeInit(void) { } ////////////////////////////////////////////////////////////////////////////// int PERDummyHandleEvents(void) { if (YabauseExec() != 0) return -1; return 0; } ////////////////////////////////////////////////////////////////////////////// u32 PERDummyScan(u32 flags) { // Scan and return next action based on flags value // See PERSF_* in peripheral.h for full list of flags. // If no specified flags are supported return 0 return 0; } ////////////////////////////////////////////////////////////////////////////// void PERDummyFlush(void) { } ////////////////////////////////////////////////////////////////////////////// void PERDummyKeyName(UNUSED u32 key, char * name, UNUSED int size) { *name = 0; } yabause-0.9.13.1/src/cd-macosx.c000644 001750 001750 00000013552 12256006161 020224 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004-2005 Lucas Newman Copyright 2004-2005 Theo Berkau Copyright 2005 Weston Yager Copyright 2006-2008 Guillaume Duhamel Copyright 2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cdbase.h" static int MacOSXCDInit(const char *); static void MacOSXCDDeInit(void); static int MacOSXCDGetStatus(void); static s32 MacOSXCDReadTOC(u32 *); static int MacOSXCDReadSectorFAD(u32, void *); static void MacOSXCDReadAheadFAD(u32); CDInterface ArchCD = { CDCORE_ARCH, "MacOSX CD Drive", MacOSXCDInit, MacOSXCDDeInit, MacOSXCDGetStatus, MacOSXCDReadTOC, MacOSXCDReadSectorFAD, MacOSXCDReadAheadFAD, }; static int hCDROM; static int MacOSXCDInit(const char * useless_for_now) { CFMutableDictionaryRef classesToMatch; io_iterator_t mediaIterator; io_object_t media; char cdrom_name[ MAXPATHLEN ]; classesToMatch = IOServiceMatching(kIOCDMediaClass); CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue); IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &mediaIterator); media = IOIteratorNext(mediaIterator); if(media) { CFTypeRef path; path = IORegistryEntryCreateCFProperty(media, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, 0); if (path) { size_t length; strcpy(cdrom_name, _PATH_DEV); strcat(cdrom_name, "r"); length = strlen(cdrom_name); CFStringGetCString(path, cdrom_name + length, MAXPATHLEN - length, kCFStringEncodingUTF8); CFRelease(path); } IOObjectRelease(media); } if ((hCDROM = open(cdrom_name, O_RDONLY)) == -1) { return -1; } return 0; } static void MacOSXCDDeInit(void) { if (hCDROM != -1) { close(hCDROM); } } static CDTOC * GetTOCFromCDPath(void) { CFMutableDictionaryRef classesToMatch; io_iterator_t mediaIterator; io_object_t media; CDTOC * TOC; classesToMatch = IOServiceMatching(kIOCDMediaClass); CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue); IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &mediaIterator); media = IOIteratorNext(mediaIterator); if(media) { CFDataRef TOCData = IORegistryEntryCreateCFProperty(media, CFSTR(kIOCDMediaTOCKey), kCFAllocatorDefault, 0); TOC = malloc(CFDataGetLength(TOCData)); CFDataGetBytes(TOCData,CFRangeMake(0,CFDataGetLength(TOCData)),(UInt8 *)TOC); CFRelease(TOCData); IOObjectRelease(media); } return TOC; } static s32 MacOSXCDReadTOC(u32 *TOC) { int add150 = 150, tracks = 0; u_char track; int i, fad = 0; CDTOC *cdTOC = GetTOCFromCDPath(); CDTOCDescriptor *pTrackDescriptors; pTrackDescriptors = cdTOC->descriptors; memset(TOC, 0xFF, 0xCC * 2); /* Convert TOC to Saturn format */ for( i = 3; i < CDTOCGetDescriptorCount(cdTOC); i++ ) { track = pTrackDescriptors[i].point; fad = CDConvertMSFToLBA(pTrackDescriptors[i].p) + add150; if ((track > 99) || (track < 1)) continue; TOC[i-3] = (pTrackDescriptors[i].control << 28 | pTrackDescriptors[i].adr << 24 | fad); tracks++; } /* First */ TOC[99] = pTrackDescriptors[0].control << 28 | pTrackDescriptors[0].adr << 24 | 1 << 16; /* Last */ TOC[100] = pTrackDescriptors[1].control << 28 | pTrackDescriptors[1].adr << 24 | tracks << 16; /* Leadout */ TOC[101] = pTrackDescriptors[2].control << 28 | pTrackDescriptors[2].adr << 24 | CDConvertMSFToLBA(pTrackDescriptors[2].p) + add150; //free(cdTOC); Looks like this is not need, will look into that. return (0xCC * 2); } static int MacOSXCDGetStatus(void) { // 0 - CD Present, disc spinning // 1 - CD Present, disc not spinning // 2 - CD not present // 3 - Tray open // see ../windows/cd.cc for more info //Return that disc is present and spinning. 2 and 3 can't happen in the mac port, i don't understand what "not spinning" is supposed to say return 0; } static int MacOSXCDReadSectorFAD(u32 FAD, void *buffer) { const int blockSize = 2352; #ifdef CRAB_REWRITE const int cacheBlocks = 32; static u8 cache[blockSize * cacheBlocks]; static u32 cacheFAD = 0xFFFFFF00; #endif if (hCDROM != -1) { #ifdef CRAB_REWRITE /* See if the block we are looking for is in the cache already... */ if(FAD < cacheFAD || FAD >= cacheFAD + cacheBlocks) { /* Cache miss, read some blocks from the cd, then we'll hit the cache below. */ if(!pread(hCDROM, cache, blockSize * cacheBlocks, (FAD - 150) * blockSize)) { return 0; } cacheFAD = FAD; } /* Cache hit, copy the block out. */ memcpy(buffer, cache + (blockSize * (FAD - cacheFAD)), blockSize); return 1; #else if (pread(hCDROM, buffer, blockSize, (FAD - 150) * blockSize)) return true; #endif } return false; } static void MacOSXCDReadAheadFAD(UNUSED u32 FAD) { // No-op } yabause-0.9.13.1/src/thr-linux.c000644 001750 001750 00000005522 12256006174 020302 0ustar00guillaumeguillaume000000 000000 /* src/thr-linux.c: Thread functions for Linux Copyright 2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "core.h" #include "threads.h" #include #include #include #include ////////////////////////////////////////////////////////////////////////////// // Thread handles for each Yabause subthread static pthread_t thread_handle[YAB_NUM_THREADS]; ////////////////////////////////////////////////////////////////////////////// static void dummy_sighandler(int signum_unused) {} // For thread sleep/wake int YabThreadStart(unsigned int id, void (*func)(void *), void *arg) { // Set up a dummy signal handler for SIGUSR1 so we can return from pause() // in YabThreadSleep() static const struct sigaction sa = {.sa_handler = dummy_sighandler}; if (sigaction(SIGUSR1, &sa, NULL) != 0) { perror("sigaction(SIGUSR1)"); return -1; } if (thread_handle[id]) { fprintf(stderr, "YabThreadStart: thread %u is already started!\n", id); return -1; } if ((errno = pthread_create(&thread_handle[id], NULL, (void *)func, arg)) != 0) { perror("pthread_create"); return -1; } return 0; } ////////////////////////////////////////////////////////////////////////////// void YabThreadWait(unsigned int id) { if (!thread_handle[id]) return; // Thread wasn't running in the first place pthread_join(thread_handle[id], NULL); thread_handle[id] = 0; } ////////////////////////////////////////////////////////////////////////////// void YabThreadYield(void) { sched_yield(); } ////////////////////////////////////////////////////////////////////////////// void YabThreadSleep(void) { pause(); } ////////////////////////////////////////////////////////////////////////////// void YabThreadRemoteSleep(unsigned int id) { } ////////////////////////////////////////////////////////////////////////////// void YabThreadWake(unsigned int id) { if (!thread_handle[id]) return; // Thread isn't running pthread_kill(thread_handle[id], SIGUSR1); } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/dreamcast/viddc.h000644 001750 001750 00000001610 12256006113 021374 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef VIDDC_H #define VIDDC_H #include "../vdp1.h" #define VIDCORE_DC 3 extern VideoInterface_struct VIDDC; #endif yabause-0.9.13.1/src/dreamcast/localtime.h000644 001750 001750 00000000226 12256006113 022256 0ustar00guillaumeguillaume000000 000000 /* internal_localtime_r() function declaration, included by smpc.c */ extern struct tm * internal_localtime_r(const time_t * tim_p, struct tm *res); yabause-0.9.13.1/src/dreamcast/localtime.c000644 001750 001750 00000004510 12256006113 022251 0ustar00guillaumeguillaume000000 000000 /* * localtime_r.c * Original Author: Adapted from tzcode maintained by Arthur David Olson. * * Converts the calendar time pointed to by tim_p into a broken-down time * expressed as local time. Returns a pointer to a structure containing the * broken-down time. */ /* This file was taken from newlib , it's a * modified version of Arthur David Olsons localtime.c from tzcode which * is under Public Domain */ #include #include #include "localtime.h" #define SECSPERMIN 60L #define MINSPERHOUR 60L #define HOURSPERDAY 24L #define SECSPERHOUR (SECSPERMIN * MINSPERHOUR) #define SECSPERDAY (SECSPERHOUR * HOURSPERDAY) #define DAYSPERWEEK 7 #define MONSPERYEAR 12 #define YEAR_BASE 1900 #define EPOCH_YEAR 1970 #define EPOCH_WDAY 4 #define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0) static const int mon_lengths[2][MONSPERYEAR] = { {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} } ; static const int year_lengths[2] = { 365, 366 } ; struct tm * internal_localtime_r(const time_t * tim_p, struct tm *res) { long days, rem; int y; int yleap; const int *ip; days = ((long) *tim_p) / SECSPERDAY; rem = ((long) *tim_p) % SECSPERDAY; while (rem < 0) { rem += SECSPERDAY; --days; } while (rem >= SECSPERDAY) { rem -= SECSPERDAY; ++days; } /* compute hour, min, and sec */ res->tm_hour = (int) (rem / SECSPERHOUR); rem %= SECSPERHOUR; res->tm_min = (int) (rem / SECSPERMIN); res->tm_sec = (int) (rem % SECSPERMIN); /* compute day of week */ if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0) res->tm_wday += DAYSPERWEEK; /* compute year & day of year */ y = EPOCH_YEAR; if (days >= 0) { for (;;) { yleap = isleap(y); if (days < year_lengths[yleap]) break; y++; days -= year_lengths[yleap]; } } else { do { --y; yleap = isleap(y); days += year_lengths[yleap]; } while (days < 0); } res->tm_year = y - YEAR_BASE; res->tm_yday = days; ip = mon_lengths[yleap]; for (res->tm_mon = 0; days >= ip[res->tm_mon]; ++res->tm_mon) days -= ip[res->tm_mon]; res->tm_mday = days + 1; /* set daylight saving time flag */ res->tm_isdst = -1; return (res); } yabause-0.9.13.1/src/dreamcast/viddc.c000644 001750 001750 00000241716 12256006113 021404 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2006 Guillaume Duhamel Copyright 2004-2009 Lawrence Sebald Copyright 2004-2006 Theo Berkau Copyright 2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "viddc.h" #include "../debug.h" #include "../vdp2.h" #include #include #include #include #include #include #include #include #define SAT2YAB1(temp) ((temp & 0x8000) | (temp & 0x1F) << 10 | \ (temp & 0x3E0) | (temp & 0x7C00) >> 10) #define SAT2YAB32(alpha, temp) (alpha << 24 | (temp & 0x1F) << 3 | \ (temp & 0x3E0) << 6 | (temp & 0x7C00) << 9) #define SAT2YAB2(dot1, dot2) ((dot1 & 0xF8) << 7 | (dot2 & 0xF800) >> 6 | \ (dot2 & 0x00F8) >> 3 | 0x8000) #define SAT2YAB2_32(alpha, dot1, dot2) (alpha << 24 | ((dot1 & 0xFF) << 16) | \ (dot2 & 0xFF00) | (dot2 & 0xFF)) #define COLOR_ADDt32(b) (b > 0xFF ? 0xFF : (b < 0 ? 0 : b)) #define COLOR_ADDb32(b1,b2) COLOR_ADDt32((signed) (b1) + (b2)) #define COLOR_ADD32(l,r,g,b) COLOR_ADDb32((l & 0xFF), r) | \ (COLOR_ADDb32((l >> 8) & 0xFF, g) << 8) | \ (COLOR_ADDb32((l >> 16) & 0xFF, b) << 16) | \ (l & 0xFF000000) #define COLOR_ADDt(b) (b > 0xF8 ? 0xF8 : (b < 0x08 ? 0 : b)) #define COLOR_ADDb(b1,b2) COLOR_ADDt((signed) (b1) + (b2)) #define COLOR_ADD(l,r,g,b) ((COLOR_ADDb((l >> 7) & 0xF8, \ r) & 0xF8) << 7) | \ ((COLOR_ADDb((l >> 2) & 0xF8, \ g) & 0xF8) << 2) | \ ((COLOR_ADDb((l << 3) & 0xF8, \ b) & 0xF8) >> 3) | \ (l & 0x8000) static pvr_init_params_t pvr_params = { /* Enable Opaque, Translucent, and Punch-Thru polygons with binsize 16 */ { PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_16, PVR_BINSIZE_0, PVR_BINSIZE_16 }, /* 512KB Vertex Buffer */ 512 * 1024, /* DMA Enabled */ 1, /* FSAA Disabled */ 0 }; struct sprite_info { uint32 pvr_base; uint32 vdp1_base; float uf, vf; int w, h; }; typedef struct { int cellw, cellh; int flipfunction; int priority; int mapwh; int planew, planeh; int pagewh; int patternwh; int patterndatasize; int specialfunction; u32 addr, charaddr, paladdr; int colornumber; int isbitmap; u16 supplementdata; int auxmode; int enable; int x, y; int alpha; int coloroffset; int transparencyenable; int specialprimode; s32 cor; s32 cog; s32 cob; float coordincx, coordincy; void (* PlaneAddr)(void *, int); u16 (* PostPixelFetchCalc)(void *, u16); int patternpixelwh; int draww; int drawh; } vdp2draw_struct; static struct sprite_info cur_spr; static struct sprite_info cache[1024]; int cached_spr = 0; /* Polygon Headers */ static pvr_sprite_hdr_t op_poly_hdr; static pvr_sprite_hdr_t tr_poly_hdr; static pvr_sprite_hdr_t tr_sprite_hdr; static pvr_sprite_hdr_t pt_sprite_hdr; /* DMA Vertex Buffers 256KB Each */ static uint8 vbuf_opaque[1024 * 256] __attribute__((aligned(32))); static uint8 vbuf_translucent[1024 * 256] __attribute__((aligned(32))); static uint8 vbuf_punchthru[1024 * 256] __attribute__((aligned(32))); /* VDP2 Framebuffer */ static uint16 *vdp2_fb; static int vdp2_fbnum = 0; static uint16 vdp2_fbs[2][512 * 256] __attribute__((aligned(32))); static uint8 vdp2_prio[352][240]; static semaphore_t *dmadone; static pvr_ptr_t vdp2_tex; static uint32 cur_vdp2; /* Priority levels, sprites drawn last get drawn on top */ static float priority_levels[8]; /* Texture space for VDP1 sprites */ static pvr_ptr_t tex_space; static uint32 cur_addr; /* Misc parameters */ static int vdp1cor = 0; static int vdp1cog = 0; static int vdp1cob = 0; static int nbg0priority = 0; static int nbg1priority = 0; static int nbg2priority = 0; static int nbg3priority = 0; static int rbg0priority = 0; static int vdp2width = 320; static int vdp2height = 224; /* Frame counter */ static time_t lastup; static int framecount; static int power_of_two(int num) { int ret = 8; while(ret < num) ret <<= 1; return ret; } static inline void vdp2putpixel(s32 x, s32 y, u16 color, int priority) { vdp2_fb[(y * 512) + x] = color; vdp2_prio[x][y] = (uint8) priority; } static u32 Vdp2ColorRamGetColor32(u32 colorindex, int alpha) { switch(Vdp2Internal.ColorMode) { case 0: case 1: { u32 tmp; colorindex <<= 1; tmp = T2ReadWord(Vdp2ColorRam, colorindex & 0xFFF); return SAT2YAB32(alpha, tmp); } case 2: { u32 tmp1, tmp2; colorindex <<= 2; colorindex &= 0xFFF; tmp1 = T2ReadWord(Vdp2ColorRam, colorindex); tmp2 = T2ReadWord(Vdp2ColorRam, colorindex+2); return SAT2YAB2_32(alpha, tmp1, tmp2); } default: break; } return 0; } static uint16 Vdp2ColorRamGetColor(u32 colorindex) { u16 tmp; switch(Vdp2Internal.ColorMode) { case 0: case 1: { colorindex <<= 1; tmp = T2ReadWord(Vdp2ColorRam, colorindex & 0xFFF); return SAT2YAB1(tmp) | 0x8000; } case 2: { u16 tmp2; colorindex <<= 2; colorindex &= 0xFFF; tmp = T2ReadWord(Vdp2ColorRam, colorindex); tmp2 = T2ReadWord(Vdp2ColorRam, colorindex+2); return SAT2YAB2(tmp, tmp2) | 0x8000; } default: break; } return 0; } static int Vdp1ReadTexture(vdp1cmd_struct *cmd, pvr_sprite_hdr_t *hdr) { u32 charAddr = cmd->CMDSRCA << 3; uint16 dot, dot2; int queuepos = 0; uint32 *store_queue; uint32 cur_base; u8 SPD = ((cmd->CMDPMOD & 0x40) != 0); int k; int wi = power_of_two(cur_spr.w); int he = power_of_two(cur_spr.h); for(k = 0; k < cached_spr; ++k) { if(cache[k].vdp1_base == charAddr) { if(cache[k].w == cur_spr.w && cache[k].h == cur_spr.h) { cur_base = cache[k].pvr_base; goto fillHeader; } } } cur_base = cur_addr; /* Set up both Store Queues for transfer to VRAM */ QACR0 = 0x00000004; QACR1 = 0x00000004; switch((cmd->CMDPMOD >> 3) & 0x07) { case 0: { // 4 bpp Bank mode u16 temp; u32 colorBank = cmd->CMDCOLR; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; int i, j; for(i = 0; i < cur_spr.h; ++i) { store_queue = (uint32 *) (0xE0000000 | (cur_addr & 0x03FFFFE0)); for(j = 0; j < cur_spr.w; j += 2) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); if(((dot & 0xF) == 0) && !SPD) dot2 = 0; else { temp = Vdp2ColorRamGetColor(((dot & 0x0F) | colorBank) + colorOffset); dot2 = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); } if(((dot >> 4) == 0) && !SPD) dot = 0; else { temp = Vdp2ColorRamGetColor(((dot >> 4) | colorBank) + colorOffset); dot = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); } ++charAddr; store_queue[queuepos++] = dot | (dot2 << 16); if(queuepos == 8) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; store_queue += 8; } } if(queuepos) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; } cur_addr += wi * 2; } break; } case 1: { // 4 bpp LUT mode u16 temp; u32 colorLut = cmd->CMDCOLR * 8; int i, j; for(i = 0; i < cur_spr.h; ++i) { store_queue = (uint32 *) (0xE0000000 | (cur_addr & 0x03FFFFE0)); for(j = 0; j < cur_spr.w; j += 2) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); if(((dot & 0xF) == 0) && !SPD) dot2 = 0; else { temp = T1ReadWord(Vdp1Ram, ((dot & 0xF) * 2 + colorLut) & 0x7FFFF); if(temp & 0x8000) dot2 = COLOR_ADD(SAT2YAB1(temp), vdp1cor, vdp1cog, vdp1cob); else dot2 = COLOR_ADD(Vdp2ColorRamGetColor(temp), vdp1cor, vdp1cog, vdp1cob); } if(((dot >> 4) == 0) && !SPD) dot = 0; else { temp = T1ReadWord(Vdp1Ram, ((dot >> 4) * 2 + colorLut) & 0x7FFFF); if (temp & 0x8000) dot = COLOR_ADD(SAT2YAB1(temp), vdp1cor, vdp1cog, vdp1cob); else dot = COLOR_ADD(Vdp2ColorRamGetColor(temp), vdp1cor, vdp1cog, vdp1cob); } ++charAddr; store_queue[queuepos++] = dot | (dot2 << 16); if(queuepos == 8) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; store_queue += 8; } } if(queuepos) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; } cur_addr += wi * 2; } break; } case 2: { // 8 bpp (64 color) Bank mode int i, j; u32 colorBank = cmd->CMDCOLR; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; u16 temp; for(i = 0; i < cur_spr.h; ++i) { store_queue = (uint32 *) (0xE0000000 | (cur_addr & 0x03FFFFE0)); for(j = 0; j < cur_spr.w; j += 2) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF) & 0x3F; dot2 = T1ReadByte(Vdp1Ram, (charAddr + 1) & 0x7FFFF) & 0x3F; charAddr = charAddr + 2; if(dot || SPD) { temp = Vdp2ColorRamGetColor((dot | colorBank) + colorOffset); dot = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); } if(dot2 || SPD) { temp = Vdp2ColorRamGetColor((dot2 | colorBank) + colorOffset); dot2 = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); } store_queue[queuepos++] = dot | (dot2 << 16); if(queuepos == 8) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; store_queue += 8; } } if(queuepos) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; } cur_addr += wi * 2; } break; } case 3: { // 8 bpp (128 color) Bank mode int i, j; u32 colorBank = cmd->CMDCOLR; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; u16 temp; for(i = 0; i < cur_spr.h; ++i) { store_queue = (uint32 *) (0xE0000000 | (cur_addr & 0x03FFFFE0)); for(j = 0; j < cur_spr.w; j += 2) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF) & 0x7F; dot2 = T1ReadByte(Vdp1Ram, (charAddr + 1) & 0x7FFFF) & 0x7F; charAddr = charAddr + 2; if(dot || SPD) { temp = Vdp2ColorRamGetColor((dot | colorBank) + colorOffset); dot = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); } if(dot2 || SPD) { temp = Vdp2ColorRamGetColor((dot2 | colorBank) + colorOffset); dot2 = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); } store_queue[queuepos++] = dot | (dot2 << 16); if(queuepos == 8) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; store_queue += 8; } } if(queuepos) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; } cur_addr += wi * 2; } break; } case 4: { // 8 bpp (256 color) Bank mode int i, j; u32 colorBank = cmd->CMDCOLR; u32 colorOffset = (Vdp2Regs->CRAOFB & 0x70) << 4; u16 temp; for(i = 0; i < cur_spr.h; ++i) { store_queue = (uint32 *) (0xE0000000 | (cur_addr & 0x03FFFFE0)); for(j = 0; j < cur_spr.w; j += 2) { dot = T1ReadByte(Vdp1Ram, charAddr & 0x7FFFF); dot2 = T1ReadByte(Vdp1Ram, (charAddr + 1) & 0x7FFFF); charAddr = charAddr + 2; if(dot || SPD) { temp = Vdp2ColorRamGetColor((dot | colorBank) + colorOffset); dot = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); } if(dot2 || SPD) { temp = Vdp2ColorRamGetColor((dot2 | colorBank) + colorOffset); dot2 = COLOR_ADD(temp, vdp1cor, vdp1cog, vdp1cob); } store_queue[queuepos++] = dot | (dot2 << 16); if(queuepos == 8) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; store_queue += 8; } } if(queuepos) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; } cur_addr += wi * 2; } break; } case 5: { // 16 bpp Bank mode int i, j; for(i = 0; i < cur_spr.h; ++i) { store_queue = (uint32 *) (0xE0000000 | (cur_addr & 0x03FFFFE0)); for(j = 0; j < cur_spr.w; j += 2) { dot = T1ReadWord(Vdp1Ram, charAddr & 0x7FFFF); dot2 = T1ReadWord(Vdp1Ram, (charAddr + 2) & 0x7FFFF); charAddr = charAddr + 4; if(dot || SPD) dot = COLOR_ADD(SAT2YAB1(dot), vdp1cor, vdp1cog, vdp1cob); if(dot2 || SPD) dot2 = COLOR_ADD(SAT2YAB1(dot2), vdp1cor, vdp1cog, vdp1cob); store_queue[queuepos++] = dot | (dot2 << 16); if(queuepos == 8) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; store_queue += 8; } } if(queuepos) { asm("pref @%0" : : "r"(store_queue)); queuepos = 0; } cur_addr += wi * 2; } break; } default: VDP1LOG("Unimplemented sprite color mode: %X\n", (cmd->CMDPMOD >> 3) & 0x7); return 0; } if(cached_spr < 1023) { cache[cached_spr].vdp1_base = cmd->CMDSRCA << 3; cache[cached_spr].pvr_base = cur_base; cache[cached_spr].w = cur_spr.w; cache[cached_spr].h = cur_spr.h; cached_spr++; } fillHeader: cur_spr.uf = (float) cur_spr.w / wi; cur_spr.vf = (float) cur_spr.h / he; hdr->mode2 &= (~(PVR_TA_PM2_USIZE_MASK | PVR_TA_PM2_VSIZE_MASK)); switch (wi) { case 8: break; case 16: hdr->mode2 |= (1 << PVR_TA_PM2_USIZE_SHIFT); break; case 32: hdr->mode2 |= (2 << PVR_TA_PM2_USIZE_SHIFT); break; case 64: hdr->mode2 |= (3 << PVR_TA_PM2_USIZE_SHIFT); break; case 128: hdr->mode2 |= (4 << PVR_TA_PM2_USIZE_SHIFT); break; case 256: hdr->mode2 |= (5 << PVR_TA_PM2_USIZE_SHIFT); break; case 512: hdr->mode2 |= (6 << PVR_TA_PM2_USIZE_SHIFT); break; case 1024: hdr->mode2 |= (7 << PVR_TA_PM2_USIZE_SHIFT); break; default: assert_msg(0, "Invalid texture U size"); break; } switch (he) { case 8: break; case 16: hdr->mode2 |= (1 << PVR_TA_PM2_VSIZE_SHIFT); break; case 32: hdr->mode2 |= (2 << PVR_TA_PM2_VSIZE_SHIFT); break; case 64: hdr->mode2 |= (3 << PVR_TA_PM2_VSIZE_SHIFT); break; case 128: hdr->mode2 |= (4 << PVR_TA_PM2_VSIZE_SHIFT); break; case 256: hdr->mode2 |= (5 << PVR_TA_PM2_VSIZE_SHIFT); break; case 512: hdr->mode2 |= (6 << PVR_TA_PM2_VSIZE_SHIFT); break; case 1024: hdr->mode2 |= (7 << PVR_TA_PM2_VSIZE_SHIFT); break; default: assert_msg(0, "Invalid texture V size"); break; } hdr->mode3 = ((cur_base & 0x00FFFFF8) >> 3) | (PVR_TXRFMT_NONTWIDDLED); /* Make sure everything is aligned nicely... */ cur_addr = (cur_addr & 0x03FFFFE0) + 0x20; return 1; } static u8 Vdp1ReadPriority(vdp1cmd_struct *cmd) { u8 SPCLMD = Vdp2Regs->SPCTL; u8 sprite_register; u8 *sprprilist = (u8 *)&Vdp2Regs->PRISA; if ((SPCLMD & 0x20) && (cmd->CMDCOLR & 0x8000)) { // RGB data, use register 0 return Vdp2Regs->PRISA & 0x07; } else { u8 sprite_type = SPCLMD & 0x0F; switch(sprite_type) { case 0: sprite_register = ((cmd->CMDCOLR & 0x8000) | (~cmd->CMDCOLR & 0x4000)) >> 14; return sprprilist[sprite_register ^ 1] & 0x07; break; case 1: sprite_register = ((cmd->CMDCOLR & 0xC000) | (~cmd->CMDCOLR & 0x2000)) >> 13; return sprprilist[sprite_register ^ 1] & 0x07; break; case 3: sprite_register = ((cmd->CMDCOLR & 0x4000) | (~cmd->CMDCOLR & 0x2000)) >> 13; return sprprilist[sprite_register ^ 1] & 0x07; break; case 4: sprite_register = ((cmd->CMDCOLR & 0x4000) | (~cmd->CMDCOLR & 0x2000)) >> 13; return sprprilist[sprite_register ^ 1] & 0x07; break; case 5: sprite_register = ((cmd->CMDCOLR & 0x6000) | (~cmd->CMDCOLR & 0x1000)) >> 12; return sprprilist[sprite_register ^ 1] & 0x07; break; case 6: sprite_register = ((cmd->CMDCOLR & 0x6000) | (~cmd->CMDCOLR & 0x1000)) >> 12; return sprprilist[sprite_register ^ 1] & 0x07; break; case 7: sprite_register = ((cmd->CMDCOLR & 0x6000) | (~cmd->CMDCOLR & 0x1000)) >> 12; return sprprilist[sprite_register ^ 1] & 0x07; break; default: VDP1LOG("sprite type %d not implemented\n", sprite_type); return 0x07; break; } } } /* This has all been imported from the vidsoft.c file. It will be updated, hopefully (roughly) synchronized with updates to it */ ////////////////////////////////////////////////////////////////////////////// typedef struct { int xstart, ystart; int xend, yend; int pixeloffset; int lineincrement; } clipping_struct; static void inline HandleClipping(vdp2draw_struct *info, clipping_struct *clip) { clip->pixeloffset=0; clip->lineincrement=0; // Handle clipping(both window and screen clipping) if (info->x < 0) { clip->xstart = 0; clip->xend = (info->x+info->cellw); clip->pixeloffset = 0 - info->x; clip->lineincrement = 0 - info->x; } else { clip->xstart = info->x; if ((info->x+info->cellw) > vdp2width) { clip->xend = vdp2width; clip->lineincrement = (info->x+info->cellw) - vdp2width; } else clip->xend = (info->x+info->cellw); } if (info->y < 0) { clip->ystart = 0; clip->yend = (info->y+info->cellh); clip->pixeloffset = (info->cellw * (0 - info->y)) + clip->pixeloffset; } else { clip->ystart = info->y; if ((info->y+info->cellh) >= vdp2height) clip->yend = vdp2height; else clip->yend = (info->y+info->cellh); } } ////////////////////////////////////////////////////////////////////////////// void Vdp2DrawScrollBitmap(vdp2draw_struct *info) { int i, i2; clipping_struct clip; HandleClipping(info, &clip); switch (info->colornumber) { case 0: // 4 BPP(16 colors) // fix me printf("vdp2 bitmap 4 bpp draw\n"); return; case 1: // 8 BPP(256 colors) info->charaddr += clip.pixeloffset; for (i = clip.ystart; i < clip.yend; i++) { for (i2 = clip.xstart; i2 < clip.xend; i2++) { u16 color = T1ReadByte(Vdp2Ram, info->charaddr); info->charaddr++; if (color == 0 && info->transparencyenable) { vdp2putpixel(i2, i, 0, info->priority); } else { color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | color)); vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, color) | 0x8000, info->priority); } } info->charaddr += clip.lineincrement; } return; case 2: printf("vdp2 bitmap 16bpp palette draw\n"); break; case 3: // 15 BPP clip.pixeloffset *= 2; clip.lineincrement *= 2; info->charaddr += clip.pixeloffset; for (i = clip.ystart; i < clip.yend; i++) { for (i2 = clip.xstart; i2 < clip.xend; i2++) { u16 color = T1ReadWord(Vdp2Ram, info->charaddr); info->charaddr += 2; if ((color & 0x8000) == 0 && info->transparencyenable) vdp2_fb[(i * vdp2width) + i2] = 0; else vdp2_fb[(i * vdp2width) + i2] = info->PostPixelFetchCalc(info, SAT2YAB1(color)) | 0x8000; vdp2_prio[i][i2] = info->priority; } info->charaddr += clip.lineincrement; } return; case 4: // 24 BPP clip.pixeloffset *= 4; clip.lineincrement *= 4; info->charaddr += clip.pixeloffset; for (i = clip.ystart; i < clip.yend; i++) { for (i2 = clip.xstart; i2 < clip.xend; i2++) { u32 color = T1ReadLong(Vdp2Ram, info->charaddr); info->charaddr += 4; if ((color & 0x80000000) == 0 && info->transparencyenable) vdp2putpixel(i2, i, 0, info->priority); else { u16 dot = ((color & 0xF80000) >> 19 | (color & 0x00F800) >> 6 | (color & 0x0000F8) << 7 | 0x8000); vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, dot), info->priority); } } info->charaddr += clip.lineincrement; } return; default: break; } } ////////////////////////////////////////////////////////////////////////////// #define Vdp2DrawCell4bpp(mask, shift) \ if ((dot & mask) == 0 && info->transparencyenable) { \ vdp2putpixel(i2, i, 0, info->priority); \ } \ else \ { \ color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | ((dot & mask) >> shift))); \ vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, color), info->priority); \ } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawCell(vdp2draw_struct *info) { u32 color; int i, i2; clipping_struct clip; u32 newcharaddr; HandleClipping(info, &clip); if (info->flipfunction & 0x1) { // Horizontal flip } if (info->flipfunction & 0x2) { // Vertical flip // clip.pixeloffset = (info.w * info.h) - clip.pixeloffset; // clip.lineincrement = 0 - clip.lineincrement; } switch(info->colornumber) { case 0: // 4 BPP if ((clip.lineincrement | clip.pixeloffset) == 0) { for (i = clip.ystart; i < clip.yend; i++) { u32 dot; u16 color; i2 = clip.xstart; // Fetch Pixel 1/2/3/4/5/6/7/8 dot = T1ReadLong(Vdp2Ram, info->charaddr); info->charaddr+=4; // Draw 8 Pixels Vdp2DrawCell4bpp(0xF0000000, 28) i2++; Vdp2DrawCell4bpp(0x0F000000, 24) i2++; Vdp2DrawCell4bpp(0x00F00000, 20) i2++; Vdp2DrawCell4bpp(0x000F0000, 16) i2++; Vdp2DrawCell4bpp(0x0000F000, 12) i2++; Vdp2DrawCell4bpp(0x00000F00, 8) i2++; Vdp2DrawCell4bpp(0x000000F0, 4) i2++; Vdp2DrawCell4bpp(0x0000000F, 0) i2++; } } else { u8 dot; newcharaddr = info->charaddr + ((info->cellw * info->cellh) >> 1); info->charaddr <<= 1; info->charaddr += clip.pixeloffset; for (i = clip.ystart; i < clip.yend; i++) { dot = T1ReadByte(Vdp2Ram, info->charaddr >> 1); info->charaddr++; for (i2 = clip.xstart; i2 < clip.xend; i2++) { u32 color; // Draw two pixels if(info->charaddr & 0x1) { Vdp2DrawCell4bpp(0xF0, 4) info->charaddr++; } else { Vdp2DrawCell4bpp(0x0F, 0) dot = T1ReadByte(Vdp2Ram, info->charaddr >> 1); info->charaddr++; } } info->charaddr += clip.lineincrement; } info->charaddr = newcharaddr; } break; case 1: // 8 BPP newcharaddr = info->charaddr + (info->cellw * info->cellh); info->charaddr += clip.pixeloffset; for (i = clip.ystart; i < clip.yend; i++) { for (i2 = clip.xstart; i2 < clip.xend; i2++) { u16 color = T1ReadByte(Vdp2Ram, info->charaddr); info->charaddr++; if (color == 0 && info->transparencyenable) { vdp2putpixel(i2, i, 0, info->priority); } else { color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | color)); vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, color), info->priority); } } info->charaddr += clip.lineincrement; } info->charaddr = newcharaddr; break; case 2: // 16 BPP(palette) printf("vdp2 cell draw 16bpp palette\n"); break; case 3: // 16 BPP(RGB) printf("vdp2 cell draw 16bpp\n"); break; case 4: // 32 BPP newcharaddr = info->charaddr + (info->cellw * info->cellh); info->charaddr += clip.pixeloffset; for (i = clip.ystart; i < clip.yend; i++) { for (i2 = clip.xstart; i2 < clip.xend; i2++) { u16 dot1, dot2; dot1 = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); info->charaddr += 2; dot2 = T1ReadWord(Vdp2Ram, info->charaddr & 0x7FFFF); info->charaddr += 2; if (!(dot1 & 0x8000) && info->transparencyenable) continue; color = SAT2YAB2(dot1, dot2); vdp2putpixel(i2, i, info->PostPixelFetchCalc(info, color), info->priority); } info->charaddr += clip.lineincrement; } info->charaddr = newcharaddr; break; } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawPattern(vdp2draw_struct *info) { // if (info->specialprimode == 1) // tile.priority = (info->priority & 0xFFFFFFFE) | info->specialfunction; // else // tile.priority = info->priority; switch(info->patternwh) { case 1: Vdp2DrawCell(info); info->x += 8; info->y += 8; break; case 2: Vdp2DrawCell(info); info->x += 8; Vdp2DrawCell(info); info->x -= 8; info->y += 8; Vdp2DrawCell(info); info->x += 8; Vdp2DrawCell(info); info->x += 8; info->y += 8; break; } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2PatternAddr(vdp2draw_struct *info) { switch(info->patterndatasize) { case 1: { u16 tmp = T1ReadWord(Vdp2Ram, info->addr); info->addr += 2; info->specialfunction = (info->supplementdata >> 9) & 0x1; switch(info->colornumber) { case 0: // in 16 colors info->paladdr = ((tmp & 0xF000) >> 8) | ((info->supplementdata & 0xE0) << 3); break; default: // not in 16 colors info->paladdr = (tmp & 0x7000) >> 4; break; } switch(info->auxmode) { case 0: info->flipfunction = (tmp & 0xC00) >> 10; switch(info->patternwh) { case 1: info->charaddr = (tmp & 0x3FF) | ((info->supplementdata & 0x1F) << 10); break; case 2: info->charaddr = ((tmp & 0x3FF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x1C) << 10); break; } break; case 1: info->flipfunction = 0; switch(info->patternwh) { case 1: info->charaddr = (tmp & 0xFFF) | ((info->supplementdata & 0x1C) << 10); break; case 2: info->charaddr = ((tmp & 0xFFF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x10) << 10); break; } break; } break; } case 2: { u16 tmp1 = T1ReadWord(Vdp2Ram, info->addr); u16 tmp2 = T1ReadWord(Vdp2Ram, info->addr+2); info->addr += 4; info->charaddr = tmp2 & 0x7FFF; info->flipfunction = (tmp1 & 0xC000) >> 14; info->paladdr = (tmp1 & 0x7F) << 4; info->specialfunction = (tmp1 & 0x2000) >> 13; break; } } if (!(Vdp2Regs->VRSIZE & 0x8000)) info->charaddr &= 0x3FFF; info->charaddr *= 0x20; // selon Runik } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawPage(vdp2draw_struct *info) { int X, Y; int i, j; X = info->x; for(i = 0;i < info->pagewh;i++) { Y = info->y; info->x = X; for(j = 0;j < info->pagewh;j++) { info->y = Y; if ((info->x >= -info->patternpixelwh) && (info->y >= -info->patternpixelwh) && (info->x <= info->draww) && (info->y <= info->drawh)) { Vdp2PatternAddr(info); Vdp2DrawPattern(info); } else { info->addr += info->patterndatasize * 2; info->x += info->patternpixelwh; info->y += info->patternpixelwh; } } } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawPlane(vdp2draw_struct *info) { int X, Y; int i, j; X = info->x; for(i = 0;i < info->planeh;i++) { Y = info->y; info->x = X; for(j = 0;j < info->planew;j++) { info->y = Y; Vdp2DrawPage(info); } } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawMap(vdp2draw_struct *info) { int i, j; int X, Y; u32 lastplane; X = info->x; lastplane=0xFFFFFFFF; info->patternpixelwh = 8 * info->patternwh; info->draww = (int)((float)vdp2width / info->coordincx); info->drawh = (int)((float)vdp2height / info->coordincy); for(i = 0;i < info->mapwh;i++) { Y = info->y; info->x = X; for(j = 0;j < info->mapwh;j++) { info->y = Y; info->PlaneAddr(info, info->mapwh * i + j); if (info->addr != lastplane) { Vdp2DrawPlane(info); lastplane = info->addr; } } } } static int VIDDCInit(void) { pvr_sprite_cxt_t op_poly_cxt, tr_poly_cxt; pvr_sprite_cxt_t pt_sprite_cxt, tr_sprite_cxt; vid_set_mode(DM_320x240, PM_RGB565); if(pvr_init(&pvr_params)) { fprintf(stderr, "VIDDCInit() - error initializing PVR\n"); return -1; } dmadone = sem_create(1); pvr_set_vertbuf(PVR_LIST_OP_POLY, vbuf_opaque, 1024 * 256); pvr_set_vertbuf(PVR_LIST_TR_POLY, vbuf_translucent, 1024 * 256); pvr_set_vertbuf(PVR_LIST_PT_POLY, vbuf_punchthru, 1024 * 256); tex_space = pvr_mem_malloc(1024 * 1024 * 2); vdp2_tex = pvr_mem_malloc(512 * 256 * 4 * 2); cur_addr = (uint32)tex_space; printf("PVR Memory Available: %lu\n", pvr_mem_available()); sq_set(tex_space, 0xFF, 1024 * 1024 * 2); pvr_sprite_cxt_col(&op_poly_cxt, PVR_LIST_OP_POLY); pvr_sprite_cxt_col(&tr_poly_cxt, PVR_LIST_TR_POLY); op_poly_cxt.gen.culling = PVR_CULLING_NONE; tr_poly_cxt.gen.culling = PVR_CULLING_NONE; pvr_sprite_compile(&op_poly_hdr, &op_poly_cxt); pvr_sprite_compile(&tr_poly_hdr, &tr_poly_cxt); pvr_sprite_cxt_txr(&tr_sprite_cxt, PVR_LIST_TR_POLY, PVR_TXRFMT_ARGB1555 | PVR_TXRFMT_NONTWIDDLED, 1024, 1024, tex_space, PVR_FILTER_NONE); pvr_sprite_cxt_txr(&pt_sprite_cxt, PVR_LIST_PT_POLY, PVR_TXRFMT_ARGB1555 | PVR_TXRFMT_NONTWIDDLED, 1024, 1024, tex_space, PVR_FILTER_NONE); pt_sprite_cxt.gen.culling = PVR_CULLING_NONE; tr_sprite_cxt.gen.culling = PVR_CULLING_NONE; pvr_sprite_compile(&tr_sprite_hdr, &tr_sprite_cxt); pvr_sprite_compile(&pt_sprite_hdr, &pt_sprite_cxt); tr_sprite_hdr.argb = PVR_PACK_COLOR(0.5f, 1.0f, 1.0f, 1.0f); priority_levels[0] = 0.0f; priority_levels[1] = 1.0f; priority_levels[2] = 2.0f; priority_levels[3] = 3.0f; priority_levels[4] = 4.0f; priority_levels[5] = 5.0f; priority_levels[6] = 6.0f; priority_levels[7] = 7.0f; framecount = 0; lastup = time(NULL); return 0; } static void VIDDCDeInit(void) { pvr_set_vertbuf(PVR_LIST_OP_POLY, NULL, 0); pvr_set_vertbuf(PVR_LIST_TR_POLY, NULL, 0); pvr_set_vertbuf(PVR_LIST_PT_POLY, NULL, 0); pvr_mem_free(tex_space); sem_destroy(dmadone); pvr_shutdown(); vid_set_mode(DM_640x480, PM_RGB565); } static void VIDDCResize(unsigned int w, unsigned int h, int unused) { } static int VIDDCIsFullscreen(void) { return 1; } static int VIDDCVdp1Reset(void) { return 0; } static void VIDDCVdp1DrawStart(void) { if(Vdp2Regs->CLOFEN & 0x40) { // color offset enable if(Vdp2Regs->CLOFSL & 0x40) { // color offset B vdp1cor = Vdp2Regs->COBR & 0xFF; if(Vdp2Regs->COBR & 0x100) vdp1cor |= 0xFFFFFF00; vdp1cog = Vdp2Regs->COBG & 0xFF; if(Vdp2Regs->COBG & 0x100) vdp1cog |= 0xFFFFFF00; vdp1cob = Vdp2Regs->COBB & 0xFF; if(Vdp2Regs->COBB & 0x100) vdp1cob |= 0xFFFFFF00; } else { // color offset A vdp1cor = Vdp2Regs->COAR & 0xFF; if(Vdp2Regs->COAR & 0x100) vdp1cor |= 0xFFFFFF00; vdp1cog = Vdp2Regs->COAG & 0xFF; if(Vdp2Regs->COAG & 0x100) vdp1cog |= 0xFFFFFF00; vdp1cob = Vdp2Regs->COAB & 0xFF; if(Vdp2Regs->COAB & 0x100) vdp1cob |= 0xFFFFFF00; } } else // color offset disable vdp1cor = vdp1cog = vdp1cob = 0; } static void VIDDCVdp1DrawEnd(void) { cached_spr = 0; priority_levels[0] = 0.0f; priority_levels[1] = 1.0f; priority_levels[2] = 2.0f; priority_levels[3] = 3.0f; priority_levels[4] = 4.0f; priority_levels[5] = 5.0f; priority_levels[6] = 6.0f; priority_levels[7] = 7.0f; } static void VIDDCVdp1NormalSpriteDraw(void) { int x, y, num; u8 z; vdp1cmd_struct cmd; pvr_sprite_txr_t sprite; pvr_list_t list; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); x = Vdp1Regs->localX + cmd.CMDXA; y = Vdp1Regs->localY + cmd.CMDYA; cur_spr.w = ((cmd.CMDSIZE >> 8) & 0x3F) << 3; cur_spr.h = cmd.CMDSIZE & 0xFF; if ((cmd.CMDPMOD & 0x07) == 0x03) { list = PVR_LIST_TR_POLY; num = Vdp1ReadTexture(&cmd, &tr_sprite_hdr); if(num == 0) return; else pvr_list_prim(PVR_LIST_TR_POLY, &tr_sprite_hdr, sizeof(pvr_sprite_hdr_t)); } else { num = Vdp1ReadTexture(&cmd, &pt_sprite_hdr); list = PVR_LIST_PT_POLY; if(num == 0) return; else pvr_list_prim(PVR_LIST_PT_POLY, &pt_sprite_hdr, sizeof(pvr_sprite_hdr_t)); } z = Vdp1ReadPriority(&cmd); sprite.flags = PVR_CMD_VERTEX_EOL; sprite.ax = x; sprite.ay = y; sprite.az = priority_levels[z]; sprite.bx = x + cur_spr.w; sprite.by = y; sprite.bz = priority_levels[z]; sprite.cx = x + cur_spr.w; sprite.cy = y + cur_spr.h; sprite.cz = priority_levels[z]; sprite.dx = x; sprite.dy = y + cur_spr.h; sprite.auv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? cur_spr.uf : 0.0f), ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); sprite.buv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); sprite.cuv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), ((cmd.CMDCTRL & 0x0020) ? 0.0f : cur_spr.vf)); pvr_list_prim(list, &sprite, sizeof(sprite)); priority_levels[z] += 0.000001f; } static void VIDDCVdp1ScaledSpriteDraw(void) { vdp1cmd_struct cmd; s16 rw = 0, rh = 0; s16 x, y; u8 z; pvr_sprite_txr_t sprite; pvr_list_t list; int num; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); x = cmd.CMDXA + Vdp1Regs->localX; y = cmd.CMDYA + Vdp1Regs->localY; cur_spr.w = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; cur_spr.h = cmd.CMDSIZE & 0xFF; if((cmd.CMDPMOD & 0x07) == 0x03) { list = PVR_LIST_TR_POLY; num = Vdp1ReadTexture(&cmd, &tr_sprite_hdr); if(num == 0) return; else pvr_list_prim(PVR_LIST_TR_POLY, &tr_sprite_hdr, sizeof(pvr_sprite_hdr_t)); } else { num = Vdp1ReadTexture(&cmd, &pt_sprite_hdr); list = PVR_LIST_PT_POLY; if(num == 0) return; else pvr_list_prim(PVR_LIST_PT_POLY, &pt_sprite_hdr, sizeof(pvr_sprite_hdr_t)); } // Setup Zoom Point switch ((cmd.CMDCTRL & 0xF00) >> 8) { case 0x0: // Only two coordinates rw = cmd.CMDXC - x + Vdp1Regs->localX + 1; rh = cmd.CMDYC - y + Vdp1Regs->localY + 1; break; case 0x5: // Upper-left rw = cmd.CMDXB + 1; rh = cmd.CMDYB + 1; break; case 0x6: // Upper-Center rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw / 2; ++rw; ++rh; break; case 0x7: // Upper-Right rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw; ++rw; ++rh; break; case 0x9: // Center-left rw = cmd.CMDXB; rh = cmd.CMDYB; y = y - rh / 2; ++rw; ++rh; break; case 0xA: // Center-center rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw / 2; y = y - rh / 2; ++rw; ++rh; break; case 0xB: // Center-right rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw; y = y - rh / 2; ++rw; ++rh; break; case 0xD: // Lower-left rw = cmd.CMDXB; rh = cmd.CMDYB; y = y - rh; ++rw; ++rh; break; case 0xE: // Lower-center rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw / 2; y = y - rh; ++rw; ++rh; break; case 0xF: // Lower-right rw = cmd.CMDXB; rh = cmd.CMDYB; x = x - rw; y = y - rh; ++rw; ++rh; break; default: break; } z = Vdp1ReadPriority(&cmd); sprite.flags = PVR_CMD_VERTEX_EOL; sprite.ax = x; sprite.ay = y; sprite.az = priority_levels[z]; sprite.bx = x + rw; sprite.by = y; sprite.bz = priority_levels[z]; sprite.cx = x + rw; sprite.cy = y + rh; sprite.cz = priority_levels[z]; sprite.dx = x; sprite.dy = y + rh; sprite.auv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? cur_spr.uf : 0.0f), ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); sprite.buv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); sprite.cuv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), ((cmd.CMDCTRL & 0x0020) ? 0.0f : cur_spr.vf)); pvr_list_prim(list, &sprite, sizeof(sprite)); priority_levels[z] += 0.000001f; } static void VIDDCVdp1DistortedSpriteDraw(void) { vdp1cmd_struct cmd; u8 z; pvr_sprite_txr_t sprite; pvr_list_t list; int num; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); cur_spr.w = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; cur_spr.h = cmd.CMDSIZE & 0xFF; if((cmd.CMDPMOD & 0x7) == 0x3) { list = PVR_LIST_TR_POLY; num = Vdp1ReadTexture(&cmd, &tr_sprite_hdr); if(num == 0) return; else pvr_list_prim(PVR_LIST_TR_POLY, &tr_sprite_hdr, sizeof(pvr_sprite_hdr_t)); } else { num = Vdp1ReadTexture(&cmd, &pt_sprite_hdr); list = PVR_LIST_PT_POLY; if(num == 0) return; else pvr_list_prim(PVR_LIST_PT_POLY, &pt_sprite_hdr, sizeof(pvr_sprite_hdr_t)); } z = Vdp1ReadPriority(&cmd); sprite.flags = PVR_CMD_VERTEX_EOL; sprite.ax = cmd.CMDXA + Vdp1Regs->localX; sprite.ay = cmd.CMDYA + Vdp1Regs->localY; sprite.az = priority_levels[z]; sprite.bx = cmd.CMDXB + Vdp1Regs->localX + 1; sprite.by = cmd.CMDYB + Vdp1Regs->localY; sprite.bz = priority_levels[z]; sprite.cx = cmd.CMDXC + Vdp1Regs->localX + 1; sprite.cy = cmd.CMDYC + Vdp1Regs->localY + 1; sprite.cz = priority_levels[z]; sprite.dx = cmd.CMDXD + Vdp1Regs->localX; sprite.dy = cmd.CMDYD + Vdp1Regs->localY + 1; sprite.auv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? cur_spr.uf : 0.0f), ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); sprite.buv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), ((cmd.CMDCTRL & 0x0020) ? cur_spr.vf : 0.0f)); sprite.cuv = PVR_PACK_16BIT_UV(((cmd.CMDCTRL & 0x0010) ? 0.0f : cur_spr.uf), ((cmd.CMDCTRL & 0x0020) ? 0.0f : cur_spr.vf)); pvr_list_prim(list, &sprite, sizeof(sprite)); priority_levels[z] += 0.000001f; } static void VIDDCVdp1PolygonDraw(void) { s16 X[4]; s16 Y[4]; u16 color; u16 CMDPMOD; u8 alpha, z; pvr_list_t list; pvr_sprite_col_t spr; pvr_sprite_hdr_t *hdr; X[0] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C); Y[0] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E); X[1] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10); Y[1] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12); X[2] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); Y[2] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); X[3] = Vdp1Regs->localX + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x18); Y[3] = Vdp1Regs->localY + T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1A); color = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x06); CMDPMOD = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x04); /* Don't bother rendering completely transparent polygons */ if((!(color & 0x8000) && !(CMDPMOD & 0x0040)) || !color) { return; } if((CMDPMOD & 0x0007) == 0x0003) { alpha = 0x80; list = PVR_LIST_TR_POLY; hdr = &tr_poly_hdr; } else { alpha = 0xFF; list = PVR_LIST_OP_POLY; hdr = &op_poly_hdr; } if(color & 0x8000) { hdr->argb = COLOR_ADD32(SAT2YAB32(alpha, color), vdp1cor, vdp1cog, vdp1cob); } else { hdr->argb = COLOR_ADD32(Vdp2ColorRamGetColor32(color, alpha), vdp1cor, vdp1cog, vdp1cob); } pvr_list_prim(list, hdr, sizeof(pvr_sprite_hdr_t)); z = Vdp2Regs->PRISA & 0x07; spr.flags = PVR_CMD_VERTEX_EOL; spr.d1 = spr.d2 = spr.d3 = spr.d4 = 0; spr.az = spr.bz = spr.cz = priority_levels[z]; spr.ax = X[0]; spr.ay = Y[0]; spr.bx = X[1]; spr.by = Y[1]; spr.cx = X[2]; spr.cy = Y[2]; spr.dx = X[3]; spr.dy = Y[3]; pvr_list_prim(list, &spr, sizeof(pvr_sprite_col_t)); priority_levels[z] += 0.000001f; } static void VIDDCVdp1PolylineDraw(void) { } static void VIDDCVdp1LineDraw(void) { } static void VIDDCVdp1UserClipping(void) { Vdp1Regs->userclipX1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C); Vdp1Regs->userclipY1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E); Vdp1Regs->userclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); Vdp1Regs->userclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); } static void VIDDCVdp1SystemClipping(void) { Vdp1Regs->systemclipX1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C); Vdp1Regs->systemclipY1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E); Vdp1Regs->systemclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); Vdp1Regs->systemclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); } static void VIDDCVdp1LocalCoordinate(void) { Vdp1Regs->localX = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C); Vdp1Regs->localY = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E); } ////////////////////////////////////////////////////////////////////////////// static u16 DoNothing(void *info, u16 pixel) { return pixel; } ////////////////////////////////////////////////////////////////////////////// static u16 DoColorOffset(void *info, u16 pixel) { return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor, ((vdp2draw_struct *)info)->cog, ((vdp2draw_struct *)info)->cob); } ////////////////////////////////////////////////////////////////////////////// static u16 DoColorCalc(void *info, u16 pixel) { // should be doing color calculation here return pixel; } ////////////////////////////////////////////////////////////////////////////// static u16 DoColorCalcWithColorOffset(void *info, u16 pixel) { // should be doing color calculation here return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor, ((vdp2draw_struct *)info)->cog, ((vdp2draw_struct *)info)->cob); } static void Vdp2DrawBackScreen() { u32 scrAddr; u16 dot; pvr_sprite_col_t spr; if(Vdp2Regs->VRSIZE & 0x8000) scrAddr = (((Vdp2Regs->BKTAU & 0x07) << 16) | Vdp2Regs->BKTAL) << 1; else scrAddr = (((Vdp2Regs->BKTAU & 0x03) << 16) | Vdp2Regs->BKTAL) << 1; if(Vdp2Regs->BKTAU & 0x8000) { int i; for(i = 0; i < vdp2height; ++i) { dot = T1ReadWord(Vdp2Ram, scrAddr); scrAddr += 2; op_poly_hdr.argb = SAT2YAB32(0xFF, dot); pvr_list_prim(PVR_LIST_OP_POLY, &op_poly_hdr, sizeof(pvr_sprite_hdr_t)); spr.flags = PVR_CMD_VERTEX_EOL; spr.ax = 0.0f; spr.ay = i + 1; spr.az = 0.1f; spr.bx = 0.0f; spr.by = i; spr.bz = 0.1f; spr.cx = vdp2width; spr.cy = i; spr.cz = 0.1f; spr.dx = vdp2width; spr.dy = i + 1; spr.d1 = spr.d2 = spr.d3 = spr.d4 = 0; pvr_list_prim(PVR_LIST_OP_POLY, &spr, sizeof(pvr_sprite_col_t)); } } else { dot = T1ReadWord(Vdp2Ram, scrAddr); op_poly_hdr.argb = SAT2YAB32(0xFF, dot); pvr_list_prim(PVR_LIST_OP_POLY, &op_poly_hdr, sizeof(pvr_sprite_hdr_t)); spr.flags = PVR_CMD_VERTEX_EOL; spr.ax = 0.0f; spr.ay = vdp2height; spr.az = 0.1f; spr.bx = 0.0f; spr.by = 0.0f; spr.bz = 0.1f; spr.cx = vdp2width; spr.cy = 0.0f; spr.cz = 0.1f; spr.dx = vdp2width; spr.dy = vdp2height; spr.d1 = spr.d2 = spr.d3 = spr.d4 = 0; pvr_list_prim(PVR_LIST_OP_POLY, &spr, sizeof(pvr_sprite_col_t)); } } static void Vdp2DrawLineColorScreen() { } ////////////////////////////////////////////////////////////////////////////// static void Vdp2NBG0PlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFN & 0x7) << 6; u32 tmp=0; int deca; int multi; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABN0 & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABN0 >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDN0 & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDN0 >> 8); break; } deca = info->planeh + info->planew - 2; multi = info->planeh * info->planew; //if (Vdp2Regs->VRSIZE & 0x8000) //{ if (info->patterndatasize == 1) { if (info->patternwh == 1) info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); else info->addr = (tmp >> deca) * (multi * 0x800); } else { if (info->patternwh == 1) info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); else info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); } /*} else { if (info->patterndatasize == 1) { if (info->patternwh == 1) info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x2000); else info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x800); } else { if (info->patternwh == 1) info->addr = ((tmp & 0xF) >> deca) * (multi * 0x4000); else info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x1000); } }*/ } ////////////////////////////////////////////////////////////////////////////// static int Vdp2DrawNBG0(void) { vdp2draw_struct info; /* FIXME should start by checking if it's a normal * or rotate scroll screen */ info.enable = Vdp2Regs->BGON & 0x1; if (!(info.enable & Vdp2External.disptoggle)) return 0; info.transparencyenable = !(Vdp2Regs->BGON & 0x100); info.specialprimode = Vdp2Regs->SFPRMD & 0x3; info.colornumber = (Vdp2Regs->CHCTLA & 0x70) >> 4; if((info.isbitmap = Vdp2Regs->CHCTLA & 0x2) != 0) { // Bitmap Mode switch((Vdp2Regs->CHCTLA & 0xC) >> 2) { case 0: info.cellw = 512; info.cellh = 256; break; case 1: info.cellw = 512; info.cellh = 512; break; case 2: info.cellw = 1024; info.cellh = 256; break; case 3: info.cellw = 1024; info.cellh = 512; break; } info.x = - ((Vdp2Regs->SCXIN0 & 0x7FF) % info.cellw); info.y = - ((Vdp2Regs->SCYIN0 & 0x7FF) % info.cellh); info.charaddr = (Vdp2Regs->MPOFN & 0x7) * 0x20000; info.paladdr = (Vdp2Regs->BMPNA & 0x7) << 8; info.flipfunction = 0; info.specialfunction = 0; } else { // Tile Mode info.mapwh = 2; switch(Vdp2Regs->PLSZ & 0x3) { case 0: info.planew = info.planeh = 1; break; case 1: info.planew = 2; info.planeh = 1; break; case 3: info.planew = info.planeh = 2; break; default: // Not sure what 0x2 does info.planew = info.planeh = 1; break; } info.x = - ((Vdp2Regs->SCXIN0 & 0x7FF) % (512 * info.planew)); info.y = - ((Vdp2Regs->SCYIN0 & 0x7FF) % (512 * info.planeh)); if(Vdp2Regs->PNCN0 & 0x8000) info.patterndatasize = 1; else info.patterndatasize = 2; if(Vdp2Regs->CHCTLA & 0x1) info.patternwh = 2; else info.patternwh = 1; info.pagewh = 64/info.patternwh; info.cellw = info.cellh = 8; info.supplementdata = Vdp2Regs->PNCN0 & 0x3FF; info.auxmode = (Vdp2Regs->PNCN0 & 0x4000) >> 14; } if (Vdp2Regs->CCCTL & 0x1) info.alpha = ((~Vdp2Regs->CCRNA & 0x1F) << 3) + 0x7; else info.alpha = 0xFF; info.coloroffset = (Vdp2Regs->CRAOFA & 0x7) << 8; if (Vdp2Regs->CLOFEN & 0x1) { // color offset enable if (Vdp2Regs->CLOFSL & 0x1) { // color offset B info.cor = Vdp2Regs->COBR & 0xFF; if (Vdp2Regs->COBR & 0x100) info.cor |= 0xFFFFFF00; info.cog = Vdp2Regs->COBG & 0xFF; if (Vdp2Regs->COBG & 0x100) info.cog |= 0xFFFFFF00; info.cob = Vdp2Regs->COBB & 0xFF; if (Vdp2Regs->COBB & 0x100) info.cob |= 0xFFFFFF00; } else { // color offset A info.cor = Vdp2Regs->COAR & 0xFF; if (Vdp2Regs->COAR & 0x100) info.cor |= 0xFFFFFF00; info.cog = Vdp2Regs->COAG & 0xFF; if (Vdp2Regs->COAG & 0x100) info.cog |= 0xFFFFFF00; info.cob = Vdp2Regs->COAB & 0xFF; if (Vdp2Regs->COAB & 0x100) info.cob |= 0xFFFFFF00; } if (Vdp2Regs->CCCTL & 0x1) info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; else info.PostPixelFetchCalc = &DoColorOffset; } else // color offset disable { if (Vdp2Regs->CCCTL & 0x1) info.PostPixelFetchCalc = &DoColorCalc; else info.PostPixelFetchCalc = &DoNothing; } info.coordincx = (float) 65536 / (Vdp2Regs->ZMXN0.all & 0x7FF00); info.coordincy = (float) 65536 / (Vdp2Regs->ZMYN0.all & 0x7FF00); info.priority = nbg0priority; info.PlaneAddr = (void (*)(void *, int))&Vdp2NBG0PlaneAddr; if (info.isbitmap) Vdp2DrawScrollBitmap(&info); else Vdp2DrawMap(&info); return 1; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2NBG1PlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFN & 0x70) << 2; u32 tmp=0; int deca; int multi; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABN1 & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABN1 >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDN1 & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDN1 >> 8); break; } deca = info->planeh + info->planew - 2; multi = info->planeh * info->planew; //if (Vdp2Regs->VRSIZE & 0x8000) //{ if (info->patterndatasize == 1) { if (info->patternwh == 1) info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); else info->addr = (tmp >> deca) * (multi * 0x800); } else { if (info->patternwh == 1) info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); else info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); } /*} else { if (info->patterndatasize == 1) { if (info->patternwh == 1) info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x2000); else info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x800); } else { if (info->patternwh == 1) info->addr = ((tmp & 0xF) >> deca) * (multi * 0x4000); else info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x1000); } }*/ } ////////////////////////////////////////////////////////////////////////////// static int Vdp2DrawNBG1(void) { vdp2draw_struct info; info.enable = Vdp2Regs->BGON & 0x2; if (!(info.enable & Vdp2External.disptoggle)) return 0; info.transparencyenable = !(Vdp2Regs->BGON & 0x200); info.specialprimode = (Vdp2Regs->SFPRMD >> 2) & 0x3; info.colornumber = (Vdp2Regs->CHCTLA & 0x3000) >> 12; if((info.isbitmap = Vdp2Regs->CHCTLA & 0x200) != 0) { switch((Vdp2Regs->CHCTLA & 0xC00) >> 10) { case 0: info.cellw = 512; info.cellh = 256; break; case 1: info.cellw = 512; info.cellh = 512; break; case 2: info.cellw = 1024; info.cellh = 256; break; case 3: info.cellw = 1024; info.cellh = 512; break; } info.x = - ((Vdp2Regs->SCXIN1 & 0x7FF) % info.cellw); info.y = - ((Vdp2Regs->SCYIN1 & 0x7FF) % info.cellh); info.charaddr = ((Vdp2Regs->MPOFN & 0x70) >> 4) * 0x20000; info.paladdr = Vdp2Regs->BMPNA & 0x700; info.flipfunction = 0; info.specialfunction = 0; } else { info.mapwh = 2; switch((Vdp2Regs->PLSZ & 0xC) >> 2) { case 0: info.planew = info.planeh = 1; break; case 1: info.planew = 2; info.planeh = 1; break; case 3: info.planew = info.planeh = 2; break; default: // Not sure what 0x2 does info.planew = info.planeh = 1; break; } info.x = - ((Vdp2Regs->SCXIN1 & 0x7FF) % (512 * info.planew)); info.y = - ((Vdp2Regs->SCYIN1 & 0x7FF) % (512 * info.planeh)); if(Vdp2Regs->PNCN1 & 0x8000) info.patterndatasize = 1; else info.patterndatasize = 2; if(Vdp2Regs->CHCTLA & 0x100) info.patternwh = 2; else info.patternwh = 1; info.pagewh = 64/info.patternwh; info.cellw = info.cellh = 8; info.supplementdata = Vdp2Regs->PNCN1 & 0x3FF; info.auxmode = (Vdp2Regs->PNCN1 & 0x4000) >> 14; } if (Vdp2Regs->CCCTL & 0x2) info.alpha = ((~Vdp2Regs->CCRNA & 0x1F00) >> 5) + 0x7; else info.alpha = 0xFF; info.coloroffset = (Vdp2Regs->CRAOFA & 0x70) << 4; if (Vdp2Regs->CLOFEN & 0x2) { // color offset enable if (Vdp2Regs->CLOFSL & 0x2) { // color offset B info.cor = Vdp2Regs->COBR & 0xFF; if (Vdp2Regs->COBR & 0x100) info.cor |= 0xFFFFFF00; info.cog = Vdp2Regs->COBG & 0xFF; if (Vdp2Regs->COBG & 0x100) info.cog |= 0xFFFFFF00; info.cob = Vdp2Regs->COBB & 0xFF; if (Vdp2Regs->COBB & 0x100) info.cob |= 0xFFFFFF00; } else { // color offset A info.cor = Vdp2Regs->COAR & 0xFF; if (Vdp2Regs->COAR & 0x100) info.cor |= 0xFFFFFF00; info.cog = Vdp2Regs->COAG & 0xFF; if (Vdp2Regs->COAG & 0x100) info.cog |= 0xFFFFFF00; info.cob = Vdp2Regs->COAB & 0xFF; if (Vdp2Regs->COAB & 0x100) info.cob |= 0xFFFFFF00; } if (Vdp2Regs->CCCTL & 0x2) info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; else info.PostPixelFetchCalc = &DoColorOffset; } else // color offset disable { if (Vdp2Regs->CCCTL & 0x2) info.PostPixelFetchCalc = &DoColorCalc; else info.PostPixelFetchCalc = &DoNothing; } info.coordincx = (float) 65536 / (Vdp2Regs->ZMXN1.all & 0x7FF00); info.coordincy = (float) 65536 / (Vdp2Regs->ZMXN1.all & 0x7FF00); info.priority = nbg1priority; info.PlaneAddr = (void (*)(void *, int))&Vdp2NBG1PlaneAddr; if (info.isbitmap) { Vdp2DrawScrollBitmap(&info); /* // Handle Scroll Wrapping(Let's see if we even need do to it to begin // with) if (info.x < (vdp2width - info.cellw)) { info.vertices[0] = (info.x+info.cellw) * info.coordincx; info.vertices[2] = (info.x + (info.cellw<<1)) * info.coordincx; info.vertices[4] = (info.x + (info.cellw<<1)) * info.coordincx; info.vertices[6] = (info.x+info.cellw) * info.coordincx; YglCachedQuad((YglSprite *)&info, tmp); if (info.y < (vdp2height - info.cellh)) { info.vertices[1] = (info.y+info.cellh) * info.coordincy; info.vertices[3] = (info.y + (info.cellh<<1)) * info.coordincy; info.vertices[5] = (info.y + (info.cellh<<1)) * info.coordincy; info.vertices[7] = (info.y+info.cellh) * info.coordincy; YglCachedQuad((YglSprite *)&info, tmp); } } else if (info.y < (vdp2height - info.cellh)) { info.vertices[1] = (info.y+info.cellh) * info.coordincy; info.vertices[3] = (info.y + (info.cellh<<1)) * info.coordincy; info.vertices[5] = (info.y + (info.cellh<<1)) * info.coordincy; info.vertices[7] = (info.y+info.cellh) * info.coordincy; YglCachedQuad((YglSprite *)&info, tmp); } */ } else Vdp2DrawMap(&info); return 1; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2NBG2PlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFN & 0x700) >> 2; u32 tmp=0; int deca; int multi; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABN2 & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABN2 >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDN2 & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDN2 >> 8); break; } deca = info->planeh + info->planew - 2; multi = info->planeh * info->planew; //if (Vdp2Regs->VRSIZE & 0x8000) //{ if (info->patterndatasize == 1) { if (info->patternwh == 1) info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); else info->addr = (tmp >> deca) * (multi * 0x800); } else { if (info->patternwh == 1) info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); else info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); } /*} else { if (info->patterndatasize == 1) { if (info->patternwh == 1) info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x2000); else info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x800); } else { if (info->patternwh == 1) info->addr = ((tmp & 0xF) >> deca) * (multi * 0x4000); else info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x1000); } }*/ } ////////////////////////////////////////////////////////////////////////////// static int Vdp2DrawNBG2(void) { vdp2draw_struct info; info.enable = Vdp2Regs->BGON & 0x4; if (!(info.enable & Vdp2External.disptoggle)) return 0; info.transparencyenable = !(Vdp2Regs->BGON & 0x400); info.specialprimode = (Vdp2Regs->SFPRMD >> 4) & 0x3; info.colornumber = (Vdp2Regs->CHCTLB & 0x2) >> 1; info.mapwh = 2; switch((Vdp2Regs->PLSZ & 0x30) >> 4) { case 0: info.planew = info.planeh = 1; break; case 1: info.planew = 2; info.planeh = 1; break; case 3: info.planew = info.planeh = 2; break; default: // Not sure what 0x2 does info.planew = info.planeh = 1; break; } info.x = - ((Vdp2Regs->SCXN2 & 0x7FF) % (512 * info.planew)); info.y = - ((Vdp2Regs->SCYN2 & 0x7FF) % (512 * info.planeh)); if(Vdp2Regs->PNCN2 & 0x8000) info.patterndatasize = 1; else info.patterndatasize = 2; if(Vdp2Regs->CHCTLB & 0x1) info.patternwh = 2; else info.patternwh = 1; info.pagewh = 64/info.patternwh; info.cellw = info.cellh = 8; info.supplementdata = Vdp2Regs->PNCN2 & 0x3FF; info.auxmode = (Vdp2Regs->PNCN2 & 0x4000) >> 14; if (Vdp2Regs->CCCTL & 0x4) info.alpha = ((~Vdp2Regs->CCRNB & 0x1F) << 3) + 0x7; else info.alpha = 0xFF; info.coloroffset = Vdp2Regs->CRAOFA & 0x700; if (Vdp2Regs->CLOFEN & 0x4) { // color offset enable if (Vdp2Regs->CLOFSL & 0x4) { // color offset B info.cor = Vdp2Regs->COBR & 0xFF; if (Vdp2Regs->COBR & 0x100) info.cor |= 0xFFFFFF00; info.cog = Vdp2Regs->COBG & 0xFF; if (Vdp2Regs->COBG & 0x100) info.cog |= 0xFFFFFF00; info.cob = Vdp2Regs->COBB & 0xFF; if (Vdp2Regs->COBB & 0x100) info.cob |= 0xFFFFFF00; } else { // color offset A info.cor = Vdp2Regs->COAR & 0xFF; if (Vdp2Regs->COAR & 0x100) info.cor |= 0xFFFFFF00; info.cog = Vdp2Regs->COAG & 0xFF; if (Vdp2Regs->COAG & 0x100) info.cog |= 0xFFFFFF00; info.cob = Vdp2Regs->COAB & 0xFF; if (Vdp2Regs->COAB & 0x100) info.cob |= 0xFFFFFF00; } if (Vdp2Regs->CCCTL & 0x4) info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; else info.PostPixelFetchCalc = &DoColorOffset; } else // color offset disable { if (Vdp2Regs->CCCTL & 0x4) info.PostPixelFetchCalc = &DoColorCalc; else info.PostPixelFetchCalc = &DoNothing; } info.coordincx = info.coordincy = 1; info.priority = nbg2priority; info.PlaneAddr = (void (*)(void *, int))&Vdp2NBG2PlaneAddr; Vdp2DrawMap(&info); return 1; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2NBG3PlaneAddr(vdp2draw_struct *info, int i) { u32 offset = (Vdp2Regs->MPOFN & 0x7000) >> 6; u32 tmp=0; int deca; int multi; switch(i) { case 0: tmp = offset | (Vdp2Regs->MPABN3 & 0xFF); break; case 1: tmp = offset | (Vdp2Regs->MPABN3 >> 8); break; case 2: tmp = offset | (Vdp2Regs->MPCDN3 & 0xFF); break; case 3: tmp = offset | (Vdp2Regs->MPCDN3 >> 8); break; } deca = info->planeh + info->planew - 2; multi = info->planeh * info->planew; //if (Vdp2Regs->VRSIZE & 0x8000) { if (info->patterndatasize == 1) { if (info->patternwh == 1) info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); else info->addr = (tmp >> deca) * (multi * 0x800); } else { if (info->patternwh == 1) info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); else info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); } /*} else { if (info->patterndatasize == 1) { if (info->patternwh == 1) info->addr = ((tmp & 0x1F) >> deca) * (multi * 0x2000); else info->addr = ((tmp & 0x7F) >> deca) * (multi * 0x800); } else { if (info->patternwh == 1) info->addr = ((tmp & 0xF) >> deca) * (multi * 0x4000); else info->addr = ((tmp & 0x3F) >> deca) * (multi * 0x1000); } }*/ } ////////////////////////////////////////////////////////////////////////////// static int Vdp2DrawNBG3(void) { vdp2draw_struct info; info.enable = Vdp2Regs->BGON & 0x8; if (!(info.enable & Vdp2External.disptoggle)) return 0; info.transparencyenable = !(Vdp2Regs->BGON & 0x800); info.specialprimode = (Vdp2Regs->SFPRMD >> 6) & 0x3; info.colornumber = (Vdp2Regs->CHCTLB & 0x20) >> 5; info.mapwh = 2; switch((Vdp2Regs->PLSZ & 0xC0) >> 6) { case 0: info.planew = info.planeh = 1; break; case 1: info.planew = 2; info.planeh = 1; break; case 3: info.planew = info.planeh = 2; break; default: // Not sure what 0x2 does info.planew = info.planeh = 1; break; } info.x = - ((Vdp2Regs->SCXN3 & 0x7FF) % (512 * info.planew)); info.y = - ((Vdp2Regs->SCYN3 & 0x7FF) % (512 * info.planeh)); if(Vdp2Regs->PNCN3 & 0x8000) info.patterndatasize = 1; else info.patterndatasize = 2; if(Vdp2Regs->CHCTLB & 0x10) info.patternwh = 2; else info.patternwh = 1; info.pagewh = 64/info.patternwh; info.cellw = info.cellh = 8; info.supplementdata = Vdp2Regs->PNCN3 & 0x3FF; info.auxmode = (Vdp2Regs->PNCN3 & 0x4000) >> 14; if (Vdp2Regs->CCCTL & 0x8) info.alpha = ((~Vdp2Regs->CCRNB & 0x1F00) >> 5) + 0x7; else info.alpha = 0xFF; info.coloroffset = (Vdp2Regs->CRAOFA & 0x7000) >> 4; if (Vdp2Regs->CLOFEN & 0x8) { // color offset enable if (Vdp2Regs->CLOFSL & 0x8) { // color offset B info.cor = Vdp2Regs->COBR & 0xFF; if (Vdp2Regs->COBR & 0x100) info.cor |= 0xFFFFFF00; info.cog = Vdp2Regs->COBG & 0xFF; if (Vdp2Regs->COBG & 0x100) info.cog |= 0xFFFFFF00; info.cob = Vdp2Regs->COBB & 0xFF; if (Vdp2Regs->COBB & 0x100) info.cob |= 0xFFFFFF00; } else { // color offset A info.cor = Vdp2Regs->COAR & 0xFF; if (Vdp2Regs->COAR & 0x100) info.cor |= 0xFFFFFF00; info.cog = Vdp2Regs->COAG & 0xFF; if (Vdp2Regs->COAG & 0x100) info.cog |= 0xFFFFFF00; info.cob = Vdp2Regs->COAB & 0xFF; if (Vdp2Regs->COAB & 0x100) info.cob |= 0xFFFFFF00; } if (Vdp2Regs->CCCTL & 0x8) info.PostPixelFetchCalc = &DoColorCalcWithColorOffset; else info.PostPixelFetchCalc = &DoColorOffset; } else // color offset disable { if (Vdp2Regs->CCCTL & 0x8) info.PostPixelFetchCalc = &DoColorCalc; else info.PostPixelFetchCalc = &DoNothing; } info.coordincx = info.coordincy = 1; info.priority = nbg3priority; info.PlaneAddr = (void (*)(void *, int))&Vdp2NBG3PlaneAddr; Vdp2DrawMap(&info); return 1; } static int VIDDCVdp2Reset(void) { return 0; } static void VIDDCVdp2DrawStart(void) { cur_addr = (uint32) tex_space; cur_vdp2 = (uint32) vdp2_tex; pvr_wait_ready(); pvr_scene_begin(); Vdp2DrawBackScreen(); Vdp2DrawLineColorScreen(); } static void VIDDCVdp2DrawEnd(void) { /* Make sure we don't have any texture dma still going on... */ sem_wait(dmadone); sem_signal(dmadone); pvr_scene_finish(); ++framecount; if(lastup + 10 <= time(NULL)) { printf("%d frames in %d seconds FPS: %f\n", framecount, time(NULL) - lastup, ((float)(framecount)) / (time(NULL) - lastup)); framecount = 0; lastup = time(NULL); } } static void dma_callback(ptr_t data __attribute__((unused))) { sem_signal(dmadone); } static void Vdp2Draw(int priority) { pvr_sprite_txr_t sprite; pt_sprite_hdr.mode2 &= (~(PVR_TA_PM2_USIZE_MASK | PVR_TA_PM2_VSIZE_MASK)); pt_sprite_hdr.mode2 |= (6 << PVR_TA_PM2_USIZE_SHIFT) | (5 << PVR_TA_PM2_VSIZE_SHIFT); pt_sprite_hdr.mode3 = ((cur_vdp2 & 0x00FFFFF8) >> 3) | (PVR_TXRFMT_NONTWIDDLED); pvr_list_prim(PVR_LIST_PT_POLY, &pt_sprite_hdr, sizeof(pvr_sprite_hdr_t)); sprite.flags = PVR_CMD_VERTEX_EOL; sprite.ax = 0; sprite.ay = 0; sprite.az = priority_levels[priority]; sprite.bx = vdp2width; sprite.by = 0; sprite.bz = priority_levels[priority]; sprite.cx = vdp2width; sprite.cy = vdp2height; sprite.cz = priority_levels[priority]; sprite.dx = 0; sprite.dy = vdp2height; sprite.auv = PVR_PACK_16BIT_UV(0.0f, 0.0f); sprite.buv = PVR_PACK_16BIT_UV(vdp2width / 512.0f, 0.0f); sprite.cuv = PVR_PACK_16BIT_UV(vdp2width / 512.0f, vdp2height / 256.0f); pvr_list_prim(PVR_LIST_PT_POLY, &sprite, sizeof(pvr_sprite_txr_t)); priority_levels[priority] += 0.000001f; } static void VIDDCVdp2DrawScreens(void) { int i; VIDDCVdp2SetResolution(Vdp2Regs->TVMD); VIDDCVdp2SetPriorityNBG0(Vdp2Regs->PRINA & 0x7); VIDDCVdp2SetPriorityNBG1((Vdp2Regs->PRINA >> 8) & 0x7); VIDDCVdp2SetPriorityNBG2(Vdp2Regs->PRINB & 0x7); VIDDCVdp2SetPriorityNBG3((Vdp2Regs->PRINB >> 8) & 0x7); VIDDCVdp2SetPriorityRBG0(Vdp2Regs->PRIR & 0x7); vdp2_fb = vdp2_fbs[0]; vdp2_fbnum = 0; for(i = 1; i < 8; i++) { if(nbg3priority == i) { if(Vdp2DrawNBG3()) { dcache_flush_range((ptr_t)(vdp2_fb), 512 * 256 * 2); sem_wait(dmadone); pvr_txr_load_dma(vdp2_fb, (pvr_ptr_t) cur_vdp2, 512 * 256 * 2, 0, dma_callback, 0); Vdp2Draw(i); cur_vdp2 += 512 * 256 * 2; vdp2_fbnum ^= 1; vdp2_fb = vdp2_fbs[vdp2_fbnum]; } } if(nbg2priority == i) { if(Vdp2DrawNBG2()) { dcache_flush_range((ptr_t)(vdp2_fb), 512 * 256 * 2); sem_wait(dmadone); pvr_txr_load_dma(vdp2_fb, (pvr_ptr_t) cur_vdp2, 512 * 256 * 2, 0, dma_callback, 0); Vdp2Draw(i); cur_vdp2 += 512 * 256 * 2; vdp2_fbnum ^= 1; vdp2_fb = vdp2_fbs[vdp2_fbnum]; } } if(nbg1priority == i) { if(Vdp2DrawNBG1()) { dcache_flush_range((ptr_t)(vdp2_fb), 512 * 256 * 2); sem_wait(dmadone); pvr_txr_load_dma(vdp2_fb, (pvr_ptr_t) cur_vdp2, 512 * 256 * 2, 0, dma_callback, 0); Vdp2Draw(i); cur_vdp2 += 512 * 256 * 2; vdp2_fbnum ^= 1; vdp2_fb = vdp2_fbs[vdp2_fbnum]; } } if(nbg0priority == i) { if(Vdp2DrawNBG0()) { dcache_flush_range((ptr_t)(vdp2_fb), 512 * 256 * 2); sem_wait(dmadone); pvr_txr_load_dma(vdp2_fb, (pvr_ptr_t) cur_vdp2, 512 * 256 * 2, 0, dma_callback, 0); Vdp2Draw(i); cur_vdp2 += 512 * 256 * 2; vdp2_fbnum ^= 1; vdp2_fb = vdp2_fbs[vdp2_fbnum]; } } // if (rbg0priority == i) // Vdp2DrawRBG0(); } } static void VIDDCVdp2SetResolution(u16 TVMD) { int w = 0, h = 0; switch(TVMD & 0x03) { case 0: w = 320; break; case 1: w = 352; break; case 2: w = 640; break; case 3: w = 704; break; } switch((TVMD >> 4) & 0x03) { case 0: h = 224; break; case 1: h = 240; break; case 2: h = 256; break; } switch((TVMD >> 6) & 0x03) { case 2: case 3: h <<= 1; default: break; } vdp2width = w; vdp2height = h; if(w > 352 || h > 256) { printf("Unsupported resolution set %d x %d\n", w, h); printf("Bailing out!\n"); exit(-1); } } static void VIDDCVdp2SetPriorityNBG0(int priority) { nbg0priority = priority; } static void VIDDCVdp2SetPriorityNBG1(int priority) { nbg1priority = priority; } static void VIDDCVdp2SetPriorityNBG2(int priority) { nbg2priority = priority; } static void VIDDCVdp2SetPriorityNBG3(int priority) { nbg3priority = priority; } static void VIDDCVdp2SetPriorityRBG0(int priority) { rbg0priority = priority; } VideoInterface_struct VIDDC = { VIDCORE_DC, "Dreamcast PVR Video Interface", VIDDCInit, VIDDCDeInit, VIDDCResize, VIDDCIsFullscreen, VIDDCVdp1Reset, VIDDCVdp1DrawStart, VIDDCVdp1DrawEnd, VIDDCVdp1NormalSpriteDraw, VIDDCVdp1ScaledSpriteDraw, VIDDCVdp1DistortedSpriteDraw, VIDDCVdp1PolygonDraw, VIDDCVdp1PolylineDraw, VIDDCVdp1LineDraw, VIDDCVdp1UserClipping, VIDDCVdp1SystemClipping, VIDDCVdp1LocalCoordinate, VIDDCVdp2Reset, VIDDCVdp2DrawStart, VIDDCVdp2DrawEnd, VIDDCVdp2DrawScreens }; yabause-0.9.13.1/src/dreamcast/cd.s000644 001750 001750 00000014731 12256006113 020714 0ustar00guillaumeguillaume000000 000000 ! Copyright 2008 Lawrence Sebald ! ! This file is part of Yabause. ! ! Yabause is free software; you can redistribute it and/or modify ! it under the terms of the GNU General Public License as published by ! the Free Software Foundation; either version 2 of the License, or ! (at your option) any later version. ! ! Yabause 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 General Public License for more details. ! ! You should have received a copy of the GNU General Public License ! along with Yabause; if not, write to the Free Software ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ! Ok, so this is the start of me assemblerizing different parts of the core ! of the Dreamcast port of Yabause. I picked the CD core since its one of the ! parts of the emulator that is least likely to change in the future ! (hopefully anyway), and its somewhat simple to start with. .file "cd.s" .little .text .align 2 ! static int DCCDInit(const char *cdrom_name) ! Initialize the GD drive to read 2352 byte sectors. DCCDInit: sts.l pr, @-r15 .do_reinit: mov.l .cdrom_exec_cmd, r0 mov #0, r5 jsr @r0 mov #24, r4 ! CMD_INIT cmp/eq #1, r0 ! ERR_NO_DISC bt .init_return_success cmp/eq #3, r0 ! ERR_SYS bt .init_return_error mov.l .gdc_syscall_vector, r1 mova .DCCDInitParams, r0 mov #10, r7 mov.l @r1, r1 mov r0, r4 mov #0, r6 jsr @r1 mov #0, r5 lds.l @r15+, pr rts nop .init_return_success: lds.l @r15+, pr rts mov #0, r0 .init_return_error: lds.l @r15+, pr rts mov #-1, r0 .align 4 .cdrom_exec_cmd: .long _cdrom_exec_cmd .DCCDInitParams: .long 0 ! 0 = set .long 4096 ! Magic value for RAW sector reads .long 0x0400 ! Ditto .long 2352 ! Sector Size? (Maybe not for RAW though?) ! static int DCCDGetStatus(void) ! Execute the BIOS syscall of the Dreamcast to get the GD drive status, ! translating that into the format expected by the core of Yabause. DCCDGetStatus: sts.l pr, @-r15 .status_startgame: mov.l .gdc_syscall_vector, r1 mova .get_status_scratchpad, r0 mov #4, r7 mov.l @r1, r1 mov #0, r5 mov r0, r4 jsr @r1 mov #0, r6 cmp/eq #2, r0 ! 2 = Disc change error bt .status_reinit cmp/eq #0, r0 bf .status_error .status_endgame: ! status in 1st entry in scratchpad mova .get_status_scratchpad, r0 mov #0x07, r2 mov.l @r0, r1 mova .get_status_return_value, r0 and r2, r1 add r1, r0 lds.l @r15+, pr rts mov.b @r0, r0 .status_reinit: mov.l .get_status_init_func, r0 jsr @r0 mov #0, r4 cmp/eq #0, r0 bt .status_startgame .status_error: lds.l @r15+, pr rts mov #2, r0 .align 4 .gdc_syscall_vector: .long 0x8c0000bc .get_status_scratchpad: .long 0 .long 0 .get_status_return_value: .byte 0, 1, 1, 0, 0, 0, 3, 2 .get_status_init_func: .long DCCDInit ! static int DCCDDeInit(void) ! Deinitialize the CD Drive of the Dreamcast (i.e., undo the odd ! initialization stuff that the code does for Yabause). DCCDDeInit: mov.l .cdrom_reinit, r0 sts.l pr, @-r15 jsr @r0 nop lds.l @r15+, pr rts nop ! Leave the return value from cdrom_reinit as the return here. ! static int DCCDReadSectorFAD(u32 FAD, void *buffer) ! Read a single 2352 byte sector from the given position on the disc. DCCDReadSectorFAD: sts.l pr, @-r15 mov r4, r2 mov.l r4, @-r15 mov r5, r4 mov.l .cdrom_read_sectors, r0 mov.l r5, @-r15 mov r2, r5 .read_sector_start: jsr @r0 mov #1, r6 cmp/eq #2, r0 bt .read_reinit cmp/eq #0, r0 add #8, r15 bf/s .read_error lds.l @r15+, pr rts mov #1, r0 .read_reinit: mov.l .DCCDInit, r0 jsr @r0 mov #0, r4 cmp/eq #0, r0 mov.l @r15, r4 mov.l .cdrom_read_sectors, r0 bt/s .read_sector_start mov.l @(4, r15), r5 add #8, r15 .read_error: rts mov #0, r0 ! static int DCCDReadAheadFAD(u32 FAD) ! No-op (for the moment). DCCDReadAheadFAD: rts nop ! static s32 DCCDReadTOC(u32 *TOC); ! Read the TOC of the CD inserted in the drive. ! Amusingly enough, I just realized that the format that Yabause expects ! and what the Dreamcast spews out are exactly the same. Go figure! DCCDReadTOC: sts.l pr, @-r15 mov.l .cdrom_read_toc, r0 mov.l r4, @-r15 .readtoc_start: jsr @r0 mov #0, r5 cmp/eq #2, r0 bt .readtoc_reinit cmp/eq #0, r0 add #4, r15 bf/s .readtoc_error mov #0xCC, r0 lds.l @r15+, pr extu.b r0, r0 rts shll r0 .readtoc_reinit: mov.l .DCCDInit, r0 jsr @r0 mov #0, r4 cmp/eq #0, r0 mov.l @r15, r4 bt/s .readtoc_start mov.l .cdrom_read_toc, r0 add #4, r15 .readtoc_error: rts mov #0, r0 .align 4 .cdrom_reinit: .long _cdrom_reinit .cdrom_read_sectors: .long _cdrom_read_sectors .DCCDInit: .long DCCDInit .cdrom_read_toc: .long _cdrom_read_toc .section .rodata .align 2 .CDInterfaceName: .string "Dreamcast CD Drive" .data .align 4 .globl _ArchCD .size _ArchCD, 32 _ArchCD: .long 2 .long .CDInterfaceName .long DCCDInit .long DCCDDeInit .long DCCDGetStatus .long DCCDReadTOC .long DCCDReadSectorFAD .long DCCDReadAheadFAD yabause-0.9.13.1/src/dreamcast/perdc.c000644 001750 001750 00000007260 12256006113 021402 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2008 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "perdc.h" #include "../yabause.h" #include "../yui.h" #include "../vdp2.h" #include #include int PERDCInit(void); void PERDCDeInit(void); int PERDCHandleEvents(void); void PERDCNothing(void); u32 PERDCScan(u32 flags); static PerPad_struct *pad1; PerInterface_struct PERDC = { PERCORE_DC, "Dreamcast Input Interface", PERDCInit, PERDCDeInit, PERDCHandleEvents, PERDCScan, 0, PERDCNothing, PERDCKeyName }; int PERDCInit(void) { PerPortReset(); pad1 = PerPadAdd(&PORTDATA1); return 0; } void PERDCDeInit(void) { } int PERDCHandleEvents(void) { maple_device_t *dev; dev = maple_enum_type(0, MAPLE_FUNC_CONTROLLER); if(dev != NULL) { cont_state_t *state = (cont_state_t *) maple_dev_status(dev); if(state != NULL) { if(state->buttons & CONT_DPAD_UP) *pad1->padbits &= 0xEF; else *pad1->padbits |= 0x10; if(state->buttons & CONT_DPAD_DOWN) *pad1->padbits &= 0xDF; else *pad1->padbits |= 0x20; if(state->buttons & CONT_DPAD_RIGHT) *pad1->padbits &= 0x7F; else *pad1->padbits |= 0x80; if(state->buttons & CONT_DPAD_LEFT) *pad1->padbits &= 0xBF; else *pad1->padbits |= 0x40; if(state->buttons & CONT_START) *pad1->padbits &= 0xF7; else *pad1->padbits |= 0x08; if(state->buttons & CONT_A) *pad1->padbits &= 0xFB; else *pad1->padbits |= 0x04; if(state->buttons & CONT_B) *pad1->padbits &= 0xFE; else *pad1->padbits |= 0x01; if(state->buttons & CONT_X) *(pad1->padbits + 1) &= 0xBF; else *(pad1->padbits + 1) |= 0x40; if(state->buttons & CONT_Y) *(pad1->padbits + 1) &= 0xDF; else *(pad1->padbits + 1) |= 0x20; if(state->rtrig > 20) *(pad1->padbits + 1) &= 0x7F; else *(pad1->padbits + 1) |= 0x80; if(state->ltrig > 20) *(pad1->padbits + 1) &= 0xF7; else *(pad1->padbits + 1) |= 0x08; if(state->joyx > 20) *pad1->padbits &= 0xFD; else *pad1->padbits |= 0x02; if(state->joyy > 20) *(pad1->padbits + 1) &= 0xEF; else *(pad1->padbits + 1) |= 0x10; } } YabauseExec(); return 0; } void PERDCNothing(void) { /* Nothing */ } u32 PERDCScan(u32 flags) { /* Nothing */ return 0; } void PERDCKeyName(u32 key, char *name, int size) { snprintf(name, size, "%x", (unsigned int)key); } yabause-0.9.13.1/src/dreamcast/perdc.h000644 001750 001750 00000001615 12256006113 021405 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PERDC_H #define PERDC_H #include "../peripheral.h" #define PERCORE_DC 2 extern PerInterface_struct PERDC; #endif yabause-0.9.13.1/src/dreamcast/CMakeLists.txt000644 001750 001750 00000001207 12256006113 022674 0ustar00guillaumeguillaume000000 000000 project(yabause-dc) if(NOT dreamcast) return() endif(NOT dreamcast) enable_language(ASM-ATT) include_directories(${PORT_INCLUDE_DIRS}) add_definitions(${PORT_CFLAGS}) set(yabause_dc_SOURCES cd.s localtime.c perdc.c viddc.c yui.c) set(yabause_dc_HEADERS localtime.h perdc.h viddc.h) link_directories(..) add_executable(yabause-dc ${yabause_dc_SOURCES}) set_target_properties(yabause-dc PROPERTIES OUTPUT_NAME yabause.elf) target_link_libraries(yabause-dc ${YABAUSE_LIBRARIES}) target_link_libraries(yabause-dc ${PORT_LIBRARIES}) target_link_libraries(yabause-dc yabause) target_link_libraries(yabause-dc m) yabause-0.9.13.1/src/dreamcast/yui.c000644 001750 001750 00000013165 12256006113 021114 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003 Guillaume Duhamel Copyright 2004-2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include "../yui.h" #include "../peripheral.h" #include "../cs0.h" #include "../m68kcore.h" #include "../m68kc68k.h" #include "perdc.h" #include "viddc.h" #include "sh2rec/sh2rec.h" SH2Interface_struct *SH2CoreList[] = { &SH2Interpreter, &SH2Dynarec, NULL }; PerInterface_struct *PERCoreList[] = { &PERDC, NULL }; CDInterface *CDCoreList[] = { &ArchCD, &DummyCD, NULL }; SoundInterface_struct *SNDCoreList[] = { &SNDDummy, NULL }; VideoInterface_struct *VIDCoreList[] = { &VIDDummy, &VIDDC, NULL }; M68K_struct * M68KCoreList[] = { &M68KDummy, &M68KC68K, #ifdef HAVE_Q68 &M68KQ68, #endif NULL }; static const char *bios = "/ram/saturn.bin"; static int emulate_bios = 0; int YuiInit(int sh2core) { yabauseinit_struct yinit; yinit.percoretype = PERCORE_DC; yinit.sh2coretype = sh2core; yinit.vidcoretype = VIDCORE_DC; yinit.m68kcoretype = M68KCORE_C68K; yinit.sndcoretype = SNDCORE_DUMMY; yinit.cdcoretype = CDCORE_ARCH; yinit.carttype = CART_NONE; yinit.regionid = REGION_AUTODETECT; yinit.biospath = emulate_bios ? NULL : bios; yinit.cdpath = NULL; yinit.buppath = NULL; yinit.mpegpath = NULL; yinit.cartpath = NULL; yinit.frameskip = 0; yinit.videoformattype = VIDEOFORMATTYPE_NTSC; yinit.clocksync = 0; yinit.basetime = 0; if(YabauseInit(&yinit) != 0) return -1; for(;;) { PERCore->HandleEvents(); } return 0; } void YuiErrorMsg(const char *error_text) { fprintf(stderr, "Error: %s\n", error_text); arch_exit(); } void YuiSwapBuffers(void) { /* Nothing here. */ } int DoGui() { struct coord { int x; int y; }; struct coord snowflakes[1024]; int i; int offset; int start_pressed = 0; int phase = 0; int core = SH2CORE_INTERPRETER; srand(time(NULL)); for(i = 0; i < 1024; ++i) { snowflakes[i].x = (rand() % 640); snowflakes[i].y = -(rand() % 480); } while(!start_pressed) { offset = 64 * 640 + 64; /* 64 pixels in from the left, 64 down */ bfont_draw_str(vram_s + offset, 640, 0, "Yabause " VERSION); offset += 640 * 128; if(phase == 0) { FILE *fp; fp = fopen("/cd/saturn.bin", "r"); if(fp) { fclose(fp); fs_copy("/cd/saturn.bin", bios); phase = 1; continue; } bfont_draw_str(vram_s + offset, 640, 0, "Please insert a CD containing the Saturn BIOS"); offset += 640 * 24; bfont_draw_str(vram_s + offset, 640, 0, "on the root of the disc, named saturn.bin."); offset += 640 * 48; bfont_draw_str(vram_s + offset, 640, 0, "Or, to use the BIOS emulation feature, insert"); offset += 640 * 24; bfont_draw_str(vram_s + offset, 640, 0, "a Sega Saturn CD and press Start."); } else { bfont_draw_str(vram_s + offset, 640, 0, "Please insert a Sega Saturn CD"); offset += 640 * 24; bfont_draw_str(vram_s + offset, 640, 0, "and press start."); } for(i = 0; i < 1024; ++i) { int dx = 1 - (rand() % 3); if(snowflakes[i].y >= 0) vram_s[640 * snowflakes[i].y + snowflakes[i].x] = 0x0000; snowflakes[i].x += dx; snowflakes[i].y += 1; if(snowflakes[i].x < 0) snowflakes[i].x = 639; else if(snowflakes[i].x > 639) snowflakes[i].x = 0; if(snowflakes[i].y > 479) snowflakes[i].y = 0; if(snowflakes[i].y >= 0) vram_s[640 * snowflakes[i].y + snowflakes[i].x] = 0xD555; } MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st) if(st->buttons & CONT_START) { if(phase == 0) { emulate_bios = 1; } start_pressed = 1; } if(st->buttons & CONT_Y) { core = SH2CORE_DYNAREC; if(phase == 0) { emulate_bios = 1; } start_pressed = 1; } MAPLE_FOREACH_END() vid_waitvbl(); vid_flip(1); } return core; } int main(int argc, char *argv[]) { int core; printf("...\n"); bfont_set_encoding(BFONT_CODE_ISO8859_1); core = DoGui(); YuiInit(core); return 0; } yabause-0.9.13.1/src/dreamcast/dreamcast.cmake000644 001750 001750 00000001315 12256006113 023101 0ustar00guillaumeguillaume000000 000000 # CMake toolchain file for building Yabause on the Dreamcast set(CMAKE_SYSTEM_NAME Generic) # Use the gnu_wrappers for the various GNU utilities set(CMAKE_C_COMPILER kos-cc) set(CMAKE_CXX_COMPILER kos-c++) set(CMAKE_ASM_COMPILER kos-as) # KOS Sets this nicely for us. set(CMAKE_FIND_ROOT_PATH $ENV{KOS_CC_BASE}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # Set some stuff so that it doesn't complain about the lack of a normal looking # pthreads flag/library for the compiler. set(THREADS_HAVE_PTHREAD_ARG 1) set(CMAKE_HAVE_THREADS_LIBRARY 1) # Set a flag so we know we're trying to compile for Dreamcast set(dreamcast 1) yabause-0.9.13.1/src/dreamcast/sh2rec/sh2exec.s000644 001750 001750 00000011375 12256006113 023056 0ustar00guillaumeguillaume000000 000000 ! Copyright 2010 Lawrence Sebald ! ! This file is part of Yabause. ! ! Yabause is free software; you can redistribute it and/or modify ! it under the terms of the GNU General Public License as published by ! the Free Software Foundation; either version 2 of the License, or ! (at your option) any later version. ! ! Yabause 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 General Public License for more details. ! ! You should have received a copy of the GNU General Public License ! along with Yabause; if not, write to the Free Software ! Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ! Code for calling upon the code generated by sh2rec (SuperH). .file "sh2exec.s" .little .text .balign 8 ! void sh2rec_exec(SH2_struct *cxt, u32 cycles) ! Execute the specified number of cycles on the given SH2 context .globl _sh2rec_exec _sh2rec_exec: stc.l gbr, @-r15 ! Save call-preserved stuff sts.l mach, @-r15 ! Ditto sts.l macl, @-r15 ! Ditto mov.l r8, @-r15 ! Ditto mov r4, r8 ! Put the SH2 struct in r8 mov.l r9, @-r15 ! More call-preserved stuff add #76, r4 ! Point at MACH in the SH2 struct mov.l r10, @-r15 ! More call-preserved stuff mov.l r11, @-r15 ! Last one for now... lds.l @r4+, mach ! Load the SH2 MACH into our MACH lds.l @r4+, macl ! Ditto for MACL mov.l checkInterrupts, r0 ! We need to check for interrupts... mov.l sh2memfuncsptr, r9 ! Memory access function pointer table sts.l pr, @-r15 ! Helps to know where to go back to mov r5, r11 ! This is important enough to keep here mov.l r5, @-r15 ! Save this on the stack too mov r8, r4 ! We need the original SH2_struct back jsr @r0 ! Call sh2rec_check_interrupts ldc r8, gbr ! Put the SH2 struct in gbr (delay slot) mov.l findBlock, r1 ! Grab the sh2rec_find_block function mov.l @(88, gbr), r0 ! Grab the PC we are at .exec_loop: ! This is where the fun is! jsr @r1 ! Call sh2rec_find_block mov r0, r4 ! Move the PC to argument 1 (delay slot) mov.l @r0, r2 ! Grab where the code is mov.l @(8, r0), r1 ! Figure out the number of cycles used jsr @r2 ! Call the block sub r1, r11 ! Chop off the cycles (delay slot) cmp/pl r11 ! Are we done? mov.l findBlock, r1 ! Grab the sh2rec_find_block function bt .exec_loop ! Continue on if needed ! When we are done, we will be here. mov.l r0, @(88, gbr) ! Save the next PC value mov.l @r15+, r5 ! Pop the requested number of cycles mov r8, r4 ! Keep this for sanity for now add #84, r8 ! Point just after MACL in SH2 struct sts.l macl, @-r8 ! Store the SH2 MACL back in the struct sts.l mach, @-r8 ! Ditto for MACH lds.l @r15+, pr ! Restore stuff from the stack sub r11, r5 ! Our counter is negitive, so this works mov.l cycleOffset, r2 ! Where is the cycles member at? mov.l @r15+, r11 ! More restoring... add r2, r4 ! Point r4 at the cycles member mov.l @r15+, r10 mov.l @r15+, r9 mov.l @r15+, r8 lds.l @r15+, macl lds.l @r15+, mach mov.l r5, @r4 ! Save the cycles we spent rts ! Return to the caller ldc.l @r15+, gbr ! Last thing to restore (delay slot) .balign 4 sh2memfuncsptr: .long sh2memfuncs checkInterrupts: .long _sh2rec_check_interrupts findBlock: .long _sh2rec_find_block cycleOffset: .long 5516 .data .balign 4 sh2memfuncs: .long _MappedMemoryReadByte .long _MappedMemoryReadWord .long _MappedMemoryReadLong .long _MappedMemoryWriteByte .long _MappedMemoryWriteWord .long _MappedMemoryWriteLong yabause-0.9.13.1/src/dreamcast/sh2rec/sh2rec_htab.c000644 001750 001750 00000007706 12256006113 023664 0ustar00guillaumeguillaume000000 000000 /* Copyright 2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "core.h" #include "sh2core.h" #include "sh2rec.h" #include "sh2rec_htab.h" #include "sh2rec_mem.h" typedef struct htab_entry { sh2rec_block_t block; struct htab_entry *next; } htab_entry_t; /* The actual hash table. It can't be resized dynamically, and is essentially just an array of singly-linked lists. */ static htab_entry_t *table[SH2REC_HTAB_ENTRIES]; /* Internal functions */ static void htab_free_chain(htab_entry_t *ent) { htab_entry_t *i, *tmp; i = ent; while(i) { tmp = i->next; free(i->block.block); free(i); i = tmp; } } /* Hash an address into something slightly nicer to work with. The large constant in here is about 2^32 / phi (where phi is the golden ratio). Why use the golden ratio? Because its always fun to use in code. */ static inline int hash_addr(u32 addr) { return ((addr ^ 2654435761U) >> 2) & (SH2REC_HTAB_ENTRIES - 1); } /* Public functions */ void sh2rec_htab_init(void) { memset(table, 0, sizeof(htab_entry_t *) * SH2REC_HTAB_ENTRIES); } void sh2rec_htab_reset(void) { int i; for(i = 0; i < SH2REC_HTAB_ENTRIES; ++i) { if(table[i]) { htab_free_chain(table[i]); } } memset(table, 0, sizeof(htab_entry_t *) * SH2REC_HTAB_ENTRIES); } sh2rec_block_t *sh2rec_htab_lookup(u32 addr) { htab_entry_t *i = table[hash_addr(addr)]; /* Look through the chain for the entry we're after */ while(i) { if(i->block.start_pc == addr) { return &i->block; } i = i->next; } /* Didn't find it, punt. */ return NULL; } /* Create a new block assuming an old one does not exist. */ sh2rec_block_t *sh2rec_htab_block_create(u32 addr, int length) { uint8_t *ptr; htab_entry_t *ent; int index = hash_addr(addr); ptr = (uint8_t *)sh2rec_mem_alloc(length + sizeof(htab_entry_t)); #ifdef DEBUG if(!ptr) { return NULL; } #endif /* Allocate space for the block */ ent = (htab_entry_t *)ptr; ent->block.block = (u16 *)(ptr + sizeof(htab_entry_t)); /* Fill in the struct */ ent->block.start_pc = addr; ent->block.cycles = 0; ent->block.pc = addr; ent->block.length = length; ent->block.ptr = ent->block.block; /* Put the item in the list (puts it at the head of the index in the table where it would go) */ ent->next = table[index]; table[index] = ent; return &ent->block; } void sh2rec_htab_block_remove(u32 addr) { int index = hash_addr(addr); htab_entry_t *i, *tmp, *last; i = table[index]; last = NULL; /* Look through everything for the entry we're supposed to remove */ while(i) { tmp = i->next; /* Is this the entry we're looking for? */ if(i->block.start_pc == addr) { /* Unhook the entry from the list */ if(last) { last->next = tmp; } else { table[index] = tmp; } /* Free any memory used by the block */ sh2rec_mem_free(i); return; } last = i; i = tmp; } } yabause-0.9.13.1/src/dreamcast/sh2rec/sh2rec_mem.c000644 001750 001750 00000013631 12256006113 023516 0ustar00guillaumeguillaume000000 000000 /* Copyright 2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "sh2rec_mem.h" typedef struct block_s { uint8_t *ptr; int size; struct block_s *prev; struct block_s *next; } sh2rec_mem_block; typedef struct usedblock_s { sh2rec_mem_block base; sh2rec_mem_block *freespace; } sh2rec_mem_usedblock; typedef struct allocblock_s { struct allocblock_s *next; } sh2rec_mem_allocblock; static sh2rec_mem_block *freeblocks = NULL; static sh2rec_mem_usedblock *usedblocks = NULL; static sh2rec_mem_usedblock *usedblocks_tail = NULL; static sh2rec_mem_allocblock *allocblocks = NULL; static int cur_allocation = 0; #define BSSIZE (sizeof(sh2rec_mem_allocblock) + sizeof(sh2rec_mem_block) + \ sizeof(sh2rec_mem_usedblock)) int sh2rec_mem_init(void) { sh2rec_mem_block *initblock; sh2rec_mem_allocblock *allocblock; uint8_t *block; /* Allocate our initial space for storing rec'd instructions in */ block = (uint8_t *)malloc(SH2REC_MEM_INITIAL); #ifdef DEBUG if(!block) { return -1; } #endif /* Carve our structures out of the beginning of the block */ allocblock = (sh2rec_mem_allocblock *)block; initblock = (sh2rec_mem_block *)(block + sizeof(sh2rec_mem_allocblock)); cur_allocation = SH2REC_MEM_INITIAL; /* Fill in the rest of the structs */ initblock->size = SH2REC_MEM_INITIAL - sizeof(sh2rec_mem_allocblock) - sizeof(sh2rec_mem_block); initblock->prev = NULL; initblock->next = NULL; allocblock->next = NULL; allocblocks = allocblock; /* The whole block is free, so put it in the free list */ freeblocks = initblock; return 0; } void sh2rec_mem_shutdown(void) { sh2rec_mem_allocblock *i, *tmp; /* Loop through and free any blocks we allocated */ i = allocblocks; while(i) { tmp = i->next; free(i); i = tmp; } /* Clean up the stale pointers */ allocblocks = NULL; freeblocks = NULL; usedblocks = NULL; } void *sh2rec_mem_alloc(int sz) { sh2rec_mem_block *i; sh2rec_mem_usedblock *rv; sh2rec_mem_allocblock *b; int szlook = sz + SH2REC_MEM_FUDGE + sizeof(sh2rec_mem_usedblock); uint8_t *block; /* Look for a free block of enough size */ i = freeblocks; while(i) { if(i->size >= szlook) { /* We've found a candidate, so, start working with it */ rv = (sh2rec_mem_usedblock *)i->ptr; rv->freespace = i; rv->base.ptr = i->ptr + sizeof(sh2rec_mem_usedblock); rv->base.size = sz; rv->base.prev = (sh2rec_mem_block *)usedblocks_tail; rv->base.next = NULL; /* Update the tail */ if(usedblocks_tail) { usedblocks_tail->base.next = (sh2rec_mem_block *)rv; } usedblocks_tail = rv; /* The freeblock is now smaller, so reflect that */ i->size -= sz + sizeof(sh2rec_mem_usedblock); return rv; } i = i->next; } /* We didn't find one, so allocate a new block */ block = malloc(SH2REC_MEM_ALLOCSZ); #ifdef DEBUG if(!block) { return NULL; } #endif /* Fill in the allocblock */ b = (sh2rec_mem_allocblock *)block; b->next = allocblocks; allocblocks = b; /* Now, create a freeblock, and work from that */ i = (sh2rec_mem_block *)(block + sizeof(sh2rec_mem_allocblock)); i->ptr = block + BSSIZE + sz; i->prev = NULL; i->next = freeblocks; i->size = SH2REC_MEM_ALLOCSZ - BSSIZE - sz; freeblocks = i; /* Create the usedblock */ rv = (sh2rec_mem_usedblock *)(i->ptr - sz); rv->freespace = i; rv->base.ptr = i->ptr; rv->base.size = sz; rv->base.prev = (sh2rec_mem_block *)usedblocks_tail; rv->base.next = NULL; /* Update the tail */ if(usedblocks_tail) { usedblocks_tail->base.next = (sh2rec_mem_block *)rv; } usedblocks_tail = rv; /* Keep track of our allocation */ cur_allocation += SH2REC_MEM_ALLOCSZ; return rv; } int sh2rec_mem_expand(void *block, int amt) { sh2rec_mem_usedblock *b = (sh2rec_mem_usedblock *)block; /* If the freeblock has space, allow it */ if(b->freespace->size > amt) { b->freespace->size -= amt; b->base.size += amt; b->freespace->ptr += amt; return 1; } return 0; } void sh2rec_mem_free(void *block) { sh2rec_mem_usedblock *b = (sh2rec_mem_usedblock *)block; /* Remove the usedblock from the chain */ if(b->base.next) { b->base.next->prev = b->base.prev; } if(b->base.prev) { b->base.prev->next = b->base.next; } if(b == usedblocks) { usedblocks = (sh2rec_mem_usedblock *)b->base.next; } if(b == usedblocks_tail) { usedblocks_tail = (sh2rec_mem_usedblock *)b->base.prev; } /* Treat the usedblock like its a freeblock (it is an extension of the freeblock), and just link it into the free blocks list */ b->freespace = NULL; b->base.next = freeblocks; b->base.prev = NULL; b->base.size += sizeof(sh2rec_mem_usedblock) - sizeof(sh2rec_mem_block); freeblocks = (sh2rec_mem_block *)b; } yabause-0.9.13.1/src/dreamcast/sh2rec/sh2rec_htab.h000644 001750 001750 00000002434 12256006113 023662 0ustar00guillaumeguillaume000000 000000 /* Copyright 2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SH2REC_HTAB_H #define SH2REC_HTAB_H /* This MUST be set to a power of two. It is done this way to avoid using a mod operation to get the entry where the object will go, since division (and thus modulus) is quite expensive on SuperH. */ #define SH2REC_HTAB_ENTRIES 4096 void sh2rec_htab_init(void); void sh2rec_htab_reset(void); sh2rec_block_t *sh2rec_htab_lookup(u32 addr); sh2rec_block_t *sh2rec_htab_block_create(u32 addr, int length); void sh2rec_htab_block_remove(u32 addr); #endif /* !SH2REC_HTAB_H */ yabause-0.9.13.1/src/dreamcast/sh2rec/sh2rec_mem.h000644 001750 001750 00000002575 12256006113 023530 0ustar00guillaumeguillaume000000 000000 /* Copyright 2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SH2REC_MEM_H #define SH2REC_MEM_H /* Initial allocation of memory: 1MB */ #define SH2REC_MEM_INITIAL (1024 * 1024) /* Size of future allocations: 256KB */ #define SH2REC_MEM_ALLOCSZ (256 * 1024) /* Maximum allocation of memory: 2MB */ #define SH2REC_MEM_MAX (2 * 1024 * 1024) /* Fudge factor... make sure at least this much is in any block above the inital request */ #define SH2REC_MEM_FUDGE 48 int sh2rec_mem_init(void); void sh2rec_mem_shutdown(void); void *sh2rec_mem_alloc(int sz); int sh2rec_mem_expand(void *block, int amt); void sh2rec_mem_free(void *block); #endif /* !SH2REC_MEM_H */ yabause-0.9.13.1/src/dreamcast/sh2rec/sh2rec.h000644 001750 001750 00000002737 12256006113 022672 0ustar00guillaumeguillaume000000 000000 /* Copyright 2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SH2REC_H #define SH2REC_H #define SH2CORE_DYNAREC 10 #define INSTRUCTION_A(x) ((x & 0xF000) >> 12) #define INSTRUCTION_B(x) ((x & 0x0F00) >> 8) #define INSTRUCTION_C(x) ((x & 0x00F0) >> 4) #define INSTRUCTION_D(x) (x & 0x000F) #define INSTRUCTION_CD(x) (x & 0x00FF) #define INSTRUCTION_BCD(x) (x & 0x0FFF) typedef struct sh2rec_block { u16 *block; u32 start_pc; int cycles; int length; u16 *ptr; u32 pc; } sh2rec_block_t; /* Recompile a single instruction */ int sh2rec_rec_inst(sh2rec_block_t *b, int isdelay); /* Recompile a block at the PC specified in the block */ int sh2rec_rec_block(sh2rec_block_t *b); extern SH2Interface_struct SH2Dynarec; #endif /* !SH2REC_H */ yabause-0.9.13.1/src/dreamcast/sh2rec/sh2rec.c000644 001750 001750 00000320767 12256006113 022673 0ustar00guillaumeguillaume000000 000000 /* Copyright 2010 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* SH2 Dynarec Core (for SH4) */ #include #include #include #include #include "sh2core.h" #include "sh2rec.h" #include "sh2rec_htab.h" #include "sh2int.h" /* Registers */ #define R0 0 #define R1 1 #define R2 2 #define R3 3 #define R4 4 #define R5 5 #define R6 6 #define R7 7 #define R8 8 #define R9 9 #define R10 10 #define R11 11 #define R12 12 #define R13 13 #define R14 14 #define R15 15 /* Control Registers (use with emitSTC/emitLDC) */ #define R_SR 0 #define R_GBR 1 #define R_VBR 2 /* System Registers (use with emitSTS/emitLDS) */ #define R_MACH 0 #define R_MACL 1 #define R_PR 2 /* ALU Ops, to be used with the emitALU function */ #define OP_ADD 0x300C #define OP_ADDC 0x300E #define OP_AND 0x2009 #define OP_EXTSB 0x600E #define OP_EXTSW 0x600F #define OP_EXTUB 0x600C #define OP_EXTUW 0x600D #define OP_NEG 0x600B #define OP_NEGC 0x600A #define OP_NOT 0x6007 #define OP_OR 0x200B #define OP_SUB 0x3008 #define OP_SUBC 0x300A #define OP_SWAPB 0x6008 #define OP_SWAPW 0x6009 #define OP_XOR 0x200A #define OP_XTRCT 0x200D /* Shift/Rotate Ops, to be used with the emitSHIFT function */ #define OP_ROTCL 0x4024 #define OP_ROTCR 0x4025 #define OP_ROTL 0x4004 #define OP_ROTR 0x4005 #define OP_SHAL 0x4020 #define OP_SHAR 0x4021 #define OP_SHLL 0x4000 #define OP_SHLR 0x4001 /* Comparison Ops, to be used with the emitALU function */ #define OP_CMPEQ 0x3000 #define OP_CMPGE 0x3003 #define OP_CMPGT 0x3007 #define OP_CMPHI 0x3006 #define OP_CMPHS 0x3002 #define OP_CMPSTR 0x200C #define OP_TST 0x2008 /* Multiplication ops, to be used with the emitALU function */ #define OP_DMULS 0x300D #define OP_DMULU 0x3005 #define OP_MULL 0x0007 #define OP_MULS 0x200F #define OP_MULU 0x200E #ifdef SH2REC__DEBUG #define EMIT_INST {\ printf("%s\n", __PRETTY_FUNCTION__); \ printf("Emitting %04x at %p\n", inst, (void *)b->ptr); \ *b->ptr++ = inst; \ } #else #define EMIT_INST *b->ptr++ = inst #endif #ifdef SH2REC__DEBUG #define EMIT_32 {\ uint32_t *__ptr = (uint32_t *)b->ptr; \ printf("%s\n", __PRETTY_FUNCTION__); \ printf("Emitting %08x at %p\n", (unsigned int)v, (void *)__ptr); \ *__ptr = v; \ b->ptr += 2; \ } #else #define EMIT_32 uint32_t *__ptr = (uint32_t *)b->ptr; *__ptr = v; b->ptr += 2 #endif static inline void emit16(sh2rec_block_t *b, uint16_t inst) { EMIT_INST; } static inline void emit32(sh2rec_block_t *b, uint32_t v) { EMIT_32; } static inline void emitMOV(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x6003 | (n << 8) | (m << 4); EMIT_INST; } static inline void emitMOVWI(sh2rec_block_t *b, int d, int n) { uint16_t inst = 0x9000 | (n << 8) | (d); EMIT_INST; } static inline void emitMOVLI(sh2rec_block_t *b, int d, int n) { uint16_t inst = 0xD000 | (n << 8) | (d); EMIT_INST; } static inline void emitMOVLS(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x2002 | (n << 8) | (m << 4); EMIT_INST; } static inline void emitMOVLL(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x6002 | (n << 8) | (m << 4); EMIT_INST; } static inline void emitMOVWM(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x2005 | (n << 8) | (m << 4); EMIT_INST; } static inline void emitMOVLM(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x2006 | (n << 8) | (m << 4); EMIT_INST; } static inline void emitMOVLP(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x6006 | (n << 8) | (m << 4); EMIT_INST; } static inline void emitMOVI(sh2rec_block_t *b, int imm, int n) { uint16_t inst = 0xE000 | (n << 8) | (imm & 0xFF); EMIT_INST; } static inline void emitMOVLL4(sh2rec_block_t *b, int m, int d, int n) { uint16_t inst = 0x5000 | (n << 8) | (m << 4) | (d); EMIT_INST; } static inline void emitMOVLS4(sh2rec_block_t *b, int m, int d, int n) { uint16_t inst = 0x1000 | (n << 8) | (m << 4) | (d); EMIT_INST; } static inline void emitMOVLLG(sh2rec_block_t *b, int imm) { uint16_t inst = 0xC600 | (imm & 0xFF); EMIT_INST; } static inline void emitMOVLSG(sh2rec_block_t *b, int imm) { uint16_t inst = 0xC200 | (imm & 0xFF); EMIT_INST; } static inline void emitMOVT(sh2rec_block_t *b, int n) { uint16_t inst = 0x0029 | (n << 8); EMIT_INST; } static inline void emitALU(sh2rec_block_t *b, int m, int n, uint16_t op) { uint16_t inst = (n << 8) | (m << 4) | op; EMIT_INST; } static inline void emitSHIFT(sh2rec_block_t *b, int n, uint16_t op) { uint16_t inst = (n << 8) | op; EMIT_INST; } static inline void emitADDI(sh2rec_block_t *b, int imm, int n) { uint16_t inst = 0x7000 | (n << 8) | (imm & 0xFF); EMIT_INST; } static inline void emitANDI(sh2rec_block_t *b, int imm) { uint16_t inst = 0xC900 | (imm & 0xFF); EMIT_INST; } static inline void emitORI(sh2rec_block_t *b, int imm) { uint16_t inst = 0xCB00 | (imm & 0xFF); EMIT_INST; } static inline void emitXORI(sh2rec_block_t *b, int imm) { uint16_t inst = 0xCA00 | (imm & 0xFF); EMIT_INST; } static inline void emitSHLL2(sh2rec_block_t *b, int n) { uint16_t inst = 0x4008 | (n << 8); EMIT_INST; } static inline void emitSHLL8(sh2rec_block_t *b, int n) { uint16_t inst = 0x4018 | (n << 8); EMIT_INST; } static inline void emitSHLL16(sh2rec_block_t *b, int n) { uint16_t inst = 0x4028 | (n << 8); EMIT_INST; } static inline void emitSHLR2(sh2rec_block_t *b, int n) { uint16_t inst = 0x4009 | (n << 8); EMIT_INST; } static inline void emitSHLR8(sh2rec_block_t *b, int n) { uint16_t inst = 0x4019 | (n << 8); EMIT_INST; } static inline void emitSHLR16(sh2rec_block_t *b, int n) { uint16_t inst = 0x4029 | (n << 8); EMIT_INST; } static inline void emitCMPIM(sh2rec_block_t *b, int imm) { uint16_t inst = 0x8800 | (imm & 0xFF); EMIT_INST; } static inline void emitCMPPL(sh2rec_block_t *b, int n) { uint16_t inst = 0x4015 | (n << 8); EMIT_INST; } static inline void emitCMPPZ(sh2rec_block_t *b, int n) { uint16_t inst = 0x4011 | (n << 8); EMIT_INST; } static inline void emitADDV(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x300F | (n << 8) | (m << 4); EMIT_INST; } static inline void emitSUBV(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x300B | (n << 8) | (m << 4); EMIT_INST; } static inline void emitLDS(sh2rec_block_t *b, int m, int sr) { uint16_t inst = 0x400A | (m << 8) | (sr << 4); EMIT_INST; } static inline void emitSTS(sh2rec_block_t *b, int sr, int n) { uint16_t inst = 0x000A | (n << 8) | (sr << 4); EMIT_INST; } static inline void emitLDC(sh2rec_block_t *b, int m, int sr) { uint16_t inst = 0x400E | (m << 8) | (sr << 4); EMIT_INST; } static inline void emitSTC(sh2rec_block_t *b, int sr, int n) { uint16_t inst = 0x0002 | (n << 8) | (sr << 4); EMIT_INST; } static inline void emitDT(sh2rec_block_t *b, int n) { uint16_t inst = 0x4010 | (n << 8); EMIT_INST; } static inline void emitTSTI(sh2rec_block_t *b, int imm) { uint16_t inst = 0xC800 | (imm & 0xFF); EMIT_INST; } static inline void emitBRA(sh2rec_block_t *b, int d) { uint16_t inst = 0xA000 | (d); EMIT_INST; } static inline void emitDIV0S(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x2007 | (n << 8) | (m << 4); EMIT_INST; } static inline void emitDIV1(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x3004 | (n << 8) | (m << 4); EMIT_INST; } static inline void emitRTS(sh2rec_block_t *b) { uint16_t inst = 0x000B; EMIT_INST; } static inline void emitNOP(sh2rec_block_t *b) { uint16_t inst = 0x0009; EMIT_INST; } static inline void emitJSR(sh2rec_block_t *b, int m) { uint16_t inst = 0x400B | (m << 8); EMIT_INST; } static inline void emitMACL(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x000F | (n << 8) | (m << 4); EMIT_INST; } static inline void emitMACW(sh2rec_block_t *b, int m, int n) { uint16_t inst = 0x400F | (n << 8) | (m << 4); EMIT_INST; } static inline void emitCLRMAC(sh2rec_block_t *b) { uint16_t inst = 0x0028; EMIT_INST; } static inline void emitBF(sh2rec_block_t *b, int disp) { uint16_t inst = 0x8B00 | (disp & 0xFF); EMIT_INST; } static inline void emitBT(sh2rec_block_t *b, int disp) { uint16_t inst = 0x8900 | (disp & 0xFF); EMIT_INST; } static inline void generateALUOP(uint16_t inst, sh2rec_block_t *b, int op) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitALU(b, R3, R2, op); /* R2 <- R2 o R3 */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ } static inline void generateSHIFT(uint16_t inst, sh2rec_block_t *b, int op) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitSHIFT(b, R2, op); /* R2 <- R2 op */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ } static inline void generateCOMP(uint16_t inst, sh2rec_block_t *b, int op) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitALU(b, R3, R2, op); /* R2 op R3 */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ } static void generateADD(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_ADD); b->pc += 2; } static void generateADDI(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int imm = INSTRUCTION_CD(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitADDI(b, imm, R2); /* R2 <- R2 + #imm */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateADDC(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitALU(b, R3, R2, OP_ADDC); /* R2 = R2 + R3 + T (carry to T) */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateADDV(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitADDV(b, R3, R2); /* R2 = R2 + R3 (overflow to T Bit) */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateAND(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_AND); b->pc += 2; } static void generateANDI(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ emitANDI(b, imm); /* R0 <- R0 & #imm */ emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateANDM(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitMOVLL4(b, R8, 0, R4); /* R4 <- sh2[R0] */ emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitJSR(b, R1); /* Call MappedMemoryReadByte */ emitMOVLM(b, R4, R15); /* Push R4 on the stack (delay slot) */ emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ emitANDI(b, imm); /* R0 <- R0 & #imm */ emitMOVLP(b, R15, R4); /* Pop R4 off the stack */ emitJSR(b, R1); /* Call MappedMemoryWriteByte */ emitMOV(b, R0, R5); /* R5 <- R0 (delay slot) */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateBF(uint16_t inst, sh2rec_block_t *b) { int disp = INSTRUCTION_CD(inst); uint32_t val = b->pc + 2; emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLI(b, 4, R2); /* R2 <- sh2[PC] + 2 */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitMOVI(b, 0, R0); /* R0 <- 0 */ emitBT(b, 2); /* Branch around the addition if needed */ emitMOVI(b, disp, R0); /* R0 <- displacement */ emitSHIFT(b, R0, OP_SHLL); /* R0 <- R0 << 1 */ emitADDI(b, 2, R0); /* R0 <- R0 + 2 */ emitRTS(b); /* Return to sender! */ emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ if(((uint32_t)b->ptr) & 0x03) emit16(b, 0); /* Padding if we need it */ emit32(b, val); /* The next PC value (if not taken) */ b->cycles += 2; /* 2 Cycles (if not taken) */ /* XXXX: Handle taken case cycle difference */ } static void generateBFS(uint16_t inst, sh2rec_block_t *b) { int disp = INSTRUCTION_CD(inst); uint32_t val = b->pc + 4; int n = (((uint32_t)b->ptr) & 0x03) ? 3 : 4; emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLI(b, n, R2); /* R2 <- sh2[PC] + 4 */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitMOVI(b, 0, R0); /* R0 <- 0 */ emitBT(b, 1); /* Branch around the addition if needed */ emitMOVI(b, disp, R0); /* R0 <- displacement */ emitSHIFT(b, R0, OP_SHLL); /* R0 <- R0 << 1 */ if(((uint32_t)b->ptr) & 0x03) { emitBRA(b, 3); /* Branch around the constant */ emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ emit16(b, 0); /* Padding since we need it */ } else { emitBRA(b, 2); /* Branch around the constant */ emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ } emit32(b, val); /* The next PC value (if not taken) */ emitMOVLM(b, R0, R15); /* Push the next PC on the stack */ /* Deal with the delay slot here */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ ++b->cycles; /* 1 Cycle (if not taken) */ /* XXXX: Handle taken case cycle difference */ } static void generateBRA(uint16_t inst, sh2rec_block_t *b) { int disp = INSTRUCTION_BCD(inst); int32_t val; if(disp & 0x00000800) { disp |= 0xFFFFF000; } val = b->pc + 4 + (disp << 1); emitMOVLI(b, 1, R2); /* R2 <- sh2[PC] + 4 + disp */ if(((uint32_t)b->ptr) & 0x03) { emitBRA(b, 3); /* Branch around the constant */ emitMOVLM(b, R2, R15); /* Push the next PC (delay slot) */ emit16(b, 0); /* Padding since we need it */ } else { emitBRA(b, 2); /* Branch around the constant */ emitMOVLM(b, R2, R15); /* Push the next PC (delay slot) */ } emit32(b, (uint32_t )val); /* The next PC */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ b->cycles += 2; /* 2 Cycles */ } static void generateBRAF(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); uint32_t val = b->pc + 4; if(((uint32_t)b->ptr) & 0x03) { emitMOVLI(b, 2, R0); /* R0 <- sh2[PC] + 4 */ emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] */ emitBRA(b, 3); /* Branch around the constant */ emitALU(b, R0, R2, OP_ADD); /* R2 <- R0 + R2 (delay slot) */ emit16(b, 0); /* Padding since we need it */ } else { emitMOVLI(b, 1, R0); /* R0 <- sh2[PC] + 4 */ emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] */ emitBRA(b, 2); /* Branch around the constant */ emitALU(b, R0, R2, OP_ADD); /* R2 <- R0 + R2 (delay slot) */ } emit32(b, val); /* The value to use as the base for PC */ emitMOVLM(b, R2, R15); /* Push the next PC */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ b->cycles += 2; /* 2 Cycles */ } static void generateBSR(uint16_t inst, sh2rec_block_t *b) { int disp = INSTRUCTION_BCD(inst); int32_t val; int32_t val2 = b->pc + 4; if(disp & 0x00000800) { disp |= 0xFFFFF000; } val = b->pc + 4 + (disp << 1); if(((uint32_t)b->ptr) & 0x03) { emitMOVLI(b, 2, R2); /* R2 <- sh2[PC] + 4 + disp */ emitMOVLI(b, 2, R0); /* R0 <- sh2[PC] + 4 */ emitBRA(b, 5); /* Branch around the constant */ emitMOVLM(b, R2, R15); /* Push the next PC (delay slot) */ emit16(b, 0); /* Padding since we need it */ } else { emitMOVLI(b, 1, R2); /* R2 <- sh2[PC] + 4 + disp */ emitMOVLI(b, 2, R0); /* R0 <- sh2[PC] + 4 */ emitBRA(b, 4); /* Branch around the constant */ emitMOVLM(b, R2, R15); /* Push the next PC (delay slot) */ } emit32(b, (uint32_t)val); /* The next PC */ emit32(b, (uint32_t)val2); /* The value for PR */ emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ b->cycles += 2; /* 2 Cycles */ } static void generateBSRF(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); uint32_t val = b->pc + 4; emitMOVLI(b, 1, R0); /* R0 <- sh2[PC] + 4 */ if(((uint32_t)b->ptr) & 0x03) { emitBRA(b, 3); /* Branch around the constant */ emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] (delay slot) */ emit16(b, 0); /* Padding since we need it */ } else { emitBRA(b, 2); /* Branch around the constant */ emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] (delay slot) */ } emit32(b, val); /* The value to put in PR */ emitALU(b, R0, R2, OP_ADD); /* R2 <- R0 + R2 (branch target) */ emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ emitMOVLM(b, R2, R15); /* Push the next PC */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ b->cycles += 2; /* 2 Cycles */ } static void generateBT(uint16_t inst, sh2rec_block_t *b) { int disp = INSTRUCTION_CD(inst); uint32_t val = b->pc + 2; emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLI(b, 4, R2); /* R2 <- sh2[PC] + 4 */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitMOVI(b, 0, R0); /* R0 <- 0 */ emitBF(b, 2); /* Branch around the addition if needed */ emitMOVI(b, disp, R0); /* R0 <- displacement */ emitSHIFT(b, R0, OP_SHLL); /* R0 <- R0 << 1 */ emitADDI(b, 2, R0); /* R0 <- R0 + 2 */ emitRTS(b); /* Return to sender! */ emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ if(((uint32_t)b->ptr) & 0x03) emit16(b, 0); /* Padding if we need it */ emit32(b, val); /* The next PC value (if not taken) */ b->cycles += 2; /* 2 Cycles (if not taken) */ /* XXXX: Handle taken case cycle difference */ } static void generateBTS(uint16_t inst, sh2rec_block_t *b) { int disp = INSTRUCTION_CD(inst); uint32_t val = b->pc + 4; int n = (((uint32_t)b->ptr) & 0x03) ? 3 : 4; emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLI(b, n, R2); /* R2 <- sh2[PC] + 2 */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitMOVI(b, 0, R0); /* R0 <- 0 */ emitBF(b, 1); /* Branch around the addition if needed */ emitMOVI(b, disp, R0); /* R0 <- displacement */ emitSHIFT(b, R0, OP_SHLL); /* R0 <- R0 << 1 */ if(((uint32_t)b->ptr) & 0x03) { emitBRA(b, 3); /* Branch around the constant */ emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ emit16(b, 0); /* Padding since we need it */ } else { emitBRA(b, 2); /* Branch around the constant */ emitALU(b, R2, R0, OP_ADD); /* R0 <- R0 + R2 (delay slot) */ } emit32(b, val); /* The next PC value (if not taken) */ emitMOVLM(b, R0, R15); /* Push the next PC */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ ++b->cycles; /* 1 Cycle (if not taken) */ /* XXXX: Handle taken case cycle difference */ } static void generateCLRMAC(uint16_t inst, sh2rec_block_t *b) { emitCLRMAC(b); /* MACL/MACH <- 0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateCLRT(uint16_t inst, sh2rec_block_t *b) { emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVI(b, 0xFE, R1); /* R1 <- 0xFFFFFFFE */ emitALU(b, R3, R0, OP_AND); /* Clear T bit */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateCMPEQ(uint16_t inst, sh2rec_block_t *b) { generateCOMP(inst, b, OP_CMPEQ); b->pc += 2; } static void generateCMPGE(uint16_t inst, sh2rec_block_t *b) { generateCOMP(inst, b, OP_CMPGE); b->pc += 2; } static void generateCMPGT(uint16_t inst, sh2rec_block_t *b) { generateCOMP(inst, b, OP_CMPGT); b->pc += 2; } static void generateCMPHI(uint16_t inst, sh2rec_block_t *b) { generateCOMP(inst, b, OP_CMPHI); b->pc += 2; } static void generateCMPHS(uint16_t inst, sh2rec_block_t *b) { generateCOMP(inst, b, OP_CMPHS); b->pc += 2; } static void generateCMPSTR(uint16_t inst, sh2rec_block_t *b) { generateCOMP(inst, b, OP_CMPSTR); b->pc += 2; } static void generateCMPPL(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitCMPPL(b, R2); /* cmp/pl R2 */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateCMPPZ(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitCMPPZ(b, R2); /* cmp/pz R2 */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateCMPIM(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOV(b, R0, R2); /* R2 <- R0 */ emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ emitSHIFT(b, R2, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitCMPIM(b, imm); /* cmp/eq R0, #imm */ emitSHIFT(b, R2, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOV(b, R2, R0); /* R0 <- R2 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateDIV0S(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVI(b, 0x03, R4); /* R4 <- 0x03 */ emitANDI(b, 0xF2); /* Clear M, Q, and T */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitSHLL8(b, R4); /* R4 <<= 8 */ emitSHIFT(b, R0, OP_SHLR); /* Chop off the T from the SH2 reg */ emitDIV0S(b, R3, R2); /* div0s to grab the M, Q, T bits needed */ emitSTC(b, R_SR, R5); /* Save SR to R5 */ emitALU(b, R4, R5, OP_AND); /* Grab M, Q from the SR */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitALU(b, R5, R0, OP_OR); /* Save M, Q into the SH2 reg */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateDIV0U(uint16_t inst, sh2rec_block_t *b) { emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitANDI(b, 0xF2); /* Mask off M, Q, and T bits */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateDIV1(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVI(b, 0x03, R4); /* R4 <- 0x03 */ emitSHLL8(b, R4); /* R4 <<= 8 */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitMOV(b, R4, R6); /* R6 <- R4 */ emitALU(b, R0, R6, OP_AND); /* Grab SH2 M and Q bits */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T in place */ emitSTC(b, R_SR, R5); /* Save SR to R5 */ emitALU(b, R4, R7, OP_NOT); /* Set up the mask to clear M and Q */ emitALU(b, R7, R5, OP_AND); /* Clear M, Q */ emitALU(b, R6, R5, OP_OR); /* Put SH2's M and Q in place */ emitLDC(b, R5, R_SR); /* Put the modified SR in place */ emitDIV1(b, R3, R2); /* Do the division! */ emitSTC(b, R_SR, R5); /* Save updated SR to R5 */ emitALU(b, R4, R5, OP_AND); /* Grab M and Q from the SR */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ emitANDI(b, 0xF3); /* Clear M and Q from the SH2 reg */ emitALU(b, R5, R0, OP_OR); /* Save M and Q into the SH2 reg */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateDMULS(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitALU(b, R3, R2, OP_DMULS); /* MACH/MACL <- (s32)R2 * (s32)R3 */ b->cycles += 2; /* 2 Cycles */ b->pc += 2; } static void generateDMULU(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitALU(b, R3, R2, OP_DMULU); /* MACH/MACL <- (u32)R2 * (u32)R3 */ b->cycles += 2; /* 2 Cycles */ b->pc += 2; } static void generateDT(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitDT(b, R2); /* R2 = R2 - 1 (T Bit = non-zero) */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateEXTSB(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_EXTSB); b->pc += 2; } static void generateEXTSW(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_EXTSW); b->pc += 2; } static void generateEXTUB(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_EXTUB); b->pc += 2; } static void generateEXTUW(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_EXTUW); b->pc += 2; } static void generateJMP(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regm, R0); /* Grab the next PC value */ emitMOVLM(b, R0, R15); /* Push the next PC on the stack */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ b->cycles += 2; /* 2 Cycles */ } static void generateJSR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); uint32_t val = b->pc + 4; emitMOVLI(b, 1, R0); /* R0 <- sh2[PC] + 4 */ if(((uint32_t)b->ptr) & 0x03) { emitBRA(b, 3); /* Branch around the constant */ emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] (delay slot) */ emit16(b, 0); /* Padding since we need it */ } else { emitBRA(b, 2); /* Branch around the constant */ emitMOVLL4(b, R8, regm, R2);/* R2 <- sh2[Rm] (delay slot) */ } emit32(b, val); /* The value to put in PR */ emitMOVLM(b, R2, R15); /* Push the next PC */ emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ b->cycles += 2; /* 2 Cycles */ } static void generateLDCSR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVWI(b, 2, R2); /* R2 <- 0x03F3 */ emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ emitBRA(b, 1); /* Jump beyond the constant */ emitALU(b, R2, R0, OP_AND); /* R0 <- R0 & R2 (delay slot) */ emit16(b, 0x03F3); /* 0x03F3, grabbed by the emitMOVWI */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateLDCGBR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ emitMOVLSG(b, 17); /* sh2[GBR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateLDCVBR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ emitMOVLSG(b, 18); /* sh2[VBR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateLDCMSR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 4, R1); /* R1 <- 4 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVWI(b, 2, R2); /* R2 <- 0x03F3 */ emitBRA(b, 1); /* Jump beyond the constant */ emitALU(b, R2, R0, OP_AND); /* R0 <- R0 & R2 (delay slot) */ emit16(b, 0x03F3); /* 0x03F3, grabbed by the emitMOVWI */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateLDCMGBR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 4, R1); /* R1 <- 4 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLSG(b, 17); /* sh2[GBR] <- R0 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateLDCMVBR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 4, R1); /* R1 <- 4 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLSG(b, 18); /* sh2[VBR] <- R0 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateLDSMACH(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ emitLDS(b, R0, R_MACH); /* MACH <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateLDSMACL(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ emitLDS(b, R0, R_MACL); /* MACL <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateLDSPR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regm, R0); /* R0 <- sh2[Rm] */ emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateLDSMMACH(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 4, R1); /* R1 <- 4 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitLDS(b, R0, R_MACH); /* MACH <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateLDSMMACL(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 4, R1); /* R1 <- 4 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitLDS(b, R0, R_MACL); /* MACL <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateLDSMPR(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_B(inst); emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 4, R1); /* R1 <- 4 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLSG(b, 21); /* sh2[PR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMACL(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitADDI(b, 4, R4); /* R4 <- R4 + 4 */ emitMOVLS4(b, R4, regm, R8); /* sh2[Rm] <- R4 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitADDI(b, -4, R4); /* R4 <- R4 - 4 (delay slot) */ emitMOVLM(b, R0, R15); /* Push R0 onto the stack */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitADDI(b, 4, R4); /* R4 <- R4 + 4 */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitADDI(b, -4, R4); /* R4 <- R4 - 4 (delay slot) */ emitSTC(b, R_SR, R2); /* R2 <- SR */ emitMOVI(b, 0xFD, R3); /* R3 <- 0xFFFFFFFD */ emitMOVLM(b, R0, R15); /* Push R0 onto the stack */ emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitALU(b, R2, R3, OP_AND); /* R3 <- R2 & R3 (Mask out S Bit) */ emitANDI(b, 0x02); /* R0 <- R0 & 0x02 (S Bit) */ emitALU(b, R0, R3, OP_OR); /* R3 <- R0 | R3 (Put SH2 S Bit in) */ emitLDC(b, R3, R_SR); /* SR <- R3 */ emitMACL(b, R15, R15); /* Perform the MAC.L */ emitLDC(b, R2, R_SR); /* SR <- R2 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateMACW(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ emitADDI(b, 2, R4); /* R4 <- R4 + 2 */ emitMOVLS4(b, R4, regm, R8); /* sh2[Rm] <- R4 */ emitJSR(b, R0); /* Call MappedMemoryReadWord */ emitADDI(b, -2, R4); /* R4 <- R4 - 2 (delay slot) */ emitMOVWM(b, R0, R15); /* Push R0 onto the stack */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ emitADDI(b, 2, R4); /* R4 <- R4 + 2 */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitJSR(b, R0); /* Call MappedMemoryReadWord */ emitADDI(b, -2, R4); /* R4 <- R4 - 2 (delay slot) */ emitMOVWM(b, R0, R15); /* Push R0 onto the stack */ emitSTC(b, R_SR, R2); /* R2 <- SR */ emitMOVI(b, 0xFD, R3); /* R3 <- 0xFFFFFFFD */ emitMOVLM(b, R0, R15); /* Push R0 onto the stack */ emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitALU(b, R2, R3, OP_AND); /* R3 <- R2 & R3 (Mask out S Bit) */ emitANDI(b, 0x02); /* R0 <- R0 & 0x02 (S Bit) */ emitALU(b, R0, R3, OP_OR); /* R3 <- R0 | R3 (Put SH2 S Bit in) */ emitLDC(b, R3, R_SR); /* SR <- R3 */ emitMACW(b, R15, R15); /* Perform the MAC.W */ emitLDC(b, R2, R_SR); /* SR <- R2 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateMOV(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regm, R2); /* R2 <- sh2[Rm] */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBS(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R9, 3, R0); /* R0 <- MappedMemoryWriteByte */ emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryWriteByte */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWS(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R9, 4, R0); /* R0 <- MappedMemoryWriteWord */ emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryWriteWord */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLS(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R9, 5, R0); /* R0 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBL(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R9, 0, R0); /* R0 <- MappedMemoryReadByte */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryReadByte */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read byte */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWL(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryReadWord */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read word */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLL(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBM(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitMOVLL4(b, R9, 3, R0); /* R0 <- MappedMemoryWriteByte */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -1, R4); /* R4 -= 1 */ emitJSR(b, R0); /* Call MappedMemoryWriteByte */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWM(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitMOVLL4(b, R9, 4, R0); /* R0 <- MappedMemoryWriteWord */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -2, R4); /* R4 -= 2 */ emitJSR(b, R0); /* Call MappedMemoryWriteWord */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLM(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitMOVLL4(b, R9, 5, R0); /* R0 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -4, R4); /* R4 -= 4 */ emitJSR(b, R0); /* Call MappedMemoryWriteLong */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBP(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 0, R0); /* R0 <- MappedMemoryReadByte */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 1, R1); /* R1 <- 1 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadByte */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read byte */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWP(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 2, R1); /* R1 <- 2 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadWord */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read word */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLP(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVI(b, 4, R1); /* R1 <- 4 */ emitALU(b, R4, R1, OP_ADD); /* R1 <- R4 + R1 */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLS4(b, R1, regm, R8); /* sh2[Rm] <- R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBS0(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ emitMOVLL4(b, R9, 3, R0); /* R0 <- MappedMemoryWriteByte */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitJSR(b, R0); /* Call MappedMemoryWriteByte */ emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWS0(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ emitMOVLL4(b, R9, 4, R0); /* R0 <- MappedMemoryWriteWord */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitJSR(b, R0); /* Call MappedMemoryWriteWord */ emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLS0(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ emitMOVLL4(b, R9, 5, R0); /* R0 <- MappedMemoryWriteLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitJSR(b, R0); /* Call MappedMemoryWriteLong */ emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBL0(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 0, R0); /* R0 <- MappedMemoryReadByte */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ emitJSR(b, R0); /* Call MappedMemoryReadByte */ emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read byte */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWL0(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ emitJSR(b, R0); /* Call MappedMemoryReadWord */ emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read word */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLL0(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVLL4(b, R8, 0, R1); /* R1 <- sh2[R0] */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitALU(b, R1, R4, OP_ADD); /* R4 <- R4 + R1 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVI(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int imm = INSTRUCTION_CD(inst); emitMOVI(b, imm, R2); /* R2 <- #imm */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWI(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int imm = INSTRUCTION_CD(inst); uint32_t addr = b->pc + 4 + (imm << 1); if(((uint32_t)b->ptr) & 0x03) { emitMOVLI(b, 1, R4); /* R4 <- calculated effective addr */ emitBRA(b, 2); /* Jump beyond the constant */ emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ emit32(b, addr); /* MOV.W effective address */ } else { emitMOVLI(b, 1, R4); /* R4 <- calculated effective addr */ emitBRA(b, 3); /* Jump beyond the constant */ emitMOVLL4(b, R9, 1, R0); /* R0 <- MappedMemoryReadWord */ emit16(b, 0); /* Padding, for alignment issues */ emit32(b, addr); /* MOV.W effective address */ } emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryReadWord */ emitNOP(b); /* XXXX: Nothing to put here */ emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read word */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLI(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int imm = INSTRUCTION_CD(inst); uint32_t addr = ((b->pc + 4) & 0xFFFFFFFC) + (imm << 2); if(((uint32_t)b->ptr) & 0x03) { emitMOVLI(b, 1, R4); /* R4 <- calculated effective addr */ emitBRA(b, 2); /* Jump beyond the constant */ emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emit32(b, addr); /* MOV.L effective address */ } else { emitMOVLI(b, 1, R4); /* R4 <- calculated effective addr */ emitBRA(b, 3); /* Jump beyond the constant */ emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emit16(b, 0); /* Padding, for alignment issues */ emit32(b, addr); /* MOV.L effective address */ } emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitNOP(b); /* XXXX: Nothing to put here */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBLG(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ emitMOVI(b, imm, R4); /* R4 <- Displacement */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitALU(b, R4, R4, OP_EXTUB); /* Zero extend displacement */ emitJSR(b, R1); /* Call MappedMemoryReadByte */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read byte */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWLG(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVI(b, imm, R4); /* R4 <- Displacement */ emitMOVLL4(b, R9, 1, R1); /* R1 <- MappedMemoryReadWord */ emitALU(b, R4, R4, OP_EXTUB); /* Zero extend displacement */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitSHIFT(b, R4, OP_SHLL); /* Double displacement */ emitJSR(b, R1); /* Call MappedMemoryReadWord */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read word */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLLG(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVI(b, imm, R4); /* R4 <- Displacement */ emitMOVLL4(b, R9, 2, R1); /* R1 <- MappedMemoryReadLong */ emitALU(b, R4, R4, OP_EXTUB); /* Zero extend displacement */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitSHLL2(b, R4); /* Quadruple displacement */ emitJSR(b, R1); /* Call MappedMemoryReadLong */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read long */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBSG(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ emitMOVI(b, imm, R4); /* R4 <- Displacement */ emitALU(b, R4, R4, OP_EXTUB); /* Zero extend Displacement */ emitJSR(b, R1); /* Call MappedMemoryWriteByte */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWSG(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ emitMOVI(b, imm, R4); /* R4 <- Displacement */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitALU(b, R4, R4, OP_EXTUB); /* Zero extend Displacement */ emitMOVLL4(b, R9, 4, R1); /* R1 <- MappedMemoryWriteWord */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitSHIFT(b, R4, OP_SHLL); /* Double displacement */ emitJSR(b, R1); /* Call MappedMemoryWriteWord */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLSG(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ emitMOVI(b, imm, R4); /* R4 <- Displacement */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitALU(b, R4, R4, OP_EXTUB); /* Zero extend Displacement */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitSHLL2(b, R4); /* Quadruple displacement */ emitJSR(b, R1); /* Call MappedMemoryWriteLong */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBS4(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_C(inst); int imm = INSTRUCTION_D(inst); emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R1); /* Call MappedMemoryWriteByte */ emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWS4(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_C(inst); int imm = INSTRUCTION_D(inst) << 1; emitMOVLL4(b, R8, 0, R5); /* R5 <- sh2[R0] */ emitMOVLL4(b, R9, 4, R1); /* R1 <- MappedMemoryWriteWord */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R1); /* Call MappedMemoryWriteWord */ emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLS4(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); int imm = INSTRUCTION_D(inst) << 2; emitMOVLL4(b, R8, regm, R5); /* R5 <- sh2[Rm] */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R1); /* Call MappedMemoryWriteLong */ emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVBL4(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_C(inst); int imm = INSTRUCTION_D(inst); emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R1); /* Call MappedMemoryReadByte */ emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ emitALU(b, R0, R0, OP_EXTSB); /* Sign extend read byte */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read byte */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVWL4(uint16_t inst, sh2rec_block_t *b) { int regm = INSTRUCTION_C(inst); int imm = INSTRUCTION_D(inst) << 1; emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVLL4(b, R9, 1, R1); /* R1 <- MappedMemoryReadWord */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R1); /* Call MappedMemoryReadWord */ emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ emitALU(b, R0, R0, OP_EXTSW); /* Sign extend read word */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- read word */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVLL4(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); int imm = INSTRUCTION_D(inst) << 2; emitMOVLL4(b, R8, regm, R4); /* R4 <- sh2[Rm] */ emitMOVLL4(b, R9, 2, R1); /* R1 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R1); /* Call MappedMemoryReadLong */ emitADDI(b, imm, R4); /* R4 <- R4 + displacement */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- read long */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVA(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); uint32_t addr = ((b->pc + 4) & 0xFFFFFFFC) + (imm << 2); if(((uint32_t)b->ptr) & 0x03) { emitMOVLI(b, 1, R2); /* R2 <- calculated effective addr */ emitBRA(b, 2); /* Jump beyond the constant */ emitMOVLS4(b, R2, 0, R8); /* sh2[R0] <- R2 */ emit32(b, addr); /* MOVA effective address */ } else { emitMOVLI(b, 1, R2); /* R2 <- calculated effective addr */ emitBRA(b, 3); /* Jump beyond the constant */ emitMOVLS4(b, R2, 0, R8); /* sh2[R0] <- R2 */ emit16(b, 0); /* Padding, for alignment issues */ emit32(b, addr); /* MOVA effective address */ } ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMOVT(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitANDI(b, 0x01); /* Grab T Bit */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- T Bit */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMULL(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitALU(b, R3, R2, OP_MULL); /* MACL <- R2 * R3 */ b->cycles += 2; /* 2 Cycles */ b->pc += 2; } static void generateMULS(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitALU(b, R3, R2, OP_MULS); /* MACL <- (s16)R2 * (s16)R3 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateMULU(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitALU(b, R3, R2, OP_MULU); /* MACL <- (u16)R2 * (u16)R3 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateNEG(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitALU(b, R3, R2, OP_NEG); /* R2 <- 0 - R3 */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateNEGC(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitALU(b, R3, R2, OP_NEGC); /* R2 = 0 - R3 - T (borrow to T) */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateNOP(uint16_t inst, sh2rec_block_t *b) { ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateNOT(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_NOT); b->pc += 2; } static void generateOR(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_OR); b->pc += 2; } static void generateORI(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ emitORI(b, imm); /* R0 <- R0 | #imm */ emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateORM(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitMOVLL4(b, R8, 0, R4); /* R4 <- sh2[R0] */ emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitJSR(b, R1); /* Call MappedMemoryReadByte */ emitMOVLM(b, R4, R15); /* Push R4 on the stack (delay slot) */ emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ emitORI(b, imm); /* R0 <- R0 | #imm */ emitMOVLP(b, R15, R4); /* Pop R4 off the stack */ emitJSR(b, R1); /* Call MappedMemoryWriteByte */ emitMOV(b, R0, R5); /* R5 <- R0 (delay slot) */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateROTCL(uint16_t inst, sh2rec_block_t *b) { generateSHIFT(inst, b, OP_ROTCL); b->pc += 2; } static void generateROTCR(uint16_t inst, sh2rec_block_t *b) { generateSHIFT(inst, b, OP_ROTCR); b->pc += 2; } static void generateROTL(uint16_t inst, sh2rec_block_t *b) { generateSHIFT(inst, b, OP_ROTL); b->pc += 2; } static void generateROTR(uint16_t inst, sh2rec_block_t *b) { generateSHIFT(inst, b, OP_ROTR); b->pc += 2; } static void generateRTE(uint16_t inst, sh2rec_block_t *b) { emitMOVLL4(b, R9, 2, R0); /* R0 <- MappedMemoryReadLong */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryReadLong */ emitMOVLL4(b, R8, 15, R4); /* R4 <- sh2[R15] (delay slot) */ emitMOVLL4(b, R9, 2, R1); /* R1 <- MappedMemoryReadLong */ emitMOVLL4(b, R8, 15, R4); /* R4 <- sh2[R15] */ emitMOVI(b, 4, R2); /* R2 <- 4 */ emitALU(b, R2, R4, OP_ADD); /* R4 <- R4 + R2 */ emitMOVLM(b, R0, R15); /* Push the next PC */ emitALU(b, R4, R2, OP_ADD); /* R2 <- R4 + R2 */ emitJSR(b, R1); /* Call MappedMemoryReadLong */ emitMOVLS4(b, R2, 15, R8); /* sh2[R15] <- R2 (delay slot) */ emitMOVWI(b, 1, R1); /* R1 <- 0x000003F3 */ emitBRA(b, 1); /* Branch around the constant */ emitALU(b, R1, R0, OP_AND); /* R0 <- R0 & R1 */ emit16(b, 0x03F3); /* Mask for SR register */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ b->cycles += 4; /* 4 Cycles */ } static void generateRTS(uint16_t inst, sh2rec_block_t *b) { emitMOVLLG(b, 21); /* R0 <- sh2[PR] */ emitMOVLM(b, R0, R15); /* Push the PR on the stack */ /* Deal with the delay slot */ b->pc += 2; sh2rec_rec_inst(b, 1); emitRTS(b); /* Return to sender! */ emitMOVLP(b, R15, R0); /* Pop the next PC (delay slot) */ b->cycles += 2; /* 2 Cycles */ } static void generateSETT(uint16_t inst, sh2rec_block_t *b) { emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitORI(b, 0x01); /* Set T Bit */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSHAL(uint16_t inst, sh2rec_block_t *b) { generateSHIFT(inst, b, OP_SHAL); b->pc += 2; } static void generateSHAR(uint16_t inst, sh2rec_block_t *b) { generateSHIFT(inst, b, OP_SHAR); b->pc += 2; } static void generateSHLL(uint16_t inst, sh2rec_block_t *b) { generateSHIFT(inst, b, OP_SHLL); b->pc += 2; } static void generateSHLL2(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHLL2(b, R2); /* R2 <- R2 << 2 */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSHLL8(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHLL8(b, R2); /* R2 <- R2 << 8 */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSHLL16(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHLL16(b, R2); /* R2 <- R2 << 16 */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSHLR(uint16_t inst, sh2rec_block_t *b) { generateSHIFT(inst, b, OP_SHLR); b->pc += 2; } static void generateSHLR2(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHLR2(b, R2); /* R2 <- R2 >> 2 */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSHLR8(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHLR8(b, R2); /* R2 <- R2 >> 8 */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSHLR16(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitSHLR16(b, R2); /* R2 <- R2 >> 16 */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSLEEP(uint16_t inst, sh2rec_block_t *b) { b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateSTCSR(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSTCGBR(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSTCVBR(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 18); /* R0 <- sh2[VBR] */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSTCMSR(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -4, R4); /* R4 -= 4 */ emitMOV(b, R0, R5); /* R5 <- R0 */ emitJSR(b, R1); /* Call MappedMemoryWriteLong */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 2; /* 2 Cycles */ b->pc += 2; } static void generateSTCMGBR(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -4, R4); /* R4 -= 4 */ emitMOV(b, R0, R5); /* R5 <- R0 */ emitJSR(b, R1); /* Call MappedMemoryWriteLong */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 2; /* 2 Cycles */ b->pc += 2; } static void generateSTCMVBR(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 18); /* R0 <- sh2[VBR] */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -4, R4); /* R4 -= 4 */ emitMOV(b, R0, R5); /* R5 <- R0 */ emitJSR(b, R1); /* Call MappedMemoryWriteLong */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 2; /* 2 Cycles */ b->pc += 2; } static void generateSTSMACH(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitSTS(b, R_MACH, R0); /* R0 <- MACH */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSTSMACL(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitSTS(b, R_MACL, R0); /* R0 <- MACL */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSTSPR(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 21); /* R0 <- sh2[PR] */ emitMOVLS4(b, R0, regn, R8); /* sh2[Rn] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSTSMMACH(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitSTS(b, R_MACH, R5); /* R5 <- MACH */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -4, R4); /* R4 -= 4 */ emitJSR(b, R1); /* Call MappedMemoryWriteLong */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSTSMMACL(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitSTS(b, R_MACL, R5); /* R5 <- MACL */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -4, R4); /* R4 -= 4 */ emitJSR(b, R1); /* Call MappedMemoryWriteLong */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSTSMPR(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLLG(b, 21); /* R0 <- sh2[PR] */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MappedMemoryWriteLong */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitADDI(b, -4, R4); /* R4 -= 4 */ emitMOV(b, R0, R5); /* R5 <- R0 */ emitJSR(b, R1); /* Call MappedMemoryWriteLong */ emitMOVLS4(b, R4, regn, R8); /* sh2[Rn] <- R4 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSUB(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_SUB); b->pc += 2; } static void generateSUBC(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitALU(b, R3, R2, OP_SUBC); /* R2 = R2 - R3 - T (borrow to T) */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLS4(b, R2, regn, R8); /* sh2[Rn] <- R2 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSUBV(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); int regm = INSTRUCTION_C(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVLL4(b, R8, regn, R2); /* R2 <- sh2[Rn] */ emitMOVLL4(b, R8, regm, R3); /* R3 <- sh2[Rm] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitSUBV(b, R3, R2); /* R2 = R2 - R3 (underflow to T Bit) */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateSWAPB(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_SWAPB); b->pc += 2; } static void generateSWAPW(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_SWAPW); b->pc += 2; } static void generateTAS(uint16_t inst, sh2rec_block_t *b) { int regn = INSTRUCTION_B(inst); emitMOVLL4(b, R9, 0, R0); /* R0 <- MappedMemoryReadByte */ emitSTS(b, R_PR, R10); /* R10 <- PR */ emitJSR(b, R0); /* Call MappedMemoryReadByte */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] (delay slot) */ emitMOV(b, R0, R5); /* R5 <- R0 (byte read) */ emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOVI(b, 0x80, R2); /* R2 <- 0x80 */ emitMOVLL4(b, R8, regn, R4); /* R4 <- sh2[Rn] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitALU(b, R5, R5, OP_TST); /* T <- 1 if byte == 0, 0 otherwise */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ emitJSR(b, R1); /* Call MappedMemoryWriteByte */ emitALU(b, R2, R5, OP_OR); /* R5 <- R5 | 0x80 (delay slot) */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 4; /* 4 Cycles */ b->pc += 2; } static void generateTRAPA(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); int disp = (((uint32_t)(b->ptr)) & 0x03) ? 5 : 6; uint32_t val = b->pc + 2; emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R8, 15, R4); /* R4 <- sh2[R15] */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MemoryMappedWriteLong */ emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitADDI(b, -4, R4); /* R4 <- R4 - 4 */ emitJSR(b, R1); /* Call MemoryMappedWriteLong */ emitMOV(b, R0, R5); /* R5 <- R0 (delay slot) */ emitMOVLL4(b, R8, 15, R4); /* R4 <- sh2[R15] */ emitMOVLL4(b, R9, 5, R1); /* R1 <- MemoryMappedWriteLong */ emitADDI(b, -8, R4); /* R4 <- R4 - 8 */ emitMOVLI(b, disp, R5); /* R5 <- Updated PC value (to be stacked) */ emitJSR(b, R1); /* Call MemoryMappedWriteLong */ emitMOVLS4(b, R4, 15, R8); /* sh2[R15] <- R4 (delay slot) */ emitMOVI(b, imm, R4); /* R4 <- immediate data */ emitALU(b, R4, R4, OP_EXTUB); /* Zero-extend R4 */ emitMOVLL4(b, R9, 2, R1); /* R1 <- MemoryMappedReadLong */ emitMOVLLG(b, 18); /* R0 <- sh2[VBR] */ emitSHLL2(b, R4); /* R4 <- R4 << 2 */ emitJSR(b, R1); /* Call MemoryMappedReadLong */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 (delay slot) */ emitLDS(b, R10, R_PR); /* PR <- R10 */ emitRTS(b); /* Return to sender! */ emitNOP(b); /* XXXX: Nothing here */ if(((uint32_t)b->ptr) & 0x03) emit16(b, 0); /* Padding for the alignment */ emit32(b, val); /* The PC value to be loaded by the MOVLI */ b->cycles += 8; /* 8 Cycles */ } static void generateTST(uint16_t inst, sh2rec_block_t *b) { generateCOMP(inst, b, OP_TST); b->pc += 2; } static void generateTSTI(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitMOV(b, R0, R2); /* R2 <- R0 */ emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ emitSHIFT(b, R2, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitTSTI(b, imm); /* tst #imm, r0 */ emitSHIFT(b, R2, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOV(b, R2, R0); /* R0 <- R2 */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateTSTM(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitMOVLL4(b, R8, 0, R4); /* R4 <- sh2[R0] */ emitJSR(b, R1); /* Call MappedMemoryReadByte */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 (delay slot) */ emitMOV(b, R0, R5); /* R5 <- R0 (byte read) */ emitMOVI(b, imm, R3); /* R3 <- immediate value */ emitMOVLLG(b, 16); /* R0 <- sh2[SR] */ emitSHIFT(b, R0, OP_ROTCR); /* Rotate SH2's T Bit in place */ emitALU(b, R3, R5, OP_TST); /* T <- 1 if (R5 & imm) == 0, 0 otherwise */ emitSHIFT(b, R0, OP_ROTCL); /* Rotate T back to SH2 reg */ emitMOVLSG(b, 16); /* sh2[SR] <- R0 */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateXOR(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_XOR); b->pc += 2; } static void generateXORI(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitMOVLL4(b, R8, 0, R0); /* R0 <- sh2[R0] */ emitXORI(b, imm); /* R0 <- R0 ^ #imm */ emitMOVLS4(b, R0, 0, R8); /* sh2[R0] <- R0 */ ++b->cycles; /* 1 Cycle */ b->pc += 2; } static void generateXORM(uint16_t inst, sh2rec_block_t *b) { int imm = INSTRUCTION_CD(inst); emitSTS(b, R_PR, R10); /* R10 <- PR */ emitMOVLLG(b, 17); /* R0 <- sh2[GBR] */ emitMOVLL4(b, R8, 0, R4); /* R4 <- sh2[R0] */ emitMOVLL4(b, R9, 0, R1); /* R1 <- MappedMemoryReadByte */ emitALU(b, R0, R4, OP_ADD); /* R4 <- R4 + R0 */ emitJSR(b, R1); /* Call MappedMemoryReadByte */ emitMOVLM(b, R4, R15); /* Push R4 on the stack (delay slot) */ emitMOVLL4(b, R9, 3, R1); /* R1 <- MappedMemoryWriteByte */ emitXORI(b, imm); /* R0 <- R0 ^ #imm */ emitMOVLP(b, R15, R4); /* Pop R4 off the stack */ emitJSR(b, R1); /* Call MappedMemoryWriteByte */ emitMOV(b, R0, R5); /* R5 <- R0 (delay slot) */ emitLDS(b, R10, R_PR); /* PR <- R10 */ b->cycles += 3; /* 3 Cycles */ b->pc += 2; } static void generateXTRCT(uint16_t inst, sh2rec_block_t *b) { generateALUOP(inst, b, OP_XTRCT); b->pc += 2; } int sh2rec_rec_inst(sh2rec_block_t *b, int isdelay) { uint16_t inst = MappedMemoryReadWord(b->pc); int done = 0; switch(INSTRUCTION_A(inst)) { case 0: switch(INSTRUCTION_D(inst)) { case 2: switch(INSTRUCTION_C(inst)) { case 0: generateSTCSR(inst, b); break; case 1: generateSTCGBR(inst, b); break; case 2: generateSTCVBR(inst, b); break; default: return -1; } break; case 3: switch(INSTRUCTION_C(inst)) { case 0: generateBSRF(inst, b); done = 1; break; case 2: generateBRAF(inst, b); done = 1; break; default: return -1; } break; case 4: generateMOVBS0(inst, b); break; case 5: generateMOVWS0(inst, b); break; case 6: generateMOVLS0(inst, b); break; case 7: generateMULL(inst, b); break; case 8: switch(INSTRUCTION_C(inst)) { case 0: generateCLRT(inst, b); break; case 1: generateSETT(inst, b); break; case 2: generateCLRMAC(inst, b); break; default: return -1; } break; case 9: switch(INSTRUCTION_C(inst)) { case 0: generateNOP(inst, b); break; case 1: generateDIV0U(inst, b); break; case 2: generateMOVT(inst, b); break; default: return -1; } break; case 10: switch(INSTRUCTION_C(inst)) { case 0: generateSTSMACH(inst, b); break; case 1: generateSTSMACL(inst, b); break; case 2: generateSTSPR(inst, b); break; default: return -1; } break; case 11: switch(INSTRUCTION_C(inst)) { case 0: generateRTS(inst, b); done = 1; break; case 1: generateSLEEP(inst, b); break; case 2: generateRTE(inst, b); done = 1; break; default: return -1; } break; case 12: generateMOVBL0(inst, b); break; case 13: generateMOVWL0(inst, b); break; case 14: generateMOVLL0(inst, b); break; case 15: generateMACL(inst, b); break; default: return -1; } break; case 1: generateMOVLS4(inst, b); break; case 2: switch(INSTRUCTION_D(inst)) { case 0: generateMOVBS(inst, b); break; case 1: generateMOVWS(inst, b); break; case 2: generateMOVLS(inst, b); break; case 4: generateMOVBM(inst, b); break; case 5: generateMOVWM(inst, b); break; case 6: generateMOVLM(inst, b); break; case 7: generateDIV0S(inst, b); break; case 8: generateTST(inst, b); break; case 9: generateAND(inst, b); break; case 10: generateXOR(inst, b); break; case 11: generateOR(inst, b); break; case 12: generateCMPSTR(inst, b); break; case 13: generateXTRCT(inst, b); break; case 14: generateMULU(inst, b); break; case 15: generateMULS(inst, b); break; default: return -1; } break; case 3: switch(INSTRUCTION_D(inst)) { case 0: generateCMPEQ(inst, b); break; case 2: generateCMPHS(inst, b); break; case 3: generateCMPGE(inst, b); break; case 4: generateDIV1(inst, b); break; case 5: generateDMULU(inst, b); break; case 6: generateCMPHI(inst, b); break; case 7: generateCMPGT(inst, b); break; case 8: generateSUB(inst, b); break; case 10: generateSUBC(inst, b); break; case 11: generateSUBV(inst, b); break; case 12: generateADD(inst, b); break; case 13: generateDMULS(inst, b); break; case 14: generateADDC(inst, b); break; case 15: generateADDV(inst, b); break; default: return -1; } break; case 4: switch(INSTRUCTION_D(inst)) { case 0: switch(INSTRUCTION_C(inst)) { case 0: generateSHLL(inst, b); break; case 1: generateDT(inst, b); break; case 2: generateSHAL(inst, b); break; default: return -1; } break; case 1: switch(INSTRUCTION_C(inst)) { case 0: generateSHLR(inst, b); break; case 1: generateCMPPZ(inst, b); break; case 2: generateSHAR(inst, b); break; default: return -1; } break; case 2: switch(INSTRUCTION_C(inst)) { case 0: generateSTSMMACH(inst, b); break; case 1: generateSTSMMACL(inst, b); break; case 2: generateSTSMPR(inst, b); break; default: return -1; } break; case 3: switch(INSTRUCTION_C(inst)) { case 0: generateSTCMSR(inst, b); break; case 1: generateSTCMGBR(inst, b); break; case 2: generateSTCMVBR(inst, b); break; default: return -1; } break; case 4: switch(INSTRUCTION_C(inst)) { case 0: generateROTL(inst, b); break; case 2: generateROTCL(inst, b); break; default: return -1; } break; case 5: switch(INSTRUCTION_C(inst)) { case 0: generateROTR(inst, b); break; case 1: generateCMPPL(inst, b); break; case 2: generateROTCR(inst, b); break; default: return -1; } break; case 6: switch(INSTRUCTION_C(inst)) { case 0: generateLDSMMACH(inst, b); break; case 1: generateLDSMMACL(inst, b); break; case 2: generateLDSMPR(inst, b); break; default: return -1; } break; case 7: switch(INSTRUCTION_C(inst)) { case 0: generateLDCMSR(inst, b); break; case 1: generateLDCMGBR(inst, b); break; case 2: generateLDCMVBR(inst, b); break; default: return -1; } break; case 8: switch(INSTRUCTION_C(inst)) { case 0: generateSHLL2(inst, b); break; case 1: generateSHLL8(inst, b); break; case 2: generateSHLL16(inst, b); break; default: return -1; } break; case 9: switch(INSTRUCTION_C(inst)) { case 0: generateSHLR2(inst, b); break; case 1: generateSHLR8(inst, b); break; case 2: generateSHLR16(inst, b); break; default: return -1; } break; case 10: switch(INSTRUCTION_C(inst)) { case 0: generateLDSMACH(inst, b); break; case 1: generateLDSMACL(inst, b); break; case 2: generateLDSPR(inst, b); break; default: return -1; } break; case 11: switch(INSTRUCTION_C(inst)) { case 0: generateJSR(inst, b); done = 1; break; case 1: generateTAS(inst, b); break; case 2: generateJMP(inst, b); done = 1; break; default: return -1; } break; case 14: switch(INSTRUCTION_C(inst)) { case 0: generateLDCSR(inst, b); break; case 1: generateLDCGBR(inst, b); break; case 2: generateLDCVBR(inst, b); break; default: return -1; } break; case 15: generateMACW(inst, b); break; default: return -1; } break; case 5: generateMOVLL4(inst, b); break; case 6: switch(INSTRUCTION_D(inst)) { case 0: generateMOVBL(inst, b); break; case 1: generateMOVWL(inst, b); break; case 2: generateMOVLL(inst, b); break; case 3: generateMOV(inst, b); break; case 4: generateMOVBP(inst, b); break; case 5: generateMOVWP(inst, b); break; case 6: generateMOVLP(inst, b); break; case 7: generateNOT(inst, b); break; case 8: generateSWAPB(inst, b); break; case 9: generateSWAPW(inst, b); break; case 10: generateNEGC(inst, b); break; case 11: generateNEG(inst, b); break; case 12: generateEXTUB(inst, b); break; case 13: generateEXTUW(inst, b); break; case 14: generateEXTSB(inst, b); break; case 15: generateEXTSW(inst, b); break; } break; case 7: generateADDI(inst, b); break; case 8: switch(INSTRUCTION_B(inst)) { case 0: generateMOVBS4(inst, b); break; case 1: generateMOVWS4(inst, b); break; case 4: generateMOVBL4(inst, b); break; case 5: generateMOVWL4(inst, b); break; case 8: generateCMPIM(inst, b); break; case 9: generateBT(inst, b); done = 1; break; case 11: generateBF(inst, b); done = 1; break; case 13: generateBTS(inst, b); done = 1; break; case 15: generateBFS(inst, b); done = 1; break; default: return -1; } break; case 9: generateMOVWI(inst, b); break; case 10: generateBRA(inst, b); done = 1; break; case 11: generateBSR(inst, b); done = 1; break; case 12: switch(INSTRUCTION_B(inst)) { case 0: generateMOVBSG(inst, b); break; case 1: generateMOVWSG(inst, b); break; case 2: generateMOVLSG(inst, b); break; case 3: generateTRAPA(inst, b); done = 1; break; case 4: generateMOVBLG(inst, b); break; case 5: generateMOVWLG(inst, b); break; case 6: generateMOVLLG(inst, b); break; case 7: generateMOVA(inst, b); break; case 8: generateTSTI(inst, b); break; case 9: generateANDI(inst, b); break; case 10: generateXORI(inst, b); break; case 11: generateORI(inst, b); break; case 12: generateTSTM(inst, b); break; case 13: generateANDM(inst, b); break; case 14: generateXORM(inst, b); break; case 15: generateORM(inst, b); break; } break; case 13: generateMOVLI(inst, b); break; case 14: generateMOVI(inst, b); break; default: return -1; } return done; } int sh2rec_rec_block(sh2rec_block_t *b) { int done = 0; while(!done) { done = sh2rec_rec_inst(b, 0); } /* Flush the icache, so we don't execute stale data */ icache_flush_range((uint32)b->block, ((u32)b->ptr) - ((u32)b->block)); return 0; } /* In sh2exec.s */ extern void sh2rec_exec(SH2_struct *cxt, u32 cycles); static int sh2rec_init(void) { /* Initialize anything important here */ sh2rec_htab_init(); return 0; } static void sh2rec_deinit(void) { /* Clean stuff up here */ sh2rec_htab_reset(); } static void sh2rec_reset(void) { /* Reset to a sane state */ sh2rec_htab_reset(); } /* This function borrowed from the interpreter core */ void sh2rec_check_interrupts(SH2_struct *c) { if(c->NumberOfInterrupts != 0) { if(c->interrupts[c->NumberOfInterrupts-1].level > c->regs.SR.part.I) { c->regs.R[15] -= 4; MappedMemoryWriteLong(c->regs.R[15], c->regs.SR.all); c->regs.R[15] -= 4; MappedMemoryWriteLong(c->regs.R[15], c->regs.PC); c->regs.SR.part.I = c->interrupts[c->NumberOfInterrupts - 1].level; c->regs.PC = MappedMemoryReadLong(c->regs.VBR + (c->interrupts[c->NumberOfInterrupts-1].vector << 2)); c->NumberOfInterrupts--; c->isSleeping = 0; } } } sh2rec_block_t *sh2rec_find_block(u32 pc) { sh2rec_block_t *b = sh2rec_htab_lookup(pc); if(!b) { b = sh2rec_htab_block_create(pc, 4096); sh2rec_rec_block(b); } return b; } SH2Interface_struct SH2Dynarec = { SH2CORE_DYNAREC, "SH2 -> SH4 Dynarec", sh2rec_init, /* Init */ sh2rec_deinit, /* DeInit */ sh2rec_reset, /* Reset */ sh2rec_exec, /* Exec */ SH2InterpreterGetRegisters, /* GetRegisters */ SH2InterpreterGetGPR, /* GetGPR */ SH2InterpreterGetSR, /* GetSR */ SH2InterpreterGetGBR, /* GetGBR */ SH2InterpreterGetVBR, /* GetVBR */ SH2InterpreterGetMACH, /* GetMACH */ SH2InterpreterGetMACL, /* GetMACL */ SH2InterpreterGetPR, /* GetPR */ SH2InterpreterGetPC, /* GetPC */ SH2InterpreterSetRegisters, /* SetRegisters */ SH2InterpreterSetGPR, /* SetGPR */ SH2InterpreterSetSR, /* SetSR */ SH2InterpreterSetGBR, /* SetGBR */ SH2InterpreterSetVBR, /* SetVBR */ SH2InterpreterSetMACH, /* SetMACH */ SH2InterpreterSetMACL, /* SetMACL */ SH2InterpreterSetPR, /* SetPR */ SH2InterpreterSetPC, /* SetPC */ SH2InterpreterSendInterrupt, /* SendInterrupt */ SH2InterpreterGetInterrupts, /* GetInterrupts */ SH2InterpreterSetInterrupts, /* SetInterrupts */ NULL /* WriteNotify */ }; yabause-0.9.13.1/src/vdp2debug.c000644 001750 001750 00000145666 12256006126 020245 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2008 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "vdp2.h" #include "ygl.h" #include "vdp2debug.h" #include "vidshared.h" #include "vidsoft.h" #include "titan/titan.h" ////////////////////////////////////////////////////////////////////////////// static INLINE void Vdp2GetPlaneSize(int planedata, int *planew, int *planeh) { switch(planedata) { case 0: *planew = *planeh = 1; break; case 1: *planew = 2; *planeh = 1; break; case 2: *planew = *planeh = 2; break; default: *planew = *planeh = 1; break; } } ////////////////////////////////////////////////////////////////////////////// static INLINE char *AddBppString(char *outstring, int bpp) { switch (bpp) { case 0: AddString(outstring, "4-bit(16 colors)\r\n"); break; case 1: AddString(outstring, "8-bit(256 colors)\r\n"); break; case 2: AddString(outstring, "16-bit(2048 colors)\r\n"); break; case 3: AddString(outstring, "16-bit(32,768 colors)\r\n"); break; case 4: AddString(outstring, "32-bit(16.7 mil colors)\r\n"); break; default: AddString(outstring, "Unsupported BPP\r\n"); break; } return outstring; } ////////////////////////////////////////////////////////////////////////////// static INLINE char *AddMosaicString(char *outstring, int mask) { if (Vdp2Regs->MZCTL & mask) { AddString(outstring, "Mosaic Size = width %d height %d\r\n", ((Vdp2Regs->MZCTL >> 8) & 0xf) + 1, (Vdp2Regs->MZCTL >> 12) + 1); } return outstring; } ////////////////////////////////////////////////////////////////////////////// static INLINE char *AddBitmapInfoString(char *outstring, int wh, int palnum, int mapofn) { int cellw=0, cellh=0; // Bitmap switch(wh) { case 0: cellw = 512; cellh = 256; break; case 1: cellw = 512; cellh = 512; break; case 2: cellw = 1024; cellh = 256; break; case 3: cellw = 1024; cellh = 512; break; } AddString(outstring, "Bitmap(%dx%d)\r\n", cellw, cellh); if (palnum & 0x20) { AddString(outstring, "Bitmap Special Priority enabled\r\n"); } if (palnum & 0x10) { AddString(outstring, "Bitmap Special Color Calculation enabled\r\n"); } AddString(outstring, "Bitmap Address = %X\r\n", (mapofn & 0x7) * 0x20000); AddString(outstring, "Bitmap Palette Address = %X\r\n", (palnum & 0x7) << 4); return outstring; } ////////////////////////////////////////////////////////////////////////////// static void CalcWindowCoordinates(int num, int *hstart, int *vstart, int *hend, int *vend) { clipping_struct clip; ReadWindowCoordinates(num, &clip); *hstart = clip.xstart; *vstart = clip.ystart; *hend = clip.xend; *vend = clip.yend; } ////////////////////////////////////////////////////////////////////////////// static INLINE char *AddWindowInfoString(char *outstring, int wctl, int issprite) { if (wctl & 0x2) { int hstart=0, vstart=0, hend=0, vend=0; AddString(outstring, "Window W0 Enabled:\r\n"); // Retrieve Window Points if (Vdp2Regs->LWTA0.all & 0x80000000) { // Line Window AddString(outstring, "Line Window Table Address = %08lX\r\n", 0x05E00000UL | ((Vdp2Regs->LWTA0.all & 0x7FFFEUL) << 1)); } else { // Normal Window CalcWindowCoordinates(0, &hstart, &vstart, &hend, &vend); AddString(outstring, "Horizontal start = %d\r\n", hstart); AddString(outstring, "Vertical start = %d\r\n", vstart); AddString(outstring, "Horizontal end = %d\r\n", hend); AddString(outstring, "Vertical end = %d\r\n", vend); } AddString(outstring, "Display %s of Window\r\n", (wctl & 0x1) ? "inside" : "outside"); } if (wctl & 0x8) { int hstart=0, vstart=0, hend=0, vend=0; AddString(outstring, "Window W1 Enabled:\r\n"); // Retrieve Window Points if (Vdp2Regs->LWTA1.all & 0x80000000) { // Line Window AddString(outstring, "Line Table address = %08lX\r\n", 0x05E00000UL | ((Vdp2Regs->LWTA1.all & 0x7FFFEUL) << 1)); } else { // Normal Window CalcWindowCoordinates(1, &hstart, &vstart, &hend, &vend); AddString(outstring, "Horizontal start = %d\r\n", hstart); AddString(outstring, "Vertical start = %d\r\n", vstart); AddString(outstring, "Horizontal end = %d\r\n", hend); AddString(outstring, "Vertical end = %d\r\n", vend); } AddString(outstring, "Display %s of Window\r\n", (wctl & 0x4) ? "inside" : "outside"); } if (wctl & 0x20) { AddString(outstring, "Sprite Window Enabled:\r\n"); AddString(outstring, "Display %s of Window\r\n", (wctl & 0x10) ? "inside" : "outside"); } if (wctl & 0x2A) { AddString(outstring, "Window Overlap Logic: %s\r\n", (wctl & 0x80) ? "AND" : "OR"); } else { if (wctl & 0x80) { // Whole screen window enabled AddString(outstring, "Window enabled whole screen\r\n"); } else { // Whole screen window disabled AddString(outstring, "Window disabled whole screen\r\n"); } } return outstring; } ////////////////////////////////////////////////////////////////////////////// static INLINE char *AddMapInfo(char *outstring, int patternwh, u16 PNC, u8 PLSZ, int mapoffset, int numplanes, u8 *map) { int deca; int multi; int i; int patterndatasize; int planew, planeh; u32 tmp=0; u32 addr; if(PNC & 0x8000) patterndatasize = 1; else patterndatasize = 2; switch(PLSZ) { case 0: planew = planeh = 1; break; case 1: planew = 2; planeh = 1; break; case 2: planew = planeh = 2; break; default: // Not sure what 0x3 does planew = planeh = 1; break; } deca = planeh + planew - 2; multi = planeh * planew; // Map Planes A-D for (i = 0; i < numplanes; i++) { tmp = mapoffset | map[i]; if (patterndatasize == 1) { if (patternwh == 1) addr = ((tmp & 0x3F) >> deca) * (multi * 0x2000); else addr = (tmp >> deca) * (multi * 0x800); } else { if (patternwh == 1) addr = ((tmp & 0x1F) >> deca) * (multi * 0x4000); else addr = ((tmp & 0x7F) >> deca) * (multi * 0x1000); } AddString(outstring, "Plane %C Address = %08X\r\n", 0x41+i, (unsigned int)addr); } return outstring; } ////////////////////////////////////////////////////////////////////////////// static INLINE char *AddColorCalcInfo(char *outstring, u16 calcenab, u16 gradnum, u16 calcratio, u16 sfcnum) { if (Vdp2Regs->CCCTL & calcenab) { AddString(outstring, "Color Calculation Enabled\r\n"); if (Vdp2Regs->CCCTL & 0x8000 && (Vdp2Regs->CCCTL & 0x0700) == gradnum) { AddString(outstring, "Gradation Calculation Enabled\r\n"); } else if (Vdp2Regs->CCCTL & 0x0400) { AddString(outstring, "Extended Color Calculation Enabled\r\n"); } else { AddString(outstring, "Special Color Calculation Mode = %d\r\n", sfcnum); } AddString(outstring, "Color Calculation Ratio = %d:%d\r\n", 31 - calcratio, 1 + calcratio); } return outstring; } ////////////////////////////////////////////////////////////////////////////// static INLINE char *AddColorOffsetInfo(char *outstring, u16 offsetselectenab) { s32 r, g, b; if (Vdp2Regs->CLOFEN & offsetselectenab) { if (Vdp2Regs->CLOFSL & offsetselectenab) { r = Vdp2Regs->COBR & 0xFF; if (Vdp2Regs->COBR & 0x100) r |= 0xFFFFFF00; g = Vdp2Regs->COBG & 0xFF; if (Vdp2Regs->COBG & 0x100) g |= 0xFFFFFF00; b = Vdp2Regs->COBB & 0xFF; if (Vdp2Regs->COBB & 0x100) b |= 0xFFFFFF00; AddString(outstring, "Color Offset B Enabled\r\n"); AddString(outstring, "R = %ld, G = %ld, B = %ld\r\n", (long)r, (long)g, (long)b); } else { r = Vdp2Regs->COAR & 0xFF; if (Vdp2Regs->COAR & 0x100) r |= 0xFFFFFF00; g = Vdp2Regs->COAG & 0xFF; if (Vdp2Regs->COAG & 0x100) g |= 0xFFFFFF00; b = Vdp2Regs->COAB & 0xFF; if (Vdp2Regs->COAB & 0x100) b |= 0xFFFFFF00; AddString(outstring, "Color Offset A Enabled\r\n"); AddString(outstring, "R = %ld, G = %ld, B = %ld\r\n", (long)r, (long)g, (long)b); } } return outstring; } ////////////////////////////////////////////////////////////////////////////// static INLINE char *AddSpecialPriorityInfo(char *outstring, u16 spriority) { if (spriority & 0x3) { AddString(outstring, "Special Priority Mode %d used", spriority & 0x3); switch (spriority & 0x3) { case 1: AddString(outstring, "(per tile)\r\n"); break; case 2: AddString(outstring, "(per pixel)\r\n"); break; case 3: AddString(outstring, "(undocumented)\r\n"); break; default: break; } } return outstring; } ////////////////////////////////////////////////////////////////////////////// void Vdp2DebugStatsRBG0(char *outstring, int *isenabled) { int patternwh=((Vdp2Regs->CHCTLB & 0x100) >> 8) + 1; u8 map[16]; int hstart, vstart, hend, vend; if (Vdp2Regs->BGON & 0x10) { // enabled int rotatenum=0; int coeftbl=0, coefmode=0; *isenabled = 1; // Which Rotation Parameter is being used switch (Vdp2Regs->RPMD & 0x3) { case 0: // Parameter A rotatenum = 0; coeftbl = Vdp2Regs->KTCTL & 0x1; coefmode = (Vdp2Regs->KTCTL >> 2) & 0x3; AddString(outstring, "Using Parameter %C\r\n", 'A' + rotatenum); break; case 1: // Parameter B rotatenum = 1; coeftbl = Vdp2Regs->KTCTL & 0x100; coefmode = (Vdp2Regs->KTCTL >> 10) & 0x3; AddString(outstring, "Using Parameter B\r\n"); break; case 2: // Parameter A+B switched via coefficients AddString(outstring, "Parameter A/B switched via coefficients\r\n"); break; case 3: // Parameter A+B switched via rotation parameter window AddString(outstring, "Parameter A/B switched parameter window\r\n"); if (Vdp2Regs->WCTLD & 0x2) { AddString(outstring, "Rotation Window 0 Enabled\r\n"); CalcWindowCoordinates(0, &hstart, &vstart, &hend, &vend); AddString(outstring, "Horizontal start = %d\r\n", hstart); AddString(outstring, "Vertical start = %d\r\n", vstart); AddString(outstring, "Horizontal end = %d\r\n", hend); AddString(outstring, "Vertical end = %d\r\n", vend); } else if (Vdp2Regs->WCTLD & 0x4) { AddString(outstring, "Rotation Window 1 Enabled\r\n"); CalcWindowCoordinates(1, &hstart, &vstart, &hend, &vend); AddString(outstring, "Horizontal start = %d\r\n", hstart); AddString(outstring, "Vertical start = %d\r\n", vstart); AddString(outstring, "Horizontal end = %d\r\n", hend); AddString(outstring, "Vertical end = %d\r\n", vend); } break; } if (coeftbl) { AddString(outstring, "Coefficient Table Enabled(Mode %d)\r\n", coefmode); } // Mosaic outstring = AddMosaicString(outstring, 0x10); // BPP outstring = AddBppString(outstring, (Vdp2Regs->CHCTLB >> 12) & 0x7); // Bitmap or Tile mode? if (Vdp2Regs->CHCTLB & 0x200) { // Bitmap mode if (rotatenum == 0) { // Parameter A outstring = AddBitmapInfoString(outstring, (Vdp2Regs->CHCTLB & 0x400) >> 10, Vdp2Regs->BMPNB, Vdp2Regs->MPOFR); } else { // Parameter B outstring = AddBitmapInfoString(outstring, (Vdp2Regs->CHCTLB & 0x400) >> 10, Vdp2Regs->BMPNB, Vdp2Regs->MPOFR >> 4); } } else { // Tile mode int patterndatasize; u16 supplementdata=Vdp2Regs->PNCR & 0x3FF; int planew=0, planeh=0; if(Vdp2Regs->PNCR & 0x8000) patterndatasize = 1; else patterndatasize = 2; AddString(outstring, "Tile(%dH x %dV)\r\n", patternwh, patternwh); if (rotatenum == 0) { // Parameter A Vdp2GetPlaneSize((Vdp2Regs->PLSZ & 0x300) >> 8, &planew, &planeh); } else { // Parameter B Vdp2GetPlaneSize((Vdp2Regs->PLSZ & 0x3000) >> 8, &planew, &planeh); } AddString(outstring, "Plane Size = %dH x %dV\r\n", planew, planeh); // Pattern Name Control stuff if (patterndatasize == 2) { AddString(outstring, "Pattern Name data size = 2 words\r\n"); } else { AddString(outstring, "Pattern Name data size = 1 word\r\n"); AddString(outstring, "Character Number Supplement bit = %d\r\n", (supplementdata >> 14) & 0x1); AddString(outstring, "Special Priority bit = %d\r\n", (supplementdata >> 9) & 0x1); AddString(outstring, "Special Color Calculation bit = %d\r\n", (supplementdata >> 8) & 0x1); AddString(outstring, "Supplementary Palette number = %d\r\n", (supplementdata >> 5) & 0x7); AddString(outstring, "Supplementary Color number = %d\r\n", supplementdata & 0x1f); } if (rotatenum == 0) { // Parameter A map[0] = Vdp2Regs->MPABRA & 0xFF; map[1] = Vdp2Regs->MPABRA >> 8; map[2] = Vdp2Regs->MPCDRA & 0xFF; map[3] = Vdp2Regs->MPCDRA >> 8; map[4] = Vdp2Regs->MPEFRA & 0xFF; map[5] = Vdp2Regs->MPEFRA >> 8; map[6] = Vdp2Regs->MPGHRA & 0xFF; map[7] = Vdp2Regs->MPGHRA >> 8; map[8] = Vdp2Regs->MPIJRA & 0xFF; map[9] = Vdp2Regs->MPIJRA >> 8; map[10] = Vdp2Regs->MPKLRA & 0xFF; map[11] = Vdp2Regs->MPKLRA >> 8; map[12] = Vdp2Regs->MPMNRA & 0xFF; map[13] = Vdp2Regs->MPMNRA >> 8; map[14] = Vdp2Regs->MPOPRA & 0xFF; map[15] = Vdp2Regs->MPOPRA >> 8; outstring = AddMapInfo(outstring, patternwh, Vdp2Regs->PNCR, (Vdp2Regs->PLSZ >> 8) & 0x3, (Vdp2Regs->MPOFR & 0x7) << 6, 16, map); } else { // Parameter B map[0] = Vdp2Regs->MPABRB & 0xFF; map[1] = Vdp2Regs->MPABRB >> 8; map[2] = Vdp2Regs->MPCDRB & 0xFF; map[3] = Vdp2Regs->MPCDRB >> 8; map[4] = Vdp2Regs->MPEFRB & 0xFF; map[5] = Vdp2Regs->MPEFRB >> 8; map[6] = Vdp2Regs->MPGHRB & 0xFF; map[7] = Vdp2Regs->MPGHRB >> 8; map[8] = Vdp2Regs->MPIJRB & 0xFF; map[9] = Vdp2Regs->MPIJRB >> 8; map[10] = Vdp2Regs->MPKLRB & 0xFF; map[11] = Vdp2Regs->MPKLRB >> 8; map[12] = Vdp2Regs->MPMNRB & 0xFF; map[13] = Vdp2Regs->MPMNRB >> 8; map[14] = Vdp2Regs->MPOPRB & 0xFF; map[15] = Vdp2Regs->MPOPRB >> 8; outstring = AddMapInfo(outstring, patternwh, Vdp2Regs->PNCR, (Vdp2Regs->PLSZ >> 12) & 0x3, (Vdp2Regs->MPOFR & 0x70) << 2, 16, map); } /* // Figure out Cell start address switch(patterndatasize) { case 1: { tmp = readWord(vram, addr); switch(auxMode) { case 0: switch(patternwh) { case 1: charAddr = (tmp & 0x3FF) | ((supplementdata & 0x1F) << 10); break; case 2: charAddr = ((tmp & 0x3FF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x1C) << 10); break; } break; case 1: switch(patternwh) { case 1: charAddr = (tmp & 0xFFF) | ((supplementdata & 0x1C) << 10); break; case 4: charAddr = ((tmp & 0xFFF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x10) << 10); break; } break; } break; } case 2: { unsigned short tmp1 = readWord(vram, addr); unsigned short tmp2 = readWord(vram, addr+2); charAddr = tmp2 & 0x7FFF; break; } } if (!(readWord(reg, 0x6) & 0x8000)) charAddr &= 0x3FFF; charAddr *= 0x20; // selon Runik AddString(outstring, "Cell Data Address = %X\r\n", charAddr); */ } // Window Control outstring = AddWindowInfoString(outstring, Vdp2Regs->WCTLC, 0); // Shadow Control here // Color Ram Address Offset AddString(outstring, "Color Ram Address Offset = %X\r\n", (Vdp2Regs->CRAOFB & 0x7) << 8); // Special Priority Mode outstring = AddSpecialPriorityInfo(outstring, Vdp2Regs->SFPRMD >> 8); // Color Calculation Control here // Special Color Calculation Mode here // Priority Number AddString(outstring, "Priority = %d\r\n", Vdp2Regs->PRIR & 0x7); // Color Calculation outstring = AddColorCalcInfo(outstring, 0x0010, 0x0001, Vdp2Regs->CCRR & 0x1F, (Vdp2Regs->SFCCMD >> 8) & 0x3); // Color Offset outstring = AddColorOffsetInfo(outstring, 0x0010); AddString(outstring, "Special Color Calculation %d\r\n",(Vdp2Regs->SFCCMD>>8)&0x03); } else { // disabled *isenabled = 0; } } ////////////////////////////////////////////////////////////////////////////// void Vdp2DebugStatsNBG0(char *outstring, int *isenabled) { u16 lineVerticalScrollReg = Vdp2Regs->SCRCTL & 0x3F; int isbitmap=Vdp2Regs->CHCTLA & 0x2; int patternwh=(Vdp2Regs->CHCTLA & 0x1) + 1; u8 map[4]; if (Vdp2Regs->BGON & 0x1 || Vdp2Regs->BGON & 0x20) { // enabled *isenabled = 1; // Generate specific Info for NBG0/RBG1 if (Vdp2Regs->BGON & 0x20) { AddString(outstring, "RBG1 mode\r\n"); if (Vdp2Regs->KTCTL & 0x100) { AddString(outstring, "Coefficient Table Enabled(Mode %d)\r\n", (Vdp2Regs->KTCTL >> 10) & 0x3); } } else { AddString(outstring, "NBG0 mode\r\n"); } // Mosaic outstring = AddMosaicString(outstring, 0x1); // BPP outstring = AddBppString(outstring, (Vdp2Regs->CHCTLA & 0x70) >> 4); // Bitmap or Tile mode?(RBG1 can only do Tile mode) if (isbitmap && !(Vdp2Regs->BGON & 0x20)) { // Bitmap outstring = AddBitmapInfoString(outstring, (Vdp2Regs->CHCTLA & 0xC) >> 2, Vdp2Regs->BMPNA, Vdp2Regs->MPOFN); } else { // Tile int patterndatasize; u16 supplementdata=Vdp2Regs->PNCN0 & 0x3FF; int planew=0, planeh=0; if(Vdp2Regs->PNCN0 & 0x8000) patterndatasize = 1; else patterndatasize = 2; AddString(outstring, "Tile(%dH x %dV)\r\n", patternwh, patternwh); Vdp2GetPlaneSize(Vdp2Regs->PLSZ & 0x3, &planew, &planeh); AddString(outstring, "Plane Size = %dH x %dV\r\n", planew, planeh); // Pattern Name Control stuff if (patterndatasize == 2) { AddString(outstring, "Pattern Name data size = 2 words\r\n"); } else { AddString(outstring, "Pattern Name data size = 1 word\r\n"); AddString(outstring, "Character Number Supplement bit = %d\r\n", (supplementdata >> 14) & 0x1); AddString(outstring, "Special Priority bit = %d\r\n", (supplementdata >> 9) & 0x1); AddString(outstring, "Special Color Calculation bit = %d\r\n", (supplementdata >> 8) & 0x1); AddString(outstring, "Supplementary Palette number = %d\r\n", (supplementdata >> 5) & 0x7); AddString(outstring, "Supplementary Color number = %d\r\n", supplementdata & 0x1f); } map[0] = Vdp2Regs->MPABN0 & 0xFF; map[1] = Vdp2Regs->MPABN0 >> 8; map[2] = Vdp2Regs->MPCDN0 & 0xFF; map[3] = Vdp2Regs->MPCDN0 >> 8; outstring = AddMapInfo(outstring, patternwh, Vdp2Regs->PNCN0, Vdp2Regs->PLSZ & 0x3, (Vdp2Regs->MPOFN & 0x7) << 6, 4, map); /* // Figure out Cell start address switch(patterndatasize) { case 1: { tmp = T1ReadWord(Vdp2Ram, addr); switch(auxMode) { case 0: switch(patternwh) { case 1: charAddr = (tmp & 0x3FF) | ((supplementdata & 0x1F) << 10); break; case 2: charAddr = ((tmp & 0x3FF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x1C) << 10); break; } break; case 1: switch(patternwh) { case 1: charAddr = (tmp & 0xFFF) | ((supplementdata & 0x1C) << 10); break; case 4: charAddr = ((tmp & 0xFFF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x10) << 10); break; } break; } break; } case 2: { u16 tmp1 = T1ReadWord(Vdp2Ram, addr); u16 tmp2 = T1ReadWord(Vdp2Ram, addr+2); charAddr = tmp2 & 0x7FFF; break; } } if (!(readWord(reg, 0x6) & 0x8000)) charAddr &= 0x3FFF; charAddr *= 0x20; // selon Runik AddString(outstring, "Cell Data Address = %X\r\n", charAddr); */ } if (Vdp2Regs->BGON & 0x20) { // unsigned long mapOffsetReg=(readWord(reg, 0x3E) & 0x70) << 2; // RBG1 // Map Planes A-P here // Rotation Parameter Read Control if (Vdp2Regs->RPRCTL & 0x400) { AddString(outstring, "Read KAst Parameter = TRUE\r\n"); } else { AddString(outstring, "Read KAst Parameter = FALSE\r\n"); } if (Vdp2Regs->RPRCTL & 0x200) { AddString(outstring, "Read Yst Parameter = TRUE\r\n"); } else { AddString(outstring, "Read Yst Parameter = FALSE\r\n"); } if (Vdp2Regs->RPRCTL & 0x100) { AddString(outstring, "Read Xst Parameter = TRUE\r\n"); } else { AddString(outstring, "Read Xst Parameter = FALSE\r\n"); } // Coefficient Table Control // Coefficient Table Address Offset // Screen Over Pattern Name(should this be moved?) // Rotation Parameter Table Address } else { // NBG0 /* // Screen scroll values AddString(outstring, "Screen Scroll x = %f, y = %f\r\n", (float)(reg->getLong(0x70) & 0x7FFFF00) / 65536, (float)(reg->getLong(0x74) & 0x7FFFF00) / 65536); */ // Coordinate Increments AddString(outstring, "Coordinate Increments x = %f, y = %f\r\n", (float) 65536 / (Vdp2Regs->ZMXN0.all & 0x7FF00), (float) 65536 / (Vdp2Regs->ZMYN0.all & 0x7FF00)); // Reduction Enable switch (Vdp2Regs->ZMCTL & 3) { case 1: AddString(outstring, "Horizontal Reduction = 1/2\r\n"); break; case 2: case 3: AddString(outstring, "Horizontal Reduction = 1/4\r\n"); break; default: break; } if (lineVerticalScrollReg & 0x8) { AddString(outstring, "Line Zoom enabled\r\n"); } if (lineVerticalScrollReg & 0x4) { AddString(outstring, "Line Scroll Vertical enabled\r\n"); } if (lineVerticalScrollReg & 0x2) { AddString(outstring, "Line Scroll Horizontal enabled\r\n"); } if (lineVerticalScrollReg & 0x6) { AddString(outstring, "Line Scroll Enabled\r\n"); AddString(outstring, "Line Scroll Table Address = %08X\r\n", (int)(0x05E00000 + ((Vdp2Regs->LSTA0.all & 0x7FFFE) << 1))); switch (lineVerticalScrollReg >> 4) { case 0: AddString(outstring, "Line Scroll Interval = Each Line\r\n"); break; case 1: AddString(outstring, "Line Scroll Interval = Every 2 Lines\r\n"); break; case 2: AddString(outstring, "Line Scroll Interval = Every 4 Lines\r\n"); break; case 3: AddString(outstring, "Line Scroll Interval = Every 8 Lines\r\n"); break; } } if (lineVerticalScrollReg & 0x1) { AddString(outstring, "Vertical Cell Scroll enabled\r\n"); AddString(outstring, "Vertical Cell Scroll Table Address = %08X\r\n", (int)(0x05E00000 + ((Vdp2Regs->VCSTA.all & 0x7FFFE) << 1))); } } // Window Control outstring = AddWindowInfoString(outstring, Vdp2Regs->WCTLA, 0); // Shadow Control here // Color Ram Address Offset AddString(outstring, "Color Ram Address Offset = %X\r\n", (Vdp2Regs->CRAOFA & 0x7) << 8); // Special Priority Mode outstring = AddSpecialPriorityInfo(outstring, Vdp2Regs->SFPRMD); // Color Calculation Control here // Special Color Calculation Mode here // Priority Number AddString(outstring, "Priority = %d\r\n", Vdp2Regs->PRINA & 0x7); // Color Calculation outstring = AddColorCalcInfo(outstring, 0x0001, 0x0002, Vdp2Regs->CCRNA & 0x1F, Vdp2Regs->SFCCMD & 0x3); // Color Offset outstring = AddColorOffsetInfo(outstring, 0x0001); AddString(outstring, "Special Color Calculation %d\r\n",(Vdp2Regs->SFCCMD>>0)&0x03); } else { // disabled *isenabled = 0; } } ////////////////////////////////////////////////////////////////////////////// void Vdp2DebugStatsNBG1(char *outstring, int *isenabled) { u16 lineVerticalScrollReg = (Vdp2Regs->SCRCTL >> 8) & 0x3F; int isbitmap=Vdp2Regs->CHCTLA & 0x200; int patternwh=((Vdp2Regs->CHCTLA & 0x100) >> 8) + 1; u8 map[4]; if (Vdp2Regs->BGON & 0x2) { // enabled *isenabled = 1; // Mosaic outstring = AddMosaicString(outstring, 0x2); // BPP outstring = AddBppString(outstring, (Vdp2Regs->CHCTLA & 0x3000) >> 12); // Bitmap or Tile mode? if (isbitmap) { // Bitmap outstring = AddBitmapInfoString(outstring, (Vdp2Regs->CHCTLA & 0xC00) >> 10, Vdp2Regs->BMPNA >> 8, Vdp2Regs->MPOFN >> 4); } else { int patterndatasize; u16 supplementdata=Vdp2Regs->PNCN1 & 0x3FF; int planew=0, planeh=0; if(Vdp2Regs->PNCN1 & 0x8000) patterndatasize = 1; else patterndatasize = 2; // Tile AddString(outstring, "Tile(%dH x %dV)\r\n", patternwh, patternwh); Vdp2GetPlaneSize((Vdp2Regs->PLSZ & 0xC) >> 2, &planew, &planeh); AddString(outstring, "Plane Size = %dH x %dV\r\n", planew, planeh); // Pattern Name Control stuff if (patterndatasize == 2) { AddString(outstring, "Pattern Name data size = 2 words\r\n"); } else { AddString(outstring, "Pattern Name data size = 1 word\r\n"); AddString(outstring, "Character Number Supplement bit = %d\r\n", (supplementdata >> 14) & 0x1); AddString(outstring, "Special Priority bit = %d\r\n", (supplementdata >> 9) & 0x1); AddString(outstring, "Special Color Calculation bit = %d\r\n", (supplementdata >> 8) & 0x1); AddString(outstring, "Supplementary Palette number = %d\r\n", (supplementdata >> 5) & 0x7); AddString(outstring, "Supplementary Color number = %d\r\n", supplementdata & 0x1f); } map[0] = Vdp2Regs->MPABN1 & 0xFF; map[1] = Vdp2Regs->MPABN1 >> 8; map[2] = Vdp2Regs->MPCDN1 & 0xFF; map[3] = Vdp2Regs->MPCDN1 >> 8; outstring = AddMapInfo(outstring, patternwh, Vdp2Regs->PNCN1, (Vdp2Regs->PLSZ & 0xC) >> 2, (Vdp2Regs->MPOFN & 0x70) << 2, 4, map); /* // Figure out Cell start address switch(patterndatasize) { case 1: { tmp = readWord(vram, addr); switch(auxMode) { case 0: switch(patternwh) { case 1: charAddr = (tmp & 0x3FF) | ((supplementdata & 0x1F) << 10); break; case 2: charAddr = ((tmp & 0x3FF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x1C) << 10); break; } break; case 1: switch(patternwh) { case 1: charAddr = (tmp & 0xFFF) | ((supplementdata & 0x1C) << 10); break; case 4: charAddr = ((tmp & 0xFFF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x10) << 10); break; } break; } break; } case 2: { unsigned short tmp1 = readWord(vram, addr); unsigned short tmp2 = readWord(vram, addr+2); charAddr = tmp2 & 0x7FFF; break; } } if (!(readWord(reg, 0x6) & 0x8000)) charAddr &= 0x3FFF; charAddr *= 0x20; // selon Runik AddString(outstring, "Cell Data Address = %X\r\n", charAddr); */ } /* // Screen scroll values AddString(outstring, "Screen Scroll x = %f, y = %f\r\n", (float)(reg->getLong(0x80) & 0x7FFFF00) / 65536, (float)(reg->getLong(0x84) & 0x7FFFF00) / 65536); */ // Coordinate Increments AddString(outstring, "Coordinate Increments x = %f, y = %f\r\n", (float) 65536 / (Vdp2Regs->ZMXN1.all & 0x7FF00), (float) 65536 / (Vdp2Regs->ZMXN1.all & 0x7FF00)); // Reduction Enable switch ((Vdp2Regs->ZMCTL >> 8) & 3) { case 1: AddString(outstring, "Horizontal Reduction = 1/2\r\n"); break; case 2: case 3: AddString(outstring, "Horizontal Reduction = 1/4\r\n"); break; default: break; } if (lineVerticalScrollReg & 0x8) { AddString(outstring, "Line Zoom X enabled\r\n"); } if (lineVerticalScrollReg & 0x4) { AddString(outstring, "Line Scroll Vertical enabled\r\n"); } if (lineVerticalScrollReg & 0x2) { AddString(outstring, "Line Scroll Horizontal enabled\r\n"); } if (lineVerticalScrollReg & 0x6) { AddString(outstring, "Line Scroll Enabled\r\n"); AddString(outstring, "Line Scroll Table Address = %08X\r\n", (int)(0x05E00000 + ((Vdp2Regs->LSTA1.all & 0x7FFFE) << 1))); switch (lineVerticalScrollReg >> 4) { case 0: AddString(outstring, "Line Scroll Interval = Each Line\r\n"); break; case 1: AddString(outstring, "Line Scroll Interval = Every 2 Lines\r\n"); break; case 2: AddString(outstring, "Line Scroll Interval = Every 4 Lines\r\n"); break; case 3: AddString(outstring, "Line Scroll Interval = Every 8 Lines\r\n"); break; } } if (lineVerticalScrollReg & 0x1) { AddString(outstring, "Vertical Cell Scroll enabled\r\n"); AddString(outstring, "Vertical Cell Scroll Table Address = %08X\r\n", (int)(0x05E00000 + ((Vdp2Regs->VCSTA.all & 0x7FFFE) << 1))); } // Window Control outstring = AddWindowInfoString(outstring, Vdp2Regs->WCTLA >> 8, 0); // Shadow Control here // Color Ram Address Offset AddString(outstring, "Color Ram Address Offset = %X\r\n", (Vdp2Regs->CRAOFA & 0x70) << 4); // Special Priority Mode outstring = AddSpecialPriorityInfo(outstring, Vdp2Regs->SFPRMD >> 2); // Color Calculation Control here // Special Color Calculation Mode here // Priority Number AddString(outstring, "Priority = %d\r\n", (Vdp2Regs->PRINA >> 8) & 0x7); // Color Calculation outstring = AddColorCalcInfo(outstring, 0x0002, 0x0004, (Vdp2Regs->CCRNA >> 8) & 0x1F, (Vdp2Regs->SFCCMD >> 2) & 0x3); // Color Offset outstring = AddColorOffsetInfo(outstring, 0x0002); AddString(outstring, "Special Color Calculation %d\r\n",(Vdp2Regs->SFCCMD>>2)&0x03); } else // disabled *isenabled = 0; } ////////////////////////////////////////////////////////////////////////////// void Vdp2DebugStatsNBG2(char *outstring, int *isenabled) { u8 map[4]; if (Vdp2Regs->BGON & 0x4) { int patterndatasize; u16 supplementdata=Vdp2Regs->PNCN2 & 0x3FF; int planew=0, planeh=0; int patternwh=(Vdp2Regs->CHCTLB & 0x1) + 1; // enabled *isenabled = 1; // Mosaic outstring = AddMosaicString(outstring, 0x4); // BPP outstring = AddBppString(outstring, (Vdp2Regs->CHCTLB & 0x2) >> 1); if(Vdp2Regs->PNCN2 & 0x8000) patterndatasize = 1; else patterndatasize = 2; AddString(outstring, "Tile(%dH x %dV)\r\n", patternwh, patternwh); Vdp2GetPlaneSize((Vdp2Regs->PLSZ & 0x30) >> 4, &planew, &planeh); AddString(outstring, "Plane Size = %dH x %dV\r\n", planew, planeh); // Pattern Name Control stuff if (patterndatasize == 2) { AddString(outstring, "Pattern Name data size = 2 words\r\n"); } else { AddString(outstring, "Pattern Name data size = 1 word\r\n"); AddString(outstring, "Character Number Supplement bit = %d\r\n", (supplementdata >> 14) & 0x1); AddString(outstring, "Special Priority bit = %d\r\n", (supplementdata >> 9) & 0x1); AddString(outstring, "Special Color Calculation bit = %d\r\n", (supplementdata >> 8) & 0x1); AddString(outstring, "Supplementary Palette number = %d\r\n", (supplementdata >> 5) & 0x7); AddString(outstring, "Supplementary Color number = %d\r\n", supplementdata & 0x1f); } map[0] = Vdp2Regs->MPABN2 & 0xFF; map[1] = Vdp2Regs->MPABN2 >> 8; map[2] = Vdp2Regs->MPCDN2 & 0xFF; map[3] = Vdp2Regs->MPCDN2 >> 8; outstring = AddMapInfo(outstring, patternwh, Vdp2Regs->PNCN2, (Vdp2Regs->PLSZ >> 4) & 0x3, (Vdp2Regs->MPOFN & 0x700) >> 2, 4, map); /* // Figure out Cell start address switch(patterndatasize) { case 1: { tmp = readWord(vram, addr); switch(auxMode) { case 0: switch(patternwh) { case 1: charAddr = (tmp & 0x3FF) | ((supplementdata & 0x1F) << 10); break; case 2: charAddr = ((tmp & 0x3FF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x1C) << 10); break; } break; case 1: switch(patternwh) { case 1: charAddr = (tmp & 0xFFF) | ((supplementdata & 0x1C) << 10); break; case 4: charAddr = ((tmp & 0xFFF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x10) << 10); break; } break; } break; } case 2: { unsigned short tmp1 = readWord(vram, addr); unsigned short tmp2 = readWord(vram, addr+2); charAddr = tmp2 & 0x7FFF; break; } } if (!(readWord(reg, 0x6) & 0x8000)) charAddr &= 0x3FFF; charAddr *= 0x20; // selon Runik AddString(outstring, "Cell Data Address = %X\r\n", charAddr); */ // Screen scroll values AddString(outstring, "Screen Scroll x = %d, y = %d\r\n", - ((Vdp2Regs->SCXN2 & 0x7FF) % 512), - ((Vdp2Regs->SCYN2 & 0x7FF) % 512)); // Window Control outstring = AddWindowInfoString(outstring, Vdp2Regs->WCTLB, 0); // Shadow Control here // Color Ram Address Offset AddString(outstring, "Color Ram Address Offset = %X\r\n", Vdp2Regs->CRAOFA & 0x700); // Special Priority Mode outstring = AddSpecialPriorityInfo(outstring, Vdp2Regs->SFPRMD >> 4); // Color Calculation Control here // Special Color Calculation Mode here // Priority Number AddString(outstring, "Priority = %d\r\n", Vdp2Regs->PRINB & 0x7); // Color Calculation outstring = AddColorCalcInfo(outstring, 0x0004, 0x0005, Vdp2Regs->CCRNB & 0x1F, (Vdp2Regs->SFCCMD >> 4) & 0x3); // Color Offset outstring = AddColorOffsetInfo(outstring, 0x0004); AddString(outstring, "Special Color Calculation %d\r\n",(Vdp2Regs->SFCCMD>>4)&0x03); } else { // disabled *isenabled = 0; } } ////////////////////////////////////////////////////////////////////////////// void Vdp2DebugStatsNBG3(char *outstring, int *isenabled) { u8 map[4]; if (Vdp2Regs->BGON & 0x8) { int patterndatasize; u16 supplementdata=Vdp2Regs->PNCN3 & 0x3FF; int planew=0, planeh=0; int patternwh=((Vdp2Regs->CHCTLB & 0x10) >> 4) + 1; // enabled *isenabled = 1; // Mosaic outstring = AddMosaicString(outstring, 0x8); // BPP outstring = AddBppString(outstring, (Vdp2Regs->CHCTLB & 0x20) >> 5); if(Vdp2Regs->PNCN3 & 0x8000) patterndatasize = 1; else patterndatasize = 2; AddString(outstring, "Tile(%dH x %dV)\r\n", patternwh, patternwh); Vdp2GetPlaneSize((Vdp2Regs->PLSZ & 0xC0) >> 6, &planew, &planeh); AddString(outstring, "Plane Size = %dH x %dV\r\n", planew, planeh); // Pattern Name Control stuff if (patterndatasize == 2) { AddString(outstring, "Pattern Name data size = 2 words\r\n"); } else { AddString(outstring, "Pattern Name data size = 1 word\r\n"); AddString(outstring, "Character Number Supplement bit = %d\r\n", (supplementdata >> 14) & 0x1); AddString(outstring, "Special Priority bit = %d\r\n", (supplementdata >> 9) & 0x1); AddString(outstring, "Special Color Calculation bit = %d\r\n", (supplementdata >> 8) & 0x1); AddString(outstring, "Supplementary Palette number = %d\r\n", (supplementdata >> 5) & 0x7); AddString(outstring, "Supplementary Color number = %d\r\n", supplementdata & 0x1f); } map[0] = Vdp2Regs->MPABN3 & 0xFF; map[1] = Vdp2Regs->MPABN3 >> 8; map[2] = Vdp2Regs->MPCDN3 & 0xFF; map[3] = Vdp2Regs->MPCDN3 >> 8; outstring = AddMapInfo(outstring, patternwh, Vdp2Regs->PNCN3, (Vdp2Regs->PLSZ & 0xC0) >> 6, (Vdp2Regs->MPOFN & 0x7000) >> 6, 4, map); /* // Figure out Cell start address switch(patterndatasize) { case 1: { tmp = readWord(vram, addr); switch(auxMode) { case 0: switch(patternwh) { case 1: charAddr = (tmp & 0x3FF) | ((supplementdata & 0x1F) << 10); break; case 2: charAddr = ((tmp & 0x3FF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x1C) << 10); break; } break; case 1: switch(patternwh) { case 1: charAddr = (tmp & 0xFFF) | ((supplementdata & 0x1C) << 10); break; case 4: charAddr = ((tmp & 0xFFF) << 2) | (supplementdata & 0x3) | ((supplementdata & 0x10) << 10); break; } break; } break; } case 2: { unsigned short tmp1 = readWord(vram, addr); unsigned short tmp2 = readWord(vram, addr+2); charAddr = tmp2 & 0x7FFF; break; } } if (!(readWord(reg, 0x6) & 0x8000)) charAddr &= 0x3FFF; charAddr *= 0x20; // selon Runik AddString(outstring, "Cell Data Address = %X\r\n", charAddr); */ // Screen scroll values AddString(outstring, "Screen Scroll x = %d, y = %d\r\n", - ((Vdp2Regs->SCXN3 & 0x7FF) % 512), - ((Vdp2Regs->SCYN3 & 0x7FF) % 512)); // Window Control outstring = AddWindowInfoString(outstring, Vdp2Regs->WCTLB >> 8, 0); // Shadow Control here // Color Ram Address Offset AddString(outstring, "Color Ram Address Offset = %X\r\n", Vdp2Regs->CRAOFA & 0x7000); // Special Priority Mode outstring = AddSpecialPriorityInfo(outstring, Vdp2Regs->SFPRMD >> 6); // Special Color Calculation Mode here // Priority Number AddString(outstring, "Priority = %d\r\n", (Vdp2Regs->PRINB >> 8) & 0x7); // Color Calculation outstring = AddColorCalcInfo(outstring, 0x0008, 0x0006, (Vdp2Regs->CCRNB >> 8) & 0x1F, (Vdp2Regs->SFCCMD >> 6) & 0x3); // Color Offset outstring = AddColorOffsetInfo(outstring, 0x0008); AddString(outstring, "Special Color Calculation %d\r\n",(Vdp2Regs->SFCCMD>>6)&0x03); } else { // disabled *isenabled = 0; } } ////////////////////////////////////////////////////////////////////////////// void Vdp2DebugStatsGeneral(char *outstring, int *isenabled) { u8 *sprprilist = (u8 *)&Vdp2Regs->PRISA; u8 *sprccrlist = (u8 *)&Vdp2Regs->CCRSA; int i; if (Vdp2Regs->TVMD & 0x8000) { // TVMD stuff AddString(outstring, "Border Color Mode = %s\r\n", Vdp2Regs->TVMD & 0x100 ? "Back screen" : "Black"); AddString(outstring, "Display Resolution = "); switch (Vdp2Regs->TVMD & 0x7) { case 0: case 4: AddString(outstring, "320"); break; case 1: case 5: AddString(outstring, "352"); break; case 2: case 6: AddString(outstring, "640"); break; case 3: case 7: AddString(outstring, "704"); break; default: AddString(outstring, "Invalid"); break; } AddString(outstring, " x "); switch ((Vdp2Regs->TVMD >> 4) & 0x3) { case 0: AddString(outstring, "224"); break; case 1: AddString(outstring, "240"); break; case 2: AddString(outstring, "256"); break; default: AddString(outstring, "Invalid"); break; } if (Vdp2Regs->TVSTAT & 0x1) { AddString(outstring, "(PAL)\r\n"); } else { AddString(outstring, "(NTSC)\r\n"); } AddString(outstring, "Interlace Mode = "); switch ((Vdp2Regs->TVMD >> 6) & 0x3) { case 0: AddString(outstring, "Non-Interlace\r\n"); break; case 2: AddString(outstring, "Single-Density Interlace\r\n"); break; case 3: AddString(outstring, "Double-Density Interlace\r\n"); break; default: AddString(outstring, "Invalid\r\n"); break; } // Latch stuff AddString(outstring, "Latches HV counter when %s\r\n", Vdp2Regs->EXTEN & 0x200 ? "external signal triggers it" : "external latch flag is read"); if (Vdp2Regs->EXTEN & 0x100) { AddString(outstring, "External Sync is being inputed\r\n"); } // Screen status stuff if (Vdp2Regs->TVSTAT & 0x200) { AddString(outstring, "HV is latched\r\n"); } if (Vdp2Regs->TVSTAT & 0x4) { AddString(outstring, "During H-Blank\r\n"); } if (Vdp2Regs->TVSTAT & 0x8) { AddString(outstring, "During V-Blank\r\n"); } if ((Vdp2Regs->TVMD >> 6) & 0x2) { AddString(outstring, "During %s Field\r\n", Vdp2Regs->TVSTAT & 0x2 ? "Odd" : "Even"); } AddString(outstring, "H Counter = %d\r\n", Vdp2Regs->HCNT); AddString(outstring, "V Counter = %d\r\n", Vdp2Regs->VCNT); AddString(outstring, "\r\n"); // Line color screen stuff AddString(outstring, "Line Color Screen Stuff\r\n"); AddString(outstring, "-----------------------\r\n"); AddString(outstring, "Mode = %s\r\n", Vdp2Regs->LCTA.part.U & 0x8000 ? "Color per line" : "Single color"); AddString(outstring, "Address = %08lX\r\n", 0x05E00000UL | ((Vdp2Regs->LCTA.all & 0x7FFFFUL) * 2)); AddString(outstring, "\r\n"); // Back screen stuff AddString(outstring, "Back Screen Stuff\r\n"); AddString(outstring, "-----------------\r\n"); AddString(outstring, "Mode = %s\r\n", Vdp2Regs->BKTAU & 0x8000 ? "Color per line" : "Single color"); AddString(outstring, "Address = %08X\r\n", 0x05E00000 | (((Vdp2Regs->BKTAU & 0x7) << 16) | Vdp2Regs->BKTAL) * 2); outstring = AddColorOffsetInfo(outstring, 0x0020); AddString(outstring, "\r\n"); // Cycle patterns here // Sprite stuff AddString(outstring, "Sprite Stuff\r\n"); AddString(outstring, "------------\r\n"); AddString(outstring, "Sprite Type = %X\r\n", Vdp2Regs->SPCTL & 0xF); AddString(outstring, "VDP1 Framebuffer Data Format = %s\r\n", Vdp2Regs->SPCTL & 0x20 ? "RGB and palette" : "Palette only"); if (Vdp2Regs->SDCTL & 0x100) { AddString(outstring, "Transparent Shadow Enabled\r\n"); } if (Vdp2Regs->SPCTL & 0x20) { AddString(outstring, "Sprite Window Enabled\r\n"); } outstring = AddWindowInfoString(outstring, Vdp2Regs->WCTLC >> 8, 1); AddString(outstring, "Color RAM Offset = %X\r\n", (Vdp2Regs->CRAOFB >> 4) & 0x7); if (Vdp2Regs->CCCTL & 0x40) { AddString(outstring, "Color Calculation Enabled\r\n"); if (Vdp2Regs->CCCTL & 0x8000 && (Vdp2Regs->CCCTL & 0x0700) == 0) { AddString(outstring, "Gradation Calculation Enabled\r\n"); } else if (Vdp2Regs->CCCTL & 0x0400) { AddString(outstring, "Extended Color Calculation Enabled\r\n"); } AddString(outstring, "Color Calculation Condition = "); switch ((Vdp2Regs->SPCTL >> 12) & 0x3) { case 0: AddString(outstring, "Priority <= CC Condition Number"); break; case 1: AddString(outstring, "Priority == CC Condition Number"); break; case 2: AddString(outstring, "Priority >= CC Condition Number"); break; case 3: AddString(outstring, "Color Data MSB"); break; default: break; } AddString(outstring, "\r\n"); if (((Vdp2Regs->SPCTL >> 12) & 0x3) != 0x3) { AddString(outstring, "Color Calculation Condition Number = %d\r\n", (Vdp2Regs->SPCTL >> 8) & 0x7); } for (i = 0; i < 8; i++) { #ifdef WORDS_BIGENDIAN int ratio = sprccrlist[i ^ 1] & 0x7; #else int ratio = sprccrlist[i] & 0x7; #endif AddString(outstring, "Color Calculation Ratio %d = %d:%d\r\n", i, 31 - ratio, 1 + ratio); } } for (i = 0; i < 8; i++) { #ifdef WORDS_BIGENDIAN int priority = sprprilist[i ^ 1] & 0x7; #else int priority = sprprilist[i] & 0x7; #endif AddString(outstring, "Priority %d = %d\r\n", i, priority); } outstring = AddColorOffsetInfo(outstring, 0x0040); *isenabled = 1; } else { *isenabled = 0; } } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL DoNothing(UNUSED void *info, u32 pixel) { return pixel; } ////////////////////////////////////////////////////////////////////////////// static void ClearTextureToColor(u32 *texture, u32 color, int w, int h) { int i; for (i = 0; i < (w * h); i++) texture[i] = color; } ////////////////////////////////////////////////////////////////////////////// pixel_t *Vdp2DebugTexture(u32 screen, int * w, int * h) { pixel_t * bitmap; TitanInit(); VIDSoftVdp2DrawScreen(screen); if ((bitmap = (pixel_t *)calloc(sizeof(pixel_t), 704 * 512)) == NULL) return NULL; TitanGetResolution(w, h); TitanRender(bitmap); return bitmap; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/fakeddk.h000644 001750 001750 00000002453 12256006161 017742 0ustar00guillaumeguillaume000000 000000 /* * This file is not copyrighted, it comes from: * ntddcdrm.h and ntddstor.h from w32api package * ntddscsi.h from w64 mingw-runtime package * * Those three files are in the public domain. */ #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM #define MAXIMUM_NUMBER_TRACKS 100 #define SCSI_IOCTL_DATA_IN 1 #define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER #define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE(IOCTL_SCSI_BASE,0x0405,METHOD_BUFFERED,FILE_READ_ACCESS | FILE_WRITE_ACCESS) #define IOCTL_CDROM_READ_TOC \ CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS) typedef struct _TRACK_DATA { UCHAR Reserved; UCHAR Control : 4; UCHAR Adr : 4; UCHAR TrackNumber; UCHAR Reserved1; UCHAR Address[4]; } TRACK_DATA, *PTRACK_DATA; typedef struct _CDROM_TOC { UCHAR Length[2]; UCHAR FirstTrack; UCHAR LastTrack; TRACK_DATA TrackData[MAXIMUM_NUMBER_TRACKS]; } CDROM_TOC, *PCDROM_TOC; typedef struct _SCSI_PASS_THROUGH_DIRECT { USHORT Length; UCHAR ScsiStatus; UCHAR PathId; UCHAR TargetId; UCHAR Lun; UCHAR CdbLength; UCHAR SenseInfoLength; UCHAR DataIn; ULONG DataTransferLength; ULONG TimeOutValue; PVOID DataBuffer; ULONG SenseInfoOffset; UCHAR Cdb[16]; }SCSI_PASS_THROUGH_DIRECT,*PSCSI_PASS_THROUGH_DIRECT; yabause-0.9.13.1/src/sndwav.c000644 001750 001750 00000012477 12256006200 017647 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004 Stephane Dallongeville Copyright 2004-2007 Theo Berkau Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "scsp.h" ////////////////////////////////////////////////////////////////////////////// // Wave File Output Sound Interface ////////////////////////////////////////////////////////////////////////////// static int SNDWavInit(void); static void SNDWavDeInit(void); static int SNDWavReset(void); static int SNDWavChangeVideoFormat(int vertfreq); static void SNDWavUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples); static u32 SNDWavGetAudioSpace(void); static void SNDWavMuteAudio(void); static void SNDWavUnMuteAudio(void); static void SNDWavSetVolume(int volume); SoundInterface_struct SNDWave = { SNDCORE_WAV, "Wave File Ouput Interface", SNDWavInit, SNDWavDeInit, SNDWavReset, SNDWavChangeVideoFormat, SNDWavUpdateAudio, SNDWavGetAudioSpace, SNDWavMuteAudio, SNDWavUnMuteAudio, SNDWavSetVolume }; char *wavefilename=NULL; static FILE *wavefp; typedef struct { char id[4]; u32 size; } chunk_struct; typedef struct { chunk_struct riff; char rifftype[4]; } waveheader_struct; typedef struct { chunk_struct chunk; u16 compress; u16 numchan; u32 rate; u32 bytespersec; u16 blockalign; u16 bitspersample; } fmt_struct; ////////////////////////////////////////////////////////////////////////////// static int SNDWavInit(void) { waveheader_struct waveheader; fmt_struct fmt; chunk_struct data; IOCheck_struct check; if (wavefilename) { if ((wavefp = fopen(wavefilename, "wb")) == NULL) return -1; } else { if ((wavefp = fopen("scsp.wav", "wb")) == NULL) return -1; } // Do wave header memcpy(waveheader.riff.id, "RIFF", 4); waveheader.riff.size = 0; // we'll fix this after the file is closed memcpy(waveheader.rifftype, "WAVE", 4); ywrite(&check, (void *)&waveheader, 1, sizeof(waveheader_struct), wavefp); // fmt chunk memcpy(fmt.chunk.id, "fmt ", 4); fmt.chunk.size = 16; // we'll fix this at the end fmt.compress = 1; // PCM fmt.numchan = 2; // Stereo fmt.rate = 44100; fmt.bitspersample = 16; fmt.blockalign = fmt.bitspersample / 8 * fmt.numchan; fmt.bytespersec = fmt.rate * fmt.blockalign; ywrite(&check, (void *)&fmt, 1, sizeof(fmt_struct), wavefp); // data chunk memcpy(data.id, "data", 4); data.size = 0; // we'll fix this at the end ywrite(&check, (void *)&data, 1, sizeof(chunk_struct), wavefp); return 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDWavDeInit(void) { if (wavefp) { long length = ftell(wavefp); IOCheck_struct check; // Let's fix the riff chunk size and the data chunk size fseek(wavefp, sizeof(waveheader_struct)-0x8, SEEK_SET); length -= 0x4; ywrite(&check, (void *)&length, 1, 4, wavefp); fseek(wavefp, sizeof(waveheader_struct)+sizeof(fmt_struct)+0x4, SEEK_SET); length -= sizeof(waveheader_struct)+sizeof(fmt_struct); ywrite(&check, (void *)&length, 1, 4, wavefp); fclose(wavefp); } } ////////////////////////////////////////////////////////////////////////////// static int SNDWavReset(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// static int SNDWavChangeVideoFormat(UNUSED int vertfreq) { return 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDWavUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples) { s16 stereodata16[44100 / 50]; ScspConvert32uto16s((s32 *)leftchanbuffer, (s32 *)rightchanbuffer, (s16 *)stereodata16, num_samples); fwrite((void *)stereodata16, sizeof(s16) * 2, num_samples, wavefp); } ////////////////////////////////////////////////////////////////////////////// static u32 SNDWavGetAudioSpace(void) { /* A "hack" to get sound core working enough * so videos are not "freezing". Values have been * found by experiments... I don't have a clue why * they are working ^^; */ static int i = 0; i++; if (i == 55) { i = 0; return 85; } else { return 0; } } ////////////////////////////////////////////////////////////////////////////// static void SNDWavMuteAudio(void) { } ////////////////////////////////////////////////////////////////////////////// static void SNDWavUnMuteAudio(void) { } ////////////////////////////////////////////////////////////////////////////// static void SNDWavSetVolume(UNUSED int volume) { } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/CMakeLists.txt000644 001750 001750 00000044343 12256006177 020753 0ustar00guillaumeguillaume000000 000000 project(yabause) include(CheckFunctionExists) include(CheckIncludeFile) set(yabause_SOURCES bios.c cdbase.c cheat.c coffelf.c cs0.c cs1.c cs2.c debug.c error.c m68kcore.c m68kd.c memory.c movie.c japmodem.c netlink.c osdcore.c peripheral.c profile.c scu.c sh2core.c sh2d.c sh2idle.c sh2int.c sh2trace.c smpc.c snddummy.c titan/titan.c vdp1.c vdp2.c vdp2debug.c vidogl.c vidshared.c vidsoft.c yabause.c ygl.c yglshader.c) # new SCSP option(YAB_USE_SCSP2 "Use the new SCSP implementation.") if (YAB_USE_SCSP2) add_definitions(-DUSE_SCSP2=1) set(yabause_SOURCES ${yabause_SOURCES} scsp2.c) else() set(yabause_SOURCES ${yabause_SOURCES} scsp.c) endif() # disable strdup warning in MSVC if (MSVC) add_definitions(/wd4996) endif () # math library if (UNIX) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} "m") endif() # Bigendian include(TestBigEndian) test_big_endian(WORDS_BIGENDIAN) if (WORDS_BIGENDIAN) add_definitions(-DWORDS_BIGENDIAN=1) endif (WORDS_BIGENDIAN) include(CheckCSourceCompiles) # variadic macros check_c_source_compiles("#define MACRO(...) puts(__VA_ARGS__) int main(int argc, char ** argv) { MACRO(\"foo\"); }" VARIADIC_MACROS_OK) if (VARIADIC_MACROS_OK) add_definitions(-DHAVE_C99_VARIADIC_MACROS=1) endif (VARIADIC_MACROS_OK) # gettimeofday check_function_exists(gettimeofday GETTIMEOFDAY_OK) if (GETTIMEOFDAY_OK) add_definitions(-DHAVE_GETTIMEOFDAY=1) endif () # floorf set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} "-lm") check_function_exists(floorf FLOORF_OK) if (FLOORF_OK) add_definitions(-DHAVE_FLOORF=1) endif () # _wfopen check_function_exists(_wfopen WFOPEN_OK) if (WFOPEN_OK) add_definitions(-DHAVE_WFOPEN=1) endif () # stricmp/strcasecmp check_function_exists(strcasecmp STRCASECMP_OK) if (STRCASECMP_OK) add_definitions(-DHAVE_STRCASECMP=1) endif () check_function_exists(stricmp STRICMP_OK) if (STRICMP_OK) add_definitions(-DHAVE_STRICMP=1) endif () # __builtin_bswap16 check_function_exists(__builtin_bswap16 BSWAP16_OK) if (BSWAP16_OK) add_definitions(-DHAVE_BUILTIN_BSWAP16=1) endif() # sys/time.h check_include_file("sys/time.h" SYSTIME_OK) if (SYSTIME_OK) add_definitions(-DHAVE_SYS_TIME_H=1) endif() # Find stdint.h check_include_file("stdint.h" STDINT_H_FOUND) if (STDINT_H_FOUND) add_definitions(-DHAVE_STDINT_H=1) endif() # 16BPP set(YAB_RGB "" CACHE STRING "Bit configuration of pixels in the display buffer.") if (YAB_RGB STREQUAL "555") add_definitions(-DUSE_16BPP=1 -DUSE_RGB_555=1) elseif (YAB_RGB STREQUAL "565") add_definitions(-DUSE_16BPP=1 -DUSE_RGB_565=1) endif () # OpenGL option(YAB_WANT_OPENGL "use OpenGL for video output (most ports require it)" ON) if (YAB_WANT_OPENGL AND (YAB_RGB STREQUAL "")) include(FindOpenGL) if (OPENGL_FOUND) add_definitions(-DHAVE_LIBGL=1) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${OPENGL_LIBRARIES}) include(FindGLUT) if (GLUT_FOUND) include_directories(${GLUT_INCLUDE_DIR}) add_definitions(-DHAVE_LIBGLUT=1) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${GLUT_LIBRARIES}) endif() # glXGetProcAddress set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${OPENGL_LIBRARIES}) check_function_exists(glXGetProcAddress GLXGETPROCADDRESS_OK) if (GLXGETPROCADDRESS_OK) add_definitions(-DHAVE_GLXGETPROCADDRESS=1) endif() endif(OPENGL_FOUND) endif () # SDL option(YAB_WANT_SDL "use SDL cores if available" ON) if (YAB_WANT_SDL) include(FindSDL) if (SDL_FOUND) add_definitions(-DHAVE_LIBSDL=1) include_directories(${SDL_INCLUDE_DIR}) set(yabause_SOURCES ${yabause_SOURCES} persdljoy.c sndsdl.c) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${SDL_LIBRARY}) endif (SDL_FOUND) endif (YAB_WANT_SDL) # OpenAL option(YAB_WANT_OPENAL "use OpenAL sound core if available" ON) if (YAB_WANT_OPENAL) include(FindOpenAL) if (OPENAL_FOUND) find_package(Threads) add_definitions(-DHAVE_LIBAL=1) include_directories(${OPENAL_INCLUDE_DIR}) set(yabause_SOURCES ${yabause_SOURCES} sndal.c) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${OPENAL_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) endif (OPENAL_FOUND) endif (YAB_WANT_OPENAL) # mini18n find_path(MINI18N_INCLUDE_DIR mini18n.h) find_library(MINI18N_LIBRARY mini18n) if (NOT MINI18N_INCLUDE_DIR STREQUAL "MINI18N_INCLUDE_DIR-NOTFOUND" AND NOT MINI18N_LIBRARY STREQUAL "MINI18N_LIBRARY-NOTFOUND") set(MINI18N_FOUND TRUE) include_directories(${MINI18N_INCLUDE_DIR}) add_definitions(-DHAVE_LIBMINI18N=1) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${MINI18N_LIBRARY}) endif (NOT MINI18N_INCLUDE_DIR STREQUAL "MINI18N_INCLUDE_DIR-NOTFOUND" AND NOT MINI18N_LIBRARY STREQUAL "MINI18N_LIBRARY-NOTFOUND") # xrandr find_library(XRANDR_LIBRARY Xrandr) if(XRANDR_LIBRARY) add_definitions(-DHAVE_LIBXRANDR=1) set(yabause_SOURCES ${yabause_SOURCES} scr-x.c) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} Xrandr X11) endif() if (MINI18N_FOUND) if (UNIX) add_definitions(-DYTSDIR=\"${CMAKE_INSTALL_PREFIX}/share/${YAB_PACKAGE}/yts\") elseif (WIN32) add_definitions(-DYTSDIR=\"trans\") endif() endif() # APPLE // not necessary mac os x, but i don't care ;) if (APPLE) FIND_LIBRARY(COREFOUNDATION_LIBRARY NAMES CoreFoundation ) FIND_LIBRARY(IOKIT_LIBRARY NAMES IOKit ) set(yabause_SOURCES ${yabause_SOURCES} macjoy.c permacjoy.c cd-macosx.c sndmac.c) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${COREFOUNDATION_LIBRARY} ${IOKIT_LIBRARY}) check_function_exists(glBindRenderbuffer HAVE_FBO) if (HAVE_FBO) add_definitions(-DHAVE_FBO=1) endif() endif (APPLE) # Visual Studio if (MSVC) # Find DDK if (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/3790.1830/") set(DDK_DIR "$ENV{SYSTEMDRIVE}/WINDDK/3790.1830/") elseif (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/6000/") set(DDK_DIR "$ENV{SYSTEMDRIVE}/WINDDK/6000/") elseif (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/7600.16385.0/") set(DDK_DIR "$ENV{SYSTEMDRIVE}/WINDDK/7600.16385.0/") endif (EXISTS "$ENV{SYSTEMDRIVE}/WINDDK/3790.1830/") add_definitions(-DHAVE_C99_VARIADIC_MACROS -D_CRT_SECURE_NO_WARNINGS -DC68K_NO_JUMP_TABLE -D_UNICODE -DUNICODE) endif (MSVC) if (WIN32) # Windows ddk option(YAB_WANT_DDK "Use the real DDK instead of the built-in one") if(YAB_WANT_DDK) # Find ntddcdrm.h find_path(ntddcdrm_INCLUDE_DIR ntddcdrm.h PATHS "${DDK_DIR}" "${DDK_DIR}/inc" PATH_SUFFIXES ddk api) if (ntddcdrm_INCLUDE_DIR) include_directories(${ntddcdrm_INCLUDE_DIR}) message(STATUS "Found ntddcdrm.h: ${ntddcdrm_INCLUDE_DIR}") add_definitions(-DHAVE_NTDDCDRM=1) else (ntddcdrm_INCLUDE_DIR) message(STATUS "Could not find ntddcdrm.h") endif (ntddcdrm_INCLUDE_DIR) endif(YAB_WANT_DDK) set(yabause_SOURCES ${yabause_SOURCES} cd-windows.c) option(YAB_WANT_DIRECTSOUND "use DirectX sound core if available") option(YAB_WANT_DIRECTINPUT "use DirectX input core if available") # Direct X if (YAB_WANT_DIRECTSOUND OR YAB_WANT_DIRECTINPUT) find_path(DirectX_INCLUDE_DIR dxerr9.h "$ENV{DXSDK_DIR}/Include") if (NOT DirectX_INCLUDE_DIR) find_path(DirectX_INCLUDE_DIR "dxerr.h" "$ENV{DXSDK_DIR}/Include") if (DirectX_INCLUDE_DIR) set(DXERRH_IS_BROKEN 1 CACHE INTERNAL "dxerr is broken") endif (DirectX_INCLUDE_DIR) endif(NOT DirectX_INCLUDE_DIR) message (STATUS "system processor = ${CMAKE_SYSTEM_PROCESSOR}") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") set (DIRECTX_SEARCH_PATH "$ENV{DXSDK_DIR}/Lib/x64") else() set (DIRECTX_SEARCH_PATH "$ENV{DXSDK_DIR}/Lib/x86") endif() find_library(DirectX_GUID_LIBRARY dxguid "${DIRECTX_SEARCH_PATH}" "$ENV{DXSDK_DIR}/Lib") if (YAB_WANT_DIRECTINPUT) find_library(DirectX_INPUT8_LIBRARY dinput8 "${DIRECTX_SEARCH_PATH}" "$ENV{DXSDK_DIR}/Lib") find_library(DirectX_XINPUT_LIBRARY xinput "${DIRECTX_SEARCH_PATH}" "$ENV{DXSDK_DIR}/Lib") endif(YAB_WANT_DIRECTINPUT) if (YAB_WANT_DIRECTSOUND) find_library(DirectX_SOUND_LIBRARY dsound "${DIRECTX_SEARCH_PATH}" "$ENV{DXSDK_DIR}/Lib") endif(YAB_WANT_DIRECTSOUND) if (DXERRH_IS_BROKEN) find_library(DirectX_ERR_LIBRARY dxerr "${DIRECTX_SEARCH_PATH}" "$ENV{DXSDK_DIR}/Lib") elseif(MINGW) find_library(DirectX_ERR_LIBRARY dxerr8 "${DIRECTX_SEARCH_PATH}" "$ENV{DXSDK_DIR}/Lib") else() find_library(DirectX_ERR_LIBRARY dxerr9 "${DIRECTX_SEARCH_PATH}" "$ENV{DXSDK_DIR}/Lib") endif() if (DirectX_INCLUDE_DIR AND DirectX_GUID_LIBRARY AND DirectX_ERR_LIBRARY) include_directories(${DirectX_INCLUDE_DIR}) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${DirectX_GUID_LIBRARY} ${DirectX_ERR_LIBRARY}) if (YAB_WANT_DIRECTINPUT AND DirectX_INPUT8_LIBRARY) add_definitions(-DHAVE_DIRECTINPUT) set(yabause_SOURCES ${yabause_SOURCES} perdx.c) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${DirectX_INPUT8_LIBRARY}) if (DirectX_XINPUT_LIBRARY) add_definitions(-DHAVE_XINPUT) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${DirectX_XINPUT_LIBRARY} wbemuuid) endif() endif () if (YAB_WANT_DIRECTSOUND AND DirectX_SOUND_LIBRARY) add_definitions(-DHAVE_DIRECTSOUND) set(yabause_SOURCES ${yabause_SOURCES} snddx.c) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} ${DirectX_SOUND_LIBRARY}) endif () if (DXERRH_IS_BROKEN) add_definitions(-DDXERRH_IS_BROKEN) message(STATUS "Using work-around for dxerr.h") endif(DXERRH_IS_BROKEN) endif (DirectX_INCLUDE_DIR AND DirectX_GUID_LIBRARY AND DirectX_ERR_LIBRARY) endif (YAB_WANT_DIRECTSOUND OR YAB_WANT_DIRECTINPUT) if (YAB_NETWORK OR YAB_WANT_GDBSTUB) # Add Winsock if necessary set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} "wsock32") set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} "ws2_32") endif() endif (WIN32) if (WII) set(CMAKE_C_FLAGS "-mrvl -mcpu=750 -meabi -mhard-float") add_definitions(-DGEKKO=1) # that shouldn't be hardcoded, either use an ENV variable or try to detect it... include_directories(/opt/devkitpro/libogc/include/) endif() option(YAB_WANT_ARM7 "Build a binary with arm7 support") # SH2 dynamic recompiler message(STATUS "CMAKE_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}") message(STATUS "CMAKE_SYSTEM_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR}") option(SH2_DYNAREC "SH2 dynamic recompiler" ON) if (SH2_DYNAREC) if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") enable_language(ASM-ATT) set(yabause_SOURCES ${yabause_SOURCES} sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_x86.s) set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") add_definitions(-DSH2_DYNAREC=1) endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") enable_language(ASM-ATT) set(yabause_SOURCES ${yabause_SOURCES} sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_x64.s) set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") add_definitions(-DSH2_DYNAREC=1) endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv5tel") enable_language(ASM-ATT) set(yabause_SOURCES ${yabause_SOURCES} sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_arm.s) set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") add_definitions(-DSH2_DYNAREC=1) endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv5tel") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") enable_language(ASM-ATT) set(yabause_SOURCES ${yabause_SOURCES} sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_arm.s) set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") add_definitions(-DSH2_DYNAREC=1 -DHAVE_ARMv6=1 -DHAVE_ARMv7=1) endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") if (ANDROID) enable_language(ASM-ATT) set(yabause_SOURCES ${yabause_SOURCES} sh2_dynarec/sh2_dynarec.c sh2_dynarec/linkage_arm.s) set_source_files_properties(sh2_dynarec/sh2_dynarec.c PROPERTIES COMPILE_FLAGS "-Wno-pointer-to-int-cast -Wno-int-to-pointer-cast") add_definitions(-DSH2_DYNAREC=1) add_definitions(-DANDROID=1) if (YAB_WANT_ARM7) add_definitions(-DHAVE_ARMv6=1 -DHAVE_ARMv7=1) endif() endif () endif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") endif (SH2_DYNAREC) # c68k option(YAB_WANT_C68K "enable c68k compilation" ON) if (YAB_WANT_C68K) include(ExternalProject) ExternalProject_Add(c68kinc DOWNLOAD_COMMAND "" SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/c68k CMAKE_GENERATOR "${CMAKE_GENERATOR}" INSTALL_COMMAND "" BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/c68k ) add_definitions(-DHAVE_C68K=1) include_directories(${CMAKE_CURRENT_BINARY_DIR}/c68k) set(yabause_SOURCES ${yabause_SOURCES} c68k/c68kexec.c c68k/c68k.c m68kc68k.c) if (MSVC) set_source_files_properties(c68k/c68kexec.c PROPERTIES COMPILE_FLAGS "/Od /wd4146") else() set_source_files_properties(c68k/c68kexec.c PROPERTIES COMPILE_FLAGS "-O0") endif() endif(YAB_WANT_C68K) # q68 option(YAB_WANT_Q68 "enable q68 compilation" OFF) if (YAB_WANT_Q68) add_definitions(-DHAVE_Q68=1) set(yabause_SOURCES ${yabause_SOURCES} m68kq68.c q68/q68.c q68/q68-core.c q68/q68-disasm.c q68/q68-const.h q68/q68.h q68/q68-internal.h q68/q68-jit.h q68/q68-jit-psp.h q68/q68-jit-x86.h) endif() # gdb stub option(YAB_WANT_GDBSTUB "enable gdb stub" OFF) if (YAB_WANT_GDBSTUB) add_definitions(-DHAVE_GDBSTUB=1) set(yabause_SOURCES ${yabause_SOURCES} gdb/stub.c gdb/client.c gdb/packet.c) endif() # *DEBUG set(YAB_DEBUG "" CACHE STRING "List of enabled debug information") foreach(DEBUG IN LISTS YAB_DEBUG) if (${DEBUG} STREQUAL "main") add_definitions(-DDEBUG=1) elseif (${DEBUG} STREQUAL "cd") add_definitions(-DCDDEBUG=1) elseif (${DEBUG} STREQUAL "idle") add_definitions(-DIDLE_DETECT_VERBOSE=1) else (${DEBUG} STREQUAL "main") string(TOUPPER ${DEBUG} UPDEBUG) add_definitions(-D${UPDEBUG}_DEBUG=1) endif (${DEBUG} STREQUAL "main") endforeach(DEBUG) # Network option(YAB_NETWORK "Enable network") if (YAB_NETWORK) add_definitions(-DUSESOCKET=1) endif() option(YAB_PORT_OSD "Let ports provides their own OSD core list" OFF) if (YAB_PORT_OSD) add_definitions(-DYAB_PORT_OSD=1) endif() # Exec from cache option(YAB_EXEC_FROM_CACHE "Allow code execution from 0xC0000000" OFF) if (YAB_EXEC_FROM_CACHE) add_definitions(-DEXEC_FROM_CACHE=1) endif() # Optimized DMA option(YAB_OPTIMIZED_DMA "Use optimized DMA when possible" OFF) if (YAB_OPTIMIZED_DMA) add_definitions(-DOPTIMIZED_DMA=1) endif() # Yabause Arch if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") add_definitions(-DARCH_IS_MACOSX=1) set(yabause_SOURCES ${yabause_SOURCES} sock-dummy.c thr-dummy.c) elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") add_definitions(-DARCH_IS_FREEBSD=1) set(yabause_SOURCES ${yabause_SOURCES} sock-dummy.c thr-dummy.c cd-freebsd.c) elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") add_definitions(-DARCH_IS_LINUX=1) set(yabause_SOURCES ${yabause_SOURCES} sock-linux.c thr-linux.c cd-linux.c) check_include_file("linux/joystick.h" LINUX_HAS_JOYSTICK) if (LINUX_HAS_JOYSTICK) set(yabause_SOURCES ${yabause_SOURCES} perlinuxjoy.c) endif() check_c_source_compiles(" #include int main(int argc, char ** argv) { int i = CDSL_CURRENT; } " LINUX_CDROM_H_OK) if (NOT LINUX_CDROM_H_OK) add_definitions(-DLINUX_CDROM_H_IS_BROKEN) endif (NOT LINUX_CDROM_H_OK) elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD") add_definitions(-DARCH_IS_NETBSD=1) set(yabause_SOURCES ${yabause_SOURCES} sock-dummy.c thr-dummy.c cd-netbsd.c) elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") add_definitions(-DARCH_IS_NETBSD=1) set(yabause_SOURCES ${yabause_SOURCES} sock-dummy.c thr-dummy.c cd-netbsd.c) elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows") add_definitions(-DARCH_IS_WINDOWS=1) set(yabause_SOURCES ${yabause_SOURCES} sock-windows.c thr-windows.c) else () add_definitions(-DUNKNOWN_ARCH=1) set(yabause_SOURCES ${yabause_SOURCES} sock-dummy.c thr-dummy.c) endif () set(YAB_OPTIMIZATION "-O3" CACHE STRING "Override optimization level") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION} -march=i686 -msse") endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "i686") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION}") endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv5tel") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION}") endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv5tel") if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION}") endif("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "armv7l") if(ANDROID) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${YAB_OPTIMIZATION}") endif() # Warnings defined to know when we're breaking compilation with MSVC if (CMAKE_COMPILER_IS_GNUCC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wdeclaration-after-statement") endif () if (MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /wd4018 /wd4244") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244") endif () add_definitions(-DPACKAGE=\"${YAB_PACKAGE}\") add_definitions(-DVERSION=\"${YAB_VERSION}\") add_library(yabause ${yabause_SOURCES}) if (YAB_WANT_C68K) add_dependencies(yabause c68kinc) endif(YAB_WANT_C68K) macro(yab_port_start) if (YAB_PORT_BUILT AND NOT YAB_MULTIBUILD) return() endif () endmacro(yab_port_start) macro(yab_port_stop) set(YAB_PORT_BUILT TRUE PARENT_SCOPE) endmacro(yab_port_stop) macro(yab_port_success YAB_TARGET) if (NOT YAB_MULTIBUILD) set_target_properties(${YAB_TARGET} PROPERTIES OUTPUT_NAME yabause) set(YAB_PORT_NAME "yabause") else () set(YAB_PORT_NAME ${YAB_TARGET}) endif () set(YAB_PORT_BUILT TRUE PARENT_SCOPE) endmacro(yab_port_success) set(YAB_MAN_DIR "share/man") if (NOT $ENV{PKGMANDIR} STREQUAL "") set(YAB_MAN_DIR $ENV{PKGMANDIR}) endif () option(YAB_MULTIBUILD "Choose wether to build all ports or only a single one") set(YAB_PORT_BUILT FALSE) set(YAB_PORTS "gtk;qt;dreamcast;cocoa" CACHE STRING "List of ports to build") foreach(PORT IN LISTS YAB_PORTS) add_subdirectory(${PORT}) endforeach(PORT) # this is stupid, but CMake automatic definitions are based on variables... if (YAB_WANT_C68K) set(HAVE_C68K ON) endif() if (YAB_WANT_Q68) set(HAVE_Q68 ON) endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) if (YAB_NETWORK AND UNIX) set(YABAUSE_LIBRARIES ${YABAUSE_LIBRARIES} "socket") endif() yabause-0.9.13.1/src/Makefile.dc000644 001750 001750 00000004154 12256006174 020231 0ustar00guillaumeguillaume000000 000000 # Makefile.dc # Dreamcast Makefile # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Lawrence Sebald # Based on KOS makefiles (C) by Dan Potter # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # all: yabause.bin include $(KOS_BASE)/Makefile.rules KOS_CFLAGS += -I. -DDEBUG -DNO_CLI -DVERSION="0.9.11" KOS_ASFLAGS += -g OBJS = bios.o cdbase.o cheat.o cs0.o cs1.o cs2.o debug.o error.o m68kd.o \ memory.o netlink.o peripheral.o profile.o scsp.o scu.o sh2core.o sh2idle.o \ sh2int.o sh2d.o smpc.o vdp1.o vdp2.o yabause.o m68kcore.o coffelf.o \ m68kc68k.o movie.o snddummy.o C68K_OBJS = c68k/c68k.o c68k/c68kexec.o c68k/gen68k.o ARCH_OBJS = dreamcast/yui.o dreamcast/perdc.o dreamcast/viddc.o \ dreamcast/localtime.o dreamcast/cd.o dreamcast/sh2rec/sh2rec.o \ dreamcast/sh2rec/sh2rec_htab.o dreamcast/sh2rec/sh2exec.o \ dreamcast/sh2rec/sh2rec_mem.o c68k/c68kexec.o: c68k/gen68k c68k/gen68k: c68k/c68kexec.c c68k/c68k.c c68k/gen68k.c $(CC) $(CFLAGS) -DC68K_GEN -o $@ $^ cd c68k && ./gen68k yabause.elf: $(OBJS) $(ARCH_OBJS) $(C68K_OBJS) kos-cc -o $@ $^ -lm yabause.bin: yabause.elf $(KOS_OBJCOPY) -R .stack -O binary yabause.elf yabause.bin cdtest.elf: dreamcast/cd.o tools/cdtest.o kos-cc -o $@ $^ cdtest.bin: cdtest.elf $(KOS_OBJCOPY) -R .stack -O binary cdtest.elf cdtest.bin run: yabause.bin $(KOS_LOADER) yabause.bin clean: rm -f $(OBJS) $(ARCH_OBJS) rm -f tools/cdtest.o rm -f yabause.elf rm -f cdtest.elf rm -f cdtest.bin distclean: clean rm -f yabause.bin yabause-0.9.13.1/src/movie.h000644 001750 001750 00000003661 12256006124 017471 0ustar00guillaumeguillaume000000 000000 /* This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef MOVIE_H #define MOVIE_H #include "core.h" #define Stopped 1 #define Recording 2 #define Playback 3 #define RunNormal 0 #define Paused 1 #define NeedAdvance 2 void DoMovie(void); struct MovieStruct { int Status; FILE *fp; int ReadOnly; int Rerecords; int Size; int Frames; const char* filename; }; extern struct MovieStruct Movie; struct MovieBufferStruct { int size; char* data; }; struct MovieBufferStruct ReadMovieIntoABuffer(FILE* fp); void MovieLoadState(const char * filename); void SaveMovieInState(FILE* fp, IOCheck_struct check); void ReadMovieInState(FILE* fp); void TestWrite(struct MovieBufferStruct tempbuffer); void MovieToggleReadOnly(void); void TruncateMovie(struct MovieStruct Movie); void DoFrameAdvance(void); int SaveMovie(const char *filename); int PlayMovie(const char *filename); void StopMovie(void); const char *MakeMovieStateName(const char *filename); void MovieReadState(FILE* fp, const char * filename); void PauseOrUnpause(void); int IsMovieLoaded(void); extern int framecounter; extern int LagFrameFlag; extern int lagframecounter; extern char MovieStatus[40]; extern char InputDisplayString[40]; extern int FrameAdvanceVariable; #endif yabause-0.9.13.1/src/sh2int.c000644 001750 001750 00000232645 12256006124 017562 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2005 Guillaume Duhamel Copyright 2004-2007, 2013 Theo Berkau Copyright 2005 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // SH2 Interpreter Core #include "sh2core.h" #include "sh2int.h" #include "sh2idle.h" #include "cs0.h" #include "debug.h" #include "error.h" #include "memory.h" #include "bios.h" #include "yabause.h" // #define SH2_TRACE // Uncomment to enable tracing #ifdef SH2_TRACE # include "sh2trace.h" # define MappedMemoryWriteByte(a,v) do { \ uint32_t __a = (a), __v = (v); \ sh2_trace_writeb(__a, __v); \ MappedMemoryWriteByte(__a, __v); \ } while (0) # define MappedMemoryWriteWord(a,v) do { \ uint32_t __a = (a), __v = (v); \ sh2_trace_writew(__a, __v); \ MappedMemoryWriteWord(__a, __v); \ } while (0) # define MappedMemoryWriteLong(a,v) do { \ uint32_t __a = (a), __v = (v); \ sh2_trace_writel(__a, __v); \ MappedMemoryWriteLong(__a, __v); \ } while (0) #endif opcodefunc opcodes[0x10000]; SH2Interface_struct SH2Interpreter = { SH2CORE_INTERPRETER, "SH2 Interpreter", SH2InterpreterInit, SH2InterpreterDeInit, SH2InterpreterReset, SH2InterpreterExec, SH2InterpreterGetRegisters, SH2InterpreterGetGPR, SH2InterpreterGetSR, SH2InterpreterGetGBR, SH2InterpreterGetVBR, SH2InterpreterGetMACH, SH2InterpreterGetMACL, SH2InterpreterGetPR, SH2InterpreterGetPC, SH2InterpreterSetRegisters, SH2InterpreterSetGPR, SH2InterpreterSetSR, SH2InterpreterSetGBR, SH2InterpreterSetVBR, SH2InterpreterSetMACH, SH2InterpreterSetMACL, SH2InterpreterSetPR, SH2InterpreterSetPC, SH2InterpreterSendInterrupt, SH2InterpreterGetInterrupts, SH2InterpreterSetInterrupts, NULL // SH2WriteNotify not used }; SH2Interface_struct SH2DebugInterpreter = { SH2CORE_DEBUGINTERPRETER, "SH2 Debugger Interpreter", SH2DebugInterpreterInit, SH2InterpreterDeInit, SH2InterpreterReset, SH2DebugInterpreterExec, SH2InterpreterGetRegisters, SH2InterpreterGetGPR, SH2InterpreterGetSR, SH2InterpreterGetGBR, SH2InterpreterGetVBR, SH2InterpreterGetMACH, SH2InterpreterGetMACL, SH2InterpreterGetPR, SH2InterpreterGetPC, SH2InterpreterSetRegisters, SH2InterpreterSetGPR, SH2InterpreterSetSR, SH2InterpreterSetGBR, SH2InterpreterSetVBR, SH2InterpreterSetMACH, SH2InterpreterSetMACL, SH2InterpreterSetPR, SH2InterpreterSetPC, SH2InterpreterSendInterrupt, SH2InterpreterGetInterrupts, SH2InterpreterSetInterrupts, NULL // SH2WriteNotify not used }; fetchfunc fetchlist[0x100]; ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL FetchBios(u32 addr) { return T2ReadWord(BiosRom, addr & 0x7FFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL FetchCs0(u32 addr) { return CartridgeArea->Cs0ReadWord(addr); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL FetchLWram(u32 addr) { return T2ReadWord(LowWram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL FetchHWram(u32 addr) { return T2ReadWord(HighWram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL FetchInvalid(UNUSED u32 addr) { return 0xFFFF; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2delay(SH2_struct * sh, u32 addr) { #ifdef SH2_TRACE sh2_trace(sh, addr); #endif // Fetch Instruction #ifdef EXEC_FROM_CACHE if ((addr & 0xC0000000) == 0xC0000000) sh->instruction = DataArrayReadWord(addr); else #endif sh->instruction = fetchlist[(addr >> 20) & 0x0FF](addr); // Execute it opcodes[sh->instruction](sh); sh->regs.PC -= 2; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2undecoded(SH2_struct * sh) { int vectnum; if (yabsys.emulatebios) { if (BiosHandleFunc(sh)) return; } YabSetError(YAB_ERR_SH2INVALIDOPCODE, sh); // Save regs.SR on stack sh->regs.R[15]-=4; MappedMemoryWriteLong(sh->regs.R[15],sh->regs.SR.all); // Save regs.PC on stack sh->regs.R[15]-=4; MappedMemoryWriteLong(sh->regs.R[15],sh->regs.PC + 2); // What caused the exception? The delay slot or a general instruction? // 4 for General Instructions, 6 for delay slot vectnum = 4; // Fix me // Jump to Exception service routine sh->regs.PC = MappedMemoryReadLong(sh->regs.VBR+(vectnum<<2)); sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2add(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)] += sh->regs.R[INSTRUCTION_C(sh->instruction)]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2addi(SH2_struct * sh) { s32 cd = (s32)(s8)INSTRUCTION_CD(sh->instruction); s32 b = INSTRUCTION_B(sh->instruction); sh->regs.R[b] += cd; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2addc(SH2_struct * sh) { u32 tmp0, tmp1; s32 source = INSTRUCTION_C(sh->instruction); s32 dest = INSTRUCTION_B(sh->instruction); tmp1 = sh->regs.R[source] + sh->regs.R[dest]; tmp0 = sh->regs.R[dest]; sh->regs.R[dest] = tmp1 + sh->regs.SR.part.T; if (tmp0 > tmp1) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; if (tmp1 > sh->regs.R[dest]) sh->regs.SR.part.T = 1; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2addv(SH2_struct * sh) { s32 dest,src,ans; s32 n = INSTRUCTION_B(sh->instruction); s32 m = INSTRUCTION_C(sh->instruction); if ((s32) sh->regs.R[n] >= 0) dest = 0; else dest = 1; if ((s32) sh->regs.R[m] >= 0) src = 0; else src = 1; src += dest; sh->regs.R[n] += sh->regs.R[m]; if ((s32) sh->regs.R[n] >= 0) ans = 0; else ans = 1; ans += dest; if (src == 0 || src == 2) if (ans == 1) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; else sh->regs.SR.part.T = 0; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2y_and(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)] &= sh->regs.R[INSTRUCTION_C(sh->instruction)]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2andi(SH2_struct * sh) { sh->regs.R[0] &= INSTRUCTION_CD(sh->instruction); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2andm(SH2_struct * sh) { s32 temp; s32 source = INSTRUCTION_CD(sh->instruction); temp = (s32) MappedMemoryReadByte(sh->regs.GBR + sh->regs.R[0]); temp &= source; MappedMemoryWriteByte((sh->regs.GBR + sh->regs.R[0]),temp); sh->regs.PC += 2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2bf(SH2_struct * sh) { if (sh->regs.SR.part.T == 0) { s32 disp = (s32)(s8)sh->instruction; sh->regs.PC = sh->regs.PC+(disp<<1)+4; sh->cycles += 3; } else { sh->regs.PC+=2; sh->cycles++; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2bfs(SH2_struct * sh) { if (sh->regs.SR.part.T == 0) { s32 disp = (s32)(s8)sh->instruction; u32 temp = sh->regs.PC; sh->regs.PC = sh->regs.PC + (disp << 1) + 4; sh->cycles += 2; SH2delay(sh, temp + 2); } else { sh->regs.PC += 2; sh->cycles++; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2bra(SH2_struct * sh) { s32 disp = INSTRUCTION_BCD(sh->instruction); u32 temp = sh->regs.PC; if ((disp&0x800) != 0) disp |= 0xFFFFF000; sh->regs.PC = sh->regs.PC + (disp<<1) + 4; sh->cycles += 2; SH2delay(sh, temp + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2braf(SH2_struct * sh) { u32 temp; s32 m = INSTRUCTION_B(sh->instruction); temp = sh->regs.PC; sh->regs.PC += sh->regs.R[m] + 4; sh->cycles += 2; SH2delay(sh, temp + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2bsr(SH2_struct * sh) { u32 temp; s32 disp = INSTRUCTION_BCD(sh->instruction); temp = sh->regs.PC; if ((disp&0x800) != 0) disp |= 0xFFFFF000; sh->regs.PR = sh->regs.PC + 4; sh->regs.PC = sh->regs.PC+(disp<<1) + 4; sh->cycles += 2; SH2delay(sh, temp + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2bsrf(SH2_struct * sh) { u32 temp = sh->regs.PC; sh->regs.PR = sh->regs.PC + 4; sh->regs.PC += sh->regs.R[INSTRUCTION_B(sh->instruction)] + 4; sh->cycles += 2; SH2delay(sh, temp + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2bt(SH2_struct * sh) { if (sh->regs.SR.part.T == 1) { s32 disp = (s32)(s8)sh->instruction; sh->regs.PC = sh->regs.PC+(disp<<1)+4; sh->cycles += 3; } else { sh->regs.PC += 2; sh->cycles++; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2bts(SH2_struct * sh) { if (sh->regs.SR.part.T) { s32 disp = (s32)(s8)sh->instruction; u32 temp = sh->regs.PC; sh->regs.PC += (disp << 1) + 4; sh->cycles += 2; SH2delay(sh, temp + 2); } else { sh->regs.PC+=2; sh->cycles++; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2clrmac(SH2_struct * sh) { sh->regs.MACH = 0; sh->regs.MACL = 0; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2clrt(SH2_struct * sh) { sh->regs.SR.part.T = 0; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmpeq(SH2_struct * sh) { if (sh->regs.R[INSTRUCTION_B(sh->instruction)] == sh->regs.R[INSTRUCTION_C(sh->instruction)]) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmpge(SH2_struct * sh) { if ((s32)sh->regs.R[INSTRUCTION_B(sh->instruction)] >= (s32)sh->regs.R[INSTRUCTION_C(sh->instruction)]) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmpgt(SH2_struct * sh) { if ((s32)sh->regs.R[INSTRUCTION_B(sh->instruction)]>(s32)sh->regs.R[INSTRUCTION_C(sh->instruction)]) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmphi(SH2_struct * sh) { if ((u32)sh->regs.R[INSTRUCTION_B(sh->instruction)] > (u32)sh->regs.R[INSTRUCTION_C(sh->instruction)]) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmphs(SH2_struct * sh) { if ((u32)sh->regs.R[INSTRUCTION_B(sh->instruction)] >= (u32)sh->regs.R[INSTRUCTION_C(sh->instruction)]) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmpim(SH2_struct * sh) { s32 imm; s32 i = INSTRUCTION_CD(sh->instruction); imm = (s32)(s8)i; if (sh->regs.R[0] == (u32) imm) // FIXME: ouais � doit �re bon... sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmppl(SH2_struct * sh) { if ((s32)sh->regs.R[INSTRUCTION_B(sh->instruction)]>0) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmppz(SH2_struct * sh) { if ((s32)sh->regs.R[INSTRUCTION_B(sh->instruction)]>=0) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2cmpstr(SH2_struct * sh) { u32 temp; s32 HH,HL,LH,LL; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); temp=sh->regs.R[n]^sh->regs.R[m]; HH = (temp>>24) & 0x000000FF; HL = (temp>>16) & 0x000000FF; LH = (temp>>8) & 0x000000FF; LL = temp & 0x000000FF; HH = HH && HL && LH && LL; if (HH == 0) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2div0s(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&0x80000000)==0) sh->regs.SR.part.Q = 0; else sh->regs.SR.part.Q = 1; if ((sh->regs.R[m]&0x80000000)==0) sh->regs.SR.part.M = 0; else sh->regs.SR.part.M = 1; sh->regs.SR.part.T = !(sh->regs.SR.part.M == sh->regs.SR.part.Q); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2div0u(SH2_struct * sh) { sh->regs.SR.part.M = sh->regs.SR.part.Q = sh->regs.SR.part.T = 0; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2div1(SH2_struct * sh) { u32 tmp0; u8 old_q, tmp1; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); old_q = sh->regs.SR.part.Q; sh->regs.SR.part.Q = (u8)((0x80000000 & sh->regs.R[n])!=0); sh->regs.R[n] <<= 1; sh->regs.R[n]|=(u32) sh->regs.SR.part.T; switch(old_q) { case 0: switch(sh->regs.SR.part.M) { case 0: tmp0 = sh->regs.R[n]; sh->regs.R[n] -= sh->regs.R[m]; tmp1 = (sh->regs.R[n] > tmp0); switch(sh->regs.SR.part.Q) { case 0: sh->regs.SR.part.Q = tmp1; break; case 1: sh->regs.SR.part.Q = (u8) (tmp1 == 0); break; } break; case 1: tmp0 = sh->regs.R[n]; sh->regs.R[n] += sh->regs.R[m]; tmp1 = (sh->regs.R[n] < tmp0); switch(sh->regs.SR.part.Q) { case 0: sh->regs.SR.part.Q = (u8) (tmp1 == 0); break; case 1: sh->regs.SR.part.Q = tmp1; break; } break; } break; case 1: switch(sh->regs.SR.part.M) { case 0: tmp0 = sh->regs.R[n]; sh->regs.R[n] += sh->regs.R[m]; tmp1 = (sh->regs.R[n] < tmp0); switch(sh->regs.SR.part.Q) { case 0: sh->regs.SR.part.Q = tmp1; break; case 1: sh->regs.SR.part.Q = (u8) (tmp1 == 0); break; } break; case 1: tmp0 = sh->regs.R[n]; sh->regs.R[n] -= sh->regs.R[m]; tmp1 = (sh->regs.R[n] > tmp0); switch(sh->regs.SR.part.Q) { case 0: sh->regs.SR.part.Q = (u8) (tmp1 == 0); break; case 1: sh->regs.SR.part.Q = tmp1; break; } break; } break; } sh->regs.SR.part.T = (sh->regs.SR.part.Q == sh->regs.SR.part.M); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2dmuls(SH2_struct * sh) { u32 RnL,RnH,RmL,RmH,Res0,Res1,Res2; u32 temp0,temp1,temp2,temp3; s32 tempm,tempn,fnLmL; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); tempn = (s32)sh->regs.R[n]; tempm = (s32)sh->regs.R[m]; if (tempn < 0) tempn = 0 - tempn; if (tempm < 0) tempm = 0 - tempm; if ((s32) (sh->regs.R[n] ^ sh->regs.R[m]) < 0) fnLmL = -1; else fnLmL = 0; temp1 = (u32) tempn; temp2 = (u32) tempm; RnL = temp1 & 0x0000FFFF; RnH = (temp1 >> 16) & 0x0000FFFF; RmL = temp2 & 0x0000FFFF; RmH = (temp2 >> 16) & 0x0000FFFF; temp0 = RmL * RnL; temp1 = RmH * RnL; temp2 = RmL * RnH; temp3 = RmH * RnH; Res2 = 0; Res1 = temp1 + temp2; if (Res1 < temp1) Res2 += 0x00010000; temp1 = (Res1 << 16) & 0xFFFF0000; Res0 = temp0 + temp1; if (Res0 < temp0) Res2++; Res2 = Res2 + ((Res1 >> 16) & 0x0000FFFF) + temp3; if (fnLmL < 0) { Res2 = ~Res2; if (Res0 == 0) Res2++; else Res0 =(~Res0) + 1; } sh->regs.MACH = Res2; sh->regs.MACL = Res0; sh->regs.PC += 2; sh->cycles += 2; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2dmulu(SH2_struct * sh) { u32 RnL,RnH,RmL,RmH,Res0,Res1,Res2; u32 temp0,temp1,temp2,temp3; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); RnL = sh->regs.R[n] & 0x0000FFFF; RnH = (sh->regs.R[n] >> 16) & 0x0000FFFF; RmL = sh->regs.R[m] & 0x0000FFFF; RmH = (sh->regs.R[m] >> 16) & 0x0000FFFF; temp0 = RmL * RnL; temp1 = RmH * RnL; temp2 = RmL * RnH; temp3 = RmH * RnH; Res2 = 0; Res1 = temp1 + temp2; if (Res1 < temp1) Res2 += 0x00010000; temp1 = (Res1 << 16) & 0xFFFF0000; Res0 = temp0 + temp1; if (Res0 < temp0) Res2++; Res2 = Res2 + ((Res1 >> 16) & 0x0000FFFF) + temp3; sh->regs.MACH = Res2; sh->regs.MACL = Res0; sh->regs.PC += 2; sh->cycles += 2; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2dt(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]--; if (sh->regs.R[n] == 0) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2extsb(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (u32)(s8)sh->regs.R[m]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2extsw(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (u32)(s16)sh->regs.R[m]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2extub(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (u32)(u8)sh->regs.R[m]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2extuw(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (u32)(u16)sh->regs.R[m]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2jmp(SH2_struct * sh) { u32 temp; s32 m = INSTRUCTION_B(sh->instruction); temp=sh->regs.PC; sh->regs.PC = sh->regs.R[m]; sh->cycles += 2; SH2delay(sh, temp + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2jsr(SH2_struct * sh) { u32 temp; s32 m = INSTRUCTION_B(sh->instruction); temp = sh->regs.PC; sh->regs.PR = sh->regs.PC + 4; sh->regs.PC = sh->regs.R[m]; sh->cycles += 2; SH2delay(sh, temp + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldcgbr(SH2_struct * sh) { sh->regs.GBR = sh->regs.R[INSTRUCTION_B(sh->instruction)]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldcmgbr(SH2_struct * sh) { s32 m = INSTRUCTION_B(sh->instruction); sh->regs.GBR = MappedMemoryReadLong(sh->regs.R[m]); sh->regs.R[m] += 4; sh->regs.PC += 2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldcmsr(SH2_struct * sh) { s32 m = INSTRUCTION_B(sh->instruction); sh->regs.SR.all = MappedMemoryReadLong(sh->regs.R[m]) & 0x000003F3; sh->regs.R[m] += 4; sh->regs.PC += 2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldcmvbr(SH2_struct * sh) { s32 m = INSTRUCTION_B(sh->instruction); sh->regs.VBR = MappedMemoryReadLong(sh->regs.R[m]); sh->regs.R[m] += 4; sh->regs.PC += 2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldcsr(SH2_struct * sh) { sh->regs.SR.all = sh->regs.R[INSTRUCTION_B(sh->instruction)]&0x000003F3; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldcvbr(SH2_struct * sh) { s32 m = INSTRUCTION_B(sh->instruction); sh->regs.VBR = sh->regs.R[m]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldsmach(SH2_struct * sh) { sh->regs.MACH = sh->regs.R[INSTRUCTION_B(sh->instruction)]; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldsmacl(SH2_struct * sh) { sh->regs.MACL = sh->regs.R[INSTRUCTION_B(sh->instruction)]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldsmmach(SH2_struct * sh) { s32 m = INSTRUCTION_B(sh->instruction); sh->regs.MACH = MappedMemoryReadLong(sh->regs.R[m]); sh->regs.R[m] += 4; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldsmmacl(SH2_struct * sh) { s32 m = INSTRUCTION_B(sh->instruction); sh->regs.MACL = MappedMemoryReadLong(sh->regs.R[m]); sh->regs.R[m] += 4; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldsmpr(SH2_struct * sh) { s32 m = INSTRUCTION_B(sh->instruction); sh->regs.PR = MappedMemoryReadLong(sh->regs.R[m]); sh->regs.R[m] += 4; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ldspr(SH2_struct * sh) { sh->regs.PR = sh->regs.R[INSTRUCTION_B(sh->instruction)]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2macl(SH2_struct * sh) { u32 RnL,RnH,RmL,RmH,Res0,Res1,Res2; u32 temp0,temp1,temp2,temp3; s32 tempm,tempn,fnLmL; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); tempn = (s32) MappedMemoryReadLong(sh->regs.R[n]); sh->regs.R[n] += 4; tempm = (s32) MappedMemoryReadLong(sh->regs.R[m]); sh->regs.R[m] += 4; if ((s32) (tempn^tempm) < 0) fnLmL = -1; else fnLmL = 0; if (tempn < 0) tempn = 0 - tempn; if (tempm < 0) tempm = 0 - tempm; temp1 = (u32) tempn; temp2 = (u32) tempm; RnL = temp1 & 0x0000FFFF; RnH = (temp1 >> 16) & 0x0000FFFF; RmL = temp2 & 0x0000FFFF; RmH = (temp2 >> 16) & 0x0000FFFF; temp0 = RmL * RnL; temp1 = RmH * RnL; temp2 = RmL * RnH; temp3 = RmH * RnH; Res2 = 0; Res1 = temp1 + temp2; if (Res1 < temp1) Res2 += 0x00010000; temp1 = (Res1 << 16) & 0xFFFF0000; Res0 = temp0 + temp1; if (Res0 < temp0) Res2++; Res2=Res2+((Res1>>16)&0x0000FFFF)+temp3; if(fnLmL < 0) { Res2=~Res2; if (Res0==0) Res2++; else Res0=(~Res0)+1; } if(sh->regs.SR.part.S == 1) { Res0=sh->regs.MACL+Res0; if (sh->regs.MACL>Res0) Res2++; if (sh->regs.MACH & 0x00008000); else Res2 += sh->regs.MACH | 0xFFFF0000; Res2+=(sh->regs.MACH&0x0000FFFF); if(((s32)Res2<0)&&(Res2<0xFFFF8000)) { Res2=0x00008000; Res0=0x00000000; } if(((s32)Res2>0)&&(Res2>0x00007FFF)) { Res2=0x00007FFF; Res0=0xFFFFFFFF; }; sh->regs.MACH=Res2; sh->regs.MACL=Res0; } else { Res0=sh->regs.MACL+Res0; if (sh->regs.MACL>Res0) Res2++; Res2+=sh->regs.MACH; sh->regs.MACH=Res2; sh->regs.MACL=Res0; } sh->regs.PC+=2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2macw(SH2_struct * sh) { s32 tempm,tempn,dest,src,ans; u32 templ; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); tempn=(s32) MappedMemoryReadWord(sh->regs.R[n]); sh->regs.R[n]+=2; tempm=(s32) MappedMemoryReadWord(sh->regs.R[m]); sh->regs.R[m]+=2; templ=sh->regs.MACL; tempm=((s32)(s16)tempn*(s32)(s16)tempm); if ((s32)sh->regs.MACL>=0) dest=0; else dest=1; if ((s32)tempm>=0) { src=0; tempn=0; } else { src=1; tempn=0xFFFFFFFF; } src+=dest; sh->regs.MACL+=tempm; if ((s32)sh->regs.MACL>=0) ans=0; else ans=1; ans+=dest; if (sh->regs.SR.part.S == 1) { if (ans==1) { if (src==0) sh->regs.MACL=0x7FFFFFFF; if (src==2) sh->regs.MACL=0x80000000; } } else { sh->regs.MACH+=tempn; if (templ>sh->regs.MACL) sh->regs.MACH+=1; } sh->regs.PC+=2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2mov(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)]=sh->regs.R[INSTRUCTION_C(sh->instruction)]; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2mova(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); sh->regs.R[0]=((sh->regs.PC+4)&0xFFFFFFFC)+(disp<<2); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbl(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (s32)(s8)MappedMemoryReadByte(sh->regs.R[m]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbl0(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (s32)(s8)MappedMemoryReadByte(sh->regs.R[m] + sh->regs.R[0]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbl4(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 disp = INSTRUCTION_D(sh->instruction); sh->regs.R[0] = (s32)(s8)MappedMemoryReadByte(sh->regs.R[m] + disp); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movblg(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); sh->regs.R[0] = (s32)(s8)MappedMemoryReadByte(sh->regs.GBR + disp); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbm(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); MappedMemoryWriteByte((sh->regs.R[n] - 1),sh->regs.R[m]); sh->regs.R[n] -= 1; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbp(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (s32)(s8)MappedMemoryReadByte(sh->regs.R[m]); if (n != m) sh->regs.R[m] += 1; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbs(SH2_struct * sh) { int b = INSTRUCTION_B(sh->instruction); int c = INSTRUCTION_C(sh->instruction); MappedMemoryWriteByte(sh->regs.R[b], sh->regs.R[c]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbs0(SH2_struct * sh) { MappedMemoryWriteByte(sh->regs.R[INSTRUCTION_B(sh->instruction)] + sh->regs.R[0], sh->regs.R[INSTRUCTION_C(sh->instruction)]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbs4(SH2_struct * sh) { s32 disp = INSTRUCTION_D(sh->instruction); s32 n = INSTRUCTION_C(sh->instruction); MappedMemoryWriteByte(sh->regs.R[n]+disp,sh->regs.R[0]); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movbsg(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); MappedMemoryWriteByte(sh->regs.GBR + disp,sh->regs.R[0]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movi(SH2_struct * sh) { s32 i = INSTRUCTION_CD(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (s32)(s8)i; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movli(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = MappedMemoryReadLong(((sh->regs.PC + 4) & 0xFFFFFFFC) + (disp << 2)); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movll(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)] = MappedMemoryReadLong(sh->regs.R[INSTRUCTION_C(sh->instruction)]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movll0(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)] = MappedMemoryReadLong(sh->regs.R[INSTRUCTION_C(sh->instruction)] + sh->regs.R[0]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movll4(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 disp = INSTRUCTION_D(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = MappedMemoryReadLong(sh->regs.R[m] + (disp << 2)); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movllg(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); sh->regs.R[0] = MappedMemoryReadLong(sh->regs.GBR + (disp << 2)); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movlm(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); MappedMemoryWriteLong(sh->regs.R[n] - 4,sh->regs.R[m]); sh->regs.R[n] -= 4; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movlp(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = MappedMemoryReadLong(sh->regs.R[m]); if (n != m) sh->regs.R[m] += 4; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movls(SH2_struct * sh) { int b = INSTRUCTION_B(sh->instruction); int c = INSTRUCTION_C(sh->instruction); MappedMemoryWriteLong(sh->regs.R[b], sh->regs.R[c]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movls0(SH2_struct * sh) { MappedMemoryWriteLong(sh->regs.R[INSTRUCTION_B(sh->instruction)] + sh->regs.R[0], sh->regs.R[INSTRUCTION_C(sh->instruction)]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movls4(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 disp = INSTRUCTION_D(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); MappedMemoryWriteLong(sh->regs.R[n]+(disp<<2),sh->regs.R[m]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movlsg(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); MappedMemoryWriteLong(sh->regs.GBR+(disp<<2),sh->regs.R[0]); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movt(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)] = (0x00000001 & sh->regs.SR.all); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movwi(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (s32)(s16)MappedMemoryReadWord(sh->regs.PC + (disp<<1) + 4); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movwl(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (s32)(s16)MappedMemoryReadWord(sh->regs.R[m]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movwl0(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (s32)(s16)MappedMemoryReadWord(sh->regs.R[m]+sh->regs.R[0]); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movwl4(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 disp = INSTRUCTION_D(sh->instruction); sh->regs.R[0] = (s32)(s16)MappedMemoryReadWord(sh->regs.R[m]+(disp<<1)); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movwlg(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); sh->regs.R[0] = (s32)(s16)MappedMemoryReadWord(sh->regs.GBR+(disp<<1)); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movwm(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); MappedMemoryWriteWord(sh->regs.R[n] - 2,sh->regs.R[m]); sh->regs.R[n] -= 2; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movwp(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = (s32)(s16)MappedMemoryReadWord(sh->regs.R[m]); if (n != m) sh->regs.R[m] += 2; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movws(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); MappedMemoryWriteWord(sh->regs.R[n],sh->regs.R[m]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movws0(SH2_struct * sh) { MappedMemoryWriteWord(sh->regs.R[INSTRUCTION_B(sh->instruction)] + sh->regs.R[0], sh->regs.R[INSTRUCTION_C(sh->instruction)]); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movws4(SH2_struct * sh) { s32 disp = INSTRUCTION_D(sh->instruction); s32 n = INSTRUCTION_C(sh->instruction); MappedMemoryWriteWord(sh->regs.R[n]+(disp<<1),sh->regs.R[0]); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2movwsg(SH2_struct * sh) { s32 disp = INSTRUCTION_CD(sh->instruction); MappedMemoryWriteWord(sh->regs.GBR+(disp<<1),sh->regs.R[0]); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2mull(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.MACL = sh->regs.R[n] * sh->regs.R[m]; sh->regs.PC+=2; sh->cycles += 2; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2muls(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.MACL = ((s32)(s16)sh->regs.R[n]*(s32)(s16)sh->regs.R[m]); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2mulu(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.MACL = ((u32)(u16)sh->regs.R[n] * (u32)(u16)sh->regs.R[m]); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2neg(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)]=0-sh->regs.R[INSTRUCTION_C(sh->instruction)]; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2negc(SH2_struct * sh) { u32 temp; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); temp=0-sh->regs.R[m]; sh->regs.R[n] = temp - sh->regs.SR.part.T; if (0 < temp) sh->regs.SR.part.T=1; else sh->regs.SR.part.T=0; if (temp < sh->regs.R[n]) sh->regs.SR.part.T=1; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2nop(SH2_struct * sh) { sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2y_not(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)] = ~sh->regs.R[INSTRUCTION_C(sh->instruction)]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2y_or(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)] |= sh->regs.R[INSTRUCTION_C(sh->instruction)]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2ori(SH2_struct * sh) { sh->regs.R[0] |= INSTRUCTION_CD(sh->instruction); sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2orm(SH2_struct * sh) { s32 temp; s32 source = INSTRUCTION_CD(sh->instruction); temp = (s32) MappedMemoryReadByte(sh->regs.GBR + sh->regs.R[0]); temp |= source; MappedMemoryWriteByte(sh->regs.GBR + sh->regs.R[0],temp); sh->regs.PC += 2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2rotcl(SH2_struct * sh) { s32 temp; s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&0x80000000)==0) temp=0; else temp=1; sh->regs.R[n]<<=1; if (sh->regs.SR.part.T == 1) sh->regs.R[n]|=0x00000001; else sh->regs.R[n]&=0xFFFFFFFE; if (temp==1) sh->regs.SR.part.T=1; else sh->regs.SR.part.T=0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2rotcr(SH2_struct * sh) { s32 temp; s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&0x00000001)==0) temp=0; else temp=1; sh->regs.R[n]>>=1; if (sh->regs.SR.part.T == 1) sh->regs.R[n]|=0x80000000; else sh->regs.R[n]&=0x7FFFFFFF; if (temp==1) sh->regs.SR.part.T=1; else sh->regs.SR.part.T=0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2rotl(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&0x80000000)==0) sh->regs.SR.part.T=0; else sh->regs.SR.part.T=1; sh->regs.R[n]<<=1; if (sh->regs.SR.part.T==1) sh->regs.R[n]|=0x00000001; else sh->regs.R[n]&=0xFFFFFFFE; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2rotr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&0x00000001)==0) sh->regs.SR.part.T = 0; else sh->regs.SR.part.T = 1; sh->regs.R[n]>>=1; if (sh->regs.SR.part.T == 1) sh->regs.R[n]|=0x80000000; else sh->regs.R[n]&=0x7FFFFFFF; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2rte(SH2_struct * sh) { u32 temp; temp=sh->regs.PC; sh->regs.PC = MappedMemoryReadLong(sh->regs.R[15]); sh->regs.R[15] += 4; sh->regs.SR.all = MappedMemoryReadLong(sh->regs.R[15]) & 0x000003F3; sh->regs.R[15] += 4; sh->cycles += 4; SH2delay(sh, temp + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2rts(SH2_struct * sh) { u32 temp; temp = sh->regs.PC; sh->regs.PC = sh->regs.PR; sh->cycles += 2; SH2delay(sh, temp + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2sett(SH2_struct * sh) { sh->regs.SR.part.T = 1; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shal(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n] & 0x80000000) == 0) sh->regs.SR.part.T = 0; else sh->regs.SR.part.T = 1; sh->regs.R[n] <<= 1; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shar(SH2_struct * sh) { s32 temp; s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&0x00000001)==0) sh->regs.SR.part.T = 0; else sh->regs.SR.part.T = 1; if ((sh->regs.R[n]&0x80000000)==0) temp = 0; else temp = 1; sh->regs.R[n] >>= 1; if (temp == 1) sh->regs.R[n] |= 0x80000000; else sh->regs.R[n] &= 0x7FFFFFFF; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shll(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&0x80000000)==0) sh->regs.SR.part.T=0; else sh->regs.SR.part.T=1; sh->regs.R[n]<<=1; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shll2(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)] <<= 2; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shll8(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)]<<=8; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shll16(SH2_struct * sh) { sh->regs.R[INSTRUCTION_B(sh->instruction)]<<=16; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shlr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&0x00000001)==0) sh->regs.SR.part.T=0; else sh->regs.SR.part.T=1; sh->regs.R[n]>>=1; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shlr2(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]>>=2; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shlr8(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]>>=8; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2shlr16(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]>>=16; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stcgbr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]=sh->regs.GBR; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stcmgbr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]-=4; MappedMemoryWriteLong(sh->regs.R[n],sh->regs.GBR); sh->regs.PC+=2; sh->cycles += 2; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stcmsr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]-=4; MappedMemoryWriteLong(sh->regs.R[n],sh->regs.SR.all); sh->regs.PC+=2; sh->cycles += 2; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stcmvbr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]-=4; MappedMemoryWriteLong(sh->regs.R[n],sh->regs.VBR); sh->regs.PC+=2; sh->cycles += 2; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stcsr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = sh->regs.SR.all; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stcvbr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]=sh->regs.VBR; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stsmach(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]=sh->regs.MACH; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stsmacl(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]=sh->regs.MACL; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stsmmach(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] -= 4; MappedMemoryWriteLong(sh->regs.R[n],sh->regs.MACH); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stsmmacl(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] -= 4; MappedMemoryWriteLong(sh->regs.R[n],sh->regs.MACL); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stsmpr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] -= 4; MappedMemoryWriteLong(sh->regs.R[n],sh->regs.PR); sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2stspr(SH2_struct * sh) { s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n] = sh->regs.PR; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2sub(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); sh->regs.R[n]-=sh->regs.R[m]; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2subc(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); u32 tmp0,tmp1; tmp1 = sh->regs.R[n] - sh->regs.R[m]; tmp0 = sh->regs.R[n]; sh->regs.R[n] = tmp1 - sh->regs.SR.part.T; if (tmp0 < tmp1) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; if (tmp1 < sh->regs.R[n]) sh->regs.SR.part.T = 1; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2subv(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); s32 dest,src,ans; if ((s32)sh->regs.R[n]>=0) dest=0; else dest=1; if ((s32)sh->regs.R[m]>=0) src=0; else src=1; src+=dest; sh->regs.R[n]-=sh->regs.R[m]; if ((s32)sh->regs.R[n]>=0) ans=0; else ans=1; ans+=dest; if (src==1) { if (ans==1) sh->regs.SR.part.T=1; else sh->regs.SR.part.T=0; } else sh->regs.SR.part.T=0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2swapb(SH2_struct * sh) { u32 temp0,temp1; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); temp0=sh->regs.R[m]&0xffff0000; temp1=(sh->regs.R[m]&0x000000ff)<<8; sh->regs.R[n]=(sh->regs.R[m]>>8)&0x000000ff; sh->regs.R[n]=sh->regs.R[n]|temp1|temp0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2swapw(SH2_struct * sh) { u32 temp; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); temp=(sh->regs.R[m]>>16)&0x0000FFFF; sh->regs.R[n]=sh->regs.R[m]<<16; sh->regs.R[n]|=temp; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2tas(SH2_struct * sh) { s32 temp; s32 n = INSTRUCTION_B(sh->instruction); temp=(s32) MappedMemoryReadByte(sh->regs.R[n]); if (temp==0) sh->regs.SR.part.T=1; else sh->regs.SR.part.T=0; temp|=0x00000080; MappedMemoryWriteByte(sh->regs.R[n],temp); sh->regs.PC+=2; sh->cycles += 4; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2trapa(SH2_struct * sh) { s32 imm = INSTRUCTION_CD(sh->instruction); sh->regs.R[15]-=4; MappedMemoryWriteLong(sh->regs.R[15],sh->regs.SR.all); sh->regs.R[15]-=4; MappedMemoryWriteLong(sh->regs.R[15],sh->regs.PC + 2); sh->regs.PC = MappedMemoryReadLong(sh->regs.VBR+(imm<<2)); sh->cycles += 8; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2tst(SH2_struct * sh) { s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); if ((sh->regs.R[n]&sh->regs.R[m])==0) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2tsti(SH2_struct * sh) { s32 temp; s32 i = INSTRUCTION_CD(sh->instruction); temp=sh->regs.R[0]&i; if (temp==0) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2tstm(SH2_struct * sh) { s32 temp; s32 i = INSTRUCTION_CD(sh->instruction); temp=(s32) MappedMemoryReadByte(sh->regs.GBR+sh->regs.R[0]); temp&=i; if (temp==0) sh->regs.SR.part.T = 1; else sh->regs.SR.part.T = 0; sh->regs.PC+=2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2y_xor(SH2_struct * sh) { int b = INSTRUCTION_B(sh->instruction); int c = INSTRUCTION_C(sh->instruction); sh->regs.R[b] ^= sh->regs.R[c]; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2xori(SH2_struct * sh) { s32 source = INSTRUCTION_CD(sh->instruction); sh->regs.R[0] ^= source; sh->regs.PC += 2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2xorm(SH2_struct * sh) { s32 source = INSTRUCTION_CD(sh->instruction); s32 temp; temp = (s32) MappedMemoryReadByte(sh->regs.GBR + sh->regs.R[0]); temp ^= source; MappedMemoryWriteByte(sh->regs.GBR + sh->regs.R[0],temp); sh->regs.PC += 2; sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2xtrct(SH2_struct * sh) { u32 temp; s32 m = INSTRUCTION_C(sh->instruction); s32 n = INSTRUCTION_B(sh->instruction); temp=(sh->regs.R[m]<<16)&0xFFFF0000; sh->regs.R[n]=(sh->regs.R[n]>>16)&0x0000FFFF; sh->regs.R[n]|=temp; sh->regs.PC+=2; sh->cycles++; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2sleep(SH2_struct * sh) { sh->cycles += 3; } ////////////////////////////////////////////////////////////////////////////// static opcodefunc decode(u16 instruction) { switch (INSTRUCTION_A(instruction)) { case 0: switch (INSTRUCTION_D(instruction)) { case 2: switch (INSTRUCTION_C(instruction)) { case 0: return &SH2stcsr; case 1: return &SH2stcgbr; case 2: return &SH2stcvbr; default: return &SH2undecoded; } case 3: switch (INSTRUCTION_C(instruction)) { case 0: return &SH2bsrf; case 2: return &SH2braf; default: return &SH2undecoded; } case 4: return &SH2movbs0; case 5: return &SH2movws0; case 6: return &SH2movls0; case 7: return &SH2mull; case 8: switch (INSTRUCTION_C(instruction)) { case 0: return &SH2clrt; case 1: return &SH2sett; case 2: return &SH2clrmac; default: return &SH2undecoded; } case 9: switch (INSTRUCTION_C(instruction)) { case 0: return &SH2nop; case 1: return &SH2div0u; case 2: return &SH2movt; default: return &SH2undecoded; } case 10: switch (INSTRUCTION_C(instruction)) { case 0: return &SH2stsmach; case 1: return &SH2stsmacl; case 2: return &SH2stspr; default: return &SH2undecoded; } case 11: switch (INSTRUCTION_C(instruction)) { case 0: return &SH2rts; case 1: return &SH2sleep; case 2: return &SH2rte; default: return &SH2undecoded; } case 12: return &SH2movbl0; case 13: return &SH2movwl0; case 14: return &SH2movll0; case 15: return &SH2macl; default: return &SH2undecoded; } case 1: return &SH2movls4; case 2: switch (INSTRUCTION_D(instruction)) { case 0: return &SH2movbs; case 1: return &SH2movws; case 2: return &SH2movls; case 4: return &SH2movbm; case 5: return &SH2movwm; case 6: return &SH2movlm; case 7: return &SH2div0s; case 8: return &SH2tst; case 9: return &SH2y_and; case 10: return &SH2y_xor; case 11: return &SH2y_or; case 12: return &SH2cmpstr; case 13: return &SH2xtrct; case 14: return &SH2mulu; case 15: return &SH2muls; default: return &SH2undecoded; } case 3: switch(INSTRUCTION_D(instruction)) { case 0: return &SH2cmpeq; case 2: return &SH2cmphs; case 3: return &SH2cmpge; case 4: return &SH2div1; case 5: return &SH2dmulu; case 6: return &SH2cmphi; case 7: return &SH2cmpgt; case 8: return &SH2sub; case 10: return &SH2subc; case 11: return &SH2subv; case 12: return &SH2add; case 13: return &SH2dmuls; case 14: return &SH2addc; case 15: return &SH2addv; default: return &SH2undecoded; } case 4: switch(INSTRUCTION_D(instruction)) { case 0: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2shll; case 1: return &SH2dt; case 2: return &SH2shal; default: return &SH2undecoded; } case 1: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2shlr; case 1: return &SH2cmppz; case 2: return &SH2shar; default: return &SH2undecoded; } case 2: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2stsmmach; case 1: return &SH2stsmmacl; case 2: return &SH2stsmpr; default: return &SH2undecoded; } case 3: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2stcmsr; case 1: return &SH2stcmgbr; case 2: return &SH2stcmvbr; default: return &SH2undecoded; } case 4: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2rotl; case 2: return &SH2rotcl; default: return &SH2undecoded; } case 5: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2rotr; case 1: return &SH2cmppl; case 2: return &SH2rotcr; default: return &SH2undecoded; } case 6: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2ldsmmach; case 1: return &SH2ldsmmacl; case 2: return &SH2ldsmpr; default: return &SH2undecoded; } case 7: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2ldcmsr; case 1: return &SH2ldcmgbr; case 2: return &SH2ldcmvbr; default: return &SH2undecoded; } case 8: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2shll2; case 1: return &SH2shll8; case 2: return &SH2shll16; default: return &SH2undecoded; } case 9: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2shlr2; case 1: return &SH2shlr8; case 2: return &SH2shlr16; default: return &SH2undecoded; } case 10: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2ldsmach; case 1: return &SH2ldsmacl; case 2: return &SH2ldspr; default: return &SH2undecoded; } case 11: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2jsr; case 1: return &SH2tas; case 2: return &SH2jmp; default: return &SH2undecoded; } case 14: switch(INSTRUCTION_C(instruction)) { case 0: return &SH2ldcsr; case 1: return &SH2ldcgbr; case 2: return &SH2ldcvbr; default: return &SH2undecoded; } case 15: return &SH2macw; default: return &SH2undecoded; } case 5: return &SH2movll4; case 6: switch (INSTRUCTION_D(instruction)) { case 0: return &SH2movbl; case 1: return &SH2movwl; case 2: return &SH2movll; case 3: return &SH2mov; case 4: return &SH2movbp; case 5: return &SH2movwp; case 6: return &SH2movlp; case 7: return &SH2y_not; case 8: return &SH2swapb; case 9: return &SH2swapw; case 10: return &SH2negc; case 11: return &SH2neg; case 12: return &SH2extub; case 13: return &SH2extuw; case 14: return &SH2extsb; case 15: return &SH2extsw; } case 7: return &SH2addi; case 8: switch (INSTRUCTION_B(instruction)) { case 0: return &SH2movbs4; case 1: return &SH2movws4; case 4: return &SH2movbl4; case 5: return &SH2movwl4; case 8: return &SH2cmpim; case 9: return &SH2bt; case 11: return &SH2bf; case 13: return &SH2bts; case 15: return &SH2bfs; default: return &SH2undecoded; } case 9: return &SH2movwi; case 10: return &SH2bra; case 11: return &SH2bsr; case 12: switch(INSTRUCTION_B(instruction)) { case 0: return &SH2movbsg; case 1: return &SH2movwsg; case 2: return &SH2movlsg; case 3: return &SH2trapa; case 4: return &SH2movblg; case 5: return &SH2movwlg; case 6: return &SH2movllg; case 7: return &SH2mova; case 8: return &SH2tsti; case 9: return &SH2andi; case 10: return &SH2xori; case 11: return &SH2ori; case 12: return &SH2tstm; case 13: return &SH2andm; case 14: return &SH2xorm; case 15: return &SH2orm; } case 13: return &SH2movli; case 14: return &SH2movi; default: return &SH2undecoded; } } ////////////////////////////////////////////////////////////////////////////// int SH2InterpreterInit() { int i; // Initialize any internal variables for(i = 0;i < 0x10000;i++) opcodes[i] = decode(i); for (i = 0; i < 0x100; i++) { switch (i) { case 0x000: // Bios fetchlist[i] = FetchBios; break; case 0x002: // Low Work Ram fetchlist[i] = FetchLWram; break; case 0x020: // CS0 fetchlist[i] = FetchCs0; break; case 0x060: // High Work Ram case 0x061: case 0x062: case 0x063: case 0x064: case 0x065: case 0x066: case 0x067: case 0x068: case 0x069: case 0x06A: case 0x06B: case 0x06C: case 0x06D: case 0x06E: case 0x06F: fetchlist[i] = FetchHWram; break; default: fetchlist[i] = FetchInvalid; break; } } SH2ClearCodeBreakpoints(MSH2); SH2ClearCodeBreakpoints(SSH2); SH2ClearMemoryBreakpoints(MSH2); SH2ClearMemoryBreakpoints(SSH2); MSH2->breakpointEnabled = 0; SSH2->breakpointEnabled = 0; MSH2->backtraceEnabled = 0; SSH2->backtraceEnabled = 0; MSH2->stepOverOut.enabled = 0; SSH2->stepOverOut.enabled = 0; return 0; } int SH2DebugInterpreterInit() { SH2InterpreterInit(); MSH2->breakpointEnabled = 1; SSH2->breakpointEnabled = 1; MSH2->backtraceEnabled = 1; SSH2->backtraceEnabled = 1; return 0; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterDeInit() { // DeInitialize any internal variables here } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterReset(UNUSED SH2_struct *context) { // Reset any internal variables here context->stepOverOut.enabled = 0; context->stepOverOut.enabled = 0; } ////////////////////////////////////////////////////////////////////////////// static INLINE void SH2UBCInterrupt(SH2_struct *context, u32 flag) { if (15 > context->regs.SR.part.I) // Since UBC's interrupt are always level 15 { context->regs.R[15] -= 4; MappedMemoryWriteLong(context->regs.R[15], context->regs.SR.all); context->regs.R[15] -= 4; MappedMemoryWriteLong(context->regs.R[15], context->regs.PC); context->regs.SR.part.I = 15; context->regs.PC = MappedMemoryReadLong(context->regs.VBR + (12 << 2)); LOG("interrupt successfully handled\n"); } context->onchip.BRCR |= flag; } ////////////////////////////////////////////////////////////////////////////// static INLINE void SH2HandleInterrupts(SH2_struct *context) { if (context->NumberOfInterrupts != 0) { if (context->interrupts[context->NumberOfInterrupts-1].level > context->regs.SR.part.I) { context->regs.R[15] -= 4; MappedMemoryWriteLong(context->regs.R[15], context->regs.SR.all); context->regs.R[15] -= 4; MappedMemoryWriteLong(context->regs.R[15], context->regs.PC); context->regs.SR.part.I = context->interrupts[context->NumberOfInterrupts-1].level; context->regs.PC = MappedMemoryReadLong(context->regs.VBR + (context->interrupts[context->NumberOfInterrupts-1].vector << 2)); context->NumberOfInterrupts--; context->isIdle = 0; context->isSleeping = 0; } } } ////////////////////////////////////////////////////////////////////////////// FASTCALL void SH2DebugInterpreterExec(SH2_struct *context, u32 cycles) { #ifdef SH2_TRACE /* Avoid accumulating leftover cycles multiple times, since the trace * code automatically adds state->cycles to the cycle accumulator when * printing a trace line */ sh2_trace_add_cycles(-(context->cycles)); #endif SH2HandleInterrupts(context); while(context->cycles < cycles) { #ifdef EMULATEUBC int ubcinterrupt=0, ubcflag=0; #endif SH2HandleBreakpoints(context); #ifdef SH2_TRACE sh2_trace(context, context->regs.PC); #endif #ifdef EMULATEUBC if (context->onchip.BBRA & (BBR_CPA_CPU | BBR_IDA_INST | BBR_RWA_READ)) // Break on cpu, instruction, read cycles { if (context->onchip.BARA.all == (context->regs.PC & (~context->onchip.BAMRA.all))) { LOG("Trigger UBC A interrupt: PC = %08X\n", context->regs.PC); if (!(context->onchip.BRCR & BRCR_PCBA)) { // Break before instruction fetch SH2UBCInterrupt(context, BRCR_CMFCA); } else { // Break after instruction fetch ubcinterrupt=1; ubcflag = BRCR_CMFCA; } } } else if(context->onchip.BBRB & (BBR_CPA_CPU | BBR_IDA_INST | BBR_RWA_READ)) // Break on cpu, instruction, read cycles { if (context->onchip.BARB.all == (context->regs.PC & (~context->onchip.BAMRB.all))) { LOG("Trigger UBC B interrupt: PC = %08X\n", context->regs.PC); if (!(context->onchip.BRCR & BRCR_PCBB)) { // Break before instruction fetch SH2UBCInterrupt(context, BRCR_CMFCB); } else { // Break after instruction fetch ubcinterrupt=1; ubcflag = BRCR_CMFCB; } } } #endif // Fetch Instruction #ifdef EXEC_FROM_CACHE if ((context->regs.PC & 0xC0000000) == 0xC0000000) context->instruction = DataArrayReadWord(context->regs.PC); else #endif context->instruction = fetchlist[(context->regs.PC >> 20) & 0x0FF](context->regs.PC); SH2HandleBackTrace(context); SH2HandleStepOverOut(context); SH2HandleTrackInfLoop(context); // Execute it opcodes[context->instruction](context); #ifdef EMULATEUBC if (ubcinterrupt) SH2UBCInterrupt(context, ubcflag); #endif } #ifdef SH2_TRACE sh2_trace_add_cycles(context->cycles); #endif } ////////////////////////////////////////////////////////////////////////////// FASTCALL void SH2InterpreterExec(SH2_struct *context, u32 cycles) { SH2HandleInterrupts(context); if (context->isIdle) SH2idleParse(context, cycles); else SH2idleCheck(context, cycles); while(context->cycles < cycles) { // Fetch Instruction context->instruction = fetchlist[(context->regs.PC >> 20) & 0x0FF](context->regs.PC); // Execute it opcodes[context->instruction](context); } } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterGetRegisters(SH2_struct *context, sh2regs_struct *regs) { memcpy(regs, &context->regs, sizeof(sh2regs_struct)); } ////////////////////////////////////////////////////////////////////////////// u32 SH2InterpreterGetGPR(SH2_struct *context, int num) { return context->regs.R[num]; } ////////////////////////////////////////////////////////////////////////////// u32 SH2InterpreterGetSR(SH2_struct *context) { return context->regs.SR.all; } ////////////////////////////////////////////////////////////////////////////// u32 SH2InterpreterGetGBR(SH2_struct *context) { return context->regs.GBR; } ////////////////////////////////////////////////////////////////////////////// u32 SH2InterpreterGetVBR(SH2_struct *context) { return context->regs.VBR; } ////////////////////////////////////////////////////////////////////////////// u32 SH2InterpreterGetMACH(SH2_struct *context) { return context->regs.MACH; } ////////////////////////////////////////////////////////////////////////////// u32 SH2InterpreterGetMACL(SH2_struct *context) { return context->regs.MACL; } ////////////////////////////////////////////////////////////////////////////// u32 SH2InterpreterGetPR(SH2_struct *context) { return context->regs.PR; } ////////////////////////////////////////////////////////////////////////////// u32 SH2InterpreterGetPC(SH2_struct *context) { return context->regs.PC; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetRegisters(SH2_struct *context, const sh2regs_struct *regs) { memcpy(&context->regs, regs, sizeof(sh2regs_struct)); } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetGPR(SH2_struct *context, int num, u32 value) { context->regs.R[num] = value; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetSR(SH2_struct *context, u32 value) { context->regs.SR.all = value; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetGBR(SH2_struct *context, u32 value) { context->regs.GBR = value; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetVBR(SH2_struct *context, u32 value) { context->regs.VBR = value; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetMACH(SH2_struct *context, u32 value) { context->regs.MACH = value; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetMACL(SH2_struct *context, u32 value) { context->regs.MACL = value; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetPR(SH2_struct *context, u32 value) { context->regs.PR = value; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetPC(SH2_struct *context, u32 value) { context->regs.PC = value; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSendInterrupt(SH2_struct *context, u8 vector, u8 level) { u32 i, i2; interrupt_struct tmp; // Make sure interrupt doesn't already exist for (i = 0; i < context->NumberOfInterrupts; i++) { if (context->interrupts[i].vector == vector) return; } context->interrupts[context->NumberOfInterrupts].level = level; context->interrupts[context->NumberOfInterrupts].vector = vector; context->NumberOfInterrupts++; // Sort interrupts for (i = 0; i < (context->NumberOfInterrupts-1); i++) { for (i2 = i+1; i2 < context->NumberOfInterrupts; i2++) { if (context->interrupts[i].level > context->interrupts[i2].level) { tmp.level = context->interrupts[i].level; tmp.vector = context->interrupts[i].vector; context->interrupts[i].level = context->interrupts[i2].level; context->interrupts[i].vector = context->interrupts[i2].vector; context->interrupts[i2].level = tmp.level; context->interrupts[i2].vector = tmp.vector; } } } } ////////////////////////////////////////////////////////////////////////////// int SH2InterpreterGetInterrupts(SH2_struct *context, interrupt_struct interrupts[MAX_INTERRUPTS]) { memcpy(interrupts, context->interrupts, sizeof(interrupt_struct) * MAX_INTERRUPTS); return context->NumberOfInterrupts; } ////////////////////////////////////////////////////////////////////////////// void SH2InterpreterSetInterrupts(SH2_struct *context, int num_interrupts, const interrupt_struct interrupts[MAX_INTERRUPTS]) { memcpy(context->interrupts, interrupts, sizeof(interrupt_struct) * MAX_INTERRUPTS); context->NumberOfInterrupts = num_interrupts; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/thr-windows.c000644 001750 001750 00000006655 12256006161 020641 0ustar00guillaumeguillaume000000 000000 /* src/thr-windows.c: Windows thread functions Copyright 2013 Theo Berkau. Based on code by Andrew Church and Lawrence Sebald. This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define _WIN32_WINNT 0x600 #include #include "core.h" #include "threads.h" struct thd_s { int running; HANDLE thd; void (*func)(void *); void *arg; CRITICAL_SECTION mutex; CONDITION_VARIABLE cond; }; static struct thd_s thread_handle[YAB_NUM_THREADS]; static int hnd_key; static int hnd_key_once=FALSE; ////////////////////////////////////////////////////////////////////////////// static DWORD wrapper(void *hnd) { struct thd_s *hnds = (struct thd_s *)hnd; EnterCriticalSection(&hnds->mutex); /* Set the handle for the thread, and call the actual thread function. */ TlsSetValue(hnd_key, hnd); hnds->func(hnds->arg); LeaveCriticalSection(&hnds->mutex); return 0; } int YabThreadStart(unsigned int id, void (*func)(void *), void *arg) { if (!hnd_key_once) { hnd_key=TlsAlloc(); hnd_key_once = 1; } if (thread_handle[id].running) { fprintf(stderr, "YabThreadStart: thread %u is already started!\n", id); return -1; } // Create CS and condition variable for thread InitializeCriticalSection(&thread_handle[id].mutex); InitializeConditionVariable(&thread_handle[id].cond); thread_handle[id].func = func; thread_handle[id].arg = arg; if ((thread_handle[id].thd = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)wrapper, &thread_handle[id], 0, NULL)) == NULL) { perror("CreateThread"); return -1; } thread_handle[id].running = 1; return 0; } void YabThreadWait(unsigned int id) { if (!thread_handle[id].thd) return; // Thread wasn't running in the first place WaitForSingleObject(thread_handle[id].thd,INFINITE); CloseHandle(thread_handle[id].thd); thread_handle[id].thd = NULL; thread_handle[id].running = 0; } void YabThreadYield(void) { SwitchToThread(); } void YabThreadSleep(void) { struct thd_s *thd = (struct thd_s *)TlsGetValue(hnd_key); SleepConditionVariableCS(&thd->cond, &thd->mutex, INFINITE); } void YabThreadRemoteSleep(unsigned int id) { if (!thread_handle[id].thd) return; // Thread wasn't running in the first place SleepConditionVariableCS(&thread_handle[id].cond, &thread_handle[id].mutex, INFINITE); } void YabThreadWake(unsigned int id) { if (!thread_handle[id].thd) return; // Thread wasn't running in the first place WakeConditionVariable(&thread_handle[id].cond); } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/coffelf.h000644 001750 001750 00000001631 12256006106 017751 0ustar00guillaumeguillaume000000 000000 /* Copyright 2007 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COFFELF_H #define COFFELF_H int MappedMemoryLoadCoff(const char *filename); int MappedMemoryLoadElf(const char *filename); #endif yabause-0.9.13.1/src/cd-netbsd.c000644 001750 001750 00000011301 12256006161 020177 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004-2005 Theo Berkau Copyright 2004-2005 Guillaume Duhamel Copyright 2005 Joost Peters This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "cdbase.h" #include "debug.h" static int NetBSDCDInit(const char *); static void NetBSDCDDeInit(void); static s32 NetBSDCDReadTOC(u32 *); static int NetBSDCDGetStatus(void); static int NetBSDCDReadSectorFAD(u32, void *); static void NetBSDCDReadAheadFAD(u32); CDInterface ArchCD = { CDCORE_ARCH, "NetBSD CD Drive", NetBSDCDInit, NetBSDCDDeInit, NetBSDCDGetStatus, NetBSDCDReadTOC, NetBSDCDReadSectorFAD, NetBSDCDReadAheadFAD, }; static int hCDROM; static int NetBSDCDInit(const char * cdrom_name) { if ((hCDROM = open(cdrom_name, O_RDONLY | O_NONBLOCK)) == -1) { LOG("CDInit (%s) failed\n", cdrom_name); return -1; } LOG("CDInit (%s) OK\n", cdrom_name); return 0; } static void NetBSDCDDeInit(void) { if (hCDROM != -1) { close(hCDROM); } LOG("CDDeInit OK\n"); } static s32 NetBSDCDReadTOC(u32 * TOC) { int success; struct ioc_toc_header ctTOC; struct ioc_read_toc_entry ctTOCent; struct cd_toc_entry data; int i, j; int add150 = 0; ctTOCent.address_format = CD_LBA_FORMAT; ctTOCent.data_len = sizeof (struct cd_toc_entry); ctTOCent.data = &data; if (hCDROM != -1) { memset(TOC, 0xFF, 0xCC * 2); memset(&ctTOC, 0xFF, sizeof(struct ioc_toc_header)); if (ioctl(hCDROM, CDIOREADTOCHEADER, &ctTOC) == -1) { return 0; } ctTOCent.starting_track = ctTOC.starting_track; ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); if (ctTOCent.data->addr.lba == 0) add150 = 150; TOC[0] = ((ctTOCent.data->control << 28) | (ctTOCent.data->addr_type << 24) | ctTOCent.data->addr.lba + add150); // convert TOC to saturn format for (i = ctTOC.starting_track + 1; i <= ctTOC.ending_track; i++) { ctTOCent.starting_track = i; ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); TOC[i - 1] = (ctTOCent.data->control << 28) | (ctTOCent.data->addr_type << 24) | (ctTOCent.data->addr.lba + add150); } // Do First, Last, and Lead out sections here ctTOCent.starting_track = ctTOC.starting_track; ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); TOC[99] = (ctTOCent.data->control << 28) | (ctTOCent.data->addr_type << 24) | (ctTOC.starting_track << 16); ctTOCent.starting_track = ctTOC.ending_track; ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); TOC[100] = (ctTOCent.data->control << 28) | (ctTOCent.data->addr_type << 24) | (ctTOC.starting_track << 16); ctTOCent.starting_track = 0xAA; ioctl(hCDROM, CDIOREADTOCENTRYS, &ctTOCent); TOC[101] = (ctTOCent.data->control << 28) | (ctTOCent.data->addr_type << 24) | (ctTOCent.data->addr.lba + add150); return (0xCC * 2); } return 0; } static int NetBSDCDGetStatus(void) { // 0 - CD Present, disc spinning // 1 - CD Present, disc not spinning // 2 - CD not present // 3 - Tray open // see ../windows/cd.cc for more info return 0; } static int NetBSDCDReadSectorFAD(u32 FAD, void *buffer) { static const s8 syncHdr[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 }; if (hCDROM != -1) { memcpy(buffer, syncHdr, sizeof (syncHdr)); lseek(hCDROM, (FAD - 150) * 2048, SEEK_SET); read(hCDROM, (char *)buffer + 0x10, 2048); return 1; } return 0; } static void NetBSDCDReadAheadFAD(UNUSED u32 FAD) { // No-op } yabause-0.9.13.1/src/vidsoft.c000644 001750 001750 00000302712 12256006174 020027 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2004 Guillaume Duhamel Copyright 2004-2008 Theo Berkau Copyright 2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "vidsoft.h" #include "ygl.h" #include "vidshared.h" #include "debug.h" #include "vdp2.h" #include "titan/titan.h" #ifdef HAVE_LIBGL #define USE_OPENGL #endif #ifdef USE_OPENGL #include "ygl.h" #endif #include "yui.h" #include #include #if defined WORDS_BIGENDIAN static INLINE u32 COLSAT2YAB16(int priority,u32 temp) { return (priority | (temp & 0x7C00) << 1 | (temp & 0x3E0) << 14 | (temp & 0x1F) << 27); } static INLINE u32 COLSAT2YAB32(int priority,u32 temp) { return (((temp & 0xFF) << 24) | ((temp & 0xFF00) << 8) | ((temp & 0xFF0000) >> 8) | priority); } static INLINE u32 COLSAT2YAB32_2(int priority,u32 temp1,u32 temp2) { return (((temp2 & 0xFF) << 24) | ((temp2 & 0xFF00) << 8) | ((temp1 & 0xFF) << 8) | priority); } static INLINE u32 COLSATSTRIPPRIORITY(u32 pixel) { return (pixel | 0xFF); } #else static INLINE u32 COLSAT2YAB16(int priority,u32 temp) { return (priority << 24 | (temp & 0x1F) << 3 | (temp & 0x3E0) << 6 | (temp & 0x7C00) << 9); } static INLINE u32 COLSAT2YAB32(int priority, u32 temp) { return (priority << 24 | (temp & 0xFF0000) | (temp & 0xFF00) | (temp & 0xFF)); } static INLINE u32 COLSAT2YAB32_2(int priority,u32 temp1,u32 temp2) { return (priority << 24 | ((temp1 & 0xFF) << 16) | (temp2 & 0xFF00) | (temp2 & 0xFF)); } static INLINE u32 COLSATSTRIPPRIORITY(u32 pixel) { return (0xFF000000 | pixel); } #endif #define COLOR_ADDt(b) (b>0xFF?0xFF:(b<0?0:b)) #define COLOR_ADDb(b1,b2) COLOR_ADDt((signed) (b1) + (b2)) #ifdef WORDS_BIGENDIAN #define COLOR_ADD(l,r,g,b) (l & 0xFF) | \ (COLOR_ADDb((l >> 8) & 0xFF, b) << 8) | \ (COLOR_ADDb((l >> 16) & 0xFF, g) << 16) | \ (COLOR_ADDb((l >> 24), r) << 24) #else #define COLOR_ADD(l,r,g,b) COLOR_ADDb((l & 0xFF), r) | \ (COLOR_ADDb((l >> 8) & 0xFF, g) << 8) | \ (COLOR_ADDb((l >> 16) & 0xFF, b) << 16) | \ (l & 0xFF000000) #endif static void PushUserClipping(int mode); static void PopUserClipping(void); int VIDSoftInit(void); void VIDSoftDeInit(void); void VIDSoftResize(unsigned int, unsigned int, int); int VIDSoftIsFullscreen(void); int VIDSoftVdp1Reset(void); void VIDSoftVdp1DrawStart(void); void VIDSoftVdp1DrawEnd(void); void VIDSoftVdp1NormalSpriteDraw(void); void VIDSoftVdp1ScaledSpriteDraw(void); void VIDSoftVdp1DistortedSpriteDraw(void); void VIDSoftVdp1PolygonDraw(void); void VIDSoftVdp1PolylineDraw(void); void VIDSoftVdp1LineDraw(void); void VIDSoftVdp1UserClipping(void); void VIDSoftVdp1SystemClipping(void); void VIDSoftVdp1LocalCoordinate(void); int VIDSoftVdp2Reset(void); void VIDSoftVdp2DrawStart(void); void VIDSoftVdp2DrawEnd(void); void VIDSoftVdp2DrawScreens(void); void VIDSoftVdp2SetResolution(u16 TVMD); void FASTCALL VIDSoftVdp2SetPriorityNBG0(int priority); void FASTCALL VIDSoftVdp2SetPriorityNBG1(int priority); void FASTCALL VIDSoftVdp2SetPriorityNBG2(int priority); void FASTCALL VIDSoftVdp2SetPriorityNBG3(int priority); void FASTCALL VIDSoftVdp2SetPriorityRBG0(int priority); void VIDSoftGetGlSize(int *width, int *height); void VIDSoftVdp1SwapFrameBuffer(void); void VIDSoftVdp1EraseFrameBuffer(void); VideoInterface_struct VIDSoft = { VIDCORE_SOFT, "Software Video Interface", VIDSoftInit, VIDSoftDeInit, VIDSoftResize, VIDSoftIsFullscreen, VIDSoftVdp1Reset, VIDSoftVdp1DrawStart, VIDSoftVdp1DrawEnd, VIDSoftVdp1NormalSpriteDraw, VIDSoftVdp1ScaledSpriteDraw, VIDSoftVdp1DistortedSpriteDraw, //for the actual hardware, polygons are essentially identical to distorted sprites //the actual hardware draws using diagonal lines, which is why using half-transparent processing //on distorted sprites and polygons is not recommended since the hardware overdraws to prevent gaps //thus, with half-transparent processing some pixels will be processed more than once, producing moire patterns in the drawn shapes VIDSoftVdp1DistortedSpriteDraw, VIDSoftVdp1PolylineDraw, VIDSoftVdp1LineDraw, VIDSoftVdp1UserClipping, VIDSoftVdp1SystemClipping, VIDSoftVdp1LocalCoordinate, VIDSoftVdp2Reset, VIDSoftVdp2DrawStart, VIDSoftVdp2DrawEnd, VIDSoftVdp2DrawScreens, VIDSoftGetGlSize, }; pixel_t *dispbuffer=NULL; u8 *vdp1framebuffer[2]= { NULL, NULL }; u8 *vdp1frontframebuffer; u8 *vdp1backframebuffer; static int vdp1width; static int vdp1height; static int vdp1interlace; static int vdp1clipxstart; static int vdp1clipxend; static int vdp1clipystart; static int vdp1clipyend; static int vdp1pixelsize; static int vdp1spritetype; int vdp2width; int vdp2height; static int nbg0priority=0; static int nbg1priority=0; static int nbg2priority=0; static int nbg3priority=0; static int rbg0priority=0; #ifdef USE_OPENGL static int outputwidth; static int outputheight; #endif static int resxratio; static int resyratio; typedef struct { s16 x; s16 y; } vdp1vertex; typedef struct { int pagepixelwh, pagepixelwh_bits, pagepixelwh_mask; int planepixelwidth, planepixelwidth_bits, planepixelwidth_mask; int planepixelheight, planepixelheight_bits, planepixelheight_mask; int screenwidth; int screenheight; int oldcellx, oldcelly, oldcellcheck; int xmask, ymask; u32 planetbl[16]; } screeninfo_struct; ////////////////////////////////////////////////////////////////////////////// static INLINE u32 FASTCALL Vdp2ColorRamGetColor(u32 addr) { switch(Vdp2Internal.ColorMode) { case 0: { u32 tmp; addr <<= 1; tmp = T2ReadWord(Vdp2ColorRam, addr & 0xFFF); /* we preserve MSB for special color calculation mode 3 (see Vdp2 user's manual 3.4 and 12.3) */ return (((tmp & 0x1F) << 3) | ((tmp & 0x03E0) << 6) | ((tmp & 0x7C00) << 9)) | ((tmp & 0x8000) << 16); } case 1: { u32 tmp; addr <<= 1; tmp = T2ReadWord(Vdp2ColorRam, addr & 0xFFF); /* we preserve MSB for special color calculation mode 3 (see Vdp2 user's manual 3.4 and 12.3) */ return (((tmp & 0x1F) << 3) | ((tmp & 0x03E0) << 6) | ((tmp & 0x7C00) << 9)) | ((tmp & 0x8000) << 16); } case 2: { addr <<= 2; return T2ReadLong(Vdp2ColorRam, addr & 0xFFF); } default: break; } return 0; } ////////////////////////////////////////////////////////////////////////////// static INLINE void Vdp2PatternAddr(vdp2draw_struct *info) { switch(info->patterndatasize) { case 1: { u16 tmp = T1ReadWord(Vdp2Ram, info->addr); info->addr += 2; info->specialfunction = (info->supplementdata >> 9) & 0x1; info->specialcolorfunction = (info->supplementdata >> 8) & 0x1; switch(info->colornumber) { case 0: // in 16 colors info->paladdr = ((tmp & 0xF000) >> 8) | ((info->supplementdata & 0xE0) << 3); break; default: // not in 16 colors info->paladdr = (tmp & 0x7000) >> 4; break; } switch(info->auxmode) { case 0: info->flipfunction = (tmp & 0xC00) >> 10; switch(info->patternwh) { case 1: info->charaddr = (tmp & 0x3FF) | ((info->supplementdata & 0x1F) << 10); break; case 2: info->charaddr = ((tmp & 0x3FF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x1C) << 10); break; } break; case 1: info->flipfunction = 0; switch(info->patternwh) { case 1: info->charaddr = (tmp & 0xFFF) | ((info->supplementdata & 0x1C) << 10); break; case 2: info->charaddr = ((tmp & 0xFFF) << 2) | (info->supplementdata & 0x3) | ((info->supplementdata & 0x10) << 10); break; } break; } break; } case 2: { u16 tmp1 = T1ReadWord(Vdp2Ram, info->addr); u16 tmp2 = T1ReadWord(Vdp2Ram, info->addr+2); info->addr += 4; info->charaddr = tmp2 & 0x7FFF; info->flipfunction = (tmp1 & 0xC000) >> 14; switch(info->colornumber) { case 0: info->paladdr = (tmp1 & 0x7F) << 4; break; default: info->paladdr = ((tmp1 & 0x70) << 4); break; } info->specialfunction = (tmp1 & 0x2000) >> 13; info->specialcolorfunction = (tmp1 & 0x1000) >> 12; break; } } if (!(Vdp2Regs->VRSIZE & 0x8000)) info->charaddr &= 0x3FFF; info->charaddr *= 0x20; // selon Runik if (info->specialprimode == 1) { info->priority = (info->priority & 0xE) | (info->specialfunction & 1); } } ////////////////////////////////////////////////////////////////////////////// static INLINE u32 FASTCALL DoNothing(UNUSED void *info, u32 pixel) { return pixel; } ////////////////////////////////////////////////////////////////////////////// static INLINE u32 FASTCALL DoColorOffset(void *info, u32 pixel) { return COLOR_ADD(pixel, ((vdp2draw_struct *)info)->cor, ((vdp2draw_struct *)info)->cog, ((vdp2draw_struct *)info)->cob); } ////////////////////////////////////////////////////////////////////////////// static INLINE void ReadVdp2ColorOffset(Vdp2 * regs, vdp2draw_struct *info, int clofmask, int ccmask) { if (regs->CLOFEN & clofmask) { // color offset enable if (regs->CLOFSL & clofmask) { // color offset B info->cor = regs->COBR & 0xFF; if (regs->COBR & 0x100) info->cor |= 0xFFFFFF00; info->cog = regs->COBG & 0xFF; if (regs->COBG & 0x100) info->cog |= 0xFFFFFF00; info->cob = regs->COBB & 0xFF; if (regs->COBB & 0x100) info->cob |= 0xFFFFFF00; } else { // color offset A info->cor = regs->COAR & 0xFF; if (regs->COAR & 0x100) info->cor |= 0xFFFFFF00; info->cog = regs->COAG & 0xFF; if (regs->COAG & 0x100) info->cog |= 0xFFFFFF00; info->cob = regs->COAB & 0xFF; if (regs->COAB & 0x100) info->cob |= 0xFFFFFF00; } info->PostPixelFetchCalc = &DoColorOffset; } else // color offset disable info->PostPixelFetchCalc = &DoNothing; } ////////////////////////////////////////////////////////////////////////////// static INLINE int Vdp2FetchPixel(vdp2draw_struct *info, int x, int y, u32 *color) { u32 dot; switch(info->colornumber) { case 0: // 4 BPP dot = T1ReadByte(Vdp2Ram, ((info->charaddr + ((y * info->cellw) + x) / 2) & 0x7FFFF)); if (!(x & 0x1)) dot >>= 4; if (!(dot & 0xF) && info->transparencyenable) return 0; else { *color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | (dot & 0xF))); return 1; } case 1: // 8 BPP dot = T1ReadByte(Vdp2Ram, ((info->charaddr + (y * info->cellw) + x) & 0x7FFFF)); if (!(dot & 0xFF) && info->transparencyenable) return 0; else { *color = Vdp2ColorRamGetColor(info->coloroffset + (info->paladdr | (dot & 0xFF))); return 1; } case 2: // 16 BPP(palette) dot = T1ReadWord(Vdp2Ram, ((info->charaddr + ((y * info->cellw) + x) * 2) & 0x7FFFF)); if ((dot == 0) && info->transparencyenable) return 0; else { *color = Vdp2ColorRamGetColor(info->coloroffset + dot); return 1; } case 3: // 16 BPP(RGB) dot = T1ReadWord(Vdp2Ram, ((info->charaddr + ((y * info->cellw) + x) * 2) & 0x7FFFF)); if (!(dot & 0x8000) && info->transparencyenable) return 0; else { *color = COLSAT2YAB16(0, dot); return 1; } case 4: // 32 BPP dot = T1ReadLong(Vdp2Ram, ((info->charaddr + ((y * info->cellw) + x) * 4) & 0x7FFFF)); if (!(dot & 0x80000000) && info->transparencyenable) return 0; else { *color = COLSAT2YAB32(0, dot); return 1; } default: return 0; } } ////////////////////////////////////////////////////////////////////////////// static INLINE int TestWindow(int wctl, int enablemask, int inoutmask, clipping_struct *clip, int x, int y) { if (wctl & enablemask) { if (wctl & inoutmask) { // Draw inside of window if (x < clip->xstart || x > clip->xend || y < clip->ystart || y > clip->yend) return 0; } else { // Draw outside of window if (x >= clip->xstart && x <= clip->xend && y >= clip->ystart && y <= clip->yend) return 0; //it seems to overflow vertically on hardware if(clip->yend > vdp2height && (x >= clip->xstart && x <= clip->xend )) return 0; } return 1; // return inactive; } return 3; // return disabled | inactive; } ////////////////////////////////////////////////////////////////////////////// static INLINE int TestBothWindow(int wctl, clipping_struct *clip, int x, int y) { int w0 = TestWindow(wctl, 0x2, 0x1, &clip[0], x, y); int w1 = TestWindow(wctl, 0x8, 0x4, &clip[1], x, y); /* if window 0 is disabled, return window 1 */ if (w0 & 2) return w1 & 1; /* if window 1 is disabled, return window 0 */ if (w1 & 2) return w0 & 1; /* if both windows are active */ if ((wctl & 0x80) == 0x80) /* AND logic, returns 0 only if both the windows are active */ return w0 || w1; else /* OR logic, returns 0 if one of the windows is active */ return w0 && w1; } ////////////////////////////////////////////////////////////////////////////// static INLINE void GeneratePlaneAddrTable(vdp2draw_struct *info, u32 *planetbl, void FASTCALL (* PlaneAddr)(void *, int)) { int i; for (i = 0; i < (info->mapwh*info->mapwh); i++) { PlaneAddr(info, i); planetbl[i] = info->addr; } } ////////////////////////////////////////////////////////////////////////////// static INLINE void FASTCALL Vdp2MapCalcXY(vdp2draw_struct *info, int *x, int *y, screeninfo_struct *sinfo) { int planenum; const int pagesize_bits=info->pagewh_bits*2; const int cellwh=(2 + info->patternwh); const int check = ((y[0] >> cellwh) << 16) | (x[0] >> cellwh); //if ((x[0] >> cellwh) != sinfo->oldcellx || (y[0] >> cellwh) != sinfo->oldcelly) if(check != sinfo->oldcellcheck) { sinfo->oldcellx = x[0] >> cellwh; sinfo->oldcelly = y[0] >> cellwh; sinfo->oldcellcheck = (sinfo->oldcelly << 16) | sinfo->oldcellx; // Calculate which plane we're dealing with planenum = ((y[0] >> sinfo->planepixelheight_bits) * info->mapwh) + (x[0] >> sinfo->planepixelwidth_bits); x[0] = (x[0] & sinfo->planepixelwidth_mask); y[0] = (y[0] & sinfo->planepixelheight_mask); // Fetch and decode pattern name data info->addr = sinfo->planetbl[planenum]; // Figure out which page it's on(if plane size is not 1x1) info->addr += (( ((y[0] >> sinfo->pagepixelwh_bits) << pagesize_bits) << info->planew_bits) + ( (x[0] >> sinfo->pagepixelwh_bits) << pagesize_bits) + (((y[0] & sinfo->pagepixelwh_mask) >> cellwh) << info->pagewh_bits) + ((x[0] & sinfo->pagepixelwh_mask) >> cellwh)) << (info->patterndatasize_bits+1); Vdp2PatternAddr(info); // Heh, this could be optimized } // Figure out which pixel in the tile we want if (info->patternwh == 1) { x[0] &= 8-1; y[0] &= 8-1; switch(info->flipfunction & 0x3) { case 0: //none break; case 1: //horizontal flip x[0] = 8 - 1 - x[0]; break; case 2: // vertical flip y[0] = 8 - 1 - y[0]; break; case 3: //flip both x[0] = 8 - 1 - x[0]; y[0] = 8 - 1 - y[0]; break; } } else { if (info->flipfunction) { y[0] &= 16 - 1; if (info->flipfunction & 0x2) { if (!(y[0] & 8)) y[0] = 8 - 1 - y[0] + 16; else y[0] = 16 - 1 - y[0]; } else if (y[0] & 8) y[0] += 8; if (info->flipfunction & 0x1) { if (!(x[0] & 8)) y[0] += 8; x[0] &= 8-1; x[0] = 8 - 1 - x[0]; } else if (x[0] & 8) { y[0] += 8; x[0] &= 8-1; } else x[0] &= 8-1; } else { y[0] &= 16 - 1; if (y[0] & 8) y[0] += 8; if (x[0] & 8) y[0] += 8; x[0] &= 8-1; } } } ////////////////////////////////////////////////////////////////////////////// static INLINE void SetupScreenVars(vdp2draw_struct *info, screeninfo_struct *sinfo, void FASTCALL (* PlaneAddr)(void *, int)) { if (!info->isbitmap) { sinfo->pagepixelwh=64*8; sinfo->pagepixelwh_bits = 9; sinfo->pagepixelwh_mask = 511; sinfo->planepixelwidth=info->planew*sinfo->pagepixelwh; sinfo->planepixelwidth_bits = 8+info->planew; sinfo->planepixelwidth_mask = (1<<(sinfo->planepixelwidth_bits))-1; sinfo->planepixelheight=info->planeh*sinfo->pagepixelwh; sinfo->planepixelheight_bits = 8+info->planeh; sinfo->planepixelheight_mask = (1<<(sinfo->planepixelheight_bits))-1; sinfo->screenwidth=info->mapwh*sinfo->planepixelwidth; sinfo->screenheight=info->mapwh*sinfo->planepixelheight; sinfo->oldcellx=-1; sinfo->oldcelly=-1; sinfo->oldcellcheck=-1; sinfo->xmask = sinfo->screenwidth-1; sinfo->ymask = sinfo->screenheight-1; GeneratePlaneAddrTable(info, sinfo->planetbl, PlaneAddr); } else { sinfo->pagepixelwh = 0; sinfo->pagepixelwh_bits = 0; sinfo->pagepixelwh_mask = 0; sinfo->planepixelwidth=0; sinfo->planepixelwidth_bits=0; sinfo->planepixelwidth_mask=0; sinfo->planepixelheight=0; sinfo->planepixelheight_bits=0; sinfo->planepixelheight_mask=0; sinfo->screenwidth=0; sinfo->screenheight=0; sinfo->oldcellx=0; sinfo->oldcelly=0; sinfo->oldcellcheck=0; sinfo->xmask = info->cellw-1; sinfo->ymask = info->cellh-1; } } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL GetAlpha(vdp2draw_struct * info, u32 color) { if (((info->specialcolormode == 1) || (info->specialcolormode == 2)) && ((info->specialcolorfunction & 1) == 0)) { /* special color calculation mode 1 and 2 enables color calculation only when special color function = 1 */ return 0x3F; } else if (info->specialcolormode == 2) { /* special color calculation 2 enables color calculation according to lower bits of the color */ if ((info->specialcode & (1 << ((color & 0xF) >> 1))) == 0) { return 0x3F; } } else if ((info->specialcolormode == 3) && ((color & 0x80000000) == 0)) { /* special color calculation mode 3 enables color calculation only for dots with MSB = 1 */ return 0x3F; } return info->alpha; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL Vdp2DrawScroll(vdp2draw_struct *info) { int i, j; int x, y; clipping_struct clip[2]; u32 linewnd0addr, linewnd1addr; screeninfo_struct sinfo; int scrolly; int *mosaic_y, *mosaic_x; clipping_struct colorcalcwindow[2]; info->coordincx *= (float)resxratio; info->coordincy *= (float)resyratio; SetupScreenVars(info, &sinfo, info->PlaneAddr); scrolly = info->y; clip[0].xstart = clip[0].ystart = clip[0].xend = clip[0].yend = 0; clip[1].xstart = clip[1].ystart = clip[1].xend = clip[1].yend = 0; ReadWindowData(info->wctl, clip); linewnd0addr = linewnd1addr = 0; ReadLineWindowData(&info->islinewindow, info->wctl, &linewnd0addr, &linewnd1addr); /* color calculation window: in => no color calc, out => color calc */ ReadWindowData(Vdp2Regs->WCTLD >> 8, colorcalcwindow); { static int tables_initialized = 0; static int mosaic_table[16][1024]; if(!tables_initialized) { tables_initialized = 1; for(i=0;i<16;i++) { int m = i+1; for(j=0;j<1024;j++) mosaic_table[i][j] = j/m*m; } } mosaic_x = mosaic_table[info->mosaicxmask-1]; mosaic_y = mosaic_table[info->mosaicymask-1]; } for (j = 0; j < vdp2height; j++) { int Y; int linescrollx = 0; // precalculate the coordinate for the line(it's faster) and do line // scroll if (info->islinescroll) { if (info->islinescroll & 0x1) { linescrollx = (T1ReadLong(Vdp2Ram, info->linescrolltbl) >> 16) & 0x7FF; info->linescrolltbl += 4; } if (info->islinescroll & 0x2) { info->y = ((T1ReadWord(Vdp2Ram, info->linescrolltbl) & 0x7FF) * resyratio) + scrolly; info->linescrolltbl += 4; y = info->y; } else //y = info->y+((int)(info->coordincy *(float)(info->mosaicymask > 1 ? (j / info->mosaicymask * info->mosaicymask) : j))); y = info->y + info->coordincy*mosaic_y[j]; if (info->islinescroll & 0x4) { info->coordincx = (T1ReadLong(Vdp2Ram, info->linescrolltbl) & 0x7FF00) / (float)65536.0; info->coordincx *= resxratio; info->linescrolltbl += 4; } } else //y = info->y+((int)(info->coordincy *(float)(info->mosaicymask > 1 ? (j / info->mosaicymask * info->mosaicymask) : j))); y = info->y + info->coordincy*mosaic_y[j]; // if line window is enabled, adjust clipping values ReadLineWindowClip(info->islinewindow, clip, &linewnd0addr, &linewnd1addr); y &= sinfo.ymask; if (info->isverticalscroll) { // this is *wrong*, vertical scroll use a different value per cell // info->verticalscrolltbl should be incremented by info->verticalscrollinc // each time there's a cell change and reseted at the end of the line... // or something like that :) y += T1ReadLong(Vdp2Ram, info->verticalscrolltbl) >> 16; y &= 0x1FF; } Y=y; info->LoadLineParams(info, j); for (i = 0; i < vdp2width; i++) { u32 color; /* I'm really not sure about this... but I think the way we handle high resolution gets in the way with window process. I may be wrong... This was added for Cotton Boomerang */ int resxi = i * resxratio; // See if screen position is clipped, if it isn't, continue if (!TestBothWindow(info->wctl, clip, resxi, j)) { continue; } //x = info->x+((int)(info->coordincx*(float)((info->mosaicxmask > 1) ? (i / info->mosaicxmask * info->mosaicxmask) : i))); x = info->x + mosaic_x[i]*info->coordincx; x &= sinfo.xmask; if (linescrollx) { x += linescrollx; x &= 0x3FF; } // Fetch Pixel, if it isn't transparent, continue if (!info->isbitmap) { // Tile y=Y; Vdp2MapCalcXY(info, &x, &y, &sinfo); } if (!Vdp2FetchPixel(info, x, y, &color)) { continue; } // check special priority somewhere here // Apply color offset and color calculation/special color calculation // and then continue. // We almost need to know well ahead of time what the top // and second pixel is in order to work this. { u8 alpha; /* if we're in the valid area of the color calculation window, don't do color calculation */ if (!TestBothWindow(Vdp2Regs->WCTLD >> 8, colorcalcwindow, i, j)) alpha = 0x3F; else alpha = GetAlpha(info, color); TitanPutPixel(info->priority, i, j, info->PostPixelFetchCalc(info, COLSAT2YAB32(alpha, color)), info->linescreen); } } } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL Vdp2DrawRotationFP(vdp2draw_struct *info, vdp2rotationparameterfp_struct *parameter) { int i, j; int x, y; screeninfo_struct sinfo; vdp2rotationparameterfp_struct *p=¶meter[info->rotatenum]; clipping_struct clip[2]; u32 linewnd0addr, linewnd1addr; clip[0].xstart = clip[0].ystart = clip[0].xend = clip[0].yend = 0; clip[1].xstart = clip[1].ystart = clip[1].xend = clip[1].yend = 0; ReadWindowData(info->wctl, clip); linewnd0addr = linewnd1addr = 0; ReadLineWindowData(&info->islinewindow, info->wctl, &linewnd0addr, &linewnd1addr); Vdp2ReadRotationTableFP(info->rotatenum, p); if (!p->coefenab) { fixed32 xmul, ymul, C, F; // Since coefficients aren't being used, we can simplify the drawing process if (IsScreenRotatedFP(p)) { // No rotation info->x = touint(mulfixed(p->kx, (p->Xst - p->Px)) + p->Px + p->Mx); info->y = touint(mulfixed(p->ky, (p->Yst - p->Py)) + p->Py + p->My); info->coordincx = tofloat(p->kx); info->coordincy = tofloat(p->ky); } else { GenerateRotatedVarFP(p, &xmul, &ymul, &C, &F); // Do simple rotation CalculateRotationValuesFP(p); SetupScreenVars(info, &sinfo, info->PlaneAddr); for (j = 0; j < vdp2height; j++) { info->LoadLineParams(info, j); ReadLineWindowClip(info->islinewindow, clip, &linewnd0addr, &linewnd1addr); for (i = 0; i < vdp2width; i++) { u32 color; if (!TestBothWindow(info->wctl, clip, i, j)) continue; x = GenerateRotatedXPosFP(p, i, xmul, ymul, C) & sinfo.xmask; y = GenerateRotatedYPosFP(p, i, xmul, ymul, F) & sinfo.ymask; // Convert coordinates into graphics if (!info->isbitmap) { // Tile Vdp2MapCalcXY(info, &x, &y, &sinfo); } // Fetch pixel if (!Vdp2FetchPixel(info, x, y, &color)) { continue; } TitanPutPixel(info->priority, i, j, info->PostPixelFetchCalc(info, COLSAT2YAB32(GetAlpha(info, color), color)), info->linescreen); } xmul += p->deltaXst; ymul += p->deltaYst; } return; } } else { fixed32 xmul, ymul, C, F; u32 coefx, coefy; u32 rcoefx, rcoefy; u32 lineAddr, lineColor, lineInc; u16 lineColorAddr; fixed32 xmul2, ymul2, C2, F2; u32 coefx2, coefy2; u32 rcoefx2, rcoefy2; screeninfo_struct sinfo2; vdp2rotationparameterfp_struct *p2 = NULL; clipping_struct rpwindow[2]; int userpwindow = 0; int isrplinewindow = 0; u32 rplinewnd0addr, rplinewnd1addr; if ((Vdp2Regs->RPMD & 3) == 2) p2 = ¶meter[1 - info->rotatenum]; else if ((Vdp2Regs->RPMD & 3) == 3) { ReadWindowData(Vdp2Regs->WCTLD, rpwindow); rplinewnd0addr = rplinewnd1addr = 0; ReadLineWindowData(&isrplinewindow, Vdp2Regs->WCTLD, &rplinewnd0addr, &rplinewnd1addr); userpwindow = 1; p2 = ¶meter[1 - info->rotatenum]; } GenerateRotatedVarFP(p, &xmul, &ymul, &C, &F); // Rotation using Coefficient Tables(now this stuff just gets wacky. It // has to be done in software, no exceptions) CalculateRotationValuesFP(p); SetupScreenVars(info, &sinfo, p->PlaneAddr); coefx = coefy = 0; rcoefx = rcoefy = 0; if (p2 != NULL) { Vdp2ReadRotationTableFP(1 - info->rotatenum, p2); GenerateRotatedVarFP(p2, &xmul2, &ymul2, &C2, &F2); CalculateRotationValuesFP(p2); SetupScreenVars(info, &sinfo2, p2->PlaneAddr); coefx2 = coefy2 = 0; rcoefx2 = rcoefy2 = 0; } if (info->linescreen) { if ((info->rotatenum == 0) && (Vdp2Regs->KTCTL & 0x10)) info->linescreen = 2; else if (Vdp2Regs->KTCTL & 0x1000) info->linescreen = 3; if (Vdp2Regs->VRSIZE & 0x8000) lineAddr = (Vdp2Regs->LCTA.all & 0x7FFFF) << 1; else lineAddr = (Vdp2Regs->LCTA.all & 0x3FFFF) << 1; lineInc = Vdp2Regs->LCTA.part.U & 0x8000 ? 2 : 0; } for (j = 0; j < vdp2height; j++) { if (p->deltaKAx == 0) { Vdp2ReadCoefficientFP(p, p->coeftbladdr + (coefy + touint(rcoefy)) * p->coefdatasize); } if ((p2 != NULL) && p2->coefenab && (p2->deltaKAx == 0)) { Vdp2ReadCoefficientFP(p2, p2->coeftbladdr + (coefy2 + touint(rcoefy2)) * p2->coefdatasize); } if (info->linescreen > 1) { lineColorAddr = (T1ReadWord(Vdp2Ram, lineAddr) & 0x780) | p->linescreen; lineColor = Vdp2ColorRamGetColor(lineColorAddr); lineAddr += lineInc; TitanPutLineHLine(info->linescreen, j, COLSAT2YAB32(0x3F, lineColor)); } info->LoadLineParams(info, j); ReadLineWindowClip(info->islinewindow, clip, &linewnd0addr, &linewnd1addr); if (userpwindow) ReadLineWindowClip(isrplinewindow, rpwindow, &rplinewnd0addr, &rplinewnd1addr); for (i = 0; i < vdp2width; i++) { u32 color; if (p->deltaKAx != 0) { Vdp2ReadCoefficientFP(p, p->coeftbladdr + (coefy + coefx + toint(rcoefx + rcoefy)) * p->coefdatasize); coefx += toint(p->deltaKAx); rcoefx += decipart(p->deltaKAx); } if ((p2 != NULL) && p2->coefenab && (p2->deltaKAx != 0)) { Vdp2ReadCoefficientFP(p2, p2->coeftbladdr + (coefy2 + coefx2 + toint(rcoefx2 + rcoefy2)) * p2->coefdatasize); coefx2 += toint(p2->deltaKAx); rcoefx2 += decipart(p2->deltaKAx); } if (!TestBothWindow(info->wctl, clip, i, j)) continue; if (((! userpwindow) && p->msb) || (userpwindow && (! TestBothWindow(Vdp2Regs->WCTLD, rpwindow, i, j)))) { if ((p2 == NULL) || (p2->coefenab && p2->msb)) continue; x = GenerateRotatedXPosFP(p2, i, xmul2, ymul2, C2); y = GenerateRotatedYPosFP(p2, i, xmul2, ymul2, F2); switch(p2->screenover) { case 0: x &= sinfo2.xmask; y &= sinfo2.ymask; break; case 1: VDP2LOG("Screen-over mode 1 not implemented"); x &= sinfo2.xmask; y &= sinfo2.ymask; break; case 2: if ((x > sinfo2.xmask) || (y > sinfo2.ymask)) continue; break; case 3: if ((x > 512) || (y > 512)) continue; } // Convert coordinates into graphics if (!info->isbitmap) { // Tile Vdp2MapCalcXY(info, &x, &y, &sinfo2); } } else if (p->msb) continue; else { x = GenerateRotatedXPosFP(p, i, xmul, ymul, C); y = GenerateRotatedYPosFP(p, i, xmul, ymul, F); switch(p->screenover) { case 0: x &= sinfo.xmask; y &= sinfo.ymask; break; case 1: VDP2LOG("Screen-over mode 1 not implemented"); x &= sinfo.xmask; y &= sinfo.ymask; break; case 2: if ((x > sinfo.xmask) || (y > sinfo.ymask)) continue; break; case 3: if ((x > 512) || (y > 512)) continue; } // Convert coordinates into graphics if (!info->isbitmap) { // Tile Vdp2MapCalcXY(info, &x, &y, &sinfo); } } // Fetch pixel if (!Vdp2FetchPixel(info, x, y, &color)) { continue; } TitanPutPixel(info->priority, i, j, info->PostPixelFetchCalc(info, COLSAT2YAB32(GetAlpha(info, color), color)), info->linescreen); } xmul += p->deltaXst; ymul += p->deltaYst; coefx = 0; rcoefx = 0; coefy += toint(p->deltaKAst); rcoefy += decipart(p->deltaKAst); if (p2 != NULL) { xmul2 += p2->deltaXst; ymul2 += p2->deltaYst; if (p2->coefenab) { coefx2 = 0; rcoefx2 = 0; coefy2 += toint(p2->deltaKAst); rcoefy2 += decipart(p2->deltaKAst); } } } return; } Vdp2DrawScroll(info); } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawBackScreen(void) { int i, j; // Only draw black if TVMD's DISP and BDCLMD bits are cleared if ((Vdp2Regs->TVMD & 0x8000) == 0 && (Vdp2Regs->TVMD & 0x100) == 0) { // Draw Black for (j = 0; j < vdp2height; j++) TitanPutBackHLine(j, COLSAT2YAB32(0x3F, 0)); } else { // Draw Back Screen u32 scrAddr; u16 dot; if (Vdp2Regs->VRSIZE & 0x8000) scrAddr = (((Vdp2Regs->BKTAU & 0x7) << 16) | Vdp2Regs->BKTAL) * 2; else scrAddr = (((Vdp2Regs->BKTAU & 0x3) << 16) | Vdp2Regs->BKTAL) * 2; if (Vdp2Regs->BKTAU & 0x8000) { // Per Line for (i = 0; i < vdp2height; i++) { dot = T1ReadWord(Vdp2Ram, scrAddr); scrAddr += 2; TitanPutBackHLine(i, COLSAT2YAB16(0x3F, dot)); } } else { // Single Color dot = T1ReadWord(Vdp2Ram, scrAddr); for (j = 0; j < vdp2height; j++) TitanPutBackHLine(j, COLSAT2YAB16(0x3F, dot)); } } } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawLineScreen(void) { u32 scrAddr; u16 color; u32 dot; int i; /* no need to go further if no screen is using the line screen */ if (Vdp2Regs->LNCLEN == 0) return; if (Vdp2Regs->VRSIZE & 0x8000) scrAddr = (Vdp2Regs->LCTA.all & 0x7FFFF) << 1; else scrAddr = (Vdp2Regs->LCTA.all & 0x3FFFF) << 1; if (Vdp2Regs->LCTA.part.U & 0x8000) { /* per line */ for (i = 0; i < vdp2height; i++) { color = T1ReadWord(Vdp2Ram, scrAddr) & 0x7FF; dot = Vdp2ColorRamGetColor(color); scrAddr += 2; TitanPutLineHLine(1, i, COLSAT2YAB32(0x3F, dot)); } } else { /* single color, implemented but not tested... */ color = T1ReadWord(Vdp2Ram, scrAddr) & 0x7FF; dot = Vdp2ColorRamGetColor(color); for (i = 0; i < vdp2height; i++) TitanPutLineHLine(1, i, COLSAT2YAB32(0x3F, dot)); } } ////////////////////////////////////////////////////////////////////////////// static void LoadLineParamsNBG0(vdp2draw_struct * info, int line) { Vdp2 * regs; regs = Vdp2RestoreRegs(line); if (regs == NULL) return; ReadVdp2ColorOffset(regs, info, 0x1, 0x1); info->specialprimode = regs->SFPRMD & 0x3; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawNBG0(void) { vdp2draw_struct info; vdp2rotationparameterfp_struct parameter[2]; parameter[0].PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; parameter[1].PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; if (Vdp2Regs->BGON & 0x20) { // RBG1 mode info.enable = Vdp2Regs->BGON & 0x20; // Read in Parameter B Vdp2ReadRotationTableFP(1, ¶meter[1]); if((info.isbitmap = Vdp2Regs->CHCTLA & 0x2) != 0) { // Bitmap Mode ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 2, 0x3); info.charaddr = (Vdp2Regs->MPOFR & 0x70) * 0x2000; info.paladdr = (Vdp2Regs->BMPNA & 0x7) << 8; info.flipfunction = 0; info.specialfunction = 0; info.specialcolorfunction = (Vdp2Regs->BMPNA & 0x10) >> 4; } else { // Tile Mode info.mapwh = 4; ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 12); ReadPatternData(&info, Vdp2Regs->PNCN0, Vdp2Regs->CHCTLA & 0x1); } info.rotatenum = 1; info.rotatemode = 0; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; } else if (Vdp2Regs->BGON & 0x1) { // NBG0 mode info.enable = Vdp2Regs->BGON & 0x1; if((info.isbitmap = Vdp2Regs->CHCTLA & 0x2) != 0) { // Bitmap Mode ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 2, 0x3); info.x = Vdp2Regs->SCXIN0 & 0x7FF; info.y = Vdp2Regs->SCYIN0 & 0x7FF; info.charaddr = (Vdp2Regs->MPOFN & 0x7) * 0x20000; info.paladdr = (Vdp2Regs->BMPNA & 0x7) << 8; info.flipfunction = 0; info.specialfunction = 0; info.specialcolorfunction = (Vdp2Regs->BMPNA & 0x10) >> 4; } else { // Tile Mode info.mapwh = 2; ReadPlaneSize(&info, Vdp2Regs->PLSZ); info.x = Vdp2Regs->SCXIN0 & 0x7FF; info.y = Vdp2Regs->SCYIN0 & 0x7FF; ReadPatternData(&info, Vdp2Regs->PNCN0, Vdp2Regs->CHCTLA & 0x1); } info.coordincx = (Vdp2Regs->ZMXN0.all & 0x7FF00) / (float) 65536; info.coordincy = (Vdp2Regs->ZMYN0.all & 0x7FF00) / (float) 65536; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG0PlaneAddr; } else // Not enabled return; info.transparencyenable = !(Vdp2Regs->BGON & 0x100); info.specialprimode = Vdp2Regs->SFPRMD & 0x3; info.colornumber = (Vdp2Regs->CHCTLA & 0x70) >> 4; if (Vdp2Regs->CCCTL & 0x201) info.alpha = ((~Vdp2Regs->CCRNA & 0x1F) << 1) + 1; else info.alpha = 0x3F; if ((Vdp2Regs->CCCTL & 0x201) == 0x201) info.alpha |= 0x80; else if ((Vdp2Regs->CCCTL & 0x101) == 0x101) info.alpha |= 0x80; info.specialcolormode = Vdp2Regs->SFCCMD & 0x3; if (Vdp2Regs->SFSEL & 0x1) info.specialcode = Vdp2Regs->SFCODE >> 8; else info.specialcode = Vdp2Regs->SFCODE & 0xFF; info.linescreen = 0; if (Vdp2Regs->LNCLEN & 0x1) info.linescreen = 1; info.coloroffset = (Vdp2Regs->CRAOFA & 0x7) << 8; ReadVdp2ColorOffset(Vdp2Regs, &info, 0x1, 0x1); info.priority = nbg0priority; if (!(info.enable & Vdp2External.disptoggle)) return; ReadMosaicData(&info, 0x1); ReadLineScrollData(&info, Vdp2Regs->SCRCTL & 0xFF, Vdp2Regs->LSTA0.all); if (Vdp2Regs->SCRCTL & 1) { info.isverticalscroll = 1; info.verticalscrolltbl = (Vdp2Regs->VCSTA.all & 0x7FFFE) << 1; if (Vdp2Regs->SCRCTL & 0x100) info.verticalscrollinc = 8; else info.verticalscrollinc = 4; } else info.isverticalscroll = 0; info.wctl = Vdp2Regs->WCTLA; info.LoadLineParams = (void (*)(void *, int)) LoadLineParamsNBG0; if (info.enable == 1) { // NBG0 draw Vdp2DrawScroll(&info); } else { // RBG1 draw Vdp2DrawRotationFP(&info, parameter); } } ////////////////////////////////////////////////////////////////////////////// static void LoadLineParamsNBG1(vdp2draw_struct * info, int line) { Vdp2 * regs; regs = Vdp2RestoreRegs(line); if (regs == NULL) return; ReadVdp2ColorOffset(regs, info, 0x2, 0x2); info->specialprimode = (regs->SFPRMD >> 2) & 0x3; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawNBG1(void) { vdp2draw_struct info; info.enable = Vdp2Regs->BGON & 0x2; info.transparencyenable = !(Vdp2Regs->BGON & 0x200); info.specialprimode = (Vdp2Regs->SFPRMD >> 2) & 0x3; info.colornumber = (Vdp2Regs->CHCTLA & 0x3000) >> 12; if((info.isbitmap = Vdp2Regs->CHCTLA & 0x200) != 0) { ReadBitmapSize(&info, Vdp2Regs->CHCTLA >> 10, 0x3); info.x = Vdp2Regs->SCXIN1 & 0x7FF; info.y = Vdp2Regs->SCYIN1 & 0x7FF; info.charaddr = ((Vdp2Regs->MPOFN & 0x70) >> 4) * 0x20000; info.paladdr = Vdp2Regs->BMPNA & 0x700; info.flipfunction = 0; info.specialfunction = 0; info.specialcolorfunction = (Vdp2Regs->BMPNA & 0x1000) >> 12; } else { info.mapwh = 2; ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 2); info.x = Vdp2Regs->SCXIN1 & 0x7FF; info.y = Vdp2Regs->SCYIN1 & 0x7FF; ReadPatternData(&info, Vdp2Regs->PNCN1, Vdp2Regs->CHCTLA & 0x100); } if (Vdp2Regs->CCCTL & 0x202) info.alpha = ((~Vdp2Regs->CCRNA & 0x1F00) >> 7) + 1; else info.alpha = 0x3F; if ((Vdp2Regs->CCCTL & 0x202) == 0x202) info.alpha |= 0x80; else if ((Vdp2Regs->CCCTL & 0x102) == 0x102) info.alpha |= 0x80; info.specialcolormode = (Vdp2Regs->SFCCMD >> 2) & 0x3; if (Vdp2Regs->SFSEL & 0x2) info.specialcode = Vdp2Regs->SFCODE >> 8; else info.specialcode = Vdp2Regs->SFCODE & 0xFF; info.linescreen = 0; if (Vdp2Regs->LNCLEN & 0x2) info.linescreen = 1; info.coloroffset = (Vdp2Regs->CRAOFA & 0x70) << 4; ReadVdp2ColorOffset(Vdp2Regs, &info, 0x2, 0x2); info.coordincx = (Vdp2Regs->ZMXN1.all & 0x7FF00) / (float) 65536; info.coordincy = (Vdp2Regs->ZMYN1.all & 0x7FF00) / (float) 65536; info.priority = nbg1priority; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG1PlaneAddr; if (!(info.enable & Vdp2External.disptoggle) || (Vdp2Regs->BGON & 0x1 && (Vdp2Regs->CHCTLA & 0x70) >> 4 == 4)) // If NBG0 16M mode is enabled, don't draw return; ReadMosaicData(&info, 0x2); ReadLineScrollData(&info, Vdp2Regs->SCRCTL >> 8, Vdp2Regs->LSTA1.all); if (Vdp2Regs->SCRCTL & 0x100) { info.isverticalscroll = 1; if (Vdp2Regs->SCRCTL & 0x1) { info.verticalscrolltbl = 4 + ((Vdp2Regs->VCSTA.all & 0x7FFFE) << 1); info.verticalscrollinc = 8; } else { info.verticalscrolltbl = (Vdp2Regs->VCSTA.all & 0x7FFFE) << 1; info.verticalscrollinc = 4; } } else info.isverticalscroll = 0; info.wctl = Vdp2Regs->WCTLA >> 8; info.LoadLineParams = (void (*)(void *, int)) LoadLineParamsNBG1; Vdp2DrawScroll(&info); } ////////////////////////////////////////////////////////////////////////////// static void LoadLineParamsNBG2(vdp2draw_struct * info, int line) { Vdp2 * regs; regs = Vdp2RestoreRegs(line); if (regs == NULL) return; ReadVdp2ColorOffset(regs, info, 0x4, 0x4); info->specialprimode = (regs->SFPRMD >> 4) & 0x3; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawNBG2(void) { vdp2draw_struct info; info.enable = Vdp2Regs->BGON & 0x4; info.transparencyenable = !(Vdp2Regs->BGON & 0x400); info.specialprimode = (Vdp2Regs->SFPRMD >> 4) & 0x3; info.colornumber = (Vdp2Regs->CHCTLB & 0x2) >> 1; info.mapwh = 2; ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 4); info.x = Vdp2Regs->SCXN2 & 0x7FF; info.y = Vdp2Regs->SCYN2 & 0x7FF; ReadPatternData(&info, Vdp2Regs->PNCN2, Vdp2Regs->CHCTLB & 0x1); if (Vdp2Regs->CCCTL & 0x204) info.alpha = ((~Vdp2Regs->CCRNB & 0x1F) << 1) + 1; else info.alpha = 0x3F; if ((Vdp2Regs->CCCTL & 0x204) == 0x204) info.alpha |= 0x80; else if ((Vdp2Regs->CCCTL & 0x104) == 0x104) info.alpha |= 0x80; info.specialcolormode = (Vdp2Regs->SFCCMD >> 4) & 0x3; if (Vdp2Regs->SFSEL & 0x4) info.specialcode = Vdp2Regs->SFCODE >> 8; else info.specialcode = Vdp2Regs->SFCODE & 0xFF; info.linescreen = 0; if (Vdp2Regs->LNCLEN & 0x4) info.linescreen = 1; info.coloroffset = Vdp2Regs->CRAOFA & 0x700; ReadVdp2ColorOffset(Vdp2Regs, &info, 0x4, 0x4); info.coordincx = info.coordincy = 1; info.priority = nbg2priority; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG2PlaneAddr; if (!(info.enable & Vdp2External.disptoggle) || (Vdp2Regs->BGON & 0x1 && (Vdp2Regs->CHCTLA & 0x70) >> 4 >= 2)) // If NBG0 2048/32786/16M mode is enabled, don't draw return; ReadMosaicData(&info, 0x4); info.islinescroll = 0; info.isverticalscroll = 0; info.wctl = Vdp2Regs->WCTLB; info.isbitmap = 0; info.LoadLineParams = (void (*)(void *, int)) LoadLineParamsNBG2; Vdp2DrawScroll(&info); } ////////////////////////////////////////////////////////////////////////////// static void LoadLineParamsNBG3(vdp2draw_struct * info, int line) { Vdp2 * regs; regs = Vdp2RestoreRegs(line); if (regs == NULL) return; ReadVdp2ColorOffset(regs, info, 0x8, 0x8); info->specialprimode = (regs->SFPRMD >> 6) & 0x3; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawNBG3(void) { vdp2draw_struct info; info.enable = Vdp2Regs->BGON & 0x8; info.transparencyenable = !(Vdp2Regs->BGON & 0x800); info.specialprimode = (Vdp2Regs->SFPRMD >> 6) & 0x3; info.colornumber = (Vdp2Regs->CHCTLB & 0x20) >> 5; info.mapwh = 2; ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 6); info.x = Vdp2Regs->SCXN3 & 0x7FF; info.y = Vdp2Regs->SCYN3 & 0x7FF; ReadPatternData(&info, Vdp2Regs->PNCN3, Vdp2Regs->CHCTLB & 0x10); if (Vdp2Regs->CCCTL & 0x208) info.alpha = ((~Vdp2Regs->CCRNB & 0x1F00) >> 7) + 1; else info.alpha = 0x3F; if ((Vdp2Regs->CCCTL & 0x208) == 0x208) info.alpha |= 0x80; else if ((Vdp2Regs->CCCTL & 0x108) == 0x108) info.alpha |= 0x80; info.specialcolormode = (Vdp2Regs->SFCCMD >> 6) & 0x3; if (Vdp2Regs->SFSEL & 0x8) info.specialcode = Vdp2Regs->SFCODE >> 8; else info.specialcode = Vdp2Regs->SFCODE & 0xFF; info.linescreen = 0; if (Vdp2Regs->LNCLEN & 0x8) info.linescreen = 1; info.coloroffset = (Vdp2Regs->CRAOFA & 0x7000) >> 4; ReadVdp2ColorOffset(Vdp2Regs, &info, 0x8, 0x8); info.coordincx = info.coordincy = 1; info.priority = nbg3priority; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2NBG3PlaneAddr; if (!(info.enable & Vdp2External.disptoggle) || (Vdp2Regs->BGON & 0x1 && (Vdp2Regs->CHCTLA & 0x70) >> 4 == 4) || // If NBG0 16M mode is enabled, don't draw (Vdp2Regs->BGON & 0x2 && (Vdp2Regs->CHCTLA & 0x3000) >> 12 >= 2)) // If NBG1 2048/32786 is enabled, don't draw return; ReadMosaicData(&info, 0x8); info.islinescroll = 0; info.isverticalscroll = 0; info.wctl = Vdp2Regs->WCTLB >> 8; info.isbitmap = 0; info.LoadLineParams = (void (*)(void *, int)) LoadLineParamsNBG3; Vdp2DrawScroll(&info); } ////////////////////////////////////////////////////////////////////////////// static void LoadLineParamsRBG0(vdp2draw_struct * info, int line) { Vdp2 * regs; regs = Vdp2RestoreRegs(line); if (regs == NULL) return; ReadVdp2ColorOffset(regs, info, 0x10, 0x10); info->specialprimode = (regs->SFPRMD >> 8) & 0x3; } ////////////////////////////////////////////////////////////////////////////// static void Vdp2DrawRBG0(void) { vdp2draw_struct info; vdp2rotationparameterfp_struct parameter[2]; parameter[0].PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; parameter[1].PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; info.enable = Vdp2Regs->BGON & 0x10; info.priority = rbg0priority; if (!(info.enable & Vdp2External.disptoggle)) return; info.transparencyenable = !(Vdp2Regs->BGON & 0x1000); info.specialprimode = (Vdp2Regs->SFPRMD >> 8) & 0x3; info.colornumber = (Vdp2Regs->CHCTLB & 0x7000) >> 12; // Figure out which Rotation Parameter we're using switch (Vdp2Regs->RPMD & 0x3) { case 0: // Parameter A info.rotatenum = 0; info.rotatemode = 0; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; break; case 1: // Parameter B info.rotatenum = 1; info.rotatemode = 0; info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterBPlaneAddr; break; case 2: // Parameter A+B switched via coefficients case 3: // Parameter A+B switched via rotation parameter window default: info.rotatenum = 0; info.rotatemode = 1 + (Vdp2Regs->RPMD & 0x1); info.PlaneAddr = (void FASTCALL (*)(void *, int))&Vdp2ParameterAPlaneAddr; break; } Vdp2ReadRotationTableFP(info.rotatenum, ¶meter[info.rotatenum]); if((info.isbitmap = Vdp2Regs->CHCTLB & 0x200) != 0) { // Bitmap Mode ReadBitmapSize(&info, Vdp2Regs->CHCTLB >> 10, 0x1); if (info.rotatenum == 0) // Parameter A info.charaddr = (Vdp2Regs->MPOFR & 0x7) * 0x20000; else // Parameter B info.charaddr = (Vdp2Regs->MPOFR & 0x70) * 0x2000; info.paladdr = (Vdp2Regs->BMPNB & 0x7) << 8; info.flipfunction = 0; info.specialfunction = 0; info.specialcolorfunction = (Vdp2Regs->BMPNB & 0x10) >> 4; } else { // Tile Mode info.mapwh = 4; if (info.rotatenum == 0) // Parameter A ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 8); else // Parameter B ReadPlaneSize(&info, Vdp2Regs->PLSZ >> 12); ReadPatternData(&info, Vdp2Regs->PNCR, Vdp2Regs->CHCTLB & 0x100); } if (Vdp2Regs->CCCTL & 0x210) info.alpha = ((~Vdp2Regs->CCRR & 0x1F) << 1) + 1; else info.alpha = 0x3F; if ((Vdp2Regs->CCCTL & 0x210) == 0x210) info.alpha |= 0x80; else if ((Vdp2Regs->CCCTL & 0x110) == 0x110) info.alpha |= 0x80; info.specialcolormode = (Vdp2Regs->SFCCMD >> 8) & 0x3; if (Vdp2Regs->SFSEL & 0x10) info.specialcode = Vdp2Regs->SFCODE >> 8; else info.specialcode = Vdp2Regs->SFCODE & 0xFF; info.linescreen = 0; if (Vdp2Regs->LNCLEN & 0x10) info.linescreen = 1; info.coloroffset = (Vdp2Regs->CRAOFB & 0x7) << 8; ReadVdp2ColorOffset(Vdp2Regs, &info, 0x10, 0x10); info.coordincx = info.coordincy = 1; ReadMosaicData(&info, 0x10); info.islinescroll = 0; info.isverticalscroll = 0; info.wctl = Vdp2Regs->WCTLC; info.LoadLineParams = (void (*)(void *, int)) LoadLineParamsRBG0; Vdp2DrawRotationFP(&info, parameter); } ////////////////////////////////////////////////////////////////////////////// static void LoadLineParamsSprite(vdp2draw_struct * info, int line) { Vdp2 * regs; regs = Vdp2RestoreRegs(line); if (regs == NULL) return; ReadVdp2ColorOffset(regs, info, 0x40, 0x40); } ////////////////////////////////////////////////////////////////////////////// int VIDSoftInit(void) { if (TitanInit() == -1) return -1; if ((dispbuffer = (pixel_t *)calloc(sizeof(pixel_t), 704 * 512)) == NULL) return -1; // Initialize VDP1 framebuffer 1 if ((vdp1framebuffer[0] = (u8 *)calloc(sizeof(u8), 0x40000)) == NULL) return -1; // Initialize VDP1 framebuffer 2 if ((vdp1framebuffer[1] = (u8 *)calloc(sizeof(u8), 0x40000)) == NULL) return -1; vdp1backframebuffer = vdp1framebuffer[0]; vdp1frontframebuffer = vdp1framebuffer[1]; vdp2width = 320; vdp2height = 224; #ifdef USE_OPENGL glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, 320, 224, 0, 1, 0); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glOrtho(-320, 320, -224, 224, 1, 0); outputwidth = 320; outputheight = 224; #endif return 0; } ////////////////////////////////////////////////////////////////////////////// void VIDSoftDeInit(void) { if (dispbuffer) { free(dispbuffer); dispbuffer = NULL; } if (vdp1framebuffer[0]) free(vdp1framebuffer[0]); if (vdp1framebuffer[1]) free(vdp1framebuffer[1]); } ////////////////////////////////////////////////////////////////////////////// static int IsFullscreen = 0; void VIDSoftResize(unsigned int w, unsigned int h, int on) { #ifdef USE_OPENGL IsFullscreen = on; glClear(GL_COLOR_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0, w, h, 0, 1, 0); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glOrtho(-(signed)w, w, -(signed)h, h, 1, 0); glViewport(0, 0, w, h); outputwidth = w; outputheight = h; #endif } ////////////////////////////////////////////////////////////////////////////// int VIDSoftIsFullscreen(void) { return IsFullscreen; } ////////////////////////////////////////////////////////////////////////////// int VIDSoftVdp1Reset(void) { vdp1clipxstart = 0; vdp1clipxend = 512; vdp1clipystart = 0; vdp1clipyend = 256; return 0; } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp1DrawStart(void) { if (Vdp1Regs->FBCR & 8) vdp1interlace = 2; else vdp1interlace = 1; if (Vdp1Regs->TVMR & 0x1) { if (Vdp1Regs->TVMR & 0x2) { // Rotation 8-bit vdp1width = 512; vdp1height = 512; } else { // Normal 8-bit vdp1width = 1024; vdp1height = 256; } vdp1pixelsize = 1; } else { // Rotation/Normal 16-bit vdp1width = 512; vdp1height = 256; vdp1pixelsize = 2; } VIDSoftVdp1EraseFrameBuffer(); vdp1clipxstart = Vdp1Regs->userclipX1 = Vdp1Regs->systemclipX1 = 0; vdp1clipystart = Vdp1Regs->userclipY1 = Vdp1Regs->systemclipY1 = 0; vdp1clipxend = Vdp1Regs->userclipX2 = Vdp1Regs->systemclipX2 = vdp1width; vdp1clipyend = Vdp1Regs->userclipY2 = Vdp1Regs->systemclipY2 = vdp1height; } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp1DrawEnd(void) { } ////////////////////////////////////////////////////////////////////////////// static INLINE u16 Vdp1ReadPattern16( u32 base, u32 offset ) { u16 dot = T1ReadByte(Vdp1Ram, ( base + (offset>>1)) & 0x7FFFF); if ((offset & 0x1) == 0) dot >>= 4; // Even pixel else dot &= 0xF; // Odd pixel return dot; } static INLINE u16 Vdp1ReadPattern64( u32 base, u32 offset ) { return T1ReadByte(Vdp1Ram, ( base + offset ) & 0x7FFFF) & 0x3F; } static INLINE u16 Vdp1ReadPattern128( u32 base, u32 offset ) { return T1ReadByte(Vdp1Ram, ( base + offset ) & 0x7FFFF) & 0x7F; } static INLINE u16 Vdp1ReadPattern256( u32 base, u32 offset ) { return T1ReadByte(Vdp1Ram, ( base + offset ) & 0x7FFFF) & 0xFF; } static INLINE u16 Vdp1ReadPattern64k( u32 base, u32 offset ) { return T1ReadWord(Vdp1Ram, ( base + 2*offset) & 0x7FFFF); } //////////////////////////////////////////////////////////////////////////////// static INLINE u32 alphablend16(u32 d, u32 s, u32 level) { int r,g,b,sr,sg,sb,dr,dg,db; int invlevel = 256-level; sr = s & 0x001f; dr = d & 0x001f; r = (sr*level + dr*invlevel)>>8; r&= 0x1f; sg = s & 0x03e0; dg = d & 0x03e0; g = (sg*level + dg*invlevel)>>8; g&= 0x03e0; sb = s & 0x7c00; db = d & 0x7c00; b = (sb*level + db*invlevel)>>8; b&= 0x7c00; return r|g|b; } typedef struct _COLOR_PARAMS { double r,g,b; } COLOR_PARAMS; COLOR_PARAMS leftColumnColor; vdp1cmd_struct cmd; int currentPixel; int currentPixelIsVisible; int characterWidth; int characterHeight; static int getpixel(int linenumber, int currentlineindex) { u32 characterAddress; u32 colorlut; u16 colorbank; u8 SPD; int endcode; int endcodesEnabled; int untexturedColor = 0; int isTextured = 1; int currentShape = cmd.CMDCTRL & 0x7; int flip; characterAddress = cmd.CMDSRCA << 3; colorbank = cmd.CMDCOLR; colorlut = (u32)colorbank << 3; SPD = ((cmd.CMDPMOD & 0x40) != 0);//show the actual color of transparent pixels if 1 (they won't be drawn transparent) endcodesEnabled = (( cmd.CMDPMOD & 0x80) == 0 )?1:0; flip = (cmd.CMDCTRL & 0x30) >> 4; //4 polygon, 5 polyline or 6 line if(currentShape == 4 || currentShape == 5 || currentShape == 6) { isTextured = 0; untexturedColor = cmd.CMDCOLR; } switch( flip ) { case 1: // Horizontal flipping currentlineindex = characterWidth - currentlineindex-1; break; case 2: // Vertical flipping linenumber = characterHeight - linenumber-1; break; case 3: // Horizontal/Vertical flipping linenumber = characterHeight - linenumber-1; currentlineindex = characterWidth - currentlineindex-1; break; } switch ((cmd.CMDPMOD >> 3) & 0x7) { case 0x0: //4bpp bank endcode = 0xf; currentPixel = Vdp1ReadPattern16( characterAddress + (linenumber*(characterWidth>>1)), currentlineindex ); if(isTextured && endcodesEnabled && currentPixel == endcode) return 1; if (!((currentPixel == 0) && !SPD)) currentPixel = colorbank | currentPixel; currentPixelIsVisible = 0xf; break; case 0x1://4bpp lut endcode = 0xf; currentPixel = Vdp1ReadPattern16( characterAddress + (linenumber*(characterWidth>>1)), currentlineindex ); if(isTextured && endcodesEnabled && currentPixel == endcode) return 1; if (!(currentPixel == 0 && !SPD)) currentPixel = T1ReadWord(Vdp1Ram, (currentPixel * 2 + colorlut) & 0x7FFFF); currentPixelIsVisible = 0xffff; break; case 0x2://8pp bank (64 color) //is there a hardware bug with endcodes in this color mode? //there are white lines around some characters in scud //using an endcode of 63 eliminates the white lines //but also causes some dropout due to endcodes being triggered that aren't triggered on hardware //the closest thing i can do to match the hardware is make all pixels with color index 63 transparent //this needs more hardware testing endcode = 63; currentPixel = Vdp1ReadPattern64( characterAddress + (linenumber*(characterWidth)), currentlineindex ); if(isTextured && endcodesEnabled && currentPixel == endcode) currentPixel = 0; // return 1; if (!((currentPixel == 0) && !SPD)) currentPixel = colorbank | currentPixel; currentPixelIsVisible = 0x3f; break; case 0x3://128 color endcode = 0xff; currentPixel = Vdp1ReadPattern128( characterAddress + (linenumber*characterWidth), currentlineindex ); if(isTextured && endcodesEnabled && currentPixel == endcode) return 1; if (!((currentPixel == 0) && !SPD)) currentPixel = colorbank | currentPixel; currentPixelIsVisible = 0x7f; break; case 0x4://256 color endcode = 0xff; currentPixel = Vdp1ReadPattern256( characterAddress + (linenumber*characterWidth), currentlineindex ); if(isTextured && endcodesEnabled && currentPixel == endcode) return 1; currentPixelIsVisible = 0xff; if (!((currentPixel == 0) && !SPD)) currentPixel = colorbank | currentPixel; break; case 0x5://16bpp bank endcode = 0x7fff; currentPixel = Vdp1ReadPattern64k( characterAddress + (linenumber*characterWidth*2), currentlineindex ); if(isTextured && endcodesEnabled && currentPixel == endcode) return 1; /* the transparent pixel in 16bpp is supposed to be 0x0000 but some games use pixels with invalid values and expect them to be transparent (see vdp1 doc p. 92) */ if (!(currentPixel & 0x8000) && !SPD) currentPixel = 0; currentPixelIsVisible = 0xffff; break; } if(!isTextured) currentPixel = untexturedColor; //force the MSB to be on if MSBON is set //currentPixel |= cmd.CMDPMOD & (1 << 15); return 0; } static int gouraudAdjust( int color, int tableValue ) { color += (tableValue - 0x10); if ( color < 0 ) color = 0; if ( color > 0x1f ) color = 0x1f; return color; } static void putpixel8(int x, int y) { int x2 = x / 2; int y2 = y / vdp1interlace; u8 * iPix = &vdp1backframebuffer[(y2 * vdp1width) + x2]; int mesh = cmd.CMDPMOD & 0x0100; int SPD = ((cmd.CMDPMOD & 0x40) != 0);//show the actual color of transparent pixels if 1 (they won't be drawn transparent) if (iPix >= (vdp1backframebuffer + 0x40000)) return; currentPixel &= 0xFF; if(mesh && ((x2 ^ y2) & 1)) { return; } { int clipped; if (cmd.CMDPMOD & 0x0400) PushUserClipping((cmd.CMDPMOD >> 9) & 0x1); clipped = ! (x2 >= vdp1clipxstart && x2 < vdp1clipxend && y2 >= vdp1clipystart && y2 < vdp1clipyend); if (cmd.CMDPMOD & 0x0400) PopUserClipping(); if (clipped) return; } if ( SPD || (currentPixel & currentPixelIsVisible)) { switch( cmd.CMDPMOD & 0x7 )//we want bits 0,1,2 { default: case 0: // replace if (!((currentPixel == 0) && !SPD)) *(iPix) = currentPixel; break; } } } static void putpixel(int x, int y) { u16* iPix; int mesh = cmd.CMDPMOD & 0x0100; int SPD = ((cmd.CMDPMOD & 0x40) != 0);//show the actual color of transparent pixels if 1 (they won't be drawn transparent) y /= vdp1interlace; iPix = &((u16 *)vdp1backframebuffer)[(y * vdp1width) + x]; if (iPix >= (u16*) (vdp1backframebuffer + 0x40000)) return; if(mesh && (x^y)&1) return; { int clipped; if (cmd.CMDPMOD & 0x0400) PushUserClipping((cmd.CMDPMOD >> 9) & 0x1); clipped = ! (x >= vdp1clipxstart && x < vdp1clipxend && y >= vdp1clipystart && y < vdp1clipyend); if (cmd.CMDPMOD & 0x0400) PopUserClipping(); if (clipped) return; } if ((cmd.CMDPMOD & (1 << 15)) && ((Vdp2Regs->SPCTL & 0x10) == 0)) { if (currentPixel) { *iPix |= 0x8000; return; } } if ( SPD || (currentPixel & currentPixelIsVisible)) { switch( cmd.CMDPMOD & 0x7 )//we want bits 0,1,2 { case 0: // replace if (!((currentPixel == 0) && !SPD)) *(iPix) = currentPixel; break; case 1: // shadow if (*(iPix) & (1 << 15)) // only if MSB of framebuffer data is set *(iPix) = alphablend16(*(iPix), 0, (1 << 7)) | (1 << 15); break; case 2: // half luminance *(iPix) = ((currentPixel & ~0x8421) >> 1) | (1 << 15); break; case 3: // half transparent if ( *(iPix) & (1 << 15) )//only if MSB of framebuffer data is set *(iPix) = alphablend16( *(iPix), currentPixel, (1 << 7) ) | (1 << 15); else *(iPix) = currentPixel; break; case 4: //gouraud #define COLOR(r,g,b) (((r)&0x1F)|(((g)&0x1F)<<5)|(((b)&0x1F)<<10) |0x8000 ) //handle the special case demonstrated in the sgl chrome demo //if we are in a paletted bank mode and the other two colors are unused, adjust the index value instead of rgb if( (((cmd.CMDPMOD >> 3) & 0x7) != 5) && (((cmd.CMDPMOD >> 3) & 0x7) != 1) && (int)leftColumnColor.g == 16 && (int)leftColumnColor.b == 16) { int c = (int)(leftColumnColor.r-0x10); if(c < 0) c = 0; currentPixel = currentPixel+c; *(iPix) = currentPixel; break; } *(iPix) = COLOR( gouraudAdjust( currentPixel&0x001F, (int)leftColumnColor.r), gouraudAdjust( (currentPixel&0x03e0) >> 5, (int)leftColumnColor.g), gouraudAdjust( (currentPixel&0x7c00) >> 10, (int)leftColumnColor.b) ); break; default: *(iPix) = alphablend16( COLOR((int)leftColumnColor.r,(int)leftColumnColor.g, (int)leftColumnColor.b), currentPixel, (1 << 7) ) | (1 << 15); break; } } } static int iterateOverLine(int x1, int y1, int x2, int y2, int greedy, void *data, int (*line_callback)(int x, int y, int i, void *data)) { int i, a, ax, ay, dx, dy; a = i = 0; dx = x2 - x1; dy = y2 - y1; ax = (dx >= 0) ? 1 : -1; ay = (dy >= 0) ? 1 : -1; //burning rangers tries to draw huge shapes //this will at least let it run if(abs(dx) > 999 || abs(dy) > 999) return INT_MAX; if (abs(dx) > abs(dy)) { if (ax != ay) dx = -dx; for (; x1 != x2; x1 += ax, i++) { if (line_callback && line_callback(x1, y1, i, data) != 0) return i + 1; a += dy; if (abs(a) >= abs(dx)) { a -= dx; y1 += ay; // Make sure we 'fill holes' the same as the Saturn if (greedy) { i ++; if (ax == ay) { if (line_callback && line_callback(x1 + ax, y1 - ay, i, data) != 0) return i + 1; } else { if (line_callback && line_callback(x1, y1, i, data) != 0) return i + 1; } } } } // If the line isn't greedy here, we end up with gaps that don't occur on the Saturn if (/*(i == 0) || (y1 != y2)*/1) { if (line_callback) line_callback(x2, y2, i, data); i ++; } } else { if (ax != ay) dy = -dy; for (; y1 != y2; y1 += ay, i++) { if (line_callback && line_callback(x1, y1, i, data) != 0) return i + 1; a += dx; if (abs(a) >= abs(dy)) { a -= dy; x1 += ax; if (greedy) { i ++; if (ay == ax) { if (line_callback && line_callback(x1, y1, i, data) != 0) return i + 1; } else { if (line_callback && line_callback(x1 - ax, y1 + ay, i, data) != 0) return i + 1; } } } } if (/*(i == 0) || (y1 != y2)*/1) { if (line_callback) line_callback(x2, y2, i, data); i ++; } } return i; } typedef struct { double linenumber; double texturestep; double xredstep; double xgreenstep; double xbluestep; int endcodesdetected; int previousStep; } DrawLineData; static int DrawLineCallback(int x, int y, int i, void *data) { int currentStep; DrawLineData *linedata = data; leftColumnColor.r += linedata->xredstep; leftColumnColor.g += linedata->xgreenstep; leftColumnColor.b += linedata->xbluestep; currentStep = (int)i * linedata->texturestep; if (getpixel(linedata->linenumber, currentStep)) { if (currentStep != linedata->previousStep) { linedata->previousStep = currentStep; linedata->endcodesdetected ++; } } else if (vdp1pixelsize == 2) { putpixel(x, y); } else { putpixel8(x, y); } if (linedata->endcodesdetected == 2) return -1; return 0; } static int DrawLine( int x1, int y1, int x2, int y2, int greedy, double linenumber, double texturestep, double xredstep, double xgreenstep, double xbluestep) { DrawLineData data; data.linenumber = linenumber; data.texturestep = texturestep; data.xredstep = xredstep; data.xgreenstep = xgreenstep; data.xbluestep = xbluestep; data.endcodesdetected = 0; data.previousStep = 123456789; return iterateOverLine(x1, y1, x2, y2, greedy, &data, DrawLineCallback); } static INLINE double interpolate(double start, double end, int numberofsteps) { double stepvalue = 0; if(numberofsteps == 0) return 1; stepvalue = (end - start) / numberofsteps; return stepvalue; } typedef union _COLOR { // xbgr x555 struct { #ifdef WORDS_BIGENDIAN u16 x:1; u16 b:5; u16 g:5; u16 r:5; #else u16 r:5; u16 g:5; u16 b:5; u16 x:1; #endif }; u16 value; } COLOR; COLOR gouraudA; COLOR gouraudB; COLOR gouraudC; COLOR gouraudD; static void gouraudTable(void) { int gouraudTableAddress; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); gouraudTableAddress = (((unsigned int)cmd.CMDGRDA) << 3); gouraudA.value = T1ReadWord(Vdp1Ram,gouraudTableAddress); gouraudB.value = T1ReadWord(Vdp1Ram,gouraudTableAddress+2); gouraudC.value = T1ReadWord(Vdp1Ram,gouraudTableAddress+4); gouraudD.value = T1ReadWord(Vdp1Ram,gouraudTableAddress+6); } int xleft[1000]; int yleft[1000]; int xright[1000]; int yright[1000]; static int storeLineCoords(int x, int y, int i, void *arrays) { int **intArrays = arrays; intArrays[0][i] = x; intArrays[1][i] = y; return 0; } //a real vdp1 draws with arbitrary lines //this is why endcodes are possible //this is also the reason why half-transparent shading causes moire patterns //and the reason why gouraud shading can be applied to a single line draw command static void drawQuad(s16 tl_x, s16 tl_y, s16 bl_x, s16 bl_y, s16 tr_x, s16 tr_y, s16 br_x, s16 br_y){ int totalleft; int totalright; int total; int i; int *intarrays[2]; COLOR_PARAMS topLeftToBottomLeftColorStep = {0,0,0}, topRightToBottomRightColorStep = {0,0,0}; //how quickly we step through the line arrays double leftLineStep = 1; double rightLineStep = 1; //a lookup table for the gouraud colors COLOR colors[4]; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); characterWidth = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; characterHeight = cmd.CMDSIZE & 0xFF; intarrays[0] = xleft; intarrays[1] = yleft; totalleft = iterateOverLine(tl_x, tl_y, bl_x, bl_y, 0, intarrays, storeLineCoords); intarrays[0] = xright; intarrays[1] = yright; totalright = iterateOverLine(tr_x, tr_y, br_x, br_y, 0, intarrays, storeLineCoords); //just for now since burning rangers will freeze up trying to draw huge shapes if(totalleft == INT_MAX || totalright == INT_MAX) return; total = totalleft > totalright ? totalleft : totalright; if(cmd.CMDPMOD & (1 << 2)) { gouraudTable(); { colors[0] = gouraudA; colors[1] = gouraudD; colors[2] = gouraudB; colors[3] = gouraudC; } topLeftToBottomLeftColorStep.r = interpolate(colors[0].r,colors[1].r,total); topLeftToBottomLeftColorStep.g = interpolate(colors[0].g,colors[1].g,total); topLeftToBottomLeftColorStep.b = interpolate(colors[0].b,colors[1].b,total); topRightToBottomRightColorStep.r = interpolate(colors[2].r,colors[3].r,total); topRightToBottomRightColorStep.g = interpolate(colors[2].g,colors[3].g,total); topRightToBottomRightColorStep.b = interpolate(colors[2].b,colors[3].b,total); } //we have to step the equivalent of less than one pixel on the shorter side //to make sure textures stretch properly and the shape is correct if(total == totalleft && totalleft != totalright) { //left side is larger leftLineStep = 1; rightLineStep = (double)totalright / totalleft; } else if(totalleft != totalright){ //right side is larger rightLineStep = 1; leftLineStep = (double)totalleft / totalright; } for(i = 0; i < total; i++) { int xlinelength; double xtexturestep; double ytexturestep; COLOR_PARAMS rightColumnColor; COLOR_PARAMS leftToRightStep = {0,0,0}; //get the length of the line we are about to draw xlinelength = iterateOverLine( xleft[(int)(i*leftLineStep)], yleft[(int)(i*leftLineStep)], xright[(int)(i*rightLineStep)], yright[(int)(i*rightLineStep)], 1, NULL, NULL); //so from 0 to the width of the texture / the length of the line is how far we need to step xtexturestep=interpolate(0,characterWidth,xlinelength); //now we need to interpolate the y texture coordinate across multiple lines ytexturestep=interpolate(0,characterHeight,total); //gouraud interpolation if(cmd.CMDPMOD & (1 << 2)) { //for each new line we need to step once more through each column //and add the orignal color + the number of steps taken times the step value to the bottom of the shape //to get the current colors to use to interpolate across the line leftColumnColor.r = colors[0].r +(topLeftToBottomLeftColorStep.r*i); leftColumnColor.g = colors[0].g +(topLeftToBottomLeftColorStep.g*i); leftColumnColor.b = colors[0].b +(topLeftToBottomLeftColorStep.b*i); rightColumnColor.r = colors[2].r +(topRightToBottomRightColorStep.r*i); rightColumnColor.g = colors[2].g +(topRightToBottomRightColorStep.g*i); rightColumnColor.b = colors[2].b +(topRightToBottomRightColorStep.b*i); //interpolate colors across to get the right step values leftToRightStep.r = interpolate(leftColumnColor.r,rightColumnColor.r,xlinelength); leftToRightStep.g = interpolate(leftColumnColor.g,rightColumnColor.g,xlinelength); leftToRightStep.b = interpolate(leftColumnColor.b,rightColumnColor.b,xlinelength); } DrawLine( xleft[(int)(i*leftLineStep)], yleft[(int)(i*leftLineStep)], xright[(int)(i*rightLineStep)], yright[(int)(i*rightLineStep)], 1, ytexturestep*i, xtexturestep, leftToRightStep.r, leftToRightStep.g, leftToRightStep.b ); } } void VIDSoftVdp1NormalSpriteDraw() { s16 topLeftx,topLefty,topRightx,topRighty,bottomRightx,bottomRighty,bottomLeftx,bottomLefty; int spriteWidth; int spriteHeight; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); topLeftx = cmd.CMDXA + Vdp1Regs->localX; topLefty = cmd.CMDYA + Vdp1Regs->localY; spriteWidth = ((cmd.CMDSIZE >> 8) & 0x3F) * 8; spriteHeight = cmd.CMDSIZE & 0xFF; topRightx = topLeftx + (spriteWidth - 1); topRighty = topLefty; bottomRightx = topLeftx + (spriteWidth - 1); bottomRighty = topLefty + (spriteHeight - 1); bottomLeftx = topLeftx; bottomLefty = topLefty + (spriteHeight - 1); drawQuad(topLeftx,topLefty,bottomLeftx,bottomLefty,topRightx,topRighty,bottomRightx,bottomRighty); } void VIDSoftVdp1ScaledSpriteDraw(){ s32 topLeftx,topLefty,topRightx,topRighty,bottomRightx,bottomRighty,bottomLeftx,bottomLefty; int x0,y0,x1,y1; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); x0 = cmd.CMDXA + Vdp1Regs->localX; y0 = cmd.CMDYA + Vdp1Regs->localY; switch ((cmd.CMDCTRL >> 8) & 0xF) { case 0x0: // Only two coordinates default: x1 = ((int)cmd.CMDXC) - x0 + Vdp1Regs->localX + 1; y1 = ((int)cmd.CMDYC) - y0 + Vdp1Regs->localY + 1; break; case 0x5: // Upper-left x1 = ((int)cmd.CMDXB) + 1; y1 = ((int)cmd.CMDYB) + 1; break; case 0x6: // Upper-Center x1 = ((int)cmd.CMDXB); y1 = ((int)cmd.CMDYB); x0 = x0 - x1/2; x1++; y1++; break; case 0x7: // Upper-Right x1 = ((int)cmd.CMDXB); y1 = ((int)cmd.CMDYB); x0 = x0 - x1; x1++; y1++; break; case 0x9: // Center-left x1 = ((int)cmd.CMDXB); y1 = ((int)cmd.CMDYB); y0 = y0 - y1/2; x1++; y1++; break; case 0xA: // Center-center x1 = ((int)cmd.CMDXB); y1 = ((int)cmd.CMDYB); x0 = x0 - x1/2; y0 = y0 - y1/2; x1++; y1++; break; case 0xB: // Center-right x1 = ((int)cmd.CMDXB); y1 = ((int)cmd.CMDYB); x0 = x0 - x1; y0 = y0 - y1/2; x1++; y1++; break; case 0xD: // Lower-left x1 = ((int)cmd.CMDXB); y1 = ((int)cmd.CMDYB); y0 = y0 - y1; x1++; y1++; break; case 0xE: // Lower-center x1 = ((int)cmd.CMDXB); y1 = ((int)cmd.CMDYB); x0 = x0 - x1/2; y0 = y0 - y1; x1++; y1++; break; case 0xF: // Lower-right x1 = ((int)cmd.CMDXB); y1 = ((int)cmd.CMDYB); x0 = x0 - x1; y0 = y0 - y1; x1++; y1++; break; } topLeftx = x0; topLefty = y0; topRightx = x1+x0 - 1; topRighty = topLefty; bottomRightx = x1+x0 - 1; bottomRighty = y1+y0 - 1; bottomLeftx = topLeftx; bottomLefty = y1+y0 - 1; drawQuad(topLeftx,topLefty,bottomLeftx,bottomLefty,topRightx,topRighty,bottomRightx,bottomRighty); } void VIDSoftVdp1DistortedSpriteDraw() { s32 xa,ya,xb,yb,xc,yc,xd,yd; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); xa = (s32)(cmd.CMDXA + Vdp1Regs->localX); ya = (s32)(cmd.CMDYA + Vdp1Regs->localY); xb = (s32)(cmd.CMDXB + Vdp1Regs->localX); yb = (s32)(cmd.CMDYB + Vdp1Regs->localY); xc = (s32)(cmd.CMDXC + Vdp1Regs->localX); yc = (s32)(cmd.CMDYC + Vdp1Regs->localY); xd = (s32)(cmd.CMDXD + Vdp1Regs->localX); yd = (s32)(cmd.CMDYD + Vdp1Regs->localY); drawQuad(xa,ya,xd,yd,xb,yb,xc,yc); } static void gouraudLineSetup(double * redstep, double * greenstep, double * bluestep, int length, COLOR table1, COLOR table2) { gouraudTable(); *redstep =interpolate(table1.r,table2.r,length); *greenstep =interpolate(table1.g,table2.g,length); *bluestep =interpolate(table1.b,table2.b,length); leftColumnColor.r = table1.r; leftColumnColor.g = table1.g; leftColumnColor.b = table1.b; } void VIDSoftVdp1PolylineDraw(void) { int X[4]; int Y[4]; double redstep = 0, greenstep = 0, bluestep = 0; int length; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); X[0] = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C)); Y[0] = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E)); X[1] = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10)); Y[1] = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12)); X[2] = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14)); Y[2] = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16)); X[3] = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x18)); Y[3] = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x1A)); length = iterateOverLine(X[0], Y[0], X[1], Y[1], 1, NULL, NULL); gouraudLineSetup(&redstep,&greenstep,&bluestep,length, gouraudA, gouraudB); DrawLine(X[0], Y[0], X[1], Y[1], 0, 0,0,redstep,greenstep,bluestep); length = iterateOverLine(X[1], Y[1], X[2], Y[2], 1, NULL, NULL); gouraudLineSetup(&redstep,&greenstep,&bluestep,length, gouraudB, gouraudC); DrawLine(X[1], Y[1], X[2], Y[2], 0, 0,0,redstep,greenstep,bluestep); length = iterateOverLine(X[2], Y[2], X[3], Y[3], 1, NULL, NULL); gouraudLineSetup(&redstep,&greenstep,&bluestep,length, gouraudD, gouraudC); DrawLine(X[3], Y[3], X[2], Y[2], 0, 0,0,redstep,greenstep,bluestep); length = iterateOverLine(X[3], Y[3], X[0], Y[0], 1, NULL, NULL); gouraudLineSetup(&redstep,&greenstep,&bluestep,length, gouraudA,gouraudD); DrawLine(X[0], Y[0], X[3], Y[3], 0, 0,0,redstep,greenstep,bluestep); } void VIDSoftVdp1LineDraw(void) { int x1, y1, x2, y2; double redstep = 0, greenstep = 0, bluestep = 0; int length; Vdp1ReadCommand(&cmd, Vdp1Regs->addr); x1 = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0C)); y1 = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x0E)); x2 = (int)Vdp1Regs->localX + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x10)); y2 = (int)Vdp1Regs->localY + (int)((s16)T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x12)); length = iterateOverLine(x1, y1, x2, y2, 1, NULL, NULL); gouraudLineSetup(&redstep,&bluestep,&greenstep,length, gouraudA, gouraudB); DrawLine(x1, y1, x2, y2, 0, 0,0,redstep,greenstep,bluestep); } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp1UserClipping(void) { Vdp1Regs->userclipX1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); Vdp1Regs->userclipY1 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); Vdp1Regs->userclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); Vdp1Regs->userclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); #if 0 vdp1clipxstart = Vdp1Regs->userclipX1; vdp1clipxend = Vdp1Regs->userclipX2; vdp1clipystart = Vdp1Regs->userclipY1; vdp1clipyend = Vdp1Regs->userclipY2; // This needs work if (vdp1clipxstart > Vdp1Regs->systemclipX1) vdp1clipxstart = Vdp1Regs->userclipX1; else vdp1clipxstart = Vdp1Regs->systemclipX1; if (vdp1clipxend < Vdp1Regs->systemclipX2) vdp1clipxend = Vdp1Regs->userclipX2; else vdp1clipxend = Vdp1Regs->systemclipX2; if (vdp1clipystart > Vdp1Regs->systemclipY1) vdp1clipystart = Vdp1Regs->userclipY1; else vdp1clipystart = Vdp1Regs->systemclipY1; if (vdp1clipyend < Vdp1Regs->systemclipY2) vdp1clipyend = Vdp1Regs->userclipY2; else vdp1clipyend = Vdp1Regs->systemclipY2; #endif } ////////////////////////////////////////////////////////////////////////////// static void PushUserClipping(int mode) { if (mode == 1) { VDP1LOG("User clipping mode 1 not implemented\n"); return; } vdp1clipxstart = Vdp1Regs->userclipX1; vdp1clipxend = Vdp1Regs->userclipX2; vdp1clipystart = Vdp1Regs->userclipY1; vdp1clipyend = Vdp1Regs->userclipY2; // This needs work if (vdp1clipxstart > Vdp1Regs->systemclipX1) vdp1clipxstart = Vdp1Regs->userclipX1; else vdp1clipxstart = Vdp1Regs->systemclipX1; if (vdp1clipxend < Vdp1Regs->systemclipX2) vdp1clipxend = Vdp1Regs->userclipX2; else vdp1clipxend = Vdp1Regs->systemclipX2; if (vdp1clipystart > Vdp1Regs->systemclipY1) vdp1clipystart = Vdp1Regs->userclipY1; else vdp1clipystart = Vdp1Regs->systemclipY1; if (vdp1clipyend < Vdp1Regs->systemclipY2) vdp1clipyend = Vdp1Regs->userclipY2; else vdp1clipyend = Vdp1Regs->systemclipY2; } ////////////////////////////////////////////////////////////////////////////// static void PopUserClipping(void) { vdp1clipxstart = Vdp1Regs->systemclipX1; vdp1clipxend = Vdp1Regs->systemclipX2; vdp1clipystart = Vdp1Regs->systemclipY1; vdp1clipyend = Vdp1Regs->systemclipY2; } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp1SystemClipping(void) { Vdp1Regs->systemclipX1 = 0; Vdp1Regs->systemclipY1 = 0; Vdp1Regs->systemclipX2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x14); Vdp1Regs->systemclipY2 = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0x16); vdp1clipxstart = Vdp1Regs->systemclipX1; vdp1clipxend = Vdp1Regs->systemclipX2; vdp1clipystart = Vdp1Regs->systemclipY1; vdp1clipyend = Vdp1Regs->systemclipY2; } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp1LocalCoordinate(void) { Vdp1Regs->localX = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xC); Vdp1Regs->localY = T1ReadWord(Vdp1Ram, Vdp1Regs->addr + 0xE); } ////////////////////////////////////////////////////////////////////////////// int VIDSoftVdp2Reset(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp2DrawStart(void) { int titanblendmode = TITAN_BLEND_TOP; if (Vdp2Regs->CCCTL & 0x100) titanblendmode = TITAN_BLEND_ADD; else if (Vdp2Regs->CCCTL & 0x200) titanblendmode = TITAN_BLEND_BOTTOM; TitanSetBlendingMode(titanblendmode); Vdp2DrawBackScreen(); Vdp2DrawLineScreen(); } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp2DrawEnd(void) { int i, i2; u16 pixel; u8 prioritytable[8]; u32 vdp1coloroffset; int colormode = Vdp2Regs->SPCTL & 0x20; vdp2draw_struct info; int islinewindow; clipping_struct clip[2]; u32 linewnd0addr, linewnd1addr; int wctl; clipping_struct colorcalcwindow[2]; // Figure out whether to draw vdp1 framebuffer or vdp2 framebuffer pixels // based on priority if (Vdp1External.disptoggle && (Vdp2Regs->TVMD & 0x8000)) { int SPCCCS = (Vdp2Regs->SPCTL >> 12) & 0x3; int SPCCN = (Vdp2Regs->SPCTL >> 8) & 0x7; u8 colorcalctable[8]; vdp2rotationparameterfp_struct p; int x, y; prioritytable[0] = Vdp2Regs->PRISA & 0x7; prioritytable[1] = (Vdp2Regs->PRISA >> 8) & 0x7; prioritytable[2] = Vdp2Regs->PRISB & 0x7; prioritytable[3] = (Vdp2Regs->PRISB >> 8) & 0x7; prioritytable[4] = Vdp2Regs->PRISC & 0x7; prioritytable[5] = (Vdp2Regs->PRISC >> 8) & 0x7; prioritytable[6] = Vdp2Regs->PRISD & 0x7; prioritytable[7] = (Vdp2Regs->PRISD >> 8) & 0x7; colorcalctable[0] = ((~Vdp2Regs->CCRSA & 0x1F) << 1) + 1; colorcalctable[1] = ((~Vdp2Regs->CCRSA >> 7) & 0x3E) + 1; colorcalctable[2] = ((~Vdp2Regs->CCRSB & 0x1F) << 1) + 1; colorcalctable[3] = ((~Vdp2Regs->CCRSB >> 7) & 0x3E) + 1; colorcalctable[4] = ((~Vdp2Regs->CCRSC & 0x1F) << 1) + 1; colorcalctable[5] = ((~Vdp2Regs->CCRSC >> 7) & 0x3E) + 1; colorcalctable[6] = ((~Vdp2Regs->CCRSD & 0x1F) << 1) + 1; colorcalctable[7] = ((~Vdp2Regs->CCRSD >> 7) & 0x3E) + 1; vdp1coloroffset = (Vdp2Regs->CRAOFB & 0x70) << 4; vdp1spritetype = Vdp2Regs->SPCTL & 0xF; ReadVdp2ColorOffset(Vdp2Regs, &info, 0x40, 0x40); wctl = Vdp2Regs->WCTLC >> 8; clip[0].xstart = clip[0].ystart = clip[0].xend = clip[0].yend = 0; clip[1].xstart = clip[1].ystart = clip[1].xend = clip[1].yend = 0; ReadWindowData(wctl, clip); linewnd0addr = linewnd1addr = 0; ReadLineWindowData(&islinewindow, wctl, &linewnd0addr, &linewnd1addr); /* color calculation window: in => no color calc, out => color calc */ ReadWindowData(Vdp2Regs->WCTLD >> 8, colorcalcwindow); if (Vdp1Regs->TVMR & 2) Vdp2ReadRotationTableFP(0, &p); for (i2 = 0; i2 < vdp2height; i2++) { ReadLineWindowClip(islinewindow, clip, &linewnd0addr, &linewnd1addr); LoadLineParamsSprite(&info, i2); for (i = 0; i < vdp2width; i++) { // See if screen position is clipped, if it isn't, continue if (!TestBothWindow(wctl, clip, i * resxratio, i2)) { continue; } if (Vdp1Regs->TVMR & 2) { x = (touint(p.Xst + i * p.deltaX + i2 * p.deltaXst)) & (vdp1width - 1); y = (touint(p.Yst + i * p.deltaY + i2 * p.deltaYst)) & (vdp1height - 1); } else { x = i; y = i2; } if (vdp1pixelsize == 2) { // 16-bit pixel size pixel = ((u16 *)vdp1frontframebuffer)[(y * vdp1width) + x]; if (pixel == 0) ; else if (pixel & 0x8000 && colormode) { // 16 BPP u8 alpha = 0x3F; if ((SPCCCS == 3) && TestBothWindow(Vdp2Regs->WCTLD >> 8, colorcalcwindow, i, i2) && (Vdp2Regs->CCCTL & 0x40)) { alpha = colorcalctable[0]; if (Vdp2Regs->CCCTL & 0x300) alpha |= 0x80; } // if pixel is 0x8000, only draw pixel if sprite window // is disabled/sprite type 2-7. sprite types 0 and 1 are // -always- drawn and sprite types 8-F are always // transparent. if (pixel != 0x8000 || vdp1spritetype < 2 || (vdp1spritetype < 8 && !(Vdp2Regs->SPCTL & 0x10))) TitanPutPixel(prioritytable[0], i, i2, info.PostPixelFetchCalc(&info, COLSAT2YAB16(alpha, pixel)), 0); } else { // Color bank spritepixelinfo_struct spi; u8 alpha = 0x3F; u32 dot; Vdp1GetSpritePixelInfo(vdp1spritetype, &pixel, &spi); if (spi.normalshadow) { TitanPutShadow(prioritytable[spi.priority], i, i2); continue; } if (spi.msbshadow) { if (Vdp2Regs->SPCTL & 0x10) { /* sprite window, not handled yet... we avoid displaying garbage */ } else { /* msb shadow */ if (pixel) { dot = Vdp2ColorRamGetColor(vdp1coloroffset + pixel); TitanPutPixel(prioritytable[spi.priority], i, i2, info.PostPixelFetchCalc(&info, COLSAT2YAB32(0x3F, dot)), 0); } TitanPutShadow(prioritytable[spi.priority], i, i2); } continue; } dot = Vdp2ColorRamGetColor(vdp1coloroffset + pixel); if (TestBothWindow(Vdp2Regs->WCTLD >> 8, colorcalcwindow, i, i2) && (Vdp2Regs->CCCTL & 0x40)) { int transparent = 0; /* Sprite color calculation */ switch(SPCCCS) { case 0: if (prioritytable[spi.priority] <= SPCCN) transparent = 1; break; case 1: if (prioritytable[spi.priority] == SPCCN) transparent = 1; break; case 2: if (prioritytable[spi.priority] >= SPCCN) transparent = 1; break; case 3: if (dot & 0x80000000) transparent = 1; break; } if (Vdp2Regs->CCCTL & 0x200) { /* "bottom" mode, the alpha channel will be used by another layer, so we set it regardless of whether sprites are transparent or not. The highest priority bit is only set if the sprite is transparent (in this case, it's the alpha channel of the lower priority layer that will be used. */ alpha = colorcalctable[spi.colorcalc]; if (transparent) alpha |= 0x80; } else if (transparent) { alpha = colorcalctable[spi.colorcalc]; if (Vdp2Regs->CCCTL & 0x100) alpha |= 0x80; } } TitanPutPixel(prioritytable[spi.priority], i, i2, info.PostPixelFetchCalc(&info, COLSAT2YAB32(alpha, dot)), 0); } } else { // 8-bit pixel size pixel = vdp1frontframebuffer[(y * vdp1width) + x]; if (pixel != 0) { // Color bank(fix me) spritepixelinfo_struct spi; u8 alpha = 0x3F; u32 dot; Vdp1GetSpritePixelInfo(vdp1spritetype, &pixel, &spi); if (spi.normalshadow) { TitanPutShadow(prioritytable[spi.priority], i, i2); continue; } dot = Vdp2ColorRamGetColor(vdp1coloroffset + pixel); if (TestBothWindow(Vdp2Regs->WCTLD >> 8, colorcalcwindow, i, i2) && (Vdp2Regs->CCCTL & 0x40)) { int transparent = 0; /* Sprite color calculation */ switch(SPCCCS) { case 0: if (prioritytable[spi.priority] <= SPCCN) transparent = 1; break; case 1: if (prioritytable[spi.priority] == SPCCN) transparent = 1; break; case 2: if (prioritytable[spi.priority] >= SPCCN) transparent = 1; break; case 3: if (dot & 0x80000000) transparent = 1; break; } if (Vdp2Regs->CCCTL & 0x200) { /* "bottom" mode, the alpha channel will be used by another layer, so we set it regardless of whether sprites are transparent or not. The highest priority bit is only set if the sprite is transparent (in this case, it's the alpha channel of the lower priority layer that will be used. */ alpha = colorcalctable[spi.colorcalc]; if (transparent) alpha |= 0x80; } else if (transparent) { alpha = colorcalctable[spi.colorcalc]; if (Vdp2Regs->CCCTL & 0x100) alpha |= 0x80; } } TitanPutPixel(prioritytable[spi.priority], i, i2, info.PostPixelFetchCalc(&info, COLSAT2YAB32(alpha, dot)), 0); } } } } } TitanRender(dispbuffer); VIDSoftVdp1SwapFrameBuffer(); if (OSDUseBuffer()) OSDDisplayMessages(dispbuffer, vdp2width, vdp2height); #ifdef USE_OPENGL if (vdp2height == 224) i = 8; else i = 0; glRasterPos2i(0, outputheight * i / vdp2height); glPixelZoom((float)outputwidth / (float)vdp2width, 0 - ((float)outputheight / (float)(vdp2height+i+i))); glDrawPixels(vdp2width, vdp2height, GL_RGBA, GL_UNSIGNED_BYTE, dispbuffer); if (! OSDUseBuffer()) OSDDisplayMessages(NULL, -1, -1); #endif YuiSwapBuffers(); } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp2DrawScreens(void) { int i; VIDSoftVdp2SetResolution(Vdp2Regs->TVMD); VIDSoftVdp2SetPriorityNBG0(Vdp2Regs->PRINA & 0x7); VIDSoftVdp2SetPriorityNBG1((Vdp2Regs->PRINA >> 8) & 0x7); VIDSoftVdp2SetPriorityNBG2(Vdp2Regs->PRINB & 0x7); VIDSoftVdp2SetPriorityNBG3((Vdp2Regs->PRINB >> 8) & 0x7); VIDSoftVdp2SetPriorityRBG0(Vdp2Regs->PRIR & 0x7); for (i = 7; i > 0; i--) { if (nbg3priority == i) Vdp2DrawNBG3(); if (nbg2priority == i) Vdp2DrawNBG2(); if (nbg1priority == i) Vdp2DrawNBG1(); if (nbg0priority == i) Vdp2DrawNBG0(); if (rbg0priority == i) Vdp2DrawRBG0(); } } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp2DrawScreen(int screen) { VIDSoftVdp2SetResolution(Vdp2Regs->TVMD); VIDSoftVdp2SetPriorityNBG0(Vdp2Regs->PRINA & 0x7); VIDSoftVdp2SetPriorityNBG1((Vdp2Regs->PRINA >> 8) & 0x7); VIDSoftVdp2SetPriorityNBG2(Vdp2Regs->PRINB & 0x7); VIDSoftVdp2SetPriorityNBG3((Vdp2Regs->PRINB >> 8) & 0x7); VIDSoftVdp2SetPriorityRBG0(Vdp2Regs->PRIR & 0x7); switch(screen) { case 0: Vdp2DrawNBG0(); break; case 1: Vdp2DrawNBG1(); break; case 2: Vdp2DrawNBG2(); break; case 3: Vdp2DrawNBG3(); break; case 4: Vdp2DrawRBG0(); break; } } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp2SetResolution(u16 TVMD) { // This needs some work // Horizontal Resolution switch (TVMD & 0x7) { case 0: vdp2width = 320; resxratio=1; break; case 1: vdp2width = 352; resxratio=1; break; case 2: // 640 vdp2width = 320; resxratio=2; break; case 3: // 704 vdp2width = 352; resxratio=2; break; case 4: vdp2width = 320; resxratio=1; break; case 5: vdp2width = 352; resxratio=1; break; case 6: // 640 vdp2width = 320; resxratio=2; break; case 7: // 704 vdp2width = 352; resxratio=2; break; } // Vertical Resolution switch ((TVMD >> 4) & 0x3) { case 0: vdp2height = 224; break; case 1: vdp2height = 240; break; case 2: vdp2height = 256; break; default: break; } resyratio=1; // Check for interlace switch ((TVMD >> 6) & 0x3) { case 3: // Double-density Interlace // vdp2height *= 2; resyratio=2; break; case 2: // Single-density Interlace case 0: // Non-interlace default: break; } TitanSetResolution(vdp2width, vdp2height); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL VIDSoftVdp2SetPriorityNBG0(int priority) { nbg0priority = priority; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL VIDSoftVdp2SetPriorityNBG1(int priority) { nbg1priority = priority; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL VIDSoftVdp2SetPriorityNBG2(int priority) { nbg2priority = priority; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL VIDSoftVdp2SetPriorityNBG3(int priority) { nbg3priority = priority; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL VIDSoftVdp2SetPriorityRBG0(int priority) { rbg0priority = priority; } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp1SwapFrameBuffer(void) { if (((Vdp1Regs->FBCR & 2) == 0) || Vdp1External.manualchange) { u8 *temp = vdp1frontframebuffer; vdp1frontframebuffer = vdp1backframebuffer; vdp1backframebuffer = temp; Vdp1External.manualchange = 0; } } ////////////////////////////////////////////////////////////////////////////// void VIDSoftVdp1EraseFrameBuffer(void) { int i,i2; int w,h; if (((Vdp1Regs->FBCR & 2) == 0) || Vdp1External.manualerase) { h = (Vdp1Regs->EWRR & 0x1FF) + 1; if (h > vdp1height) h = vdp1height; w = ((Vdp1Regs->EWRR >> 6) & 0x3F8) + 8; if (w > vdp1width) w = vdp1width; if (vdp1pixelsize == 2) { for (i2 = (Vdp1Regs->EWLR & 0x1FF); i2 < h; i2++) { for (i = ((Vdp1Regs->EWLR >> 6) & 0x1F8); i < w; i++) ((u16 *)vdp1backframebuffer)[(i2 * vdp1width) + i] = Vdp1Regs->EWDR; } } else { for (i2 = (Vdp1Regs->EWLR & 0x1FF); i2 < h; i2++) { for (i = ((Vdp1Regs->EWLR >> 6) & 0x1F8); i < w; i++) vdp1backframebuffer[(i2 * vdp1width) + i] = Vdp1Regs->EWDR & 0xFF; } } Vdp1External.manualerase = 0; } } ////////////////////////////////////////////////////////////////////////////// void VIDSoftGetGlSize(int *width, int *height) { #ifdef USE_OPENGL *width = outputwidth; *height = outputheight; #else *width = vdp2width; *height = vdp2height; #endif } yabause-0.9.13.1/src/sh2d.c000644 001750 001750 00000035446 12256006126 017215 0ustar00guillaumeguillaume000000 000000 /* * sh2d * Bart Trzynadlowski, July 24, 2000 * Public domain */ #include #include #include #include "sh2d.h" #if 0 #include #endif #define ZERO_F 0 /* 0 format */ #define N_F 1 /* n format */ #define M_F 2 /* m format */ #define NM_F 3 /* nm format */ #define MD_F 4 /* md format */ #define ND4_F 5 /* nd4 format */ #define NMD_F 6 /* nmd format */ #define D_F 7 /* d format */ #define D12_F 8 /* d12 format */ #define ND8_F 9 /* nd8 format */ #define I_F 10 /* i format */ #define NI_F 11 /* ni format */ typedef struct { int format; const char *mnem; unsigned short mask; /* mask used to obtain opcode bits */ unsigned short bits; /* opcode bits */ int dat; /* specific data for situation */ int sh2; /* SH-2 specific */ } i_descr; i_descr tab[] = { { ZERO_F, "clrt", 0xffff, 0x8, 0, 0 }, { ZERO_F, "clrmac", 0xffff, 0x28, 0, 0 }, { ZERO_F, "div0u", 0xffff, 0x19, 0, 0 }, { ZERO_F, "nop", 0xffff, 0x9, 0, 0 }, { ZERO_F, "rte", 0xffff, 0x2b, 0, 0 }, { ZERO_F, "rts", 0xffff, 0xb, 0, 0 }, { ZERO_F, "sett", 0xffff, 0x18, 0, 0 }, { ZERO_F, "sleep", 0xffff, 0x1b, 0, 0 }, { N_F, "cmp/pl r%d", 0xf0ff, 0x4015, 0, 0 }, { N_F, "cmp/pz r%d", 0xf0ff, 0x4011, 0, 0 }, { N_F, "dt r%d", 0xf0ff, 0x4010, 0, 1 }, { N_F, "movt r%d", 0xf0ff, 0x0029, 0, 0 }, { N_F, "rotl r%d", 0xf0ff, 0x4004, 0, 0 }, { N_F, "rotr r%d", 0xf0ff, 0x4005, 0, 0 }, { N_F, "rotcl r%d", 0xf0ff, 0x4024, 0, 0 }, { N_F, "rotcr r%d", 0xf0ff, 0x4025, 0, 0 }, { N_F, "shal r%d", 0xf0ff, 0x4020, 0, 0 }, { N_F, "shar r%d", 0xf0ff, 0x4021, 0, 0 }, { N_F, "shll r%d", 0xf0ff, 0x4000, 0, 0 }, { N_F, "shlr r%d", 0xf0ff, 0x4001, 0, 0 }, { N_F, "shll2 r%d", 0xf0ff, 0x4008, 0, 0 }, { N_F, "shlr2 r%d", 0xf0ff, 0x4009, 0, 0 }, { N_F, "shll8 r%d", 0xf0ff, 0x4018, 0, 0 }, { N_F, "shlr8 r%d", 0xf0ff, 0x4019, 0, 0 }, { N_F, "shll16 r%d", 0xf0ff, 0x4028, 0, 0 }, { N_F, "shlr16 r%d", 0xf0ff, 0x4029, 0, 0 }, { N_F, "stc sr, r%d", 0xf0ff, 0x0002, 0, 0 }, { N_F, "stc gbr, r%d", 0xf0ff, 0x0012, 0, 0 }, { N_F, "stc vbr, r%d", 0xf0ff, 0x0022, 0, 0 }, { N_F, "sts mach, r%d", 0xf0ff, 0x000a, 0, 0 }, { N_F, "sts macl, r%d", 0xf0ff, 0x001a, 0, 0 }, { N_F, "sts pr, r%d", 0xf0ff, 0x002a, 0, 0 }, { N_F, "tas.b @r%d", 0xf0ff, 0x401b, 0, 0 }, { N_F, "stc.l sr, @-r%d", 0xf0ff, 0x4003, 0, 0 }, { N_F, "stc.l gbr, @-r%d", 0xf0ff, 0x4013, 0, 0 }, { N_F, "stc.l vbr, @-r%d", 0xf0ff, 0x4023, 0, 0 }, { N_F, "sts.l mach, @-r%d", 0xf0ff, 0x4002, 0, 0 }, { N_F, "sts.l macl, @-r%d", 0xf0ff, 0x4012, 0, 0 }, { N_F, "sts.l pr, @-r%d", 0xf0ff, 0x4022, 0, 0 }, { M_F, "ldc r%d, sr", 0xf0ff, 0x400e, 0, 0 }, { M_F, "ldc r%d, gbr", 0xf0ff, 0x401e, 0, 0 }, { M_F, "ldc r%d, vbr", 0xf0ff, 0x402e, 0, 0 }, { M_F, "lds r%d, mach", 0xf0ff, 0x400a, 0, 0 }, { M_F, "lds r%d, macl", 0xf0ff, 0x401a, 0, 0 }, { M_F, "lds r%d, pr", 0xf0ff, 0x402a, 0, 0 }, { M_F, "jmp @r%d", 0xf0ff, 0x402b, 0, 0 }, { M_F, "jsr @r%d", 0xf0ff, 0x400b, 0, 0 }, { M_F, "ldc.l @r%d+, sr", 0xf0ff, 0x4007, 0, 0 }, { M_F, "ldc.l @r%d+, gbr", 0xf0ff, 0x4017, 0, 0 }, { M_F, "ldc.l @r%d+, vbr", 0xf0ff, 0x4027, 0, 0 }, { M_F, "lds.l @r%d+, mach", 0xf0ff, 0x4006, 0, 0 }, { M_F, "lds.l @r%d+, macl", 0xf0ff, 0x4016, 0, 0 }, { M_F, "lds.l @r%d+, pr", 0xf0ff, 0x4026, 0, 0 }, { M_F, "braf r%d", 0xf0ff, 0x0023, 0, 1 }, { M_F, "bsrf r%d", 0xf0ff, 0x0003, 0, 1 }, { NM_F, "add r%d, r%d", 0xf00f, 0x300c, 0, 0 }, { NM_F, "addc r%d, r%d", 0xf00f, 0x300e, 0, 0 }, { NM_F, "addv r%d, r%d", 0xf00f, 0x300f, 0, 0 }, { NM_F, "and r%d, r%d", 0xf00f, 0x2009, 0, 0 }, { NM_F, "cmp/eq r%d, r%d", 0xf00f, 0x3000, 0, 0 }, { NM_F, "cmp/hs r%d, r%d", 0xf00f, 0x3002, 0, 0 }, { NM_F, "cmp/ge r%d, r%d", 0xf00f, 0x3003, 0, 0 }, { NM_F, "cmp/hi r%d, r%d", 0xf00f, 0x3006, 0, 0 }, { NM_F, "cmp/gt r%d, r%d", 0xf00f, 0x3007, 0, 0 }, { NM_F, "cmp/str r%d, r%d", 0xf00f, 0x200c, 0, 0 }, { NM_F, "div1 r%d, r%d", 0xf00f, 0x3004, 0, 0 }, { NM_F, "div0s r%d, r%d", 0xf00f, 0x2007, 0, 0 }, { NM_F, "dmuls.l r%d, r%d", 0xf00f, 0x300d, 0, 1 }, { NM_F, "dmulu.l r%d, r%d", 0xf00f, 0x3005, 0, 1 }, { NM_F, "exts.b r%d, r%d", 0xf00f, 0x600e, 0, 0 }, { NM_F, "exts.w r%d, r%d", 0xf00f, 0x600f, 0, 0 }, { NM_F, "extu.b r%d, r%d", 0xf00f, 0x600c, 0, 0 }, { NM_F, "extu.w r%d, r%d", 0xf00f, 0x600d, 0, 0 }, { NM_F, "mov r%d, r%d", 0xf00f, 0x6003, 0, 0 }, { NM_F, "mul.l r%d, r%d", 0xf00f, 0x0007, 0, 1 }, { NM_F, "muls.w r%d, r%d", 0xf00f, 0x200f, 0, 0 }, { NM_F, "mulu.w r%d, r%d", 0xf00f, 0x200e, 0, 0 }, { NM_F, "neg r%d, r%d", 0xf00f, 0x600b, 0, 0 }, { NM_F, "negc r%d, r%d", 0xf00f, 0x600a, 0, 0 }, { NM_F, "not r%d, r%d", 0xf00f, 0x6007, 0, 0 }, { NM_F, "or r%d, r%d", 0xf00f, 0x200b, 0, 0 }, { NM_F, "sub r%d, r%d", 0xf00f, 0x3008, 0, 0 }, { NM_F, "subc r%d, r%d", 0xf00f, 0x300a, 0, 0 }, { NM_F, "subv r%d, r%d", 0xf00f, 0x300b, 0, 0 }, { NM_F, "swap.b r%d, r%d", 0xf00f, 0x6008, 0, 0 }, { NM_F, "swap.w r%d, r%d", 0xf00f, 0x6009, 0, 0 }, { NM_F, "tst r%d, r%d", 0xf00f, 0x2008, 0, 0 }, { NM_F, "xor r%d, r%d", 0xf00f, 0x200a, 0, 0 }, { NM_F, "xtrct r%d, r%d", 0xf00f, 0x200d, 0, 0 }, { NM_F, "mov.b r%d, @r%d", 0xf00f, 0x2000, 0, 0 }, { NM_F, "mov.w r%d, @r%d", 0xf00f, 0x2001, 0, 0 }, { NM_F, "mov.l r%d, @r%d", 0xf00f, 0x2002, 0, 0 }, { NM_F, "mov.b @r%d, r%d", 0xf00f, 0x6000, 0, 0 }, { NM_F, "mov.w @r%d, r%d", 0xf00f, 0x6001, 0, 0 }, { NM_F, "mov.l @r%d, r%d", 0xf00f, 0x6002, 0, 0 }, { NM_F, "mac.l @r%d+, @r%d+", 0xf00f, 0x000f, 0, 1 }, { NM_F, "mac.w @r%d+, @r%d+", 0xf00f, 0x400f, 0, 0 }, { NM_F, "mov.b @r%d+, r%d", 0xf00f, 0x6004, 0, 0 }, { NM_F, "mov.w @r%d+, r%d", 0xf00f, 0x6005, 0, 0 }, { NM_F, "mov.l @r%d+, r%d", 0xf00f, 0x6006, 0, 0 }, { NM_F, "mov.b r%d, @-r%d", 0xf00f, 0x2004, 0, 0 }, { NM_F, "mov.w r%d, @-r%d", 0xf00f, 0x2005, 0, 0 }, { NM_F, "mov.l r%d, @-r%d", 0xf00f, 0x2006, 0, 0 }, { NM_F, "mov.b r%d, @(r0, r%d)", 0xf00f, 0x0004, 0, 0 }, { NM_F, "mov.w r%d, @(r0, r%d)", 0xf00f, 0x0005, 0, 0 }, { NM_F, "mov.l r%d, @(r0, r%d)", 0xf00f, 0x0006, 0, 0 }, { NM_F, "mov.b @(r0, r%d), r%d", 0xf00f, 0x000c, 0, 0 }, { NM_F, "mov.w @(r0, r%d), r%d", 0xf00f, 0x000d, 0, 0 }, { NM_F, "mov.l @(r0, r%d), r%d", 0xf00f, 0x000e, 0, 0 }, { MD_F, "mov.b @(0x%03X, r%d), r0", 0xff00, 0x8400, 0, 0 }, { MD_F, "mov.w @(0x%03X, r%d), r0", 0xff00, 0x8500, 0, 0 }, { ND4_F, "mov.b r0, @(0x%03X, r%d)", 0xff00, 0x8000, 0, 0 }, { ND4_F, "mov.w r0, @(0x%03X, r%d)", 0xff00, 0x8100, 0, 0 }, { NMD_F, "mov.l r%d, @(0x%03X, r%d)", 0xf000, 0x1000, 0,0 }, { NMD_F, "mov.l @(0x%03X, r%d), r%d", 0xf000, 0x5000, 0,0 }, { D_F, "mov.b r0, @(0x%03X, gbr)", 0xff00, 0xc000, 1, 0 }, { D_F, "mov.w r0, @(0x%03X, gbr)", 0xff00, 0xc100, 2, 0 }, { D_F, "mov.l r0, @(0x%03X, gbr)", 0xff00, 0xc200, 4, 0 }, { D_F, "mov.b @(0x%03X, gbr), r0", 0xff00, 0xc400, 1, 0 }, { D_F, "mov.w @(0x%03X, gbr), r0", 0xff00, 0xc500, 2, 0 }, { D_F, "mov.l @(0x%03X, gbr), r0", 0xff00, 0xc600, 4, 0 }, { D_F, "mova @(0x%03X, pc), r0", 0xff00, 0xc700, 4, 0 }, { D_F, "bf 0x%08X", 0xff00, 0x8b00, 5, 0 }, { D_F, "bf/s 0x%08X", 0xff00, 0x8f00, 5, 1 }, { D_F, "bt 0x%08X", 0xff00, 0x8900, 5, 0 }, { D_F, "bt/s 0x%08X", 0xff00, 0x8d00, 5, 1 }, { D12_F, "bra 0x%08X", 0xf000, 0xa000, 0, 0 }, { D12_F, "bsr 0x%08X", 0xf000, 0xb000, 0, 0 }, { ND8_F, "mov.w @(0x%03X, pc), r%d", 0xf000, 0x9000, 2, 0 }, { ND8_F, "mov.l @(0x%03X, pc), r%d", 0xf000, 0xd000, 4, 0 }, { I_F, "and.b #0x%02X, @(r0, gbr)", 0xff00, 0xcd00, 0,0 }, { I_F, "or.b #0x%02X, @(r0, gbr)", 0xff00, 0xcf00, 0,0 }, { I_F, "tst.b #0x%02X, @(r0, gbr)", 0xff00, 0xcc00, 0,0 }, { I_F, "xor.b #0x%02X, @(r0, gbr)", 0xff00, 0xce00, 0,0 }, { I_F, "and #0x%02X, r0", 0xff00, 0xc900, 0, 0 }, { I_F, "cmp/eq #0x%02X, r0", 0xff00, 0x8800, 0, 0 }, { I_F, "or #0x%02X, r0", 0xff00, 0xcb00, 0, 0 }, { I_F, "tst #0x%02X, r0", 0xff00, 0xc800, 0, 0 }, { I_F, "xor #0x%02X, r0", 0xff00, 0xca00, 0, 0 }, { I_F, "trapa #0x%X", 0xff00, 0xc300, 0, 0 }, { NI_F, "add #0x%02X, r%d", 0xf000, 0x7000, 0, 0 }, { NI_F, "mov #0x%02X, r%d", 0xf000, 0xe000, 0, 0 }, { 0, NULL, 0, 0, 0, 0 } }; /* * SH2Disasm(): SH-1/SH-2 disassembler routine. If mode = 0 then SH-2 mode, * otherwise SH-1 mode */ void SH2Disasm(u32 v_addr, u16 op, int mode, char *string) { int i; sprintf(string,"0x%08X: ", (unsigned int)v_addr); string+=strlen(string); for (i = 0; tab[i].mnem != NULL; i++) /* 0 format */ { if ((op & tab[i].mask) == tab[i].bits) { if (tab[i].sh2 && mode) /* if SH-1 mode, no SH-2 */ sprintf(string,"unrecognized"); else if (tab[i].format == ZERO_F) sprintf(string,"%s", tab[i].mnem); else if (tab[i].format == N_F) sprintf(string,tab[i].mnem, (op >> 8) & 0xf); else if (tab[i].format == M_F) sprintf(string,tab[i].mnem, (op >> 8) & 0xf); else if (tab[i].format == NM_F) sprintf(string,tab[i].mnem, (op >> 4) & 0xf, (op >> 8) & 0xf); else if (tab[i].format == MD_F) { if (op & 0x100) sprintf(string,tab[i].mnem, (op & 0xf) * 2, (op >> 4) & 0xf); else sprintf(string,tab[i].mnem, op & 0xf, (op >> 4) & 0xf); } else if (tab[i].format == ND4_F) { if (op & 0x100) sprintf(string,tab[i].mnem, (op & 0xf) * 2, (op >> 4) & 0xf); else sprintf(string,tab[i].mnem, (op & 0xf), (op >> 4) & 0xf); } else if (tab[i].format == NMD_F) { if ((op & 0xf000) == 0x1000) sprintf(string,tab[i].mnem, (op >> 4) & 0xf, (op & 0xf) * 4, (op >> 8) & 0xf); else sprintf(string,tab[i].mnem, (op & 0xf) * 4, (op >> 4) & 0xf, (op >> 8) & 0xf); } else if (tab[i].format == D_F) { if (tab[i].dat <= 4) { if ((op & 0xff00) == 0xc700) { sprintf(string,tab[i].mnem, (op & 0xff) * tab[i].dat + 4); string+=strlen(string); sprintf(string," ; 0x%08X", ((op & 0xff) * tab[i].dat + 4 + (unsigned int)v_addr) & 0xfffffffc); } else sprintf(string,tab[i].mnem, (op & 0xff) * tab[i].dat); } else { if (op & 0x80) /* sign extend */ sprintf(string,tab[i].mnem, (((op & 0xff) + 0xffffff00) * 2) + v_addr + 4); else sprintf(string,tab[i].mnem,((op & 0xff) * 2) + v_addr + 4); } } else if (tab[i].format == D12_F) { if (op & 0x800) /* sign extend */ sprintf(string,tab[i].mnem, ((op & 0xfff) + 0xfffff000) * 2 + v_addr + 4); else sprintf(string,tab[i].mnem, (op & 0xfff) * 2 + v_addr + 4); } else if (tab[i].format == ND8_F) { if ((op & 0xf000) == 0x9000) /* .W */ { sprintf(string,tab[i].mnem, (op & 0xff) * tab[i].dat + 4, (op >> 8) & 0xf); string+=strlen(string); sprintf(string," ; 0x%08X", (op & 0xff) * tab[i].dat + 4 + (unsigned int)v_addr); } else /* .L */ { sprintf(string,tab[i].mnem, (op & 0xff) * tab[i].dat + 4, (op >> 8) & 0xf); string+=strlen(string); sprintf(string," ; 0x%08X", ((op & 0xff) * tab[i].dat + 4 + (unsigned int)v_addr) & 0xfffffffc); } } else if (tab[i].format == I_F) sprintf(string,tab[i].mnem, op & 0xff); else if (tab[i].format == NI_F) sprintf(string,tab[i].mnem, op & 0xff, (op >> 8) & 0xf); else sprintf(string,"unrecognized"); return; } } sprintf(string,"unrecognized"); } yabause-0.9.13.1/src/logo.png000644 001750 001750 00000004760 12256006161 017651 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDR }JbgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe< ‚IDATxÚbüÿÿ?2 &!&Óåäqú@ ÛÝÔE@ªÖîùÿÿv_å€bq€‚ÿø'ˆ @Œèf Ø fÙÿòÊþ Ó÷°Ów Fàû^}ûù €z€üûO¿ý3O>|ë@ edVýÏÄbüj“0§ìRŸ‘Iæ?@ÁlÝäÜO/:„Ò @qF€bÚÁ 4òú¿¿ý€–ÿ©ïÞÿ‹e™§î6WžûO?g ° 0 Ò Å01€Âð: F êF&ñõÿÿ½dØæÂ ò'—÷>žO É¿¿Ï0ØV™ÿª:“þ³2Ë5¼±ÙYdÉ@ €à[ïÈíÀÈp™AM×ñ\QÝF;׺~ü~l² €@F¾ôØyƒó6³¬ã¬åWöÿûû—ašóú㱎ÿo¾ÿ¢ @ øáÙôæ{wŸým«ÝÁÄøßüëç O¿üÐ °/€Öœª1‡:|0ð#ÜwÞ{ €P¼ Š%¨iŒ01€+J8 yÿPÑô0Á¢î@ÁmJ¾gf5`fÓf`f¼ÅðýˉÀpsJùñv¯=_O ˆ¥1|ûúƒá××E !ò¬²«îÝ{@ðh†› ‡¬ (ª¬œÿ÷ ß‹6í±ù¿¹Ã\°T=+s,&mù Θ_+Zv1HH1pqs€m5©”áΕý‡&3\úûÿ¿53scÆñ‡”@ÕA© ¨™ Í¿ÿ'Ì96 «!ŽáÍýó®Ólyw[û1¼¹~‰ááÍk ‡Ÿ¿Ÿ @0Dõ,âç@ rÅ¥?ÿþücæ’dû÷ÍÈbÑïþy^}ói0¾ûñ{{ßÍ7^„’\‘Ã]¶IY³o20]åä±øô»=6µD09D±Ê/ \®€,J·X‘@W@,@âPò>0Ó!+>LD Àû-løÙ˜Îÿf€¨ùÿ˜ÿ±u‚ ËÂáÆÀÅç â;S/ž Äû¾®eæÔË11Ë€4ƒRéVB€‚‡3‹l?‹L¤l0Û†{7æ‚3 Ð5 g¿×6ªaøøá3Ó{AEÈðo Ø âo«3÷¢¿¿Ÿ0pr~ePRÑaàã—y%¤Ùŧ–ARJ˜áõó¥ å…ÀD÷d'óÌ n | òÊí«sÄ€I8)gH|¾ˆ˜<ƒŽ¾ÃÏï÷€øÍ‡®<¡zþ‚€%e §nƒJ+ Éÿ¶öË)¤Lbæ«|÷öCM¾ ÃBV›?~÷ýþ÷ÿŽ,/g7+ów€¹ ˆ{@K ¿ Ý?ÇðèÁ^>n0žÔàÌËÔ $*~Ä= ØLC˜7êÊÛOçzþ(Šœ€ø Zü‚ï¿ñ%0ÿÕó ­–ÌodååXX˜ÎÂÀŬ ’ Îq%0 9ÑÒοçOßüâ' Œ ¥Ü¬Lï\<Çpcï6†Ÿ?1|üù”F–_r¯â¹ ¢öóïÿ¼Ül¬ŠLÿÿdù6@üìÂëþÌüöëïwFFFó¦Ë//< )Rõ@Ì^îýúÇ0˜'ýü=‰Ff‚V‰ìû¿Öɨ—‚òˆýÝkÝbÀpaÅ–™gn„áÈ‹M@Qœˆb(kW€2Ü}ôò PØ$ aÏXvæ‡ðíÏA9ƒy Hþƒ±@ê>@Íú@èUçz À,2XØ‘ŒÿÃð÷ç6¦ÿøYÃÛrí J@ ¢¥ø÷ô›¿žn~ü{°ÙÃÀÎíÏðï"Aýù¹Ÿù߯-Ÿ~<ò…‰F¤@a`çòf`å``gce`cgeøøf+°Ž»’š,’@IT,½&ϿВg>»0ƒ¤\,ï_~ýüÍðãç7†o72üÿt<cØr;.P9JÎÿúþÖ4à.«(qìÕß“_ÿü—ã4c—vdàäbuì Oîocxúè(HÙ`:Bݤ D%t´ ¾ÿ Ư_\bxþh4È Ž~ Íýÿ¡Å!@!gDPÜ€Š= ÎâÕÀRÝd°€ƒ‰U&7s0<¾ŒáðÞ™0ƒA5-{[ç, Å?¾~ùÁpòðL†OÏ¡8j3Ô ôÇ @°¢”Iu¸ˆ­8<ë‘ZDûAlÿðvE`aÏÃà †¿0<z›AAYŸáÏï ß¿ý`xÿî=ÃÒ¹ Þ=e–Å+í9üþûO˜™?r²0½÷Úuó/Ô1*ÏA–ûq ³±ÐòÛhé”ØÎƒj_[ç¿Ð\¸#@¥ëŸ?Á–?¼aÑÌ `ëŒáC‘K–"÷ÿR^.C.66†ß¿2|ªyýý×K`Û¨B‘{ /ó_€b‚VéÉ@¬JDè–CK!P{QÈl8¼w ôž4Œt³iͰåLŒ çZ±Æj ³O³¶±6ôž¸”Á"§’AI]›AZ€—A‘SêóÏ¿ú˜ªƒ䀯Ðv0¨x÷‹®BèˆF}ÿÎEp-÷úÕ{†—/Þ‚éWN2øWvÆj  Ìÿþð½{xáRg Ãí³Þ<{Œ¦ß Iîÿø÷ä!€‚¥} {3Ëñ/ ^mR\ºò'ZtLU¿Y¥ëXYY^}|Ÿïó­Û’ç[ÿʲý²`ddðš¿õþ‡oâÿÿg ƒŸ'ÿ€ÞûtÀËo¿Þ}ûµS’—#¦äìÓ7„œ @¡á m]š yäˆÏP\Û€ZP€ PËlÖûŸ¿o>úøÝøÝ¯ßfÿþýg&Â+BlÊÎ=}S @8ë ƒ„€”:4[ªCK<˜…ß]¯—¿ÿ³Þù_äÙ©_ú,¯œ’{óáHí$`T!¶. ’+#`€¢ˆ‹×X©3šL¿~>eøýóˆïˆ­mŠ  X+ˆøö§'b@ xu `^öÑ«¾“ÚIEND®B`‚yabause-0.9.13.1/src/scsp.h000644 001750 001750 00000010225 12256006143 017315 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004 Stephane Dallongeville Copyright 2004-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // Quick hack so nobody else needs to know we're using a different header file #ifdef USE_SCSP2 #include "scsp2.h" // defines SCSP_H #endif #ifndef SCSP_H #define SCSP_H #include "core.h" #define SNDCORE_DEFAULT -1 #define SNDCORE_DUMMY 0 #define SNDCORE_WAV 10 // should really be 1, but I'll probably break people's stuff #define SCSP_MUTE_SYSTEM 1 #define SCSP_MUTE_USER 2 typedef struct { int id; const char *Name; int (*Init)(void); void (*DeInit)(void); int (*Reset)(void); int (*ChangeVideoFormat)(int vertfreq); void (*UpdateAudio)(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples); u32 (*GetAudioSpace)(void); void (*MuteAudio)(void); void (*UnMuteAudio)(void); void (*SetVolume)(int volume); } SoundInterface_struct; typedef struct { u32 D[8]; u32 A[8]; u32 SR; u32 PC; } m68kregs_struct; typedef struct { u32 addr; } m68kcodebreakpoint_struct; #define MAX_BREAKPOINTS 10 typedef struct { u32 scsptiming1; u32 scsptiming2; // 16.16 fixed point m68kcodebreakpoint_struct codebreakpoint[MAX_BREAKPOINTS]; int numcodebreakpoints; void (*BreakpointCallBack)(u32); int inbreakpoint; } ScspInternal; extern SoundInterface_struct SNDDummy; extern SoundInterface_struct SNDWave; extern u8 *SoundRam; u8 FASTCALL SoundRamReadByte(u32 addr); u16 FASTCALL SoundRamReadWord(u32 addr); u32 FASTCALL SoundRamReadLong(u32 addr); void FASTCALL SoundRamWriteByte(u32 addr, u8 val); void FASTCALL SoundRamWriteWord(u32 addr, u16 val); void FASTCALL SoundRamWriteLong(u32 addr, u32 val); int ScspInit(int coreid); int ScspChangeSoundCore(int coreid); void ScspSetFrameAccurate(int on); void ScspDeInit(void); void M68KStart(void); void M68KStop(void); void ScspReset(void); int ScspChangeVideoFormat(int type); void M68KExec(s32 cycles); void ScspExec(void); void ScspConvert32uto16s(s32 *srcL, s32 *srcR, s16 *dst, u32 len); void ScspReceiveCDDA(const u8 *sector); int SoundSaveState(FILE *fp); int SoundLoadState(FILE *fp, int version, int size); void ScspSlotDebugStats(u8 slotnum, char *outstring); void ScspCommonControlRegisterDebugStats(char *outstring); int ScspSlotDebugSaveRegisters(u8 slotnum, const char *filename); int ScspSlotDebugAudioSaveWav(u8 slotnum, const char *filename); void ScspMuteAudio(int flags); void ScspUnMuteAudio(int flags); void ScspSetVolume(int volume); void FASTCALL scsp_w_b(u32, u8); void FASTCALL scsp_w_w(u32, u16); void FASTCALL scsp_w_d(u32, u32); u8 FASTCALL scsp_r_b(u32); u16 FASTCALL scsp_r_w(u32); u32 FASTCALL scsp_r_d(u32); void scsp_init(u8 *scsp_ram, void (*sint_hand)(u32), void (*mint_hand)(void)); void scsp_shutdown(void); void scsp_reset(void); void scsp_midi_in_send(u8 data); void scsp_midi_out_send(u8 data); u8 scsp_midi_in_read(void); u8 scsp_midi_out_read(void); void scsp_update(s32 *bufL, s32 *bufR, u32 len); void scsp_update_monitor(void); void scsp_update_timer(u32 len); u32 FASTCALL c68k_word_read(const u32 adr); void M68KStep(void); void M68KSync(void); void M68KWriteNotify(u32 address, u32 size); void M68KGetRegisters(m68kregs_struct *regs); void M68KSetRegisters(m68kregs_struct *regs); void M68KSetBreakpointCallBack(void (*func)(u32)); int M68KAddCodeBreakpoint(u32 addr); void M68KSortCodeBreakpoints(void); int M68KDelCodeBreakpoint(u32 addr); m68kcodebreakpoint_struct *M68KGetBreakpointList(void); void M68KClearCodeBreakpoints(void); #endif yabause-0.9.13.1/src/cs2.c000644 001750 001750 00000375706 12256006135 017052 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003 Guillaume Duhamel Copyright 2004-2006, 2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "cs2.h" #include "debug.h" #include "error.h" #include "japmodem.h" #include "netlink.h" #include "scsp.h" #include "scu.h" #include "smpc.h" #include "yui.h" #define CDB_HIRQ_CMOK 0x0001 #define CDB_HIRQ_DRDY 0x0002 #define CDB_HIRQ_CSCT 0x0004 #define CDB_HIRQ_BFUL 0x0008 #define CDB_HIRQ_PEND 0x0010 #define CDB_HIRQ_DCHG 0x0020 #define CDB_HIRQ_ESEL 0x0040 #define CDB_HIRQ_EHST 0x0080 #define CDB_HIRQ_ECPY 0x0100 #define CDB_HIRQ_EFLS 0x0200 #define CDB_HIRQ_SCDQ 0x0400 #define CDB_HIRQ_MPED 0x0800 #define CDB_HIRQ_MPCM 0x1000 #define CDB_HIRQ_MPST 0x2000 #define CDB_STAT_BUSY 0x00 #define CDB_STAT_PAUSE 0x01 #define CDB_STAT_STANDBY 0x02 #define CDB_STAT_PLAY 0x03 #define CDB_STAT_SEEK 0x04 #define CDB_STAT_SCAN 0x05 #define CDB_STAT_OPEN 0x06 #define CDB_STAT_NODISC 0x07 #define CDB_STAT_RETRY 0x08 #define CDB_STAT_ERROR 0x09 #define CDB_STAT_FATAL 0x0A #define CDB_STAT_PERI 0x20 #define CDB_STAT_TRNS 0x40 #define CDB_STAT_WAIT 0x80 #define CDB_STAT_REJECT 0xFF #define CDB_PLAYTYPE_SECTOR 0x01 #define CDB_PLAYTYPE_FILE 0x02 #define ToBCD(val) ((val % 10 ) + ((val / 10 ) << 4)) Cs2 * Cs2Area = NULL; ip_struct *cdip = NULL; extern CDInterface *CDCoreList[]; ////////////////////////////////////////////////////////////////////////////// static INLINE void doCDReport(u8 status) { Cs2Area->reg.CR1 = (status << 8) | ((Cs2Area->options & 0xF) << 4) | (Cs2Area->repcnt & 0xF); Cs2Area->reg.CR2 = (Cs2Area->ctrladdr << 8) | Cs2Area->track; Cs2Area->reg.CR3 = (u16)((Cs2Area->index << 8) | ((Cs2Area->FAD >> 16) & 0xFF)); Cs2Area->reg.CR4 = (u16) Cs2Area->FAD; } ////////////////////////////////////////////////////////////////////////////// static INLINE void doMPEGReport(u8 status) { Cs2Area->reg.CR1 = (status << 8) | Cs2Area->actionstatus; Cs2Area->reg.CR2 = Cs2Area->vcounter; Cs2Area->reg.CR3 = (Cs2Area->pictureinfo << 8) | Cs2Area->mpegaudiostatus; Cs2Area->reg.CR4 = Cs2Area->mpegvideostatus; } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL Cs2ReadByte(u32 addr) { return CartridgeArea->Cs2ReadByte(addr); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Cs2WriteByte(u32 addr, u8 val) { CartridgeArea->Cs2WriteByte(addr, val); } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL Cs2ReadWord(u32 addr) { u16 val = 0; addr &= 0xFFFFF; // fix me(I should really have proper mapping) switch(addr) { case 0x90008: case 0x9000A: val = Cs2Area->reg.HIRQ; if (Cs2Area->isbufferfull) val |= CDB_HIRQ_BFUL; else val &= ~CDB_HIRQ_BFUL; if (Cs2Area->isdiskchanged) val |= CDB_HIRQ_DCHG; else val &= ~CDB_HIRQ_DCHG; if (Cs2Area->isonesectorstored) val |= CDB_HIRQ_CSCT; else val &= ~CDB_HIRQ_CSCT; Cs2Area->reg.HIRQ = val; // CDLOG("cs2\t: Hirq read, Hirq mask = %x - ret: %x\n", Memory::getWord(0x9000C), val); return val; case 0x9000C: case 0x9000E: return Cs2Area->reg.HIRQMASK; case 0x90018: case 0x9001A: return Cs2Area->reg.CR1; case 0x9001C: case 0x9001E: return Cs2Area->reg.CR2; case 0x90020: case 0x90022: return Cs2Area->reg.CR3; case 0x90024: case 0x90026: Cs2Area->_command = 0; return Cs2Area->reg.CR4; case 0x90028: case 0x9002A: return Cs2Area->reg.MPEGRGB; case 0x98000: // transfer info switch (Cs2Area->infotranstype) { case 0: // Get Toc Data if (Cs2Area->transfercount % 4 == 0) val = (u16)((Cs2Area->TOC[Cs2Area->transfercount >> 2] & 0xFFFF0000) >> 16); else val = (u16)Cs2Area->TOC[Cs2Area->transfercount >> 2]; Cs2Area->transfercount += 2; Cs2Area->cdwnum += 2; if (Cs2Area->transfercount > (0xCC * 2)) { Cs2Area->transfercount = 0; Cs2Area->infotranstype = -1; } break; case 1: // Get File Info(1 file info) val = (Cs2Area->transfileinfo[Cs2Area->transfercount] << 8) | Cs2Area->transfileinfo[Cs2Area->transfercount + 1]; Cs2Area->transfercount += 2; Cs2Area->cdwnum += 2; if (Cs2Area->transfercount > (0x6 * 2)) { Cs2Area->transfercount = 0; Cs2Area->infotranstype = -1; } break; case 2: // Get File Info(254 file info) // Do we need to retrieve the next file info? if (Cs2Area->transfercount % (0x6 * 2) == 0) { // yes we do Cs2SetupFileInfoTransfer(2 + (Cs2Area->transfercount / (0x6 * 2))); } val = (Cs2Area->transfileinfo[Cs2Area->transfercount % (0x6 * 2)] << 8) | Cs2Area->transfileinfo[Cs2Area->transfercount % (0x6 * 2) + 1]; Cs2Area->transfercount += 2; Cs2Area->cdwnum += 2; if (Cs2Area->transfercount > (254 * (0x6 * 2))) { Cs2Area->transfercount = 0; Cs2Area->infotranstype = -1; } break; case 3: // Get Subcode Q val = (Cs2Area->transscodeq[Cs2Area->transfercount] << 8) | Cs2Area->transscodeq[Cs2Area->transfercount + 1]; Cs2Area->transfercount += 2; Cs2Area->cdwnum += 2; if (Cs2Area->transfercount > (5 * 2)) { Cs2Area->transfercount = 0; Cs2Area->infotranstype = -1; } break; case 4: // Get Subcode RW val = (Cs2Area->transscoderw[Cs2Area->transfercount] << 8) | Cs2Area->transscoderw[Cs2Area->transfercount + 1]; Cs2Area->transfercount += 2; Cs2Area->cdwnum += 2; if (Cs2Area->transfercount > (12 * 2)) { Cs2Area->transfercount = 0; Cs2Area->infotranstype = -1; } break; default: break; } break; default: LOG("cs2\t: Undocumented register read %08X\n", addr); // val = T3ReadWord(Cs2Area->mem, addr); break; } return val; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Cs2WriteWord(u32 addr, u16 val) { addr &= 0xFFFFF; // fix me(I should really have proper mapping) switch(addr) { case 0x90008: case 0x9000A: Cs2Area->reg.HIRQ &= val; return; case 0x9000C: case 0x9000E: Cs2Area->reg.HIRQMASK = val; return; case 0x90018: case 0x9001A: Cs2Area->status &= ~CDB_STAT_PERI; Cs2Area->_command = 1; Cs2Area->reg.CR1 = val; return; case 0x9001C: case 0x9001E: Cs2Area->reg.CR2 = val; return; case 0x90020: case 0x90022: Cs2Area->reg.CR3 = val; return; case 0x90024: case 0x90026: Cs2Area->reg.CR4 = val; Cs2SetCommandTiming(Cs2Area->reg.CR1 >> 8); return; case 0x90028: case 0x9002A: Cs2Area->reg.MPEGRGB = val; return; default: LOG("cs2\t:Undocumented register write %08X\n", addr); // T3WriteWord(Cs2Area->mem, addr, val); break; } } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL Cs2ReadLong(u32 addr) { s32 i; u32 val = 0; addr &= 0xFFFFF; // fix me(I should really have proper mapping) switch(addr) { case 0x90008: val = Cs2Area->reg.HIRQ; if (Cs2Area->isbufferfull) val |= CDB_HIRQ_BFUL; else val &= ~CDB_HIRQ_BFUL; if (Cs2Area->isdiskchanged) val |= CDB_HIRQ_DCHG; else val &= ~CDB_HIRQ_DCHG; if (Cs2Area->isonesectorstored) val |= CDB_HIRQ_CSCT; else val &= ~CDB_HIRQ_CSCT; Cs2Area->reg.HIRQ = (u16)val; val |= (val << 16); return val; case 0x9000C: return ((Cs2Area->reg.HIRQMASK << 16) | Cs2Area->reg.HIRQMASK); case 0x90018: return ((Cs2Area->reg.CR1 << 16) | Cs2Area->reg.CR1); case 0x9001C: return ((Cs2Area->reg.CR2 << 16) | Cs2Area->reg.CR2); case 0x90020: return ((Cs2Area->reg.CR3 << 16) | Cs2Area->reg.CR3); case 0x90024: Cs2Area->_command = 0; return ((Cs2Area->reg.CR4 << 16) | Cs2Area->reg.CR4); case 0x90028: return ((Cs2Area->reg.MPEGRGB << 16) | Cs2Area->reg.MPEGRGB); case 0x18000: // transfer data if (Cs2Area->datatranstype != -1) { // get sector // Make sure we still have sectors to transfer if (Cs2Area->datanumsecttrans < Cs2Area->datasectstotrans) { // Transfer Data const u8 *ptr = &Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->data[Cs2Area->datatransoffset]; #ifdef WORDS_BIGENDIAN val = *((const u32 *) ptr); #else val = BSWAP32(*((const u32 *) ptr)); #endif // increment datatransoffset/cdwnum Cs2Area->cdwnum += 4; Cs2Area->datatransoffset += 4; // Make sure we're not beyond the sector size boundry if (Cs2Area->datatransoffset >= Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->size) { Cs2Area->datatransoffset = 0; Cs2Area->datanumsecttrans++; } } else { if (Cs2Area->datatranstype == 2) { // Ok, so we don't have any more sectors to // transfer, might as well delete them all. Cs2Area->datatranstype = -1; // free blocks for (i = Cs2Area->datatranssectpos; i < (Cs2Area->datatranssectpos+Cs2Area->datasectstotrans); i++) { Cs2FreeBlock(Cs2Area->datatranspartition->block[i]); Cs2Area->datatranspartition->block[i] = NULL; Cs2Area->datatranspartition->blocknum[i] = 0xFF; } // sort remaining blocks Cs2SortBlocks(Cs2Area->datatranspartition); Cs2Area->datatranspartition->size -= Cs2Area->cdwnum; Cs2Area->datatranspartition->numblocks -= Cs2Area->datasectstotrans; CDLOG("cs2\t: datatranspartition->size = %x\n", Cs2Area->datatranspartition->size); } } } break; default: LOG("cs2\t: Undocumented register read %08X\n", addr); // val = T3ReadLong(Cs2Area->mem, addr); break; } return val; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL Cs2WriteLong(UNUSED u32 addr, UNUSED u32 val) { LOG("cs2\t: Long writing isn't implemented\n"); // T3WriteLong(Cs2Area->mem, addr, val); } ////////////////////////////////////////////////////////////////////////////// /* Copy "count" 32-bit words from the CD buffer to type-1 memory "dest" (a * native pointer), as though 0x25818000 had been read that many times */ void FASTCALL Cs2RapidCopyT1(void *dest, u32 count) { u8 *dest8 = (u8 *) dest; if (Cs2Area->datatranstype != -1) { // Copy as many sectors as we have left, one sector at a time while (count > 0 && Cs2Area->datanumsecttrans < Cs2Area->datasectstotrans) { const u8 *src = &Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->data[Cs2Area->datatransoffset]; const u32 size = Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->size; const u32 max = size - Cs2Area->datatransoffset; const u32 copy = (max < count*4) ? max : count*4; memcpy(dest8, src, copy); dest8 += copy; count -= copy/4; Cs2Area->datatransoffset += copy; Cs2Area->cdwnum += copy; // Update the sector index if we reached the end of the sector if (Cs2Area->datatransoffset >= size) { Cs2Area->datatransoffset = 0; Cs2Area->datanumsecttrans++; } } // If we're in delete mode and we read through everything in memory, // delete the sectors if (Cs2Area->datatranstype == 2 && Cs2Area->datanumsecttrans >= Cs2Area->datasectstotrans) { u32 i; Cs2Area->datatranstype = -1; for (i = Cs2Area->datatranssectpos; i < (Cs2Area->datatranssectpos+Cs2Area->datasectstotrans); i++) { Cs2FreeBlock(Cs2Area->datatranspartition->block[i]); Cs2Area->datatranspartition->block[i] = NULL; Cs2Area->datatranspartition->blocknum[i] = 0xFF; } Cs2SortBlocks(Cs2Area->datatranspartition); Cs2Area->datatranspartition->size -= Cs2Area->cdwnum; Cs2Area->datatranspartition->numblocks -= Cs2Area->datasectstotrans; CDLOG("cs2\t: datatranspartition->size = %x\n", Cs2Area->datatranspartition->size); } } if (count > 0) { // We tried to copy more data than was stored, so fill the rest of // the buffer with dummy data memset(dest8, 0xCD, count*4); } } ////////////////////////////////////////////////////////////////////////////// /* Copy "count" 32-bit words from the CD buffer to type-2 memory "dest" (a * native pointer), as though 0x25818000 had been read that many times */ void FASTCALL Cs2RapidCopyT2(void *dest, u32 count) { u32 *dest32 = (u32 *) dest; if (Cs2Area->datatranstype != -1) { // Copy as many sectors as we have left, one sector at a time; copy // four words at a time where possible to improve data parallelism while (count > 0 && Cs2Area->datanumsecttrans < Cs2Area->datasectstotrans) { const u8 *src = &Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->data[Cs2Area->datatransoffset]; const u32 size = Cs2Area->datatranspartition->block[Cs2Area->datanumsecttrans]->size; const u32 max = size - Cs2Area->datatransoffset; const u32 copy = (max < count*4) ? max : count*4; u32 i = 0; if (copy >= 16) { for (; i < copy-12; i += 16, src += 16, dest32 += 4) { u32 word0, word1, word2, word3; #ifdef WORDS_BIGENDIAN word0 = ((u32 *)src)[0]; word1 = ((u32 *)src)[1]; word2 = ((u32 *)src)[2]; word3 = ((u32 *)src)[3]; #else word0 = BSWAP16(((u32 *)src)[0]); word1 = BSWAP16(((u32 *)src)[1]); word2 = BSWAP16(((u32 *)src)[2]); word3 = BSWAP16(((u32 *)src)[3]); #endif dest32[0] = word0; dest32[1] = word1; dest32[2] = word2; dest32[3] = word3; } } for (; i < copy; i += 4, src += 4, dest32++) { #ifdef WORDS_BIGENDIAN *dest32 = *(u32 *)src; #else *dest32 = BSWAP16(*(u32 *)src); #endif } count -= copy/4; Cs2Area->datatransoffset += copy; Cs2Area->cdwnum += copy; if (Cs2Area->datatransoffset >= size) { Cs2Area->datatransoffset = 0; Cs2Area->datanumsecttrans++; } } if (Cs2Area->datatranstype == 2 && Cs2Area->datanumsecttrans >= Cs2Area->datasectstotrans) { u32 i; Cs2Area->datatranstype = -1; for (i = Cs2Area->datatranssectpos; i < (Cs2Area->datatranssectpos+Cs2Area->datasectstotrans); i++) { Cs2FreeBlock(Cs2Area->datatranspartition->block[i]); Cs2Area->datatranspartition->block[i] = NULL; Cs2Area->datatranspartition->blocknum[i] = 0xFF; } Cs2SortBlocks(Cs2Area->datatranspartition); Cs2Area->datatranspartition->size -= Cs2Area->cdwnum; Cs2Area->datatranspartition->numblocks -= Cs2Area->datasectstotrans; CDLOG("cs2\t: datatranspartition->size = %x\n", Cs2Area->datatranspartition->size); } } if (count > 0) { memset(dest32, 0xCD, count*4); } } ////////////////////////////////////////////////////////////////////////////// int Cs2Init(int carttype, int coreid, const char *cdpath, const char *mpegpath, const char *netlinksetting) { int ret; if ((Cs2Area = (Cs2 *) malloc(sizeof(Cs2))) == NULL) return -1; memset(Cs2Area, 0, sizeof(*Cs2Area)); Cs2Area->carttype = carttype; Cs2Area->mpegpath = mpegpath; Cs2Area->cdi=NULL; if ((ret = Cs2ChangeCDCore(coreid, cdpath)) != 0) return ret; Cs2Reset(); // If Modem is connected, set the registers if(Cs2Area->carttype == CART_NETLINK) { if ((ret = NetlinkInit(netlinksetting)) != 0) return ret; } else if (Cs2Area->carttype == CART_JAPMODEM) { if ((ret = JapModemInit(netlinksetting)) != 0) return ret; } if ((cdip = (ip_struct *) calloc(sizeof(ip_struct), 1)) == NULL) return -1; return 0; } ////////////////////////////////////////////////////////////////////////////// int Cs2ChangeCDCore(int coreid, const char *cdpath) { int i; // Make sure the old core is freed if (Cs2Area->cdi != NULL) Cs2Area->cdi->DeInit(); // So which core do we want? if (coreid == CDCORE_DEFAULT) coreid = 0; // Assume we want the first one // Go through core list and find the id for (i = 0; CDCoreList[i] != NULL; i++) { if (CDCoreList[i]->id == coreid) { // Set to current core Cs2Area->cdi = CDCoreList[i]; break; } } if (Cs2Area->cdi == NULL) { Cs2Area->cdi = &DummyCD; return -1; } if (Cs2Area->cdi->Init(cdpath) != 0) { // This might be helpful. YabSetError(YAB_ERR_CANNOTINIT, (void *)Cs2Area->cdi->Name); // Since it failed, instead of it being fatal, we'll just use the dummy // core instead Cs2Area->cdi = &DummyCD; } Cs2Area->isdiskchanged = 1; Cs2Area->status = CDB_STAT_PAUSE; SmpcRecheckRegion(); return 0; } ////////////////////////////////////////////////////////////////////////////// void Cs2DeInit(void) { if(Cs2Area != NULL) { if (Cs2Area->cdi != NULL) { Cs2Area->cdi->DeInit(); } if(Cs2Area->carttype == CART_NETLINK) NetlinkDeInit(); else if (Cs2Area->carttype == CART_JAPMODEM) JapModemDeInit(); free(Cs2Area); } Cs2Area = NULL; if (cdip) free(cdip); cdip = NULL; } ////////////////////////////////////////////////////////////////////////////// void Cs2Reset(void) { u32 i, i2; switch (Cs2Area->cdi->GetStatus()) { case 0: case 1: Cs2Area->status = CDB_STAT_PAUSE; Cs2Area->FAD = 150; Cs2Area->options = 0; Cs2Area->repcnt = 0; Cs2Area->ctrladdr = 0x41; Cs2Area->track = 1; Cs2Area->index = 1; break; case 2: Cs2Area->status = CDB_STAT_NODISC; Cs2Area->FAD = 0xFFFFFFFF; Cs2Area->options = 0xFF; Cs2Area->repcnt = 0xFF; Cs2Area->ctrladdr = 0xFF; Cs2Area->track = 0xFF; Cs2Area->index = 0xFF; break; case 3: Cs2Area->status = CDB_STAT_OPEN; Cs2Area->FAD = 0xFFFFFFFF; Cs2Area->options = 0xFF; Cs2Area->repcnt = 0xFF; Cs2Area->ctrladdr = 0xFF; Cs2Area->track = 0xFF; Cs2Area->index = 0xFF; break; default: break; } Cs2Area->infotranstype = -1; Cs2Area->datatranstype = -1; Cs2Area->transfercount = 0; Cs2Area->cdwnum = 0; Cs2Area->getsectsize = Cs2Area->putsectsize = 2048; Cs2Area->isdiskchanged = 1; Cs2Area->isbufferfull = 0; Cs2Area->isonesectorstored = 0; Cs2Area->isaudio = 0; Cs2Area->reg.CR1 = ( 0 <<8) | 'C'; Cs2Area->reg.CR2 = ('D'<<8) | 'B'; Cs2Area->reg.CR3 = ('L'<<8) | 'O'; Cs2Area->reg.CR4 = ('C'<<8) | 'K'; Cs2Area->reg.HIRQ = 0xFFFF; Cs2Area->reg.HIRQMASK = 0xFFFF; Cs2Area->playFAD = 0xFFFFFFFF; Cs2Area->playendFAD = 0xFFFFFFFF; Cs2Area->playtype = 0; Cs2Area->maxrepeat = 0; // set authentication variables to 0(not authenticated) Cs2Area->satauth = 0; Cs2Area->mpgauth = 0; // clear filter conditions for (i = 0; i < MAX_SELECTORS; i++) { Cs2Area->filter[i].FAD = 0; Cs2Area->filter[i].range = 0xFFFFFFFF; Cs2Area->filter[i].mode = 0; Cs2Area->filter[i].chan = 0; Cs2Area->filter[i].smmask = 0; Cs2Area->filter[i].cimask = 0; Cs2Area->filter[i].fid = 0; Cs2Area->filter[i].smval = 0; Cs2Area->filter[i].cival = 0; Cs2Area->filter[i].condtrue = 0; Cs2Area->filter[i].condfalse = 0xFF; } // clear partitions for (i = 0; i < MAX_SELECTORS; i++) { Cs2Area->partition[i].size = -1; Cs2Area->partition[i].numblocks = 0; for (i2 = 0; i2 < MAX_BLOCKS; i2++) { Cs2Area->partition[i].block[i2] = NULL; Cs2Area->partition[i].blocknum[i2] = 0xFF; } } // clear blocks for (i = 0; i < MAX_BLOCKS; i++) { Cs2Area->block[i].size = -1; memset(Cs2Area->block[i].data, 0, 2352); } Cs2Area->blockfreespace = 200; // initialize TOC memset(Cs2Area->TOC, 0xFF, sizeof(Cs2Area->TOC)); // clear filesystem stuff Cs2Area->curdirsect = 0; Cs2Area->curdirsize = 0; Cs2Area->curdirfidoffset = 0; memset(&Cs2Area->fileinfo, 0, sizeof(Cs2Area->fileinfo)); Cs2Area->numfiles = 0; Cs2Area->lastbuffer = 0xFF; Cs2Area->_command = 0; Cs2Area->_statuscycles = 0; Cs2Area->_statustiming = 1000000; Cs2Area->_periodiccycles = 0; Cs2Area->_commandtiming = 0; Cs2SetTiming(0); // MPEG specific stuff Cs2Area->mpegcon[0].audcon = Cs2Area->mpegcon[0].vidcon = 0x00; Cs2Area->mpegcon[0].audlay = Cs2Area->mpegcon[0].vidlay = 0x00; Cs2Area->mpegcon[0].audbufnum = Cs2Area->mpegcon[0].vidbufnum = 0xFF; Cs2Area->mpegcon[1].audcon = Cs2Area->mpegcon[1].vidcon = 0x00; Cs2Area->mpegcon[1].audlay = Cs2Area->mpegcon[1].vidlay = 0x00; Cs2Area->mpegcon[1].audbufnum = Cs2Area->mpegcon[1].vidbufnum = 0xFF; // should verify the following Cs2Area->mpegstm[0].audstm = Cs2Area->mpegstm[0].vidstm = 0x00; Cs2Area->mpegstm[0].audstmid = Cs2Area->mpegstm[0].vidstmid = 0x00; Cs2Area->mpegstm[0].audchannum = Cs2Area->mpegstm[0].vidchannum = 0x00; Cs2Area->mpegstm[1].audstm = Cs2Area->mpegstm[1].vidstm = 0x00; Cs2Area->mpegstm[1].audstmid = Cs2Area->mpegstm[1].vidstmid = 0x00; Cs2Area->mpegstm[1].audchannum = Cs2Area->mpegstm[1].vidchannum = 0x00; } ////////////////////////////////////////////////////////////////////////////// void Cs2Exec(u32 timing) { Cs2Area->_statuscycles += timing * 3; Cs2Area->_periodiccycles += timing * 3; if (Cs2Area->_commandtiming > 0) { if (Cs2Area->_commandtiming < timing) { Cs2Execute(); Cs2Area->_commandtiming = 0; } else Cs2Area->_commandtiming -= timing; } if (Cs2Area->_statuscycles >= Cs2Area->_statustiming) { Cs2Area->_statuscycles -= Cs2Area->_statustiming; switch(Cs2Area->cdi->GetStatus()) { case 0: case 1: if ((Cs2Area->status & 0xF) == CDB_STAT_NODISC || (Cs2Area->status & 0xF) == CDB_STAT_OPEN) { Cs2Area->status = CDB_STAT_PAUSE; Cs2Area->isdiskchanged = 1; } break; case 2: // may need to change this if ((Cs2Area->status & 0xF) != CDB_STAT_NODISC) Cs2Area->status = CDB_STAT_NODISC; break; case 3: // may need to change this if ((Cs2Area->status & 0xF) != CDB_STAT_OPEN) Cs2Area->status = CDB_STAT_OPEN; break; default: break; } } if (Cs2Area->_periodiccycles >= Cs2Area->_periodictiming) { Cs2Area->_periodiccycles -= Cs2Area->_periodictiming; // Get Drive's current status and compare with old status switch (Cs2Area->status & 0xF) { case CDB_STAT_PAUSE: { // if (FAD >= playFAD && FAD < playendFAD) // status = CDB_STAT_PLAY; // else break; } case CDB_STAT_PLAY: { partition_struct * playpartition; int ret = Cs2ReadFilteredSector(Cs2Area->FAD, &playpartition); switch (ret) { case 0: // Sector Read OK Cs2Area->FAD++; Cs2Area->cdi->ReadAheadFAD(Cs2Area->FAD); if (playpartition != NULL) { // We can use this sector CDLOG("partition number = %d blocks = %d blockfreespace = %d fad = %x playpartition->size = %x isbufferfull = %x\n", (playpartition - Cs2Area->partition), playpartition->numblocks, Cs2Area->blockfreespace, Cs2Area->FAD, playpartition->size, Cs2Area->isbufferfull); Cs2Area->reg.HIRQ |= CDB_HIRQ_CSCT; Cs2Area->isonesectorstored = 1; if (Cs2Area->FAD >= Cs2Area->playendFAD) { // Make sure we don't have to do a repeat if (Cs2Area->repcnt >= Cs2Area->maxrepeat) { // we're done Cs2Area->status = CDB_STAT_PAUSE; Cs2SetTiming(0); Cs2Area->reg.HIRQ |= CDB_HIRQ_PEND; if (Cs2Area->playtype == CDB_PLAYTYPE_FILE) Cs2Area->reg.HIRQ |= CDB_HIRQ_EFLS; CDLOG("PLAY HAS ENDED\n"); } else { Cs2Area->FAD = Cs2Area->playFAD; if (Cs2Area->repcnt < 0xE) Cs2Area->repcnt++; Cs2Area->track = Cs2FADToTrack(Cs2Area->FAD); CDLOG("PLAY HAS REPEATED\n"); } } if (Cs2Area->isbufferfull) { CDLOG("BUFFER IS FULL\n"); // status = CDB_STAT_PAUSE; } } else { CDLOG("Sector filtered out\n"); if (Cs2Area->FAD >= Cs2Area->playendFAD) { // Make sure we don't have to do a repeat if (Cs2Area->repcnt >= Cs2Area->maxrepeat) { // we're done Cs2Area->status = CDB_STAT_PAUSE; Cs2SetTiming(0); Cs2Area->reg.HIRQ |= CDB_HIRQ_PEND; if (Cs2Area->playtype == CDB_PLAYTYPE_FILE) Cs2Area->reg.HIRQ |= CDB_HIRQ_EFLS; CDLOG("PLAY HAS ENDED\n"); } else { Cs2Area->FAD = Cs2Area->playFAD; if (Cs2Area->repcnt < 0xE) Cs2Area->repcnt++; Cs2Area->track = Cs2FADToTrack(Cs2Area->FAD); CDLOG("PLAY HAS REPEATED\n"); } } } break; case -1: // Things weren't setup correctly break; case -2: // Do a read retry break; } break; } case CDB_STAT_SEEK: break; case CDB_STAT_SCAN: break; case CDB_STAT_RETRY: break; default: break; } if (Cs2Area->_command) return; Cs2Area->status |= CDB_STAT_PERI; // adjust registers appropriately here(fix me) doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_SCDQ; } if(Cs2Area->carttype == CART_NETLINK) NetlinkExec(timing); else if (Cs2Area->carttype == CART_JAPMODEM) JapModemExec(timing); } ////////////////////////////////////////////////////////////////////////////// /* Returns the number of (emulated) microseconds before the next sector * will have been completely read in */ int Cs2GetTimeToNextSector(void) { if ((Cs2Area->status & 0xF) != CDB_STAT_PLAY) { return 0; } else { // Round up, since the caller wants to know when it'll be safe to check int time = (Cs2Area->_periodictiming - Cs2Area->_periodiccycles + 2) / 3; return time<0 ? 0 : time; } } ////////////////////////////////////////////////////////////////////////////// void Cs2Command(void) { Cs2Area->_command = 1; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetTiming(int playing) { if (playing) { if (Cs2Area->isaudio || Cs2Area->speed1x == 1) Cs2Area->_periodictiming = 40000; // 13333.333... * 3 else Cs2Area->_periodictiming = 20000; // 6666.666... * 3 } else { Cs2Area->_periodictiming = 50000; // 16666.666... * 3 } } ////////////////////////////////////////////////////////////////////////////// void Cs2SetCommandTiming(u8 cmd) { switch(cmd) { default: Cs2Area->_commandtiming = 1; break; } } ////////////////////////////////////////////////////////////////////////////// void Cs2Execute(void) { u16 instruction = Cs2Area->reg.CR1 >> 8; Cs2Area->reg.HIRQ &= ~CDB_HIRQ_CMOK; switch (instruction) { case 0x00: CDLOG("cs2\t: Command: getStatus\n"); Cs2GetStatus(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x01: CDLOG("cs2\t: Command: getHardwareInfo\n"); Cs2GetHardwareInfo(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x02: CDLOG("cs2\t: Command: getToc\n"); Cs2GetToc(); break; case 0x03: CDLOG("cs2\t: Command: getSessionInfo\n"); Cs2GetSessionInfo(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x04: CDLOG("cs2\t: Command: initializeCDSystem %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2InitializeCDSystem(); break; case 0x06: CDLOG("cs2\t: Command: endDataTransfer %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2EndDataTransfer(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x10: CDLOG("cs2\t: Command: playDisc %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2PlayDisc(); break; case 0x11: CDLOG("cs2\t: Command: seekDisc %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2SeekDisc(); break; case 0x20: CDLOG("cs2\t: Command: getSubcodeQRW %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2GetSubcodeQRW(); break; case 0x30: CDLOG("cs2\t: Command: setCDDeviceConnection %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2SetCDDeviceConnection(); break; case 0x32: CDLOG("cs2\t: Command: getLastBufferDestination %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2GetLastBufferDestination(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x40: CDLOG("cs2\t: Command: setFilterRange %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2SetFilterRange(); break; case 0x42: CDLOG("cs2\t: Command: setFilterSubheaderConditions %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2SetFilterSubheaderConditions(); break; case 0x43: CDLOG("cs2\t: Command: getFilterSubheaderConditions\n"); Cs2GetFilterSubheaderConditions(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x44: CDLOG("cs2\t: Command: setFilterMode %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2SetFilterMode(); break; case 0x45: CDLOG("cs2\t: Command: getFilterMode\n"); Cs2GetFilterMode(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x46: CDLOG("cs2\t: Command: setFilterConnection %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2SetFilterConnection(); break; case 0x48: CDLOG("cs2\t: Command: resetSelector %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2ResetSelector(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x50: CDLOG("cs2\t: Command: getBufferSize\n"); Cs2GetBufferSize(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x51: // CDLOG("cs2\t: Command: getSectorNumber %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2GetSectorNumber(); // CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x52: CDLOG("cs2\t: Command: calculateActualSize %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2CalculateActualSize(); break; case 0x53: CDLOG("cs2\t: Command: getActualSize\n"); Cs2GetActualSize(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x54: CDLOG("cs2\t: Command: getSectorInfo %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2GetSectorInfo(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x60: CDLOG("cs2\t: Command: setSectorLength %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2SetSectorLength(); break; case 0x61: CDLOG("cs2\t: Command: getSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2GetSectorData(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x62: CDLOG("cs2\t: Command: deleteSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2DeleteSectorData(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x63: CDLOG("cs2\t: Command: getThenDeleteSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2GetThenDeleteSectorData(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x64: CDLOG("cs2\t: Command: putSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2PutSectorData(); break; case 0x65: CDLOG("cs2\t: Command: Unimplemented copySectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x66: CDLOG("cs2\t: Command: Unimplemented moveSectorData %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x67: CDLOG("cs2\t: Command: getCopyError\n"); Cs2GetCopyError(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x70: CDLOG("cs2\t: Command: changeDirectory %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2ChangeDirectory(); break; case 0x71: CDLOG("cs2\t: Command: readDirectory %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2ReadDirectory(); break; case 0x72: CDLOG("cs2\t: Command: getFileSystemScope\n"); Cs2GetFileSystemScope(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x73: CDLOG("cs2\t: Command: getFileInfo %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2GetFileInfo(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x74: CDLOG("cs2\t: Command: readFile %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2ReadFile(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x75: CDLOG("cs2\t: Command: abortFile\n"); Cs2AbortFile(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x90: CDLOG("cs2\t: Command: mpegGetStatus\n"); Cs2MpegGetStatus(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x91: CDLOG("cs2\t: Command: mpegGetInterrupt\n"); Cs2MpegGetInterrupt(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x92: CDLOG("cs2\t: Command: mpegSetInterruptMask %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2); Cs2MpegSetInterruptMask(); break; case 0x93: CDLOG("cs2\t: Command: mpegInit %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2); Cs2MpegInit(); break; case 0x94: CDLOG("cs2\t: Command: mpegSetMode %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3); Cs2MpegSetMode(); break; case 0x95: CDLOG("cs2\t: Command: mpegPlay %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR4); Cs2MpegPlay(); break; case 0x96: CDLOG("cs2\t: Command: mpegSetDecodingMethod %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR4); Cs2MpegSetDecodingMethod(); break; case 0x9A: CDLOG("cs2\t: Command: mpegSetConnection %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2MpegSetConnection(); break; case 0x9B: CDLOG("cs2\t: Command: mpegGetConnection\n"); Cs2MpegGetConnection(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0x9D: CDLOG("cs2\t: Command: mpegSetStream %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2MpegSetStream(); break; case 0x9E: CDLOG("cs2\t: Command: mpegGetStream\n"); Cs2MpegGetStream(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0xA0: CDLOG("cs2\t: Command: mpegDisplay %04x %04x %04x \n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2); Cs2MpegDisplay(); break; case 0xA1: CDLOG("cs2\t: Command: mpegSetWindow %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2MpegSetWindow(); break; case 0xA2: CDLOG("cs2\t: Command: mpegSetBorderColor %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2); Cs2MpegSetBorderColor(); break; case 0xA3: CDLOG("cs2\t: Command: mpegSetFade %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2); Cs2MpegSetFade(); break; case 0xA4: CDLOG("cs2\t: Command: mpegSetVideoEffects %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2MpegSetVideoEffects(); break; case 0xAF: CDLOG("cs2\t: Command: mpegSetLSI %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2MpegSetLSI(); break; case 0xE0: CDLOG("cs2\t: Command: authenticateDevice %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2AuthenticateDevice(); break; case 0xE1: CDLOG("cs2\t: Command: isDeviceAuthenticated %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2IsDeviceAuthenticated(); CDLOG("cs2\t: ret: %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); break; case 0xE2: CDLOG("cs2\t: Command: getMPEGRom %04x %04x %04x %04x %04x\n", Cs2Area->reg.HIRQ, Cs2Area->reg.CR1, Cs2Area->reg.CR2, Cs2Area->reg.CR3, Cs2Area->reg.CR4); Cs2GetMPEGRom(); break; default: CDLOG("cs2\t: Command %02x not implemented\n", instruction); break; } } ////////////////////////////////////////////////////////////////////////////// void Cs2GetStatus(void) { doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetHardwareInfo(void) { if ((Cs2Area->status & 0xF) != CDB_STAT_OPEN && (Cs2Area->status & 0xF) != CDB_STAT_NODISC) Cs2Area->isdiskchanged = 0; Cs2Area->reg.CR1 = Cs2Area->status << 8; // hardware flags/CD Version Cs2Area->reg.CR2 = 0x0201; // mpeg card exists // mpeg version, it actually is required(at least by the bios) if (Cs2Area->mpgauth) Cs2Area->reg.CR3 = 0x1; else Cs2Area->reg.CR3 = 0; // drive info/revision Cs2Area->reg.CR4 = 0x0400; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetToc(void) { Cs2Area->cdi->ReadTOC(Cs2Area->TOC); Cs2Area->transfercount = 0; Cs2Area->infotranstype = 0; Cs2Area->reg.CR1 = Cs2Area->status << 8; Cs2Area->reg.CR2 = 0xCC; Cs2Area->reg.CR3 = 0x0; Cs2Area->reg.CR4 = 0x0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY; Cs2Area->status = CDB_STAT_PAUSE; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetSessionInfo(void) { switch (Cs2Area->reg.CR1 & 0xFF) { case 0: Cs2Area->reg.CR3 = (u16)(0x0100 | ((Cs2Area->TOC[101] & 0xFF0000) >> 16)); Cs2Area->reg.CR4 = (u16)Cs2Area->TOC[101]; break; case 1: Cs2Area->reg.CR3 = 0x0100; // return Session number(high byte)/and first byte of Session lba Cs2Area->reg.CR4 = 0; // lower word of Session lba break; default: Cs2Area->reg.CR3 = 0xFFFF; Cs2Area->reg.CR4 = 0xFFFF; break; } Cs2Area->status = CDB_STAT_PAUSE; Cs2Area->reg.CR1 = Cs2Area->status << 8; Cs2Area->reg.CR2 = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2InitializeCDSystem(void) { u16 val = 0; u8 initflag = Cs2Area->reg.CR1 & 0xFF; if ((Cs2Area->status & 0xF) != CDB_STAT_OPEN && (Cs2Area->status & 0xF) != CDB_STAT_NODISC) { Cs2Area->status = CDB_STAT_PAUSE; Cs2Area->FAD = 150; } if (initflag & 0x1) { // Reset CD block software } if (initflag & 0x2) { // Decode RW subcode } if (initflag & 0x4) { // Don't confirm Mode 2 subheader } if (initflag & 0x8) { // Retry reading Form 2 sectors } if (initflag & 0x10) Cs2Area->speed1x = 1; else Cs2Area->speed1x = 0; val = Cs2Area->reg.HIRQ & 0xFFE5; Cs2Area->isbufferfull = 0; if (Cs2Area->isdiskchanged) val |= CDB_HIRQ_DCHG; else val &= ~CDB_HIRQ_DCHG; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ = val | CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2EndDataTransfer(void) { s32 i; if (Cs2Area->cdwnum) { Cs2Area->reg.CR1 = (u16)((Cs2Area->status << 8) | ((Cs2Area->cdwnum >> 17) & 0xFF)); Cs2Area->reg.CR2 = (u16)(Cs2Area->cdwnum >> 1); Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; } else { Cs2Area->reg.CR1 = (Cs2Area->status << 8) | 0xFF; // FIXME Cs2Area->reg.CR2 = 0xFFFF; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; } // stop any transfers that may be going(this is still probably wrong), and // set/clear the appropriate flags switch (Cs2Area->datatranstype) { case 0: // Get Sector Data Cs2Area->reg.HIRQ |= CDB_HIRQ_EHST; break; case 2: { // Get Then Delete Sector // Make sure we actually have to free something if (Cs2Area->datatranspartition->size <= 0) break; Cs2Area->datatranstype = -1; // free blocks for (i = Cs2Area->datatranssectpos; i < (Cs2Area->datatranssectpos + Cs2Area->datasectstotrans); i++) { Cs2FreeBlock(Cs2Area->datatranspartition->block[i]); Cs2Area->datatranspartition->block[i] = NULL; Cs2Area->datatranspartition->blocknum[i] = 0xFF; } // sort remaining blocks Cs2SortBlocks(Cs2Area->datatranspartition); Cs2Area->datatranspartition->size -= Cs2Area->cdwnum; Cs2Area->datatranspartition->numblocks -= Cs2Area->datasectstotrans; if (Cs2Area->blockfreespace == 200) Cs2Area->isonesectorstored = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_EHST; break; } default: break; } Cs2Area->cdwnum = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2PlayDisc(void) { u32 pdspos; u32 pdepos; u32 pdpmode; // Get all the arguments pdspos = ((Cs2Area->reg.CR1 & 0xFF) << 16) | Cs2Area->reg.CR2; pdepos = ((Cs2Area->reg.CR3 & 0xFF) << 16) | Cs2Area->reg.CR4; pdpmode = Cs2Area->reg.CR3 >> 8; // Convert Start Position to playFAD if (pdspos == 0xFFFFFF || pdpmode == 0xFF) // This still isn't right { // No Change } else if (pdspos & 0x800000) { // FAD Mode Cs2Area->playFAD = (pdspos & 0xFFFFF); Cs2SetupDefaultPlayStats(Cs2FADToTrack(Cs2Area->playFAD), 0); if (!(pdpmode & 0x80)) // Move pickup to start position Cs2Area->FAD = Cs2Area->playFAD; } else { // Track Mode // If track == 0, set it to the first available track, or something like that if (pdspos == 0) pdspos = 0x0100; if (!(pdpmode & 0x80)) { Cs2SetupDefaultPlayStats((u8)(pdspos >> 8), 1); Cs2Area->playFAD = Cs2Area->FAD; Cs2Area->track = (u8)(pdspos >> 8); Cs2Area->index = (u8)pdspos; } else { // Preserve Pickup Position Cs2SetupDefaultPlayStats((u8)(pdspos >> 8), 0); } } pdpmode &= 0x7F; // Only update max repeat if bits 0-6 aren't all set if (pdpmode != 0x7F) Cs2Area->maxrepeat = pdpmode; // Convert End Position to playendFAD if (pdepos == 0xFFFFFF) { // No Change } else if (pdepos & 0x800000) { // FAD Mode Cs2Area->playendFAD = Cs2Area->playFAD+(pdepos & 0xFFFFF); } else if (pdepos != 0) { // Track Mode if ((pdepos & 0xFF) == 0) Cs2Area->playendFAD = Cs2TrackToFAD((u16)(pdepos | 0x0063)); else Cs2Area->playendFAD = Cs2TrackToFAD((u16)pdepos); } else { // Default Mode Cs2Area->playendFAD = Cs2TrackToFAD(0xFFFF); } // setup play mode here #if CDDEBUG if (pdpmode != 0) CDLOG("cs2\t: playDisc: Unsupported play mode = %02X\n", pdpmode); #endif Cs2SetTiming(1); Cs2Area->status = CDB_STAT_PLAY; Cs2Area->playtype = CDB_PLAYTYPE_SECTOR; Cs2Area->cdi->ReadAheadFAD(Cs2Area->FAD); doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2SeekDisc(void) { if (Cs2Area->reg.CR1 & 0x80) { // Seek by FAD u32 sdFAD; sdFAD = ((Cs2Area->reg.CR1 & 0xFF) << 16) | Cs2Area->reg.CR2; if (sdFAD == 0xFFFFFF) Cs2Area->status = CDB_STAT_PAUSE; else { CDLOG("cs2\t: seekDisc - FAD Mode not supported\n"); } } else { // Were we given a valid track number? if (Cs2Area->reg.CR2 >> 8) { // Seek by index Cs2Area->status = CDB_STAT_PAUSE; Cs2SetupDefaultPlayStats((Cs2Area->reg.CR2 >> 8), 1); Cs2Area->index = Cs2Area->reg.CR2 & 0xFF; } else { // Error Cs2Area->status = CDB_STAT_STANDBY; Cs2Area->options = 0xFF; Cs2Area->repcnt = 0xFF; Cs2Area->ctrladdr = 0xFF; Cs2Area->track = 0xFF; Cs2Area->index = 0xFF; Cs2Area->FAD = 0xFFFFFFFF; } } Cs2SetTiming(0); doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetSubcodeQRW(void) { u32 rel_fad; u8 rel_m, rel_s, rel_f, m, s, f; // According to Tyranid's doc, the subcode type is stored in the low byte // of CR2. However, Sega's CDC library writes the type to the low byte // of CR1. Somehow I'd sooner believe Sega is right. switch(Cs2Area->reg.CR1 & 0xFF) { case 0: // Get Q Channel Cs2Area->reg.CR1 = (Cs2Area->status << 8) | 0; Cs2Area->reg.CR2 = 5; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; rel_fad = Cs2Area->FAD-(Cs2Area->TOC[Cs2Area->track-1] & 0xFFFFFF); Cs2FADToMSF(rel_fad, &rel_m, &rel_s, &rel_f); Cs2FADToMSF(Cs2Area->FAD, &m, &s, &f); Cs2Area->transscodeq[0] = Cs2Area->ctrladdr; // ctl/adr Cs2Area->transscodeq[1] = ToBCD(Cs2Area->track); // track number Cs2Area->transscodeq[2] = ToBCD(Cs2Area->index); // index Cs2Area->transscodeq[3] = ToBCD(rel_m); // relative M Cs2Area->transscodeq[4] = ToBCD(rel_s); // relative S Cs2Area->transscodeq[5] = ToBCD(rel_f); // relative F Cs2Area->transscodeq[6] = 0; Cs2Area->transscodeq[7] = ToBCD(m); // M Cs2Area->transscodeq[8] = ToBCD(s); // S Cs2Area->transscodeq[9] = ToBCD(f); // F Cs2Area->transfercount = 0; Cs2Area->infotranstype = 3; break; case 1: { // Get RW Channel static int lastfad=0; static u16 group=0; int i; Cs2Area->reg.CR1 = (Cs2Area->status << 8) | 0; Cs2Area->reg.CR2 = 12; Cs2Area->reg.CR3 = 0; if (Cs2Area->FAD != lastfad) { lastfad = Cs2Area->FAD; group = 0; } else group++; Cs2Area->reg.CR4 = group; // Subcode flag for (i = 0; i < 24; i++) Cs2Area->transscoderw[i] = Cs2Area->workblock.data[2352+i+(24*group)] & 0x3F; Cs2Area->transfercount = 0; Cs2Area->infotranstype = 4; break; } default: break; } Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetCDDeviceConnection(void) { u32 scdcfilternum; scdcfilternum = (Cs2Area->reg.CR3 >> 8); if (scdcfilternum == 0xFF) Cs2Area->outconcddev = NULL; else if (scdcfilternum < 0x24) Cs2Area->outconcddev = Cs2Area->filter + scdcfilternum; Cs2Area->outconcddevnum = (u8)scdcfilternum; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetLastBufferDestination(void) { Cs2Area->reg.CR1 = (Cs2Area->status << 8); Cs2Area->reg.CR2 = 0; Cs2Area->reg.CR3 = Cs2Area->lastbuffer << 8; Cs2Area->reg.CR4 = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetFilterRange(void) { u8 sfrfilternum; sfrfilternum = Cs2Area->reg.CR3 >> 8; Cs2Area->filter[sfrfilternum].FAD = ((Cs2Area->reg.CR1 & 0xFF) << 16) | Cs2Area->reg.CR2; Cs2Area->filter[sfrfilternum].range = ((Cs2Area->reg.CR3 & 0xFF) << 16) | Cs2Area->reg.CR4; // return default cd stats doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetFilterSubheaderConditions(void) { u8 sfscfilternum; sfscfilternum = Cs2Area->reg.CR3 >> 8; Cs2Area->filter[sfscfilternum].chan = Cs2Area->reg.CR1 & 0xFF; Cs2Area->filter[sfscfilternum].smmask = Cs2Area->reg.CR2 >> 8; Cs2Area->filter[sfscfilternum].cimask = Cs2Area->reg.CR2 & 0xFF; Cs2Area->filter[sfscfilternum].fid = Cs2Area->reg.CR3 & 0xFF;; Cs2Area->filter[sfscfilternum].smval = Cs2Area->reg.CR4 >> 8; Cs2Area->filter[sfscfilternum].cival = Cs2Area->reg.CR4 & 0xFF; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetFilterSubheaderConditions(void) { u8 gfscfilternum; gfscfilternum = Cs2Area->reg.CR3 >> 8; Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->filter[gfscfilternum].chan; Cs2Area->reg.CR2 = (Cs2Area->filter[gfscfilternum].smmask << 8) | Cs2Area->filter[gfscfilternum].cimask; Cs2Area->reg.CR3 = Cs2Area->filter[gfscfilternum].fid; Cs2Area->reg.CR4 = (Cs2Area->filter[gfscfilternum].smval << 8) | Cs2Area->filter[gfscfilternum].cival; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetFilterMode(void) { u8 sfmfilternum; sfmfilternum = Cs2Area->reg.CR3 >> 8; Cs2Area->filter[sfmfilternum].mode = Cs2Area->reg.CR1 & 0xFF; if (Cs2Area->filter[sfmfilternum].mode & 0x80) { // Initialize filter conditions Cs2Area->filter[sfmfilternum].mode = 0; Cs2Area->filter[sfmfilternum].FAD = 0; Cs2Area->filter[sfmfilternum].range = 0; Cs2Area->filter[sfmfilternum].chan = 0; Cs2Area->filter[sfmfilternum].smmask = 0; Cs2Area->filter[sfmfilternum].cimask = 0; Cs2Area->filter[sfmfilternum].smval = 0; Cs2Area->filter[sfmfilternum].cival = 0; } doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetFilterMode(void) { u8 gfmfilternum; gfmfilternum = Cs2Area->reg.CR3 >> 8; Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->filter[gfmfilternum].mode; Cs2Area->reg.CR2 = 0; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetFilterConnection(void) { u8 sfcfilternum; sfcfilternum = Cs2Area->reg.CR3 >> 8; if (Cs2Area->reg.CR1 & 0x1) { // Set connection for true condition Cs2Area->filter[sfcfilternum].condtrue = Cs2Area->reg.CR2 >> 8; } if (Cs2Area->reg.CR1 & 0x2) { // Set connection for false condition Cs2Area->filter[sfcfilternum].condfalse = Cs2Area->reg.CR2 & 0xFF; } doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2ResetSelector(void) { // still needs a bit of work u32 i, i2; if ((Cs2Area->reg.CR1 & 0xFF) == 0) { // Reset specified partition buffer only u32 rsbufno = Cs2Area->reg.CR3 >> 8; // sort remaining blocks if (rsbufno < MAX_SELECTORS) { // clear partition for (i = 0; i < Cs2Area->partition[rsbufno].numblocks; i++) { Cs2FreeBlock(Cs2Area->partition[rsbufno].block[i]); Cs2Area->partition[rsbufno].block[i] = NULL; Cs2Area->partition[rsbufno].blocknum[i] = 0xFF; } Cs2Area->partition[rsbufno].size = -1; Cs2Area->partition[rsbufno].numblocks = 0; } if (Cs2Area->blockfreespace > 0) Cs2Area->isbufferfull = 0; if (Cs2Area->blockfreespace == 200) { Cs2Area->isonesectorstored = 0; Cs2Area->datatranstype = -1; } else if (Cs2Area->datatranspartitionnum == rsbufno) Cs2Area->datatranstype = -1; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; return; } // parse flags and reset the specified area(fix me) if (Cs2Area->reg.CR1 & 0x80) { // reset false filter output connections for (i = 0; i < MAX_SELECTORS; i++) Cs2Area->filter[i].condfalse = 0xFF; } if (Cs2Area->reg.CR1 & 0x40) { // reset true filter output connections for (i = 0; i < MAX_SELECTORS; i++) Cs2Area->filter[i].condtrue = (u8)i; } if (Cs2Area->reg.CR1 & 0x10) { // reset filter conditions for (i = 0; i < MAX_SELECTORS; i++) { Cs2Area->filter[i].FAD = 0; Cs2Area->filter[i].range = 0xFFFFFFFF; Cs2Area->filter[i].mode = 0; Cs2Area->filter[i].chan = 0; Cs2Area->filter[i].smmask = 0; Cs2Area->filter[i].cimask = 0; Cs2Area->filter[i].fid = 0; Cs2Area->filter[i].smval = 0; Cs2Area->filter[i].cival = 0; } } if (Cs2Area->reg.CR1 & 0x8) { // reset partition output connectors } if (Cs2Area->reg.CR1 & 0x4) { // reset partitions buffer data Cs2Area->isbufferfull = 0; // clear partitions for (i = 0; i < MAX_SELECTORS; i++) { Cs2Area->partition[i].size = -1; Cs2Area->partition[i].numblocks = 0; for (i2 = 0; i2 < MAX_BLOCKS; i2++) { Cs2Area->partition[i].block[i2] = NULL; Cs2Area->partition[i].blocknum[i2] = 0xFF; } } // clear blocks for (i = 0; i < MAX_BLOCKS; i++) { Cs2Area->block[i].size = -1; memset(Cs2Area->block[i].data, 0, 2352); } Cs2Area->isonesectorstored = 0; Cs2Area->datatranstype = -1; } doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetBufferSize(void) { Cs2Area->reg.CR1 = Cs2Area->status << 8; Cs2Area->reg.CR2 = (u16)Cs2Area->blockfreespace; Cs2Area->reg.CR3 = MAX_SELECTORS << 8; Cs2Area->reg.CR4 = MAX_BLOCKS; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetSectorNumber(void) { u32 gsnbufno; gsnbufno = Cs2Area->reg.CR3 >> 8; if (Cs2Area->partition[gsnbufno].size == -1) Cs2Area->reg.CR4 = 0; else Cs2Area->reg.CR4 = Cs2Area->partition[gsnbufno].numblocks; Cs2Area->reg.CR1 = Cs2Area->status << 8; Cs2Area->reg.CR2 = 0; Cs2Area->reg.CR3 = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY; } ////////////////////////////////////////////////////////////////////////////// void Cs2CalculateActualSize(void) { u16 i; u32 casbufno; u16 cassectoffset; u16 casnumsect; cassectoffset = Cs2Area->reg.CR2; casbufno = Cs2Area->reg.CR3 >> 8; casnumsect = Cs2Area->reg.CR4; if (Cs2Area->partition[casbufno].size != 0) { Cs2Area->calcsize = 0; for (i = 0; i < casnumsect; i++) { if (Cs2Area->partition[casbufno].block[cassectoffset]) Cs2Area->calcsize += (Cs2Area->partition[casbufno].block[cassectoffset]->size / 2); } } else Cs2Area->calcsize = 0; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetActualSize(void) { Cs2Area->reg.CR1 = (u16)((Cs2Area->status << 8) | ((Cs2Area->calcsize >> 16) & 0xFF)); Cs2Area->reg.CR2 = (u16)Cs2Area->calcsize; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetSectorInfo(void) { u32 gsisctnum; u32 gsibufno; gsisctnum = Cs2Area->reg.CR2 & 0xFF; gsibufno = Cs2Area->reg.CR3 >> 8; if (gsibufno < MAX_SELECTORS) { if (gsisctnum < Cs2Area->partition[gsibufno].numblocks) { Cs2Area->reg.CR1 = (u16)((Cs2Area->status << 8) | ((Cs2Area->partition[gsibufno].block[gsisctnum]->FAD >> 16) & 0xFF)); Cs2Area->reg.CR2 = (u16)Cs2Area->partition[gsibufno].block[gsisctnum]->FAD; Cs2Area->reg.CR3 = (Cs2Area->partition[gsibufno].block[gsisctnum]->fn << 8) | Cs2Area->partition[gsibufno].block[gsisctnum]->cn; Cs2Area->reg.CR4 = (Cs2Area->partition[gsibufno].block[gsisctnum]->sm << 8) | Cs2Area->partition[gsibufno].block[gsisctnum]->ci; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; return; } else { CDLOG("cs2\t: getSectorInfo: Unsupported Partition Number\n"); } } Cs2Area->reg.CR1 = (CDB_STAT_REJECT << 8) | (Cs2Area->reg.CR1 & 0xFF); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetSectorLength(void) { switch (Cs2Area->reg.CR1 & 0xFF) { case 0: Cs2Area->getsectsize = 2048; break; case 1: Cs2Area->getsectsize = 2336; break; case 2: Cs2Area->getsectsize = 2340; break; case 3: Cs2Area->getsectsize = 2352; break; default: break; } switch (Cs2Area->reg.CR2 >> 8) { case 0: Cs2Area->putsectsize = 2048; break; case 1: Cs2Area->putsectsize = 2336; break; case 2: Cs2Area->putsectsize = 2340; break; case 3: Cs2Area->putsectsize = 2352; break; default: break; } doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_ESEL; } ////////////////////////////////////////////////////////////////////////////// static INLINE void CalcSectorOffsetNumber(u32 bufno, u32 *sectoffset, u32 *sectnum) { if (*sectoffset == 0xFFFF) { // Last sector CDLOG("FIXME - Sector offset of 0xFFFF not supported\n"); } else if (*sectnum == 0xFFFF) { // From sectoffset to last sector in partition *sectnum = Cs2Area->partition[bufno].numblocks - *sectoffset; } } ////////////////////////////////////////////////////////////////////////////// void Cs2GetSectorData(void) { u32 gsdsectoffset; u32 gsdbufno; u32 gsdsectnum; gsdsectoffset = Cs2Area->reg.CR2; gsdbufno = Cs2Area->reg.CR3 >> 8; gsdsectnum = Cs2Area->reg.CR4; if (gsdbufno >= MAX_SELECTORS) { doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; return; } if (Cs2Area->partition[gsdbufno].numblocks == 0) { CDLOG("No sectors available\n"); doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; return; } CalcSectorOffsetNumber(gsdbufno, &gsdsectoffset, &gsdsectnum); // Setup Data Transfer Cs2Area->cdwnum = 0; Cs2Area->datatranstype = 0; Cs2Area->datatranspartition = Cs2Area->partition + gsdbufno; Cs2Area->datatranspartitionnum = (u8)gsdbufno; Cs2Area->datatransoffset = 0; Cs2Area->datanumsecttrans = 0; Cs2Area->datatranssectpos = (u16)gsdsectoffset; Cs2Area->datasectstotrans = (u16)gsdsectnum; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY | CDB_HIRQ_EHST; } ////////////////////////////////////////////////////////////////////////////// void Cs2DeleteSectorData(void) { u32 dsdsectoffset; u32 dsdbufno; u32 dsdsectnum; u32 i; dsdsectoffset = Cs2Area->reg.CR2; dsdbufno = Cs2Area->reg.CR3 >> 8; dsdsectnum = Cs2Area->reg.CR4; if (dsdbufno >= MAX_SELECTORS) { doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; return; } if (Cs2Area->partition[dsdbufno].numblocks == 0) { CDLOG("No sectors available\n"); doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; return; } CalcSectorOffsetNumber(dsdbufno, &dsdsectoffset, &dsdsectnum); for (i = dsdsectoffset; i < (dsdsectoffset+dsdsectnum); i++) { Cs2Area->partition[dsdbufno].size -= Cs2Area->partition[dsdbufno].block[i]->size; Cs2FreeBlock(Cs2Area->partition[dsdbufno].block[i]); Cs2Area->partition[dsdbufno].block[i] = NULL; Cs2Area->partition[dsdbufno].blocknum[i] = 0xFF; } // sort remaining blocks Cs2SortBlocks(&Cs2Area->partition[dsdbufno]); Cs2Area->partition[dsdbufno].numblocks -= (u8)dsdsectnum; if (Cs2Area->blockfreespace == 200) Cs2Area->isonesectorstored = 0; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetThenDeleteSectorData(void) { u32 gtdsdsectoffset; u32 gtdsdbufno; u32 gtdsdsectnum; gtdsdsectoffset = Cs2Area->reg.CR2; gtdsdbufno = Cs2Area->reg.CR3 >> 8; gtdsdsectnum = Cs2Area->reg.CR4; if (gtdsdbufno >= MAX_SELECTORS) { doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; return; } if (Cs2Area->partition[gtdsdbufno].numblocks == 0) { CDLOG("No sectors available\n"); doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; return; } CalcSectorOffsetNumber(gtdsdbufno, >dsdsectoffset, >dsdsectnum); // Setup Data Transfer Cs2Area->cdwnum = 0; Cs2Area->datatranstype = 2; Cs2Area->datatranspartition = Cs2Area->partition + gtdsdbufno; Cs2Area->datatransoffset = 0; Cs2Area->datanumsecttrans = 0; Cs2Area->datatranssectpos = (u16)gtdsdsectoffset; Cs2Area->datasectstotrans = (u16)gtdsdsectnum; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY | CDB_HIRQ_EHST; return; } ////////////////////////////////////////////////////////////////////////////// void Cs2PutSectorData(void) { u32 psdfiltno; psdfiltno = Cs2Area->reg.CR3 >> 8; if (psdfiltno < MAX_SELECTORS) { // I'm not really sure what I'm supposed to really be doing or returning Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; } else { doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EHST; } } ////////////////////////////////////////////////////////////////////////////// void Cs2GetCopyError(void) { Cs2Area->reg.CR1 = Cs2Area->status << 8; Cs2Area->reg.CR2 = 0; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2ChangeDirectory(void) { u32 cdfilternum; cdfilternum = (Cs2Area->reg.CR3 >> 8); if (cdfilternum == 0xFF) { doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; return; } else if (cdfilternum < 0x24) { if (Cs2ReadFileSystem(Cs2Area->filter + cdfilternum, ((Cs2Area->reg.CR3 & 0xFF) << 16) | Cs2Area->reg.CR4, 0) != 0) { CDLOG("cs2\t: ReadFileSystem failed\n"); doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; return; } } doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; } ////////////////////////////////////////////////////////////////////////////// void Cs2ReadDirectory(void) { u32 rdfilternum; rdfilternum = (Cs2Area->reg.CR3 >> 8); if (rdfilternum == 0xFF) { doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; return; } else if (rdfilternum < 0x24) { if (Cs2ReadFileSystem(Cs2Area->filter + rdfilternum, ((Cs2Area->reg.CR3 & 0xFF) << 8) | Cs2Area->reg.CR4, 1) != 0) { CDLOG("cs2\t: ReadFileSystem failed\n"); doCDReport(CDB_STAT_REJECT); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; return; } } doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetFileSystemScope(void) { // may need to fix this Cs2Area->reg.CR1 = Cs2Area->status << 8; Cs2Area->reg.CR2 = (u16)(Cs2Area->numfiles - 2); Cs2Area->reg.CR3 = 0x0100; Cs2Area->reg.CR4 = 0x0002; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetFileInfo(void) { u32 gfifid; gfifid = ((Cs2Area->reg.CR3 & 0xFF) << 16) | Cs2Area->reg.CR4; if (gfifid == 0xFFFFFF) { Cs2Area->transfercount = 0; Cs2Area->infotranstype = 2; Cs2Area->reg.CR1 = Cs2Area->status << 8; Cs2Area->reg.CR2 = 0x05F4; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; } else { Cs2SetupFileInfoTransfer(gfifid); Cs2Area->transfercount = 0; Cs2Area->infotranstype = 1; Cs2Area->reg.CR1 = Cs2Area->status << 8; Cs2Area->reg.CR2 = 0x06; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; } Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_DRDY; } ////////////////////////////////////////////////////////////////////////////// void Cs2ReadFile(void) { u32 rfoffset, rffilternum, rffid, rfsize; rfoffset = ((Cs2Area->reg.CR1 & 0xFF) << 8) | Cs2Area->reg.CR2; rffilternum = Cs2Area->reg.CR3 >> 8; rffid = ((Cs2Area->reg.CR3 & 0xFF) << 8) | Cs2Area->reg.CR4; rfsize = ((Cs2Area->fileinfo[rffid].size + Cs2Area->getsectsize - 1) / Cs2Area->getsectsize) - rfoffset; Cs2SetupDefaultPlayStats(Cs2FADToTrack(Cs2Area->fileinfo[rffid].lba + rfoffset), 0); Cs2Area->maxrepeat = 0; Cs2Area->playFAD = Cs2Area->FAD = Cs2Area->fileinfo[rffid].lba + rfoffset; Cs2Area->playendFAD = Cs2Area->playFAD + rfsize; Cs2Area->options = 0x8; Cs2SetTiming(1); Cs2Area->outconcddev = Cs2Area->filter + rffilternum; Cs2Area->status = CDB_STAT_PLAY; Cs2Area->playtype = CDB_PLAYTYPE_FILE; Cs2Area->cdi->ReadAheadFAD(Cs2Area->FAD); doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2AbortFile(void) { if ((Cs2Area->status & 0xF) != CDB_STAT_OPEN && (Cs2Area->status & 0xF) != CDB_STAT_NODISC) Cs2Area->status = CDB_STAT_PAUSE; Cs2Area->isonesectorstored = 0; Cs2Area->datatranstype = -1; Cs2Area->cdwnum = 0; doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_EFLS; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegGetStatus(void) { doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegGetInterrupt(void) { u32 mgiworkinterrupt; // mpeg interrupt should be retrieved here mgiworkinterrupt = 0; // mask interupt mgiworkinterrupt &= Cs2Area->mpegintmask; Cs2Area->reg.CR1 = (u16)((Cs2Area->status << 8) | ((mgiworkinterrupt >> 16) & 0xFF)); Cs2Area->reg.CR2 = (u16) mgiworkinterrupt; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetInterruptMask(void) { Cs2Area->mpegintmask = ((Cs2Area->reg.CR1 & 0xFF) << 16) | Cs2Area->reg.CR2; doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegInit(void) { if (Cs2Area->mpgauth) Cs2Area->reg.CR1 = Cs2Area->status << 8; else Cs2Area->reg.CR1 = 0xFF00; // double-check this if (Cs2Area->reg.CR2 == 0x0001) // software timer/reset? Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM | CDB_HIRQ_MPED | CDB_HIRQ_MPST; else Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPED | CDB_HIRQ_MPST; Cs2Area->reg.CR2 = 0; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; // future mpeg-related variables should be initialized here } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetMode(void) { u8 vidplaymode=Cs2Area->reg.CR1 & 0xFF; u8 dectimingmode=Cs2Area->reg.CR2 >> 8; u8 outmode=Cs2Area->reg.CR2 & 0xFF; u8 slmode=Cs2Area->reg.CR3 >> 8; if (vidplaymode != 0xFF) Cs2Area->mpegmode.vidplaymode = vidplaymode; if (dectimingmode != 0xFF) Cs2Area->mpegmode.dectimingmode = dectimingmode; if (outmode != 0xFF) Cs2Area->mpegmode.outmode = outmode; if (slmode != 0xFF) Cs2Area->mpegmode.slmode = slmode; doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegPlay(void) { // fix me doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetDecodingMethod(void) { // fix me doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetConnection(void) { int mscnext = (Cs2Area->reg.CR3 >> 8); if (mscnext == 0) { // Current Cs2Area->mpegcon[0].audcon = Cs2Area->reg.CR1 & 0xFF; Cs2Area->mpegcon[0].audlay = Cs2Area->reg.CR2 >> 8; Cs2Area->mpegcon[0].audbufnum = Cs2Area->reg.CR2 & 0xFF; Cs2Area->mpegcon[0].vidcon = Cs2Area->reg.CR3 & 0xFF; Cs2Area->mpegcon[0].vidlay = Cs2Area->reg.CR4 >> 8; Cs2Area->mpegcon[0].vidbufnum = Cs2Area->reg.CR4 & 0xFF; } else { // Next Cs2Area->mpegcon[1].audcon = Cs2Area->reg.CR1 & 0xFF; Cs2Area->mpegcon[1].audlay = Cs2Area->reg.CR2 >> 8; Cs2Area->mpegcon[1].audbufnum = Cs2Area->reg.CR2 & 0xFF; Cs2Area->mpegcon[1].vidcon = Cs2Area->reg.CR3 & 0xFF; Cs2Area->mpegcon[1].vidlay = Cs2Area->reg.CR4 >> 8; Cs2Area->mpegcon[1].vidbufnum = Cs2Area->reg.CR4 & 0xFF; } doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegGetConnection(void) { int mgcnext = (Cs2Area->reg.CR3 >> 8); if (mgcnext == 0) { // Current Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->mpegcon[0].audcon; Cs2Area->reg.CR2 = (Cs2Area->mpegcon[0].audlay << 8) | Cs2Area->mpegcon[0].audbufnum; Cs2Area->reg.CR3 = Cs2Area->mpegcon[0].vidcon; Cs2Area->reg.CR4 = (Cs2Area->mpegcon[0].vidlay << 8) | Cs2Area->mpegcon[0].vidbufnum; } else { // Next Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->mpegcon[1].audcon; Cs2Area->reg.CR2 = (Cs2Area->mpegcon[1].audlay << 8) | Cs2Area->mpegcon[1].audbufnum; Cs2Area->reg.CR3 = Cs2Area->mpegcon[1].vidcon; Cs2Area->reg.CR4 = (Cs2Area->mpegcon[1].vidlay << 8) | Cs2Area->mpegcon[1].vidbufnum; } Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetStream(void) { int mssnext = (Cs2Area->reg.CR3 >> 8); if (mssnext == 0) { // Current Cs2Area->mpegstm[0].audstm = Cs2Area->reg.CR1 & 0xFF; Cs2Area->mpegstm[0].audstmid = Cs2Area->reg.CR2 >> 8; Cs2Area->mpegstm[0].audchannum = Cs2Area->reg.CR2 & 0xFF; Cs2Area->mpegstm[0].vidstm = Cs2Area->reg.CR3 & 0xFF; Cs2Area->mpegstm[0].vidstmid = Cs2Area->reg.CR4 >> 8; Cs2Area->mpegstm[0].vidchannum = Cs2Area->reg.CR4 & 0xFF; } else { // Next Cs2Area->mpegstm[1].audstm = Cs2Area->reg.CR1 & 0xFF; Cs2Area->mpegstm[1].audstmid = Cs2Area->reg.CR2 >> 8; Cs2Area->mpegstm[1].audchannum = Cs2Area->reg.CR2 & 0xFF; Cs2Area->mpegstm[1].vidstm = Cs2Area->reg.CR3 & 0xFF; Cs2Area->mpegstm[1].vidstmid = Cs2Area->reg.CR4 >> 8; Cs2Area->mpegstm[1].vidchannum = Cs2Area->reg.CR4 & 0xFF; } doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegGetStream(void) { int mgsnext = (Cs2Area->reg.CR3 >> 8); if (mgsnext == 0) { // Current Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->mpegstm[0].audstm; Cs2Area->reg.CR2 = (Cs2Area->mpegstm[0].audstmid << 8) | Cs2Area->mpegstm[0].audchannum; Cs2Area->reg.CR3 = Cs2Area->mpegstm[0].vidstm; Cs2Area->reg.CR4 = (Cs2Area->mpegstm[0].vidstmid << 8) | Cs2Area->mpegstm[0].vidchannum; } else { // Next Cs2Area->reg.CR1 = (Cs2Area->status << 8) | Cs2Area->mpegstm[1].audstm; Cs2Area->reg.CR2 = (Cs2Area->mpegstm[1].audstmid << 8) | Cs2Area->mpegstm[1].audchannum; Cs2Area->reg.CR3 = Cs2Area->mpegstm[1].vidstm; Cs2Area->reg.CR4 = (Cs2Area->mpegstm[1].vidstmid << 8) | Cs2Area->mpegstm[1].vidchannum; } Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegDisplay(void) { // fix me(should be setting display setting) doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetWindow(void) { // fix me(should be setting windows settings) // return default mpeg stats doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetBorderColor(void) { // fix me(should be setting border color) doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetFade(void) { // fix me(should be setting fade setting) doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetVideoEffects(void) { // fix me(should be setting video effects settings) doMPEGReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2MpegSetLSI(void) { // fix me(should be setting the LSI, among other things) Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPCM; } ////////////////////////////////////////////////////////////////////////////// void Cs2AuthenticateDevice(void) { int mpegauth; mpegauth = Cs2Area->reg.CR2 & 0xFF; if ((Cs2Area->status & 0xF) != CDB_STAT_NODISC && (Cs2Area->status & 0xF) != CDB_STAT_OPEN) { // Set registers all to invalid values(aside from status) Cs2Area->status = CDB_STAT_BUSY; Cs2Area->reg.CR1 = (Cs2Area->status << 8) | 0xFF; Cs2Area->reg.CR2 = 0xFFFF; Cs2Area->reg.CR3 = 0xFFFF; Cs2Area->reg.CR4 = 0xFFFF; if (mpegauth == 1) { Cs2Area->reg.HIRQ |= CDB_HIRQ_MPED; Cs2Area->mpgauth = 2; } else { // if authentication passes(obviously it always does), CDB_HIRQ_CSCT is set Cs2Area->isonesectorstored = 1; Cs2Area->reg.HIRQ |= CDB_HIRQ_EFLS | CDB_HIRQ_CSCT; Cs2Area->satauth = 4; } // Set registers all back to normal values Cs2Area->status = CDB_STAT_PAUSE; } else { if (mpegauth == 1) { Cs2Area->reg.HIRQ |= CDB_HIRQ_MPED; Cs2Area->mpgauth = 2; } else Cs2Area->reg.HIRQ |= CDB_HIRQ_EFLS | CDB_HIRQ_CSCT; } doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2IsDeviceAuthenticated(void) { Cs2Area->reg.CR1 = (Cs2Area->status << 8); if (Cs2Area->reg.CR2) Cs2Area->reg.CR2 = Cs2Area->mpgauth; else Cs2Area->reg.CR2 = Cs2Area->satauth; Cs2Area->reg.CR3 = 0; Cs2Area->reg.CR4 = 0; Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK; } ////////////////////////////////////////////////////////////////////////////// void Cs2GetMPEGRom(void) { u16 i; FILE * mpgfp; partition_struct * mpgpartition; // fix me Cs2Area->mpgauth |= 0x300; Cs2Area->outconmpegrom = Cs2Area->filter + 0; Cs2Area->outconmpegromnum = 0; if (Cs2Area->mpegpath && (mpgfp = fopen(Cs2Area->mpegpath, "rb")) != NULL) { u32 readoffset = ((Cs2Area->reg.CR1 & 0xFF) << 8) | Cs2Area->reg.CR2; u16 readsize = Cs2Area->reg.CR4; fseek(mpgfp, readoffset * Cs2Area->getsectsize, SEEK_SET); if ((mpgpartition = Cs2GetPartition(Cs2Area->outconmpegrom)) != NULL && !Cs2Area->isbufferfull) { IOCheck_struct check; mpgpartition->size = 0; for (i = 0; i < readsize; i++) { mpgpartition->block[mpgpartition->numblocks] = Cs2AllocateBlock(&mpgpartition->blocknum[mpgpartition->numblocks]); if (mpgpartition->block[mpgpartition->numblocks] != NULL) { // read data yread(&check, (void *)mpgpartition->block[mpgpartition->numblocks]->data, 1, Cs2Area->getsectsize, mpgfp); mpgpartition->numblocks++; mpgpartition->size += Cs2Area->getsectsize; } } Cs2Area->isonesectorstored = 1; Cs2Area->reg.HIRQ |= CDB_HIRQ_CSCT; } fclose(mpgfp); } doCDReport(Cs2Area->status); Cs2Area->reg.HIRQ |= CDB_HIRQ_CMOK | CDB_HIRQ_MPED; } ////////////////////////////////////////////////////////////////////////////// u8 Cs2FADToTrack(u32 val) { int i; for (i = 0; i < 99; i++) { if (Cs2Area->TOC[i] == 0xFFFFFFFF) return 0xFF; if (val >= (Cs2Area->TOC[i] & 0xFFFFFF) && val < (Cs2Area->TOC[i + 1] & 0xFFFFFF)) return (i + 1); } return 0; } ////////////////////////////////////////////////////////////////////////////// u32 Cs2TrackToFAD(u16 trackandindex) { if (trackandindex == 0xFFFF) // leadout position return (Cs2Area->TOC[101] & 0x00FFFFFF); if (trackandindex != 0x0000) { // regular track // (really, we should be fetching subcode q's here) if ((trackandindex & 0xFF) == 0x01) // Return Start of Track return (Cs2Area->TOC[(trackandindex >> 8) - 1] & 0x00FFFFFF); else if ((trackandindex & 0xFF) == 0x63) // Return End of Track return ((Cs2Area->TOC[(trackandindex >> 8)] & 0x00FFFFFF) - 1); } // assume it's leadin return 0; } ////////////////////////////////////////////////////////////////////////////// void Cs2FADToMSF(u32 val, u8 *m, u8 *s, u8 *f) { u32 temp; m[0] = val / 4500; temp = val % 4500; s[0] = temp / 75; f[0] = temp % 75; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetupDefaultPlayStats(u8 track_number, int writeFAD) { if (track_number != 0xFF) { Cs2Area->options = 0; Cs2Area->repcnt = 0; Cs2Area->ctrladdr = (u8)(Cs2Area->TOC[track_number - 1] >> 24); Cs2Area->index = 1; Cs2Area->track = track_number; if (writeFAD) Cs2Area->FAD = Cs2Area->TOC[track_number - 1] & 0x00FFFFFF; } } ////////////////////////////////////////////////////////////////////////////// block_struct * Cs2AllocateBlock(u8 * blocknum) { u32 i; // find a free block for(i = 0; i < 200; i++) { if (Cs2Area->block[i].size == -1) { Cs2Area->blockfreespace--; if (Cs2Area->blockfreespace <= 0) Cs2Area->isbufferfull = 1; Cs2Area->block[i].size = Cs2Area->getsectsize; *blocknum = (u8)i; return (Cs2Area->block + i); } } Cs2Area->isbufferfull = 1; return NULL; } ////////////////////////////////////////////////////////////////////////////// void Cs2FreeBlock(block_struct * blk) { if (blk == NULL) return; blk->size = -1; Cs2Area->blockfreespace++; Cs2Area->isbufferfull = 0; Cs2Area->reg.HIRQ &= ~CDB_HIRQ_BFUL; } ////////////////////////////////////////////////////////////////////////////// void Cs2SortBlocks(partition_struct * part) { unsigned int from, to; for (from = to = 0; from < MAX_BLOCKS; from++) { if (part->block[from] != NULL) { if (to != from) { part->block[to] = part->block[from]; } to++; } } for (; to < MAX_BLOCKS; to++) { part->block[to] = NULL; } } ////////////////////////////////////////////////////////////////////////////// partition_struct * Cs2GetPartition(filter_struct * curfilter) { // go through various filter conditions here(fix me) return &Cs2Area->partition[curfilter->condtrue]; } ////////////////////////////////////////////////////////////////////////////// partition_struct * Cs2FilterData(filter_struct * curfilter, int isaudio) { int condresults; partition_struct * fltpartition = NULL; for (;;) { // reset result condresults = 1; // detect which type of sector we're dealing with // If it's not mode 2, ignore the subheader conditions if (Cs2Area->workblock.data[0xF] == 0x02 && !isaudio) { // Mode 2 // go through various subheader filter conditions if (curfilter->mode & 0x01) { // File Number Check if (Cs2Area->workblock.fn != curfilter->fid) condresults = 0; } if (curfilter->mode & 0x02) { // Channel Number Check if (Cs2Area->workblock.cn != curfilter->chan) condresults = 0; } if (curfilter->mode & 0x04) { // Sub Mode Check if ((Cs2Area->workblock.sm & curfilter->smmask) != curfilter->smval) condresults = 0; } if (curfilter->mode & 0x08) { // Coding Information Check CDLOG("cs2\t: FilterData: Coding Information Check. Coding Information = %02X. Filter's Coding Information Mask = %02X, Coding Information Value = %02X\n", Cs2Area->workblock.ci, curfilter->cimask, curfilter->cival); if ((Cs2Area->workblock.ci & curfilter->cimask) != curfilter->cival) condresults = 0; } if (curfilter->mode & 0x10) { // Reverse Subheader Conditions CDLOG("cs2\t: FilterData: Reverse Subheader Conditions\n"); condresults ^= 1; } } if (curfilter->mode & 0x40) { // FAD Range Check if (Cs2Area->workblock.FAD < curfilter->FAD || Cs2Area->workblock.FAD >= (curfilter->FAD+curfilter->range)) condresults = 0; } if (condresults == 1) { Cs2Area->lastbuffer = curfilter->condtrue; fltpartition = &Cs2Area->partition[curfilter->condtrue]; break; } else { Cs2Area->lastbuffer = curfilter->condfalse; if (curfilter->condfalse == 0xFF) return NULL; // loop and try filter that was connected to the false connector curfilter = &Cs2Area->filter[curfilter->condfalse]; } } // Allocate block fltpartition->block[fltpartition->numblocks] = Cs2AllocateBlock(&fltpartition->blocknum[fltpartition->numblocks]); if (fltpartition->block[fltpartition->numblocks] == NULL) return NULL; // Copy workblock settings to allocated block fltpartition->block[fltpartition->numblocks]->size = Cs2Area->workblock.size; fltpartition->block[fltpartition->numblocks]->FAD = Cs2Area->workblock.FAD; fltpartition->block[fltpartition->numblocks]->cn = Cs2Area->workblock.cn; fltpartition->block[fltpartition->numblocks]->fn = Cs2Area->workblock.fn; fltpartition->block[fltpartition->numblocks]->sm = Cs2Area->workblock.sm; fltpartition->block[fltpartition->numblocks]->ci = Cs2Area->workblock.ci; // convert raw sector to type specified in getsectsize switch(Cs2Area->workblock.size) { case 2048: // user data only if (Cs2Area->workblock.data[0xF] == 0x02) // m2f1 memcpy(fltpartition->block[fltpartition->numblocks]->data, Cs2Area->workblock.data + 24, Cs2Area->workblock.size); else // m1 memcpy(fltpartition->block[fltpartition->numblocks]->data, Cs2Area->workblock.data + 16, Cs2Area->workblock.size); break; case 2324: // m2f2 user data only memcpy(fltpartition->block[fltpartition->numblocks]->data, Cs2Area->workblock.data + 24, Cs2Area->workblock.size); break; case 2336: // m2f2 skip sync+header data memcpy(fltpartition->block[fltpartition->numblocks]->data, Cs2Area->workblock.data + 16, Cs2Area->workblock.size); break; case 2340: // m2f2 skip sync data memcpy(fltpartition->block[fltpartition->numblocks]->data, Cs2Area->workblock.data + 12, Cs2Area->workblock.size); break; case 2352: // Copy data as is memcpy(fltpartition->block[fltpartition->numblocks]->data, Cs2Area->workblock.data, Cs2Area->workblock.size); break; default: break; } // Modify Partition values if (fltpartition->size == -1) fltpartition->size = 0; fltpartition->size += fltpartition->block[fltpartition->numblocks]->size; fltpartition->numblocks++; return fltpartition; } ////////////////////////////////////////////////////////////////////////////// int Cs2CopyDirRecord(u8 * buffer, dirrec_struct * dirrec) { u8 * temp_pointer; temp_pointer = buffer; memcpy(&dirrec->recordsize, buffer, sizeof(dirrec->recordsize)); buffer += sizeof(dirrec->recordsize); memcpy(&dirrec->xarecordsize, buffer, sizeof(dirrec->xarecordsize)); buffer += sizeof(dirrec->xarecordsize); #ifdef WORDS_BIGENDIAN buffer += sizeof(dirrec->lba); memcpy(&dirrec->lba, buffer, sizeof(dirrec->lba)); buffer += sizeof(dirrec->lba); #else memcpy(&dirrec->lba, buffer, sizeof(dirrec->lba)); buffer += (sizeof(dirrec->lba) * 2); #endif #ifdef WORDS_BIGENDIAN buffer += sizeof(dirrec->size); memcpy(&dirrec->size, buffer, sizeof(dirrec->size)); buffer += sizeof(dirrec->size); #else memcpy(&dirrec->size, buffer, sizeof(dirrec->size)); buffer += (sizeof(dirrec->size) * 2); #endif dirrec->dateyear = buffer[0]; dirrec->datemonth = buffer[1]; dirrec->dateday = buffer[2]; dirrec->datehour = buffer[3]; dirrec->dateminute = buffer[4]; dirrec->datesecond = buffer[5]; dirrec->gmtoffset = buffer[6]; buffer += 7; dirrec->flags = buffer[0]; buffer += sizeof(dirrec->flags); dirrec->fileunitsize = buffer[0]; buffer += sizeof(dirrec->fileunitsize); dirrec->interleavegapsize = buffer[0]; buffer += sizeof(dirrec->interleavegapsize); #ifdef WORDS_BIGENDIAN buffer += sizeof(dirrec->volumesequencenumber); memcpy(&dirrec->volumesequencenumber, buffer, sizeof(dirrec->volumesequencenumber)); buffer += sizeof(dirrec->volumesequencenumber); #else memcpy(&dirrec->volumesequencenumber, buffer, sizeof(dirrec->volumesequencenumber)); buffer += (sizeof(dirrec->volumesequencenumber) * 2); #endif dirrec->namelength = buffer[0]; buffer += sizeof(dirrec->namelength); memset(dirrec->name, 0, sizeof(dirrec->name)); memcpy(dirrec->name, buffer, dirrec->namelength); buffer += dirrec->namelength; // handle padding buffer += (1 - dirrec->namelength % 2); memset(&dirrec->xarecord, 0, sizeof(dirrec->xarecord)); // sadily, this is the best way I can think of for detecting XA records if ((dirrec->recordsize - (buffer - temp_pointer)) == 14) { memcpy(&dirrec->xarecord.groupid, buffer, sizeof(dirrec->xarecord.groupid)); buffer += sizeof(dirrec->xarecord.groupid); memcpy(&dirrec->xarecord.userid, buffer, sizeof(dirrec->xarecord.userid)); buffer += sizeof(dirrec->xarecord.userid); memcpy(&dirrec->xarecord.attributes, buffer, sizeof(dirrec->xarecord.attributes)); buffer += sizeof(dirrec->xarecord.attributes); #ifndef WORDS_BIGENDIAN // byte swap it dirrec->xarecord.attributes = ((dirrec->xarecord.attributes & 0xFF00) >> 8) + ((dirrec->xarecord.attributes & 0x00FF) << 8); #endif memcpy(&dirrec->xarecord.signature, buffer, sizeof(dirrec->xarecord.signature)); buffer += sizeof(dirrec->xarecord.signature); memcpy(&dirrec->xarecord.filenumber, buffer, sizeof(dirrec->xarecord.filenumber)); buffer += sizeof(dirrec->xarecord.filenumber); memcpy(dirrec->xarecord.reserved, buffer, sizeof(dirrec->xarecord.reserved)); buffer += sizeof(dirrec->xarecord.reserved); } return 0; } ////////////////////////////////////////////////////////////////////////////// int Cs2ReadFileSystem(filter_struct * curfilter, u32 fid, int isoffset) { u8 * workbuffer; u32 i; dirrec_struct dirrec; u8 numsectorsleft = 0; u32 curdirlba = 0; partition_struct * rfspartition; u32 blocksectsize = Cs2Area->getsectsize; Cs2Area->outconcddev = curfilter; if (isoffset) { // readDirectory operation // make sure we have a valid current directory if (Cs2Area->curdirsect == 0) return -1; Cs2Area->curdirfidoffset = fid - 2; curdirlba = Cs2Area->curdirsect; numsectorsleft = (u8)Cs2Area->curdirsize; } else { // changeDirectory operation if (fid == 0xFFFFFF) { // Figure out root directory's location // Read sector 16 if ((rfspartition = Cs2ReadUnFilteredSector(166)) == NULL) return -2; blocksectsize = rfspartition->block[rfspartition->numblocks - 1]->size; // Retrieve directory record's lba Cs2CopyDirRecord(rfspartition->block[rfspartition->numblocks - 1]->data + 0x9C, &dirrec); // Free Block rfspartition->size -= rfspartition->block[rfspartition->numblocks - 1]->size; Cs2FreeBlock(rfspartition->block[rfspartition->numblocks - 1]); rfspartition->blocknum[rfspartition->numblocks - 1] = 0xFF; // Sort remaining blocks Cs2SortBlocks(rfspartition); rfspartition->numblocks -= 1; curdirlba = Cs2Area->curdirsect = dirrec.lba; Cs2Area->curdirsize = (dirrec.size / blocksectsize) - 1; numsectorsleft = (u8)Cs2Area->curdirsize; Cs2Area->curdirfidoffset = 0; } else { // Read in new directory record of specified directory // make sure we have a valid current directory if (Cs2Area->curdirsect == 0) return -1; curdirlba = Cs2Area->curdirsect = Cs2Area->fileinfo[fid - Cs2Area->curdirfidoffset].lba - 150; Cs2Area->curdirsize = (Cs2Area->fileinfo[fid - Cs2Area->curdirfidoffset].size / blocksectsize) - 1; numsectorsleft = (u8)Cs2Area->curdirsize; Cs2Area->curdirfidoffset = 0; } } // Make sure any old records are cleared memset(Cs2Area->fileinfo, 0, sizeof(dirrec_struct) * MAX_FILES); // now read in first sector of directory record if ((rfspartition = Cs2ReadUnFilteredSector(curdirlba+150)) == NULL) return -2; curdirlba++; workbuffer = rfspartition->block[rfspartition->numblocks - 1]->data; // Fill in first two entries of fileinfo for (i = 0; i < 2; i++) { Cs2CopyDirRecord(workbuffer, Cs2Area->fileinfo + i); Cs2Area->fileinfo[i].lba += 150; workbuffer += Cs2Area->fileinfo[i].recordsize; if (workbuffer[0] == 0) { Cs2Area->numfiles = i; break; } } // If doing a ReadDirectory operation, parse sector entries until we've // found the fid that matches fid if (isoffset) { for (i = 2; i < fid; i++) { Cs2CopyDirRecord(workbuffer, Cs2Area->fileinfo + 2); workbuffer += Cs2Area->fileinfo[2].recordsize; if (workbuffer[0] == 0) { if (numsectorsleft > 0) { // Free previous read sector rfspartition->size -= rfspartition->block[rfspartition->numblocks - 1]->size; Cs2FreeBlock(rfspartition->block[rfspartition->numblocks - 1]); rfspartition->blocknum[rfspartition->numblocks - 1] = 0xFF; // Sort remaining blocks Cs2SortBlocks(rfspartition); rfspartition->numblocks -= 1; // Read in next sector of directory record if ((rfspartition = Cs2ReadUnFilteredSector(curdirlba+150)) == NULL) return -2; curdirlba++; numsectorsleft--; workbuffer = rfspartition->block[rfspartition->numblocks - 1]->data; } else { break; } } } } // Now generate the last 254 entries(the first two should've already been // generated earlier) for (i = 2; i < MAX_FILES; i++) { Cs2CopyDirRecord(workbuffer, Cs2Area->fileinfo + i); Cs2Area->fileinfo[i].lba += 150; workbuffer += Cs2Area->fileinfo[i].recordsize; if (workbuffer[0] == 0) { if (numsectorsleft > 0) { // Free previous read sector rfspartition->size -= rfspartition->block[rfspartition->numblocks - 1]->size; Cs2FreeBlock(rfspartition->block[rfspartition->numblocks - 1]); rfspartition->blocknum[rfspartition->numblocks - 1] = 0xFF; // Sort remaining blocks Cs2SortBlocks(rfspartition); rfspartition->numblocks -= 1; // Read in next sector of directory record if ((rfspartition = Cs2ReadUnFilteredSector(curdirlba+150)) == NULL) return -2; curdirlba++; numsectorsleft--; workbuffer = rfspartition->block[rfspartition->numblocks - 1]->data; } else { Cs2Area->numfiles = i; break; } } } // Free the remaining sector rfspartition->size -= rfspartition->block[rfspartition->numblocks - 1]->size; Cs2FreeBlock(rfspartition->block[rfspartition->numblocks - 1]); rfspartition->blocknum[rfspartition->numblocks - 1] = 0xFF; // Sort remaining blocks Cs2SortBlocks(rfspartition); rfspartition->numblocks -= 1; //#if CDDEBUG // for (i = 0; i < MAX_FILES; i++) // { // CDLOG("fileinfo[%d].name = %s\n", i, Cs2Area->fileinfo[i].name); // } //#endif return 0; } ////////////////////////////////////////////////////////////////////////////// void Cs2SetupFileInfoTransfer(u32 fid) { Cs2Area->transfileinfo[0] = (u8)(Cs2Area->fileinfo[fid].lba >> 24); Cs2Area->transfileinfo[1] = (u8)(Cs2Area->fileinfo[fid].lba >> 16); Cs2Area->transfileinfo[2] = (u8)(Cs2Area->fileinfo[fid].lba >> 8); Cs2Area->transfileinfo[3] = (u8)Cs2Area->fileinfo[fid].lba; Cs2Area->transfileinfo[4] = (u8)(Cs2Area->fileinfo[fid].size >> 24); Cs2Area->transfileinfo[5] = (u8)(Cs2Area->fileinfo[fid].size >> 16); Cs2Area->transfileinfo[6] = (u8)(Cs2Area->fileinfo[fid].size >> 8); Cs2Area->transfileinfo[7] = (u8)Cs2Area->fileinfo[fid].size; Cs2Area->transfileinfo[8] = Cs2Area->fileinfo[fid].interleavegapsize; Cs2Area->transfileinfo[9] = Cs2Area->fileinfo[fid].fileunitsize; Cs2Area->transfileinfo[10] = (u8) fid; Cs2Area->transfileinfo[11] = Cs2Area->fileinfo[fid].flags; } ////////////////////////////////////////////////////////////////////////////// partition_struct * Cs2ReadUnFilteredSector(u32 rufsFAD) { partition_struct * rufspartition; char syncheader[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; if ((rufspartition = Cs2GetPartition(Cs2Area->outconcddev)) != NULL && !Cs2Area->isbufferfull) { // Allocate Block rufspartition->block[rufspartition->numblocks] = Cs2AllocateBlock(&rufspartition->blocknum[rufspartition->numblocks]); if (rufspartition->block[rufspartition->numblocks] == NULL) return NULL; // read a sector using cd interface function if (!Cs2Area->cdi->ReadSectorFAD(rufsFAD, Cs2Area->workblock.data)) return NULL; // convert raw sector to type specified in getsectsize switch(Cs2Area->getsectsize) { case 2048: // user data only if (Cs2Area->workblock.data[0xF] == 0x02) { // is it form1/form2 data? if (!(Cs2Area->workblock.data[0x12] & 0x20)) { // form 1 memcpy(rufspartition->block[rufspartition->numblocks]->data, Cs2Area->workblock.data + 24, 2048); Cs2Area->workblock.size = Cs2Area->getsectsize; } else { // form 2 memcpy(rufspartition->block[rufspartition->numblocks]->data, Cs2Area->workblock.data + 24, 2324); Cs2Area->workblock.size = 2324; } } else { memcpy(rufspartition->block[rufspartition->numblocks]->data, Cs2Area->workblock.data + 16, 2048); Cs2Area->workblock.size = Cs2Area->getsectsize; } break; case 2336: // skip sync+header data memcpy(rufspartition->block[rufspartition->numblocks]->data, Cs2Area->workblock.data + 16, 2336); Cs2Area->workblock.size = Cs2Area->getsectsize; break; case 2340: // skip sync data memcpy(rufspartition->block[rufspartition->numblocks]->data, Cs2Area->workblock.data + 12, 2340); Cs2Area->workblock.size = Cs2Area->getsectsize; break; case 2352: // no conversion needed Cs2Area->workblock.size = Cs2Area->getsectsize; break; default: break; } // if mode 2 track, setup the subheader values if (memcmp(syncheader, Cs2Area->workblock.data, 12) == 0 && Cs2Area->workblock.data[0xF] == 0x02) { rufspartition->block[rufspartition->numblocks]->fn = Cs2Area->workblock.data[0x10]; rufspartition->block[rufspartition->numblocks]->cn = Cs2Area->workblock.data[0x11]; rufspartition->block[rufspartition->numblocks]->sm = Cs2Area->workblock.data[0x12]; rufspartition->block[rufspartition->numblocks]->ci = Cs2Area->workblock.data[0x13]; } Cs2Area->workblock.FAD = rufsFAD; // Modify Partition values if (rufspartition->size == -1) rufspartition->size = 0; rufspartition->size += rufspartition->block[rufspartition->numblocks]->size; rufspartition->numblocks++; return rufspartition; } return NULL; } ////////////////////////////////////////////////////////////////////////////// int Cs2ReadFilteredSector(u32 rfsFAD, partition_struct **partition) { char syncheader[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00}; int isaudio = 0; if (Cs2Area->outconcddev != NULL && !Cs2Area->isbufferfull) { // read a sector using cd interface function to workblock.data if (!Cs2Area->cdi->ReadSectorFAD(rfsFAD, Cs2Area->workblock.data)) { *partition = NULL; return -2; } Cs2Area->workblock.size = Cs2Area->getsectsize; Cs2Area->workblock.FAD = rfsFAD; if (memcmp(syncheader, Cs2Area->workblock.data, 12) != 0) isaudio = 1; // force 1x speed if reading from an audio track Cs2Area->isaudio = isaudio; Cs2SetTiming(1); // if mode 2 track, setup the subheader values if (isaudio) { ScspReceiveCDDA(Cs2Area->workblock.data); *partition = NULL; return 0; } else if (Cs2Area->workblock.data[0xF] == 0x02) { // if it's form 2 data the sector size should be 2324 if (Cs2Area->workblock.data[0x12] & 0x20) Cs2Area->workblock.size = 2324; Cs2Area->workblock.fn = Cs2Area->workblock.data[0x10]; Cs2Area->workblock.cn = Cs2Area->workblock.data[0x11]; Cs2Area->workblock.sm = Cs2Area->workblock.data[0x12]; Cs2Area->workblock.ci = Cs2Area->workblock.data[0x13]; } // pass workblock to filter function(after it identifies partition, // it should allocate the partition block, setup/change the partition // values, and copy workblock to the allocated block) *partition = Cs2FilterData(Cs2Area->outconcddev, isaudio); return 0; } *partition = NULL; return -1; } ////////////////////////////////////////////////////////////////////////////// u8 Cs2GetIP(int autoregion) { partition_struct * gripartition; u8 ret = 0; Cs2Area->outconcddev = Cs2Area->filter + 0; Cs2Area->outconcddevnum = 0; // read in lba 0/FAD 150 if ((gripartition = Cs2ReadUnFilteredSector(150)) != NULL) { char *buf=(char*)gripartition->block[gripartition->numblocks - 1]->data; // Make sure we're dealing with a saturn game if (memcmp(buf, "SEGA SEGASATURN", 15) == 0) { memcpy(cdip->system, buf, 16); cdip->system[16]='\0'; memcpy(cdip->company, buf+0x10, 16); cdip->company[16]='\0'; sscanf(buf+0x20, "%s", cdip->itemnum); memcpy(cdip->version, buf+0x2A, 6); cdip->version[6]='\0'; sprintf(cdip->date, "%c%c/%c%c/%c%c%c%c", buf[0x34], buf[0x35], buf[0x36], buf[0x37], buf[0x30], buf[0x31], buf[0x32], buf[0x33]); sscanf(buf+0x38, "%s", cdip->cdinfo); sscanf(buf+0x40, "%s", cdip->region); sscanf(buf+0x50, "%s", cdip->peripheral); memcpy(cdip->gamename, buf+0x60, 112); cdip->gamename[112]='\0'; #ifdef WORDS_BIGENDIAN memcpy(&cdip->ipsize, buf+0xE0, sizeof(u32)); memcpy(&cdip->msh2stack, buf+0xE8, sizeof(u32)); memcpy(&cdip->ssh2stack, buf+0xEC, sizeof(u32)); memcpy(&cdip->firstprogaddr, buf+0xF0, sizeof(u32)); memcpy(&cdip->firstprogsize, buf+0xF4, sizeof(u32)); #else cdip->ipsize = (buf[0xE0] << 24) | (buf[0xE1] << 16) | (buf[0xE2] << 8) | buf[0xE3]; cdip->msh2stack = (buf[0xE8] << 24) | (buf[0xE9] << 16) | (buf[0xEA] << 8) | buf[0xEB]; cdip->ssh2stack = (buf[0xEC] << 24) | (buf[0xED] << 16) | (buf[0xEE] << 8) | buf[0xEF]; cdip->firstprogaddr = (buf[0xF0] << 24) | (buf[0xF1] << 16) | (buf[0xF2] << 8) | buf[0xF3]; cdip->firstprogsize = (buf[0xF4] << 24) | (buf[0xF5] << 16) | (buf[0xF6] << 8) | buf[0xF7]; #endif if (autoregion) { // Read first available region, that'll be what we'll use switch (cdip->region[0]) { case 'J': ret = 1; break; case 'T': ret = 2; break; case 'U': ret = 4; break; case 'B': ret = 5; break; case 'K': ret = 6; break; case 'A': ret = 0xA; break; case 'E': ret = 0xC; break; case 'L': ret = 0xD; break; default: break; } } } // Free Block gripartition->size -= gripartition->block[gripartition->numblocks - 1]->size; Cs2FreeBlock(gripartition->block[gripartition->numblocks - 1]); gripartition->blocknum[gripartition->numblocks - 1] = 0xFF; // Sort remaining blocks Cs2SortBlocks(gripartition); gripartition->numblocks -= 1; } return ret; } ////////////////////////////////////////////////////////////////////////////// u8 Cs2GetRegionID(void) { return Cs2GetIP(1); } ////////////////////////////////////////////////////////////////////////////// int Cs2SaveState(FILE * fp) { int offset, i; IOCheck_struct check; // This is mostly kludge, but it will have to do until I have time to rewrite it all offset = StateWriteHeader(fp, "CS2 ", 2); // Write cart type ywrite(&check, (void *) &Cs2Area->carttype, 4, 1, fp); // Write cd block registers ywrite(&check, (void *) &Cs2Area->reg, sizeof(blockregs_struct), 1, fp); // Write current Status variables(needs a rewrite) ywrite(&check, (void *) &Cs2Area->FAD, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->status, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->options, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->repcnt, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->ctrladdr, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->track, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->index, 1, 1, fp); // Write other cd block internal variables ywrite(&check, (void *) &Cs2Area->satauth, 2, 1, fp); ywrite(&check, (void *) &Cs2Area->mpgauth, 2, 1, fp); ywrite(&check, (void *) &Cs2Area->transfercount, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->cdwnum, 4, 1, fp); ywrite(&check, (void *) Cs2Area->TOC, 4, 102, fp); ywrite(&check, (void *) &Cs2Area->playFAD, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->playendFAD, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->getsectsize, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->putsectsize, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->calcsize, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->infotranstype, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->datatranstype, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->isonesectorstored, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->isdiskchanged, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->isbufferfull, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->speed1x, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->isaudio, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->transfileinfo, 1, 12, fp); ywrite(&check, (void *) &Cs2Area->lastbuffer, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->_command, 1, 1, fp); { u32 temp = (Cs2Area->_periodictiming + 3) / 3; ywrite(&check, (void *) &temp, 4, 1, fp); } ywrite(&check, (void *) &Cs2Area->_commandtiming, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->outconcddevnum, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->outconmpegfbnum, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->outconmpegbufnum, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->outconmpegromnum, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->outconhostnum, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->datatranspartitionnum, 1, 1, fp); ywrite(&check, (void *) &Cs2Area->datatransoffset, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->datanumsecttrans, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->datatranssectpos, 2, 1, fp); ywrite(&check, (void *) &Cs2Area->datasectstotrans, 2, 1, fp); ywrite(&check, (void *) &Cs2Area->blockfreespace, 4, 1, fp); ywrite(&check, (void *) &Cs2Area->curdirsect, 4, 1, fp); // Write CD buffer ywrite(&check, (void *)Cs2Area->block, sizeof(block_struct), MAX_BLOCKS, fp); // Write partition data for (i = 0; i < MAX_SELECTORS; i++) { ywrite(&check, (void *)&Cs2Area->partition[i].size, 4, 1, fp); ywrite(&check, (void *)Cs2Area->partition[i].blocknum, 1, MAX_BLOCKS, fp); ywrite(&check, (void *)&Cs2Area->partition[i].numblocks, 1, 1, fp); } // Write filter data ywrite(&check, (void *)Cs2Area->filter, sizeof(filter_struct), MAX_SELECTORS, fp); // Write File Info Table ywrite(&check, (void *)Cs2Area->fileinfo, sizeof(dirrec_struct), MAX_FILES, fp); // Write MPEG card registers here // Write current MPEG card status variables ywrite(&check, (void *)&Cs2Area->actionstatus, 1, 1, fp); ywrite(&check, (void *)&Cs2Area->pictureinfo, 1, 1, fp); ywrite(&check, (void *)&Cs2Area->mpegaudiostatus, 1, 1, fp); ywrite(&check, (void *)&Cs2Area->mpegvideostatus, 2, 1, fp); ywrite(&check, (void *)&Cs2Area->vcounter, 2, 1, fp); // Write other MPEG card internal variables ywrite(&check, (void *)&Cs2Area->mpegintmask, 4, 1, fp); ywrite(&check, (void *)Cs2Area->mpegcon, sizeof(mpegcon_struct), 2, fp); ywrite(&check, (void *)Cs2Area->mpegstm, sizeof(mpegstm_struct), 2, fp); return StateFinishHeader(fp, offset); } ////////////////////////////////////////////////////////////////////////////// int Cs2LoadState(FILE * fp, int version, int size) { int i, i2; IOCheck_struct check; // This is mostly kludge, but it will have to do until I have time to rewrite it all // Read cart type yread(&check, (void *)&Cs2Area->carttype, 4, 1, fp); // Read cd block registers yread(&check, (void *)&Cs2Area->reg, sizeof(blockregs_struct), 1, fp); // Read current Status variables(needs a reRead) yread(&check, (void *)&Cs2Area->FAD, 4, 1, fp); yread(&check, (void *)&Cs2Area->status, 1, 1, fp); yread(&check, (void *)&Cs2Area->options, 1, 1, fp); yread(&check, (void *)&Cs2Area->repcnt, 1, 1, fp); yread(&check, (void *)&Cs2Area->ctrladdr, 1, 1, fp); yread(&check, (void *)&Cs2Area->track, 1, 1, fp); yread(&check, (void *)&Cs2Area->index, 1, 1, fp); // Read other cd block internal variables yread(&check, (void *)&Cs2Area->satauth, 2, 1, fp); yread(&check, (void *)&Cs2Area->mpgauth, 2, 1, fp); yread(&check, (void *)&Cs2Area->transfercount, 4, 1, fp); yread(&check, (void *)&Cs2Area->cdwnum, 4, 1, fp); yread(&check, (void *)Cs2Area->TOC, 4, 102, fp); yread(&check, (void *)&Cs2Area->playFAD, 4, 1, fp); yread(&check, (void *)&Cs2Area->playendFAD, 4, 1, fp); yread(&check, (void *)&Cs2Area->getsectsize, 4, 1, fp); yread(&check, (void *)&Cs2Area->putsectsize, 4, 1, fp); yread(&check, (void *)&Cs2Area->calcsize, 4, 1, fp); yread(&check, (void *)&Cs2Area->infotranstype, 4, 1, fp); yread(&check, (void *)&Cs2Area->datatranstype, 4, 1, fp); yread(&check, (void *)&Cs2Area->isonesectorstored, 1, 1, fp); yread(&check, (void *)&Cs2Area->isdiskchanged, 1, 1, fp); yread(&check, (void *)&Cs2Area->isbufferfull, 1, 1, fp); yread(&check, (void *)&Cs2Area->speed1x, 1, 1, fp); if (version > 1) yread(&check, (void *)&Cs2Area->isaudio, 1, 1, fp); yread(&check, (void *)&Cs2Area->transfileinfo, 1, 12, fp); yread(&check, (void *)&Cs2Area->lastbuffer, 1, 1, fp); yread(&check, (void *)&Cs2Area->_command, 1, 1, fp); { u32 temp; yread(&check, (void *)&temp, 4, 1, fp); // Derive the actual, accurate value (always a multiple of 10) Cs2Area->_periodictiming = ((temp * 3) / 10) * 10; } yread(&check, (void *)&Cs2Area->_commandtiming, 4, 1, fp); yread(&check, (void *)&Cs2Area->outconcddevnum, 1, 1, fp); if (Cs2Area->outconcddevnum == 0xFF) Cs2Area->outconcddev = NULL; else Cs2Area->outconcddev = Cs2Area->filter + Cs2Area->outconcddevnum; yread(&check, (void *)&Cs2Area->outconmpegfbnum, 1, 1, fp); if (Cs2Area->outconmpegfbnum == 0xFF) Cs2Area->outconmpegfb = NULL; else Cs2Area->outconmpegfb = Cs2Area->filter + Cs2Area->outconmpegfbnum; yread(&check, (void *)&Cs2Area->outconmpegbufnum, 1, 1, fp); if (Cs2Area->outconmpegbufnum == 0xFF) Cs2Area->outconmpegbuf = NULL; else Cs2Area->outconmpegbuf = Cs2Area->filter + Cs2Area->outconmpegbufnum; yread(&check, (void *)&Cs2Area->outconmpegromnum, 1, 1, fp); if (Cs2Area->outconmpegromnum == 0xFF) Cs2Area->outconmpegrom = NULL; else Cs2Area->outconmpegrom = Cs2Area->filter + Cs2Area->outconmpegromnum; yread(&check, (void *)&Cs2Area->outconhostnum, 1, 1, fp); if (Cs2Area->outconhostnum == 0xFF) Cs2Area->outconhost = NULL; else Cs2Area->outconhost = Cs2Area->filter + Cs2Area->outconhostnum; yread(&check, (void *)&Cs2Area->datatranspartitionnum, 1, 1, fp); yread(&check, (void *)&Cs2Area->datatransoffset, 4, 1, fp); yread(&check, (void *)&Cs2Area->datanumsecttrans, 4, 1, fp); yread(&check, (void *)&Cs2Area->datatranssectpos, 2, 1, fp); yread(&check, (void *)&Cs2Area->datasectstotrans, 2, 1, fp); yread(&check, (void *)&Cs2Area->blockfreespace, 4, 1, fp); yread(&check, (void *)&Cs2Area->curdirsect, 4, 1, fp); // Read CD buffer yread(&check, (void *)Cs2Area->block, sizeof(block_struct), MAX_BLOCKS, fp); // Read partition data for (i = 0; i < MAX_SELECTORS; i++) { yread(&check, (void *)&Cs2Area->partition[i].size, 4, 1, fp); yread(&check, (void *)Cs2Area->partition[i].blocknum, 1, MAX_BLOCKS, fp); yread(&check, (void *)&Cs2Area->partition[i].numblocks, 1, 1, fp); for (i2 = 0; i2 < MAX_BLOCKS; i2++) { if (Cs2Area->partition[i].blocknum[i2] == 0xFF) Cs2Area->partition[i].block[i2] = NULL; else Cs2Area->partition[i].block[i2] = Cs2Area->block + Cs2Area->partition[i].blocknum[i2]; } } // Read filter data yread(&check, (void *)Cs2Area->filter, sizeof(filter_struct), MAX_SELECTORS, fp); // Read File Info Table yread(&check, (void *)Cs2Area->fileinfo, sizeof(dirrec_struct), MAX_FILES, fp); // Read MPEG card registers here // Read current MPEG card status variables yread(&check, (void *)&Cs2Area->actionstatus, 1, 1, fp); yread(&check, (void *)&Cs2Area->pictureinfo, 1, 1, fp); yread(&check, (void *)&Cs2Area->mpegaudiostatus, 1, 1, fp); yread(&check, (void *)&Cs2Area->mpegvideostatus, 2, 1, fp); yread(&check, (void *)&Cs2Area->vcounter, 2, 1, fp); // Read other MPEG card internal variables yread(&check, (void *)&Cs2Area->mpegintmask, 4, 1, fp); yread(&check, (void *)Cs2Area->mpegcon, sizeof(mpegcon_struct), 2, fp); yread(&check, (void *)Cs2Area->mpegstm, sizeof(mpegstm_struct), 2, fp); return size; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/m68kcore.h000644 001750 001750 00000003750 12256006135 020011 0ustar00guillaumeguillaume000000 000000 /* Copyright 2007 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef M68KCORE_H #define M68KCORE_H #include "core.h" #define M68KCORE_DEFAULT -1 #define M68KCORE_DUMMY 0 #define M68KCORE_C68K 1 #define M68KCORE_Q68 2 typedef u32 FASTCALL M68K_READ(const u32 adr); typedef void FASTCALL M68K_WRITE(const u32 adr, u32 data); typedef struct { int id; const char *Name; int (*Init)(void); void (*DeInit)(void); void (*Reset)(void); s32 FASTCALL (*Exec)(s32 cycle); void (*Sync)(void); u32 (*GetDReg)(u32 num); u32 (*GetAReg)(u32 num); u32 (*GetPC)(void); u32 (*GetSR)(void); u32 (*GetUSP)(void); u32 (*GetMSP)(void); void (*SetDReg)(u32 num, u32 val); void (*SetAReg)(u32 num, u32 val); void (*SetPC)(u32 val); void (*SetSR)(u32 val); void (*SetUSP)(u32 val); void (*SetMSP)(u32 val); void (*SetFetch)(u32 low_adr, u32 high_adr, pointer fetch_adr); void FASTCALL (*SetIRQ)(s32 level); void FASTCALL (*WriteNotify)(u32 address, u32 size); void (*SetReadB)(M68K_READ *Func); void (*SetReadW)(M68K_READ *Func); void (*SetWriteB)(M68K_WRITE *Func); void (*SetWriteW)(M68K_WRITE *Func); } M68K_struct; extern M68K_struct * M68K; int M68KInit(int coreid); extern M68K_struct M68KDummy; extern M68K_struct M68KC68K; extern M68K_struct M68KQ68; #endif yabause-0.9.13.1/src/snddummy.c000644 001750 001750 00000006354 12256006126 020211 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004 Stephane Dallongeville Copyright 2004-2007 Theo Berkau Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "scsp.h" ////////////////////////////////////////////////////////////////////////////// // Dummy Sound Interface ////////////////////////////////////////////////////////////////////////////// static int SNDDummyInit(void); static void SNDDummyDeInit(void); static int SNDDummyReset(void); static int SNDDummyChangeVideoFormat(int vertfreq); static void SNDDummyUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples); static u32 SNDDummyGetAudioSpace(void); static void SNDDummyMuteAudio(void); static void SNDDummyUnMuteAudio(void); static void SNDDummySetVolume(int volume); SoundInterface_struct SNDDummy = { SNDCORE_DUMMY, "Dummy Sound Interface", SNDDummyInit, SNDDummyDeInit, SNDDummyReset, SNDDummyChangeVideoFormat, SNDDummyUpdateAudio, SNDDummyGetAudioSpace, SNDDummyMuteAudio, SNDDummyUnMuteAudio, SNDDummySetVolume }; ////////////////////////////////////////////////////////////////////////////// static int SNDDummyInit(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDDummyDeInit(void) { } ////////////////////////////////////////////////////////////////////////////// static int SNDDummyReset(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// static int SNDDummyChangeVideoFormat(UNUSED int vertfreq) { return 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDDummyUpdateAudio(UNUSED u32 *leftchanbuffer, UNUSED u32 *rightchanbuffer, UNUSED u32 num_samples) { } ////////////////////////////////////////////////////////////////////////////// static u32 SNDDummyGetAudioSpace(void) { /* A "hack" to get dummy sound core working enough * so videos are not "freezing". Values have been * found by experiments... I don't have a clue why * they are working ^^; */ static int i = 0; i++; if (i == 55) { i = 0; return 85; } else { return 0; } } ////////////////////////////////////////////////////////////////////////////// void SNDDummyMuteAudio() { } ////////////////////////////////////////////////////////////////////////////// void SNDDummyUnMuteAudio() { } ////////////////////////////////////////////////////////////////////////////// void SNDDummySetVolume(UNUSED int volume) { } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/cdbase.c000644 001750 001750 00000067032 12256006134 017571 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004-2008, 2013 Theo Berkau Copyright 2005 Joost Peters Copyright 2005-2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "cdbase.h" #include "error.h" #include "debug.h" #ifndef HAVE_STRICMP #ifdef HAVE_STRCASECMP #define stricmp strcasecmp #endif #endif #ifndef HAVE_WFOPEN static char * wcsdupstr(const wchar_t * path) { char * mbs; size_t len = wcstombs(NULL, path, 0); if (len == (size_t) -1) return NULL; mbs = malloc(len); len = wcstombs(mbs, path, len); if (len == (size_t) -1) { free(mbs); return NULL; } return mbs; } static FILE * _wfopen(const wchar_t *wpath, const wchar_t *wmode) { FILE * fd; char * path = wcsdupstr(wpath); char * mode = wcsdupstr(wmode); if ((path == NULL) || (mode == NULL)) return NULL; fd = fopen(path, mode); free(path); free(mode); return fd; } #endif ////////////////////////////////////////////////////////////////////////////// // Contains the Dummy and ISO CD Interfaces static int DummyCDInit(const char *); static void DummyCDDeInit(void); static int DummyCDGetStatus(void); static s32 DummyCDReadTOC(u32 *); static int DummyCDReadSectorFAD(u32, void *); static void DummyCDReadAheadFAD(u32); CDInterface DummyCD = { CDCORE_DUMMY, "Dummy CD Drive", DummyCDInit, DummyCDDeInit, DummyCDGetStatus, DummyCDReadTOC, DummyCDReadSectorFAD, DummyCDReadAheadFAD, }; static int ISOCDInit(const char *); static void ISOCDDeInit(void); static int ISOCDGetStatus(void); static s32 ISOCDReadTOC(u32 *); static int ISOCDReadSectorFAD(u32, void *); static void ISOCDReadAheadFAD(u32); CDInterface ISOCD = { CDCORE_ISO, "ISO-File Virtual Drive", ISOCDInit, ISOCDDeInit, ISOCDGetStatus, ISOCDReadTOC, ISOCDReadSectorFAD, ISOCDReadAheadFAD, }; ////////////////////////////////////////////////////////////////////////////// // Dummy Interface ////////////////////////////////////////////////////////////////////////////// static int DummyCDInit(UNUSED const char *cdrom_name) { // Initialization function. cdrom_name can be whatever you want it to // be. Obviously with some ports(e.g. the dreamcast port) you probably // won't even use it. return 0; } ////////////////////////////////////////////////////////////////////////////// static void DummyCDDeInit(void) { // Cleanup function. Enough said. } ////////////////////////////////////////////////////////////////////////////// static int DummyCDGetStatus(void) { // This function is called periodically to see what the status of the // drive is. // // Should return one of the following values: // 0 - CD Present, disc spinning // 1 - CD Present, disc not spinning // 2 - CD not present // 3 - Tray open // // If you really don't want to bother too much with this function, just // return status 0. Though it is kind of nice when the bios's cd // player, etc. recognizes when you've ejected the tray and popped in // another disc. return 0; } ////////////////////////////////////////////////////////////////////////////// static s32 DummyCDReadTOC(UNUSED u32 *TOC) { // The format of TOC is as follows: // TOC[0] - TOC[98] are meant for tracks 1-99. Each entry has the // following format: // bits 0 - 23: track FAD address // bits 24 - 27: track addr // bits 28 - 31: track ctrl // // Any Unused tracks should be set to 0xFFFFFFFF // // TOC[99] - Point A0 information // Uses the following format: // bits 0 - 7: PFRAME(should always be 0) // bits 7 - 15: PSEC(Program area format: 0x00 - CDDA or CDROM, // 0x10 - CDI, 0x20 - CDROM-XA) // bits 16 - 23: PMIN(first track's number) // bits 24 - 27: first track's addr // bits 28 - 31: first track's ctrl // // TOC[100] - Point A1 information // Uses the following format: // bits 0 - 7: PFRAME(should always be 0) // bits 7 - 15: PSEC(should always be 0) // bits 16 - 23: PMIN(last track's number) // bits 24 - 27: last track's addr // bits 28 - 31: last track's ctrl // // TOC[101] - Point A2 information // Uses the following format: // bits 0 - 23: leadout FAD address // bits 24 - 27: leadout's addr // bits 28 - 31: leadout's ctrl // // Special Note: To convert from LBA/LSN to FAD, add 150. return 0; } ////////////////////////////////////////////////////////////////////////////// static int DummyCDReadSectorFAD(UNUSED u32 FAD, void * buffer) { // This function is supposed to read exactly 1 -RAW- 2352-byte sector // at the specified FAD address to buffer. Should return true if // successful, false if there was an error. // // Special Note: To convert from FAD to LBA/LSN, minus 150. // // The whole process needed to be changed since I need more control // over sector detection, etc. Not to mention it means less work for // the porter since they only have to implement raw sector reading as // opposed to implementing mode 1, mode 2 form1/form2, -and- raw // sector reading. memset(buffer, 0, 2352); return 1; } ////////////////////////////////////////////////////////////////////////////// static void DummyCDReadAheadFAD(UNUSED u32 FAD) { // This function is called to tell the driver which sector (FAD // address) is expected to be read next. If the driver supports // read-ahead, it should start reading the given sector in the // background while the emulation continues, so that when the // sector is actually read with ReadSectorFAD() it'll be available // immediately. (Note that there's no guarantee this sector will // actually be requested--the emulated CD might be stopped before // the sector is read, for example.) // // This function should NOT block. If the driver can't perform // asynchronous reads (or you just don't want to bother handling // them), make this function a no-op and just read sectors // normally. } ////////////////////////////////////////////////////////////////////////////// // ISO Interface ////////////////////////////////////////////////////////////////////////////// typedef struct { u8 ctl_addr; u32 fad_start; u32 fad_end; u32 file_offset; u32 sector_size; FILE *fp; int file_size; int file_id; int interleaved_sub; } track_info_struct; typedef struct { u32 fad_start; u32 fad_end; track_info_struct *track; int track_num; } session_info_struct; typedef struct { int session_num; session_info_struct *session; } disc_info_struct; #pragma pack(push, 1) typedef struct { u8 signature[16]; u8 version[2]; u16 medium_type; u16 session_count; u16 unused1[2]; u16 bca_length; u32 unused2[2]; u32 bca_offset; u32 unused3[6]; u32 disk_struct_offset; u32 unused4[3]; u32 sessions_blocks_offset; u32 dpm_blocks_offset; u32 enc_key_offset; } mds_header_struct; typedef struct { s32 session_start; s32 session_end; u16 session_number; u8 total_blocks; u8 leadin_blocks; u16 first_track; u16 last_track; u32 unused; u32 track_blocks_offset; } mds_session_struct; typedef struct { u8 mode; u8 subchannel_mode; u8 addr_ctl; u8 unused1; u8 track_num; u32 unused2; u8 m; u8 s; u8 f; u32 extra_offset; u16 sector_size; u8 unused3[18]; u32 start_sector; u64 start_offset; u8 session; u8 unused4[3]; u32 footer_offset; u8 unused5[24]; } mds_track_struct; typedef struct { u32 filename_offset; u32 is_widechar; u32 unused1; u32 unused2; } mds_footer_struct; #pragma pack(pop) static const s8 syncHdr[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }; enum IMG_TYPE { IMG_NONE, IMG_ISO, IMG_BINCUE, IMG_MDS, IMG_CCD, IMG_NRG }; enum IMG_TYPE imgtype = IMG_ISO; static u32 isoTOC[102]; static disc_info_struct disc; #define MSF_TO_FAD(m,s,f) ((m * 4500) + (s * 75) + f) ////////////////////////////////////////////////////////////////////////////// static int LoadBinCue(const char *cuefilename, FILE *iso_file) { u32 size; char *temp_buffer, *temp_buffer2; unsigned int track_num; unsigned int indexnum, min, sec, frame; unsigned int pregap=0; char *p, *p2; track_info_struct trk[100]; int file_size; int i; disc.session_num = 1; disc.session = malloc(sizeof(session_info_struct) * disc.session_num); if (disc.session == NULL) { YabSetError(YAB_ERR_MEMORYALLOC, NULL); return -1; } fseek(iso_file, 0, SEEK_END); size = ftell(iso_file); fseek(iso_file, 0, SEEK_SET); // Allocate buffer with enough space for reading cue if ((temp_buffer = (char *)calloc(size, 1)) == NULL) return -1; // Skip image filename if (fscanf(iso_file, "FILE \"%*[^\"]\" %*s\r\n") == EOF) { free(temp_buffer); return -1; } // Time to generate TOC for (;;) { // Retrieve a line in cue if (fscanf(iso_file, "%s", temp_buffer) == EOF) break; // Figure out what it is if (strncmp(temp_buffer, "TRACK", 5) == 0) { // Handle accordingly if (fscanf(iso_file, "%d %[^\r\n]\r\n", &track_num, temp_buffer) == EOF) break; if (strncmp(temp_buffer, "MODE1", 5) == 0 || strncmp(temp_buffer, "MODE2", 5) == 0) { // Figure out the track sector size trk[track_num-1].sector_size = atoi(temp_buffer + 6); trk[track_num-1].ctl_addr = 0x41; } else if (strncmp(temp_buffer, "AUDIO", 5) == 0) { // Update toc entry trk[track_num-1].sector_size = 2352; trk[track_num-1].ctl_addr = 0x01; } } else if (strncmp(temp_buffer, "INDEX", 5) == 0) { // Handle accordingly if (fscanf(iso_file, "%d %d:%d:%d\r\n", &indexnum, &min, &sec, &frame) == EOF) break; if (indexnum == 1) { // Update toc entry trk[track_num-1].fad_start = (MSF_TO_FAD(min, sec, frame) + pregap + 150); trk[track_num-1].file_offset = MSF_TO_FAD(min, sec, frame) * trk[track_num-1].sector_size; } } else if (strncmp(temp_buffer, "PREGAP", 6) == 0) { if (fscanf(iso_file, "%d:%d:%d\r\n", &min, &sec, &frame) == EOF) break; pregap += MSF_TO_FAD(min, sec, frame); } else if (strncmp(temp_buffer, "POSTGAP", 7) == 0) { if (fscanf(iso_file, "%d:%d:%d\r\n", &min, &sec, &frame) == EOF) break; } else if (strncmp(temp_buffer, "FILE", 4) == 0) { YabSetError(YAB_ERR_OTHER, "Unsupported cue format"); free(temp_buffer); return -1; } } trk[track_num].file_offset = 0; trk[track_num].fad_start = 0xFFFFFFFF; // Go back, retrieve image filename fseek(iso_file, 0, SEEK_SET); fscanf(iso_file, "FILE \"%[^\"]\" %*s\r\n", temp_buffer); fclose(iso_file); // Now go and open up the image file, figure out its size, etc. if ((iso_file = fopen(temp_buffer, "rb")) == NULL) { // Ok, exact path didn't work. Let's trim the path and try opening the // file from the same directory as the cue. // find the start of filename p = temp_buffer; for (;;) { if (strcspn(p, "/\\") == strlen(p)) break; p += strcspn(p, "/\\") + 1; } // append directory of cue file with bin filename if ((temp_buffer2 = (char *)calloc(strlen(cuefilename) + strlen(p) + 1, 1)) == NULL) { free(temp_buffer); return -1; } // find end of path p2 = (char *)cuefilename; for (;;) { if (strcspn(p2, "/\\") == strlen(p2)) break; p2 += strcspn(p2, "/\\") + 1; } // Make sure there was at least some kind of path, otherwise our // second check is pretty useless if (cuefilename == p2 && temp_buffer == p) { free(temp_buffer); free(temp_buffer2); return -1; } strncpy(temp_buffer2, cuefilename, p2 - cuefilename); strcat(temp_buffer2, p); // Let's give it another try iso_file = fopen(temp_buffer2, "rb"); free(temp_buffer2); if (iso_file == NULL) { YabSetError(YAB_ERR_FILENOTFOUND, temp_buffer); free(temp_buffer); return -1; } } fseek(iso_file, 0, SEEK_END); file_size = ftell(iso_file); fseek(iso_file, 0, SEEK_SET); for (i = 0; i < track_num; i++) { trk[i].fad_end = trk[i+1].fad_start-1; trk[i].file_id = 0; trk[i].fp = iso_file; trk[i].file_size = file_size; } trk[track_num-1].fad_end = trk[track_num-1].fad_start+(file_size-trk[track_num-1].file_offset)/trk[track_num-1].sector_size; disc.session[0].fad_start = 150; disc.session[0].fad_end = trk[track_num-1].fad_end; disc.session[0].track_num = track_num; disc.session[0].track = malloc(sizeof(track_info_struct) * disc.session[0].track_num); if (disc.session[0].track == NULL) { YabSetError(YAB_ERR_MEMORYALLOC, NULL); free(disc.session); disc.session = NULL; return -1; } memcpy(disc.session[0].track, trk, track_num * sizeof(track_info_struct)); // buffer is no longer needed free(temp_buffer); return 0; } ////////////////////////////////////////////////////////////////////////////// int LoadMDSTracks(const char *mds_filename, FILE *iso_file, mds_session_struct *mds_session, session_info_struct *session) { int i; int track_num=0; u32 fad_end; session->track = malloc(sizeof(mds_track_struct) * mds_session->last_track); if (session->track == NULL) { YabSetError(YAB_ERR_MEMORYALLOC, NULL); return -1; } for (i = 0; i < mds_session->total_blocks; i++) { mds_track_struct track; FILE *fp=NULL; int file_size; fseek(iso_file, mds_session->track_blocks_offset + i * sizeof(mds_track_struct), SEEK_SET); if (fread(&track, 1, sizeof(mds_track_struct), iso_file) != sizeof(mds_track_struct)) { YabSetError(YAB_ERR_FILEREAD, mds_filename); free(session->track); return -1; } if (track.track_num == 0xA2) fad_end = MSF_TO_FAD(track.m, track.s, track.f); if (!track.extra_offset) continue; if (track.footer_offset) { mds_footer_struct footer; int found_dupe=0; int j; // Make sure we haven't already opened file already for (j = 0; j < track_num; j++) { if (track.footer_offset == session->track[j].file_id) { found_dupe = 1; break; } } if (found_dupe) { fp = session->track[j].fp; file_size = session->track[j].file_size; } else { fseek(iso_file, track.footer_offset, SEEK_SET); if (fread(&footer, 1, sizeof(mds_footer_struct), iso_file) != sizeof(mds_footer_struct)) { YabSetError(YAB_ERR_FILEREAD, mds_filename); free(session->track); return -1; } fseek(iso_file, footer.filename_offset, SEEK_SET); if (footer.is_widechar) { wchar_t filename[512]; wchar_t img_filename[512]; memset(img_filename, 0, 512 * sizeof(wchar_t)); if (fwscanf(iso_file, L"%512c", img_filename) != 1) { YabSetError(YAB_ERR_FILEREAD, mds_filename); free(session->track); return -1; } if (wcsncmp(img_filename, L"*.", 2) == 0) { wchar_t *ext; swprintf(filename, sizeof(filename)/sizeof(wchar_t), L"%S", mds_filename); ext = wcsrchr(filename, '.'); wcscpy(ext, img_filename+1); } else wcscpy(filename, img_filename); fp = _wfopen(filename, L"rb"); } else { char filename[512]; char img_filename[512]; memset(img_filename, 0, 512); if (fscanf(iso_file, "%512c", img_filename) != 1) { YabSetError(YAB_ERR_FILEREAD, mds_filename); free(session->track); return -1; } if (strncmp(img_filename, "*.", 2) == 0) { char *ext; strcpy(filename, mds_filename); ext = strrchr(filename, '.'); strcpy(ext, img_filename+1); } else strcpy(filename, img_filename); fp = fopen(filename, "rb"); } if (fp == NULL) { YabSetError(YAB_ERR_FILEREAD, mds_filename); free(session->track); return -1; } fseek(fp, 0, SEEK_END); file_size = ftell(fp); fseek(fp, 0, SEEK_SET); } } session->track[track_num].ctl_addr = (((track.addr_ctl << 4) | (track.addr_ctl >> 4)) & 0xFF); session->track[track_num].fad_start = track.start_sector+150; if (track_num > 0) session->track[track_num-1].fad_end = session->track[track_num].fad_start; session->track[track_num].file_offset = track.start_offset; session->track[track_num].sector_size = track.sector_size; session->track[track_num].fp = fp; session->track[track_num].file_size = file_size; session->track[track_num].file_id = track.footer_offset; session->track[track_num].interleaved_sub = track.subchannel_mode != 0 ? 1 : 0; track_num++; } session->track[track_num-1].fad_end = fad_end; session->fad_start = session->track[0].fad_start; session->fad_end = fad_end; session->track_num = track_num; return 0; } ////////////////////////////////////////////////////////////////////////////// static int LoadMDS(const char *mds_filename, FILE *iso_file) { s32 i; mds_header_struct header; fseek(iso_file, 0, SEEK_SET); if (fread((void *)&header, 1, sizeof(mds_header_struct), iso_file) != sizeof(mds_header_struct)) { YabSetError(YAB_ERR_FILEREAD, mds_filename); return -1; } else if (memcmp(&header.signature, "MEDIA DESCRIPTOR", sizeof(header.signature))) { YabSetError(YAB_ERR_OTHER, "Bad MDS header"); return -1; } else if (header.version[0] > 1) { YabSetError(YAB_ERR_OTHER, "Unsupported MDS version"); return -1; } if (header.medium_type & 0x10) { // DVD's aren't supported, not will they ever be YabSetError(YAB_ERR_OTHER, "DVD's aren't supported"); return -1; } disc.session_num = header.session_count; disc.session = malloc(sizeof(session_info_struct) * disc.session_num); if (disc.session == NULL) { YabSetError(YAB_ERR_MEMORYALLOC, NULL); return -1; } for (i = 0; i < header.session_count; i++) { mds_session_struct session; fseek(iso_file, header.sessions_blocks_offset + i * sizeof(mds_session_struct), SEEK_SET); if (fread(&session, 1, sizeof(mds_session_struct), iso_file) != sizeof(mds_session_struct)) { free(disc.session); YabSetError(YAB_ERR_FILEREAD, mds_filename); return -1; } if (LoadMDSTracks(mds_filename, iso_file, &session, &disc.session[i]) != 0) return -1; } fclose(iso_file); return 0; } ////////////////////////////////////////////////////////////////////////////// static int LoadISO(FILE *iso_file) { track_info_struct *track; disc.session_num = 1; disc.session = malloc(sizeof(session_info_struct) * disc.session_num); if (disc.session == NULL) { YabSetError(YAB_ERR_MEMORYALLOC, NULL); return -1; } disc.session[0].fad_start = 150; disc.session[0].track_num = 1; disc.session[0].track = malloc(sizeof(track_info_struct) * disc.session[0].track_num); if (disc.session[0].track == NULL) { YabSetError(YAB_ERR_MEMORYALLOC, NULL); free(disc.session); disc.session = NULL; return -1; } track = disc.session[0].track; track->ctl_addr = 0x41; track->fad_start = 150; track->file_offset = 0; track->fp = iso_file; fseek(iso_file, 0, SEEK_END); track->file_size = ftell(iso_file); track->file_id = 0; if (0 == (track->file_size % 2048)) track->sector_size = 2048; else if (0 == (track->file_size % 2352)) track->sector_size = 2352; else { YabSetError(YAB_ERR_OTHER, "Unsupported CD image!\n"); return -1; } disc.session[0].fad_end = track->fad_end = disc.session[0].fad_start + (track->file_size / track->sector_size); return 0; } ////////////////////////////////////////////////////////////////////////////// void BuildTOC() { int i; session_info_struct *session=&disc.session[0]; for (i = 0; i < session->track_num; i++) { track_info_struct *track=&disc.session[0].track[i]; isoTOC[i] = (track->ctl_addr << 24) | track->fad_start; } isoTOC[99] = (isoTOC[0] & 0xFF000000) | 0x010000; isoTOC[100] = (isoTOC[session->track_num - 1] & 0xFF000000) | (session->track_num << 16); isoTOC[101] = (isoTOC[session->track_num - 1] & 0xFF000000) | session->fad_end; } ////////////////////////////////////////////////////////////////////////////// static int ISOCDInit(const char * iso) { char header[6]; char *ext; int ret; FILE *iso_file; memset(isoTOC, 0xFF, 0xCC * 2); memset(&disc, 0, sizeof(disc)); if (!iso) return -1; if (!(iso_file = fopen(iso, "rb"))) { YabSetError(YAB_ERR_FILENOTFOUND, (char *)iso); return -1; } fread((void *)header, 1, 6, iso_file); ext = strrchr(iso, '.'); // Figure out what kind of image format we're dealing with if (stricmp(ext, ".CUE") == 0 && strncmp(header, "FILE \"", 6) == 0) { // It's a BIN/CUE imgtype = IMG_BINCUE; ret = LoadBinCue(iso, iso_file); } else if (stricmp(ext, ".MDS") == 0 && strncmp(header, "MEDIA ", sizeof(header)) == 0) { // It's a MDS imgtype = IMG_MDS; ret = LoadMDS(iso, iso_file); } else { // Assume it's an ISO file imgtype = IMG_ISO; ret = LoadISO(iso_file); } if (ret != 0) { imgtype = IMG_NONE; if (iso_file) fclose(iso_file); iso_file = NULL; return -1; } BuildTOC(); return 0; } ////////////////////////////////////////////////////////////////////////////// static void ISOCDDeInit(void) { int i, j, k; if (disc.session) { for (i = 0; i < disc.session_num; i++) { if (disc.session[i].track) { for (j = 0; j < disc.session[i].track_num; j++) { if (disc.session[i].track[j].fp) { fclose(disc.session[i].track[j].fp); // Make sure we don't close the same file twice for (k = j+1; k < disc.session[i].track_num; k++) { if (disc.session[i].track[j].file_id == disc.session[i].track[k].file_id) disc.session[i].track[k].fp = NULL; } } } free(disc.session[i].track); } } free(disc.session); } } ////////////////////////////////////////////////////////////////////////////// static int ISOCDGetStatus(void) { return disc.session_num > 0 ? 0 : 2; } ////////////////////////////////////////////////////////////////////////////// static s32 ISOCDReadTOC(u32 * TOC) { memcpy(TOC, isoTOC, 0xCC * 2); return (0xCC * 2); } ////////////////////////////////////////////////////////////////////////////// static int ISOCDReadSectorFAD(u32 FAD, void *buffer) { int i,j; track_info_struct *track=NULL; assert(disc.session); memset(buffer, 0, 2448); for (i = 0; i < disc.session_num; i++) { for (j = 0; j < disc.session[i].track_num; j++) { if (FAD >= disc.session[i].track[j].fad_start && FAD <= disc.session[i].track[j].fad_end) { track = &disc.session[i].track[j]; break; } } } if (track == NULL) { CDLOG("Warning: Sector not found in track list"); return 0; } fseek(track->fp, track->file_offset + (FAD-track->fad_start) * track->sector_size, SEEK_SET); if (track->sector_size == 2448) { if (!track->interleaved_sub) fread(buffer, 2448, 1, track->fp); else { const u16 deint_offsets[] = { 0, 66, 125, 191, 100, 50, 150, 175, 8, 33, 58, 83, 108, 133, 158, 183, 16, 41, 25, 91, 116, 141, 166, 75, 24, 90, 149, 215, 124, 74, 174, 199, 32, 57, 82, 107, 132, 157, 182, 207, 40, 65, 49, 115, 140, 165, 190, 99, 48, 114, 173, 239, 148, 98, 198, 223, 56, 81, 106, 131, 156, 181, 206, 231, 64, 89, 73, 139, 164, 189, 214, 123, 72, 138, 197, 263, 172, 122, 222, 247, 80, 105, 130, 155, 180, 205, 230, 255, 88, 113, 97, 163, 188, 213, 238, 147 }; u8 subcode_buffer[96 * 3]; fread(buffer, 2352, 1, track->fp); fread(subcode_buffer, 96, 1, track->fp); fseek(track->fp, 2352, SEEK_CUR); fread(subcode_buffer+96, 96, 1, track->fp); fseek(track->fp, 2352, SEEK_CUR); fread(subcode_buffer+192, 96, 1, track->fp); for (i = 0; i < 96; i++) ((u8 *)buffer)[2352+i] = subcode_buffer[deint_offsets[i]]; } } else if (track->sector_size == 2352) { // Generate subcodes here fread(buffer, 2352, 1, track->fp); } else if (track->sector_size == 2048) { memcpy(buffer, syncHdr, 12); fread((char *)buffer + 0x10, 2048, 1, track->fp); } return 1; } ////////////////////////////////////////////////////////////////////////////// static void ISOCDReadAheadFAD(UNUSED u32 FAD) { // No-op } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/cs0.h000644 001750 001750 00000004604 12256006201 017031 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004-2005 Theo Berkau Copyright 2005 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CS0_H #define CS0_H #include "memory.h" #define CART_NONE 0 #define CART_PAR 1 #define CART_BACKUPRAM4MBIT 2 #define CART_BACKUPRAM8MBIT 3 #define CART_BACKUPRAM16MBIT 4 #define CART_BACKUPRAM32MBIT 5 #define CART_DRAM8MBIT 6 #define CART_DRAM32MBIT 7 #define CART_NETLINK 8 #define CART_ROM16MBIT 9 #define CART_JAPMODEM 10 typedef struct { int carttype; int cartid; const char *filename; u8 FASTCALL (*Cs0ReadByte)(u32 addr); u16 FASTCALL (*Cs0ReadWord)(u32 addr); u32 FASTCALL (*Cs0ReadLong)(u32 addr); void FASTCALL (*Cs0WriteByte)(u32 addr, u8 val); void FASTCALL (*Cs0WriteWord)(u32 addr, u16 val); void FASTCALL (*Cs0WriteLong)(u32 addr, u32 val); u8 FASTCALL (*Cs1ReadByte)(u32 addr); u16 FASTCALL (*Cs1ReadWord)(u32 addr); u32 FASTCALL (*Cs1ReadLong)(u32 addr); void FASTCALL (*Cs1WriteByte)(u32 addr, u8 val); void FASTCALL (*Cs1WriteWord)(u32 addr, u16 val); void FASTCALL (*Cs1WriteLong)(u32 addr, u32 val); u8 FASTCALL (*Cs2ReadByte)(u32 addr); u16 FASTCALL (*Cs2ReadWord)(u32 addr); u32 FASTCALL (*Cs2ReadLong)(u32 addr); void FASTCALL (*Cs2WriteByte)(u32 addr, u8 val); void FASTCALL (*Cs2WriteWord)(u32 addr, u16 val); void FASTCALL (*Cs2WriteLong)(u32 addr, u32 val); void *rom; void *bupram; void *dram; } cartridge_struct; extern cartridge_struct *CartridgeArea; int CartInit(const char *filename, int); void CartDeInit(void); int CartSaveState(FILE *fp); int CartLoadState(FILE *fp, int version, int size); #endif yabause-0.9.13.1/src/osdcore.h000644 001750 001750 00000004107 12256006124 020004 0ustar00guillaumeguillaume000000 000000 /* Copyright 2012 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef OSDCORE_H #define OSDCORE_H #include "core.h" #define OSDCORE_DUMMY 0 #define OSDCORE_GLUT 1 #define OSDCORE_SOFT 2 #ifdef HAVE_LIBGLUT #define OSDCORE_DEFAULT OSDCORE_GLUT #else #define OSDCORE_DEFAULT OSDCORE_SOFT #endif #define OSDMSG_FPS 0 #define OSDMSG_STATUS 1 #define OSDMSG_DEBUG 2 #define OSDMSG_COUNT 3 typedef struct { int type; char * message; int timetolive; int timeleft; int hidden; } OSDMessage_struct; typedef struct { int id; const char *Name; int (*Init)(void); void (*DeInit)(void); void (*Reset)(void); void (*DisplayMessage)(OSDMessage_struct * message, pixel_t * buffer, int w, int h); int (*UseBuffer)(void); } OSD_struct; int OSDInit(int coreid); int OSDChangeCore(int coreid); void OSDPushMessage(int msgtype, int ttl, const char * message, ...); int OSDDisplayMessages(pixel_t * buffer, int w, int h); void OSDToggle(int what); int OSDIsVisible(int what); void OSDSetVisible(int what, int visible); int OSDUseBuffer(void); extern OSD_struct OSDDummy; #ifdef HAVE_LIBGLUT extern OSD_struct OSDGlut; #endif extern OSD_struct OSDSoft; /* defined for backward compatibility (used to be in vdp2.h) */ void ToggleFPS(void); int GetOSDToggle(void); void SetOSDToggle(int toggle); void DisplayMessage(const char* str); #endif yabause-0.9.13.1/src/bios.h000644 001750 001750 00000003141 12256006124 017277 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef BIOS_H #define BIOS_H #include "sh2core.h" typedef struct { char filename[12]; char comment[11]; u8 language; u8 year; u8 month; u8 day; u8 hour; u8 minute; u8 week; u32 datasize; u16 blocksize; } saveinfo_struct; typedef struct { u8 id; char name[32]; } deviceinfo_struct; void BiosInit(void); int FASTCALL BiosHandleFunc(SH2_struct * sh); deviceinfo_struct *BupGetDeviceList(int *numdevices); int BupGetStats(u32 device, u32 *freespace, u32 *maxspace); saveinfo_struct *BupGetSaveList(u32 device, int *numsaves); int BupDeleteSave(u32 device, const char *savename); void BupFormat(u32 device); int BupCopySave(u32 srcdevice, u32 dstdevice, const char *savename); int BupImportSave(u32 device, const char *filename); int BupExportSave(u32 device, const char *savename, const char *filename); #endif yabause-0.9.13.1/src/gtk/yuish.h000644 001750 001750 00000005442 12256006135 020301 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Fabien Coulon Copyright 2008 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_SH_H #define YUI_SH_H #include #include #include #include "../sh2core.h" #include "yuiwindow.h" G_BEGIN_DECLS #define YUI_SH_TYPE (yui_sh_get_type ()) #define YUI_SH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_SH_TYPE, YuiSh)) #define YUI_SH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_SH_TYPE, YuiShClass)) #define IS_YUI_SH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_SH_TYPE)) #define IS_YUI_SH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_SH_TYPE)) typedef struct _YuiSh YuiSh; typedef struct _YuiShClass YuiShClass; struct _YuiSh { GtkWindow dialog; GtkWidget * view; GtkWidget * toolbar; GtkWidget *vbox; GtkWidget *hboxmain; GtkToolItem * buttonStep; GtkWidget *bpList, *mbpList, *regList; //, *uLabel, *uFrame; GtkListStore *bpListStore, *mbpListStore, *regListStore; GtkCellRenderer *bpListRenderer, *mbpListRenderer, *regListRenderer1, *regListRenderer2; GtkTreeViewColumn *bpListColumn, *mbpListColumn, *regListColumn1, *regListColumn2; //u32 cbp[MAX_BREAKPOINTS]; /* the list of breakpoint positions, as they can be found in the list widget */ //u32 cmbp[MAX_BREAKPOINTS]; /* the list of memory breakpoint positions, as they can be found in the list widget */ //u32 mbpFlags[MAX_BREAKPOINTS]; u32 lastCode; /* offset of last unassembly. Try to reuse it to prevent sliding. */ SH2_struct *debugsh; gboolean bMaster; gboolean breakpointEnabled; gulong paused_handler; gulong running_handler; GtkListStore * store; GtkWidget * bp_menu; GtkWidget * mbp_menu; GtkWidget * mbp_menu_item[6]; GtkWidget * vboxBp; }; struct _YuiShClass { GtkWindowClass parent_class; void (* yui_sh) (YuiSh * yv); }; GType yui_sh_get_type (void); GtkWidget * yui_msh_new(YuiWindow * y); GtkWidget * yui_ssh_new(YuiWindow * y); void yui_sh_fill (YuiSh * sh); void yui_sh_update (YuiSh * sh); void yui_sh_destroy (YuiSh * sh); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/doc/yabause.1000644 001750 001750 00000005154 12256006135 021247 0ustar00guillaumeguillaume000000 000000 .TH YABAUSE 1 "April 16, 2010" "yabause-0.9.11" .SH NAME yabause \- Yet Another Buggy And Uncomplete Saturn Emulator .SH SYNOPSIS .B yabause [ \fB\-afh\fP ] [ \fB\-ns\fP ] [ \fB\-b \fP ] [ \fB\-c \fP ] [ \fB\-i \fP ] [ \fB\-\-binary=[:
]\fP ] .SH DESCRIPTION \fBYabause\fP is a Sega Saturn emulator. \fBYabause\fP needs either a bios file, a game or a binary to run. Games can be loaded from a real cd device or from dump files. .SH OPTIONS .TP .BI \-a .TP .BI \-\-autostart Automatically start emulation. .TP .BI \-\-autoframeskip=0|1 Enable or disable auto frame skipping / limiting. .TP .BI \-\-autoload Automatically start emulation and load a save state. .TP .BI \-b .TP .BI \-\-bios Choose a bios file. .TP .BI \-\-binary Use a binary file. Content of the file will be loaded to 0x06004000 and execution will start from that address. You can provide an alternative address to load and run the binary with the \-\-binary=:
syntax. When using this option, emulation is automatically started and you shouldn't use it in cunjunction with \-a. This option is intended for homebrew developers wanting to test their programs in \fBYabause\fP. .TP .BI \-c .TP .BI \-\-cdrom Choose the cdrom device. .TP .BI \-f .TP .BI \-\-fullscreen Start the emulator in fullscreen. .TP .BI \-h Display a short help text. .TP .BI \-i .TP .BI \-\-iso Choose a dump file. The dump can be either in iso or bin/cue file format. .TP .BI \-ns .TP .BI \-\-nosound Turns sound off. This option actually set the sound core to the dummy one. .SH FILES .TP \fB\fR An executable for the Saturn, usually with a BIN extension. .TP \fB\fR A Saturn ROM BIOS image. .TP \fB\fR A CDROM device file. .TP \fB\fR A Saturn game dump. .SH AUTHORS Copyright (c) 2002-2010 Yabause Team Web: http://yabause.org Please don't ask for roms, bios files or any other copyrighted stuff. .SH COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA See the GNU General Public License details in COPYING. yabause-0.9.13.1/src/gtk/yuiwindow.h000644 001750 001750 00000005437 12256006140 021176 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_WINDOW_H #define YUI_WINDOW_H #include #include #include #include G_BEGIN_DECLS #define YUI_WINDOW_TYPE (yui_window_get_type ()) #define YUI_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_WINDOW_TYPE, YuiWindow)) #define YUI_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_WINDOW_TYPE, YuiWindowClass)) #define IS_YUI_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_WINDOW_TYPE)) #define IS_YUI_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_WINDOW_TYPE)) typedef struct _YuiAction YuiAction; typedef struct _YuiWindow YuiWindow; typedef struct _YuiWindowClass YuiWindowClass; struct _YuiAction { guint key; const char * name; void (*press)(void); void (*release)(void); }; #define YUI_IS_INIT 1 #define YUI_IS_RUNNING 2 struct _YuiWindow { GtkWindow hbox; GtkWidget * logpopup; GtkWidget * box; GtkWidget * menu; GtkWidget * area; GtkWidget * log; YuiAction * actions; gulong clean_handler; GCallback init_func; gpointer init_data; GSourceFunc run_func; GCallback reset_func; guint state; GtkActionGroup * action_group; }; struct _YuiWindowClass { GtkWindowClass parent_class; void (* yui_window_running) (YuiWindow * yw); void (* yui_window_paused) (YuiWindow * yw); }; GType yui_window_get_type (void); GtkWidget * yui_window_new (YuiAction * act, GCallback ifunc, gpointer idata, GSourceFunc rfunc, GCallback resetfunc); void yui_window_update (YuiWindow * yui); void yui_window_log (YuiWindow * yui, const char * message); void yui_window_show_log (YuiWindow * yui); void yui_window_start (YuiWindow * yui); void yui_window_run (YuiWindow * yui); void yui_window_pause (YuiWindow * yui); void yui_window_reset (YuiWindow * yui); void yui_window_invalidate (YuiWindow * yui); void yui_window_set_fullscreen(YuiWindow * yui, gboolean f); void yui_window_set_frameskip(YuiWindow * yui, gboolean f); G_END_DECLS #endif /* YUI_WINDOW_H */ yabause-0.9.13.1/src/gtk/yuiinputentry.h000644 001750 001750 00000004001 12256006135 022076 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_INPUT_ENTRY_H #define YUI_INPUT_ENTRY_H #include #include #include G_BEGIN_DECLS #define YUI_INPUT_ENTRY_TYPE (yui_input_entry_get_type ()) #define YUI_INPUT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_INPUT_ENTRY_TYPE, YuiInputEntry)) #define YUI_INPUT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_INPUT_ENTRY_TYPE, YuiInputEntryClass)) #define IS_YUI_INPUT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_INPUT_ENTRY_TYPE)) #define IS_YUI_INPUT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_INPUT_ENTRY_TYPE)) typedef struct _YuiInputEntry YuiInputEntry; typedef struct _YuiInputEntryClass YuiInputEntryClass; struct _YuiInputEntry { GtkTable table; GKeyFile * keyfile; gchar * group; }; struct _YuiInputEntryClass { GtkTableClass parent_class; void (* yui_input_entry) (YuiInputEntry *yie); }; GType yui_input_entry_get_type (void); GtkWidget* yui_input_entry_new (GKeyFile * keyfile, const gchar * group, const gchar * keys[]); void yui_input_entry_update (YuiInputEntry *yie); G_END_DECLS #endif /* __YUI_INPUT_ENTRY_H__ */ yabause-0.9.13.1/src/gtk/yuirange.c000644 001750 001750 00000011662 12256006140 020753 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "yuirange.h" static void yui_range_class_init (YuiRangeClass * klass); static void yui_range_init (YuiRange * yfe); static void yui_range_changed (GtkWidget * widget, YuiRange * yfe); GType yui_range_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiRangeClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_range_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiRange), 0, (GInstanceInitFunc) yui_range_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_HBOX, "YuiRange", &yfe_info, 0); } return yfe_type; } #define PROP_KEYFILE 1 #define PROP_GROUP 2 #define PROP_KEY 3 #define PROP_ITEMS 4 static void yui_range_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { switch(property_id) { case PROP_KEYFILE: YUI_RANGE(object)->keyfile = g_value_get_pointer(value); break; case PROP_GROUP: YUI_RANGE(object)->group = g_value_get_pointer(value); break; case PROP_KEY: YUI_RANGE(object)->key = g_value_get_pointer(value); break; case PROP_ITEMS: YUI_RANGE(object)->items = g_value_get_pointer(value); break; } } static void yui_range_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { } enum { YUI_RANGE_CHANGED_SIGNAL, LAST_SIGNAL }; static guint yui_range_signals[LAST_SIGNAL] = { 0 }; static void yui_range_class_init (YuiRangeClass * klass) { GParamSpec * param; G_OBJECT_CLASS(klass)->set_property = yui_range_set_property; G_OBJECT_CLASS(klass)->get_property = yui_range_get_property; param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); param = g_param_spec_pointer("key", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEY, param); param = g_param_spec_pointer("items", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_ITEMS, param); yui_range_signals[YUI_RANGE_CHANGED_SIGNAL] = g_signal_new ("changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(YuiRangeClass, yui_range_change), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void yui_range_init (YuiRange * yfe) { gtk_container_set_border_width(GTK_CONTAINER(yfe), 10); yfe->combo = gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(yfe), yfe->combo, TRUE, TRUE, 0); } GtkWidget * yui_range_new(GKeyFile * keyfile, const gchar * group, const gchar * key, YuiRangeItem * items) { GtkWidget * entry; YuiRange * yfe; gchar * current; guint i; entry = GTK_WIDGET(g_object_new(yui_range_get_type(), "spacing", 10, "key-file", keyfile, "group", group, "key", key, "items", items, NULL)); yfe = YUI_RANGE(entry); current = g_key_file_get_value(yfe->keyfile, yfe->group, yfe->key, 0); i = 0; while(yfe->items[i].name) { gtk_combo_box_append_text(GTK_COMBO_BOX(yfe->combo), yfe->items[i].name); if (current && !strcmp(yfe->items[i].value, current)) gtk_combo_box_set_active(GTK_COMBO_BOX(yfe->combo), i); i++; } if ( !current ) { gtk_combo_box_set_active(GTK_COMBO_BOX(yfe->combo), 0); g_key_file_set_value(yfe->keyfile, yfe->group, yfe->key, items[0].value); } g_signal_connect(GTK_COMBO_BOX(yfe->combo), "changed", G_CALLBACK(yui_range_changed), yfe); return entry; } static void yui_range_changed(GtkWidget * entry, YuiRange * yfe) { g_key_file_set_value(yfe->keyfile, yfe->group, yfe->key, yfe->items[gtk_combo_box_get_active(GTK_COMBO_BOX(yfe->combo))].value); g_signal_emit(G_OBJECT(yfe), yui_range_signals[YUI_RANGE_CHANGED_SIGNAL], 0); } gint yui_range_get_active(YuiRange * range) { return gtk_combo_box_get_active(GTK_COMBO_BOX(range->combo)); } yabause-0.9.13.1/src/gtk/gtkglwidget.h000644 001750 001750 00000003447 12256006135 021457 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_GL_H #define YUI_GL_H #include #ifdef HAVE_LIBGTKGLEXT #include #include #endif G_BEGIN_DECLS #define YUI_GL_TYPE (yui_gl_get_type ()) #define YUI_GL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_GL_TYPE, YuiGl)) #define YUI_GL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_GL_TYPE, YuiGlClass)) #define IS_YUI_GL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_GL_TYPE)) #define IS_YUI_GL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_GL_TYPE)) typedef struct _YuiGl YuiGl; typedef struct _YuiGlClass YuiGlClass; struct _YuiGl { GtkDrawingArea hbox; guint * pixels; gint pixels_width; gint pixels_height; gint pixels_rowstride; gint is_init; }; struct _YuiGlClass { GtkDrawingAreaClass parent_class; }; GType yui_gl_get_type (void); GtkWidget * yui_gl_new (void); void yui_gl_draw (YuiGl *); void yui_gl_draw_pause (YuiGl *); void yui_gl_dump_screen (YuiGl *); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuiscudsp.h000644 001750 001750 00000004572 12256006140 021167 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_SCUDSP_H #define YUI_SCUDSP_H #include #include #include #include "../scu.h" #include "yuiwindow.h" G_BEGIN_DECLS #define YUI_SCUDSP_TYPE (yui_scudsp_get_type ()) #define YUI_SCUDSP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_SCUDSP_TYPE, YuiScudsp)) #define YUI_SCUDSP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_SCUDSP_TYPE, YuiScudspClass)) #define IS_YUI_SCUDSP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_SCUDSP_TYPE)) #define IS_YUI_SCUDSP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_SCUDSP_TYPE)) typedef struct _YuiScudsp YuiScudsp; typedef struct _YuiScudspClass YuiScudspClass; struct _YuiScudsp { GtkWindow dialog; GtkWidget *vbox, *vboxmain; GtkWidget *hbox, *hboxmain; GtkWidget * buttonStep, * buttonStepOver; GtkWidget * bpList, *regList, *uLabel, *uFrame; GtkListStore *bpListStore, *regListStore; GtkCellRenderer *bpListRenderer, *regListRenderer1, *regListRenderer2; GtkTreeViewColumn *bpListColumn, *regListColumn1, *regListColumn2; u32 cbp[MAX_BREAKPOINTS]; /* the list of breakpoint positions, as they can be found in the list widget */ u32 lastCode; /* offset of last unassembly. Try to reuse it to prevent sliding. */ gulong paused_handler; gulong running_handler; }; struct _YuiScudspClass { GtkWindowClass parent_class; void (* yui_scudsp) (YuiScudsp * yv); }; GType yui_scudsp_get_type (void); GtkWidget * yui_scudsp_new(YuiWindow * y); void yui_scudsp_update(YuiScudsp * scudsp); void yui_scudsp_destroy(YuiScudsp * scudsp); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuifileentry.c000644 001750 001750 00000013153 12256006140 021655 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "yuifileentry.h" static void yui_file_entry_class_init (YuiFileEntryClass * klass); static void yui_file_entry_init (YuiFileEntry * yfe); static void yui_file_entry_browse (GtkWidget * widget, gpointer user_data); static void yui_file_entry_changed (GtkWidget * widget, YuiFileEntry * yfe); GType yui_file_entry_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiFileEntryClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_file_entry_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiFileEntry), 0, (GInstanceInitFunc) yui_file_entry_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_HBOX, "YuiFileEntry", &yfe_info, 0); } return yfe_type; } #define PROP_KEYFILE 1 #define PROP_GROUP 2 #define PROP_KEY 3 static void yui_file_entry_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { switch(property_id) { case PROP_KEYFILE: YUI_FILE_ENTRY(object)->keyfile = g_value_get_pointer(value); break; case PROP_GROUP: YUI_FILE_ENTRY(object)->group = g_value_get_pointer(value); break; case PROP_KEY: YUI_FILE_ENTRY(object)->key = g_value_get_pointer(value); break; } } static void yui_file_entry_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { } static void yui_file_entry_class_init (YuiFileEntryClass * klass) { GParamSpec * param; G_OBJECT_CLASS(klass)->set_property = yui_file_entry_set_property; G_OBJECT_CLASS(klass)->get_property = yui_file_entry_get_property; param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); param = g_param_spec_pointer("key", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEY, param); } static void yui_file_entry_init (YuiFileEntry * yfe) { gtk_container_set_border_width(GTK_CONTAINER(yfe), 10); } GtkWidget * yui_file_entry_new(GKeyFile * keyfile, const gchar * group, const gchar * key, gint flags, const gchar * label) { GtkWidget * entry; YuiFileEntry * yfe; gchar * entryText; entry = GTK_WIDGET(g_object_new(yui_file_entry_get_type(), "spacing", 10, "key-file", keyfile, "group", group, "key", key, NULL)); yfe = YUI_FILE_ENTRY(entry); yfe->flags = flags; if (label) { gtk_box_pack_start(GTK_BOX(yfe), gtk_label_new_with_mnemonic(label), FALSE, FALSE, 0); } yfe->entry = gtk_entry_new (); gtk_box_pack_start(GTK_BOX(yfe), yfe->entry, TRUE, TRUE, 0); if (flags & YUI_FILE_ENTRY_BROWSE) { yfe->button = gtk_button_new_with_mnemonic ("Browse"); g_signal_connect(yfe->button, "clicked", G_CALLBACK(yui_file_entry_browse), yfe); gtk_box_pack_start(GTK_BOX(yfe), yfe->button, FALSE, FALSE, 0); } entryText = g_key_file_get_value(yfe->keyfile, yfe->group, yfe->key, 0); if ( !entryText ) entryText = ""; gtk_entry_set_text(GTK_ENTRY(yfe->entry), entryText ); g_signal_connect(GTK_ENTRY(yfe->entry), "changed", G_CALLBACK(yui_file_entry_changed), yfe); return entry; } static void yui_file_entry_browse(GtkWidget * widget, gpointer user_data) { GtkWidget * file_selector; gint result; const gchar * filename; YuiFileEntry * yfe = user_data; GtkFileChooserAction action; if (yfe->flags & YUI_FILE_ENTRY_DIRECTORY) { action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; } else { action = GTK_FILE_CHOOSER_ACTION_OPEN; } file_selector = gtk_file_chooser_dialog_new ("Please choose a file", NULL, action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); filename = gtk_entry_get_text(GTK_ENTRY(yfe->entry)); if (filename[0] != '\0') gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(file_selector), filename); gtk_widget_show(file_selector); result = gtk_dialog_run(GTK_DIALOG(file_selector)); switch(result) { case GTK_RESPONSE_ACCEPT: filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_selector)); gtk_entry_set_text(GTK_ENTRY(yfe->entry), filename); break; case GTK_RESPONSE_CANCEL: break; } gtk_widget_destroy(file_selector); } static void yui_file_entry_changed(GtkWidget * entry, YuiFileEntry * yfe) { g_key_file_set_value(yfe->keyfile, yfe->group, yfe->key, gtk_entry_get_text(GTK_ENTRY(yfe->entry))); } yabause-0.9.13.1/src/gtk/yuiscsp.h000644 001750 001750 00000003721 12256006135 020635 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_SCSP_H #define YUI_SCSP_H #include #include #include #include "yuiwindow.h" G_BEGIN_DECLS #define YUI_SCSP_TYPE (yui_scsp_get_type ()) #define YUI_SCSP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_SCSP_TYPE, YuiScsp)) #define YUI_SCSP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_SCSP_TYPE, YuiScspClass)) #define IS_YUI_SCSP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_SCSP_TYPE)) #define IS_YUI_SCSP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_SCSP_TYPE)) typedef struct _YuiScsp YuiScsp; typedef struct _YuiScspClass YuiScspClass; struct _YuiScsp { GtkWindow dialog; GtkWidget * vbox; GtkWidget * hbox; GtkWidget * commName; GtkWidget * commDesc; GtkWidget * spin; GtkTextBuffer * buffer; gint cursor; }; struct _YuiScspClass { GtkWindowClass parent_class; void (* yui_scsp) (YuiScsp * yv); }; GType yui_scsp_get_type (void); GtkWidget * yui_scsp_new (YuiWindow * yui); void yui_scsp_fill (YuiScsp * scsp); void yui_scsp_update (YuiScsp * scsp); void yui_scsp_destroy (YuiScsp * scsp); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuitransfer.c000644 001750 001750 00000024237 12256006135 021511 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "yuitransfer.h" #include "../core.h" #include "../memory.h" static void yui_transfer_class_init (YuiTransferClass * klass); static void yui_transfer_init (YuiTransfer * yfe); static void yui_transfer_browse (GtkWidget * widget, gpointer user_data); static void yui_transfer_exec (GtkWidget * widget, YuiTransfer * yt); static void yui_transfer_load (GtkWidget * entry, YuiTransfer * yt); static void yui_transfer_load_exec (GtkWidget * entry, YuiTransfer * yt); static void yui_transfer_store (GtkWidget * entry, YuiTransfer * yt); static void yui_transfer_check (YuiTransfer * yt); GType yui_transfer_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiTransferClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_transfer_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiTransfer), 0, (GInstanceInitFunc) yui_transfer_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiTransfer", &yfe_info, 0); } return yfe_type; } static void yui_transfer_class_init (UNUSED YuiTransferClass * klass) { } static void yui_transfer_init (YuiTransfer * yt) { GtkWidget *vbox1; GtkWidget *hbox1; GtkWidget *label4; GtkWidget *button1; GtkWidget *hbox2; GtkWidget *label2; GtkWidget *hbuttonbox1; GtkWidget *button5; GtkWidget *hbox3; GSList *radiobutton1_group = NULL; GtkWidget *radiobutton1; GtkWidget *radiobutton2; GtkWidget *radiobutton3; const char * tmp; gtk_window_set_title (GTK_WINDOW (yt), _("File transfer")); vbox1 = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (yt), vbox1); hbox1 = gtk_hbox_new (FALSE, 10); gtk_box_pack_start (GTK_BOX (vbox1), hbox1, FALSE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox1), 10); tmp = _("File"); label4 = gtk_label_new (tmp); gtk_box_pack_start (GTK_BOX (hbox1), label4, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (label4), 0, 0.5); gtk_label_set_width_chars (GTK_LABEL (label4), strlen(tmp)); yt->file_entry = gtk_entry_new (); g_signal_connect_swapped(yt->file_entry, "changed", G_CALLBACK(yui_transfer_check), yt); gtk_box_pack_start (GTK_BOX (hbox1), yt->file_entry, TRUE, TRUE, 0); button1 = gtk_button_new_with_mnemonic (_("Browse")); g_signal_connect(button1, "clicked", G_CALLBACK(yui_transfer_browse), yt->file_entry); gtk_box_pack_start (GTK_BOX (hbox1), button1, FALSE, FALSE, 0); hbox3 = gtk_hbox_new (FALSE, 10); gtk_box_pack_start (GTK_BOX (vbox1), hbox3, FALSE, FALSE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox3), 10); radiobutton1 = gtk_radio_button_new_with_mnemonic (NULL, _("Load as executable")); g_signal_connect(radiobutton1, "toggled", G_CALLBACK(yui_transfer_load_exec), yt); gtk_box_pack_start (GTK_BOX (hbox3), radiobutton1, FALSE, FALSE, 0); gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton1), radiobutton1_group); radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton1)); radiobutton2 = gtk_radio_button_new_with_mnemonic (NULL, _("Load")); g_signal_connect(radiobutton2, "toggled", G_CALLBACK(yui_transfer_load), yt); gtk_box_pack_start (GTK_BOX (hbox3), radiobutton2, FALSE, FALSE, 0); gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton2), radiobutton1_group); radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton2)); radiobutton3 = gtk_radio_button_new_with_mnemonic (NULL, _("Store")); g_signal_connect(radiobutton3, "toggled", G_CALLBACK(yui_transfer_store), yt); gtk_box_pack_start (GTK_BOX (hbox3), radiobutton3, FALSE, FALSE, 0); gtk_radio_button_set_group (GTK_RADIO_BUTTON (radiobutton3), radiobutton1_group); radiobutton1_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radiobutton3)); hbox2 = gtk_hbox_new (FALSE, 10); gtk_box_pack_start (GTK_BOX (vbox1), hbox2, FALSE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbox2), 10); tmp = _("From"); label2 = gtk_label_new (tmp); gtk_box_pack_start (GTK_BOX (hbox2), label2, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (label2), 0, 0.5); gtk_label_set_width_chars (GTK_LABEL (label2), strlen(tmp)); yt->from_entry = gtk_entry_new (); g_signal_connect_swapped(yt->from_entry, "changed", G_CALLBACK(yui_transfer_check), yt); gtk_box_pack_start (GTK_BOX (hbox2), yt->from_entry, TRUE, TRUE, 0); tmp = _("To"); yt->to_label = gtk_label_new (tmp); gtk_box_pack_start (GTK_BOX (hbox2), yt->to_label, FALSE, FALSE, 0); gtk_misc_set_alignment (GTK_MISC (yt->to_label), 0, 0.5); gtk_label_set_width_chars (GTK_LABEL (yt->to_label), strlen(tmp)); yt->to_entry = gtk_entry_new (); g_signal_connect_swapped(yt->to_entry, "changed", G_CALLBACK(yui_transfer_check), yt); gtk_box_pack_start (GTK_BOX (hbox2), yt->to_entry, TRUE, TRUE, 0); hbuttonbox1 = gtk_hbutton_box_new (); gtk_box_pack_start (GTK_BOX (vbox1), hbuttonbox1, FALSE, TRUE, 0); gtk_container_set_border_width (GTK_CONTAINER (hbuttonbox1), 10); yt->transfer_button = gtk_button_new_with_mnemonic (_("Transfer")); gtk_container_add (GTK_CONTAINER (hbuttonbox1), yt->transfer_button); g_signal_connect(yt->transfer_button, "clicked", G_CALLBACK(yui_transfer_exec), yt); GTK_WIDGET_SET_FLAGS (yt->transfer_button, GTK_CAN_DEFAULT); button5 = gtk_button_new_from_stock ("gtk-cancel"); gtk_container_add (GTK_CONTAINER (hbuttonbox1), button5); g_signal_connect_swapped(button5, "clicked", G_CALLBACK(gtk_widget_destroy), yt); GTK_WIDGET_SET_FLAGS (button5, GTK_CAN_DEFAULT); gtk_widget_show_all (GTK_WIDGET(yt)); gtk_widget_set_sensitive(GTK_WIDGET(yt->to_label), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(yt->to_entry), FALSE); yt->mode = YUI_TRANSFER_LOAD_EXEC; } GtkWidget * yui_transfer_new(YuiWindow * yw) { GtkWidget * entry; YuiTransfer * yfe; entry = GTK_WIDGET(g_object_new(yui_transfer_get_type(), NULL)); yfe = YUI_TRANSFERT(entry); gtk_widget_show_all(entry); yui_transfer_check(yfe); yui_window_start(yw); return entry; } static void yui_transfer_browse(UNUSED GtkWidget * widget, gpointer user_data) { GtkWidget * file_selector; gint result; const gchar * filename; file_selector = gtk_file_chooser_dialog_new (_("Please choose a file"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); filename = gtk_entry_get_text(GTK_ENTRY(user_data)); if (filename[0] != '\0') gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(file_selector), filename); gtk_widget_show(file_selector); result = gtk_dialog_run(GTK_DIALOG(file_selector)); switch(result) { case GTK_RESPONSE_ACCEPT: filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_selector)); gtk_entry_set_text(GTK_ENTRY(user_data), filename); break; case GTK_RESPONSE_CANCEL: break; } gtk_widget_destroy(file_selector); } static void yui_transfer_exec(UNUSED GtkWidget * widget, YuiTransfer * yt) { guint32 from, to; switch(yt->mode) { case YUI_TRANSFER_LOAD: sscanf(gtk_entry_get_text(GTK_ENTRY(yt->from_entry)), "%x", &from); MappedMemoryLoad(gtk_entry_get_text(GTK_ENTRY(yt->file_entry)), from); break; case YUI_TRANSFER_LOAD_EXEC: sscanf(gtk_entry_get_text(GTK_ENTRY(yt->from_entry)), "%x", &from); MappedMemoryLoadExec(gtk_entry_get_text(GTK_ENTRY(yt->file_entry)), from); break; case YUI_TRANSFER_STORE: sscanf(gtk_entry_get_text(GTK_ENTRY(yt->from_entry)), "%x", &from); sscanf(gtk_entry_get_text(GTK_ENTRY(yt->to_entry)), "%x", &to); MappedMemorySave(gtk_entry_get_text(GTK_ENTRY(yt->file_entry)), from, to - from); break; } gtk_widget_destroy(GTK_WIDGET(yt)); } static void yui_transfer_load(GtkWidget * entry, YuiTransfer * yt) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(entry))) { yt->mode = YUI_TRANSFER_LOAD; gtk_widget_set_sensitive(GTK_WIDGET(yt->to_label), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(yt->to_entry), FALSE); yui_transfer_check(yt); } } static void yui_transfer_load_exec(GtkWidget * entry, YuiTransfer * yt) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(entry))) { yt->mode = YUI_TRANSFER_LOAD_EXEC; gtk_widget_set_sensitive(GTK_WIDGET(yt->to_label), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(yt->to_entry), FALSE); yui_transfer_check(yt); } } static void yui_transfer_store(GtkWidget * entry, YuiTransfer * yt) { if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(entry))) { yt->mode = YUI_TRANSFER_STORE; gtk_widget_set_sensitive(GTK_WIDGET(yt->to_label), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(yt->to_entry), TRUE); yui_transfer_check(yt); } } static void yui_transfer_check(YuiTransfer * yt) { gboolean ok = FALSE; if (*gtk_entry_get_text(GTK_ENTRY(yt->file_entry)) != '\0') { switch(yt->mode) { case YUI_TRANSFER_LOAD: case YUI_TRANSFER_LOAD_EXEC: if (*gtk_entry_get_text(GTK_ENTRY(yt->from_entry)) != '\0') { ok = TRUE; } break; case YUI_TRANSFER_STORE: if ((*gtk_entry_get_text(GTK_ENTRY(yt->from_entry)) != '\0') && (*gtk_entry_get_text(GTK_ENTRY(yt->to_entry)) != '\0')) { ok = TRUE; } break; } } gtk_widget_set_sensitive(yt->transfer_button, ok); } yabause-0.9.13.1/src/gtk/gtkglwidget.c000644 001750 001750 00000022251 12256006135 021444 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gtkglwidget.h" #ifdef HAVE_LIBGTKGLEXT #include #endif #include "../vidsoft.h" #include "../peripheral.h" #define X_NOSCALE 160 #define Y_NOSCALE 120 static void yui_gl_class_init (YuiGlClass * klass); static void yui_gl_init (YuiGl * yfe); static gboolean yui_gl_resize (GtkWidget *w,GdkEventConfigure *event, gpointer data); void yui_gl_draw(YuiGl * glxarea) { #ifdef HAVE_LIBGTKGLEXT GdkGLContext *glcontext = gtk_widget_get_gl_context (GTK_WIDGET(glxarea)); GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (GTK_WIDGET(glxarea)); if (!gdk_gl_drawable_make_current (gldrawable, glcontext)) { g_print("Cannot set gl drawable current\n"); return; } gdk_gl_drawable_swap_buffers(gldrawable); #else int buf_width, buf_height; GdkPixbuf * pixbuf, * scaledpixbuf; VIDCore->GetGlSize( &buf_width, &buf_height ); glxarea->pixels_width = GTK_WIDGET(glxarea)->allocation.width; glxarea->pixels_height = GTK_WIDGET(glxarea)->allocation.height; glxarea->pixels_rowstride = glxarea->pixels_width * 4; glxarea->pixels_rowstride += (glxarea->pixels_rowstride % 4)? (4 - (glxarea->pixels_rowstride % 4)): 0; if (dispbuffer == NULL) return; pixbuf = gdk_pixbuf_new_from_data((const guchar *) dispbuffer, GDK_COLORSPACE_RGB, TRUE, 8, buf_width, buf_height, buf_width*4, NULL, NULL); if (( glxarea->pixels_width < buf_width + X_NOSCALE )&&( glxarea->pixels_height < buf_height + Y_NOSCALE )) { gdk_draw_pixbuf(GTK_WIDGET(glxarea)->window, NULL, pixbuf, 0, 0, (glxarea->pixels_width-buf_width)/2, (glxarea->pixels_height-buf_height)/2, buf_width, buf_height, GDK_RGB_DITHER_NONE, 0, 0); } else { scaledpixbuf = gdk_pixbuf_scale_simple(pixbuf, glxarea->pixels_width, glxarea->pixels_height, GDK_INTERP_NEAREST ); gdk_draw_pixbuf(GTK_WIDGET(glxarea)->window, NULL, scaledpixbuf, 0, 0, 0, 0, glxarea->pixels_width, glxarea->pixels_height, GDK_RGB_DITHER_NONE, 0, 0); g_object_unref(scaledpixbuf); } g_object_unref(pixbuf); #endif glxarea->is_init = 1; } void yui_gl_draw_pause(YuiGl * glxarea) { #ifdef HAVE_LIBGTKGLEXT if (glxarea->pixels) { /* The "correct" raster position would be (0, height) but it's not a * valid position, so I have to use this hack... found here: * http://www.opengl.org/resources/features/KilgardTechniques/oglpitfall/ */ glRasterPos2i(0, 0); glBitmap(0, 0, 0, 0, 0, - glxarea->pixels_height, NULL); glPixelZoom(1, 1); glDrawPixels(glxarea->pixels_width, glxarea->pixels_height, GL_RGB, GL_UNSIGNED_BYTE, glxarea->pixels); yui_gl_draw(glxarea); } else { gdk_draw_rectangle(GTK_WIDGET(glxarea)->window, GTK_WIDGET(glxarea)->style->bg_gc[GTK_WIDGET_STATE (glxarea)], TRUE, 0, 0, GTK_WIDGET(glxarea)->allocation.width, GTK_WIDGET(glxarea)->allocation.height); } #else if (dispbuffer) yui_gl_draw(glxarea); #endif } static gboolean yui_gl_resize(GtkWidget *w,GdkEventConfigure *event, gpointer data) { #ifdef HAVE_LIBGTKGLEXT GdkGLContext *glcontext = gtk_widget_get_gl_context (w); GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (w); if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext)) return FALSE; glViewport(0, 0, event->width, event->height); if ( YUI_GL(w)->is_init ) VIDCore->Resize(event->width, event->height, FALSE ); #endif return FALSE; } int beforehiding = 0; static gboolean gonna_hide(gpointer data) { beforehiding--; if (beforehiding == 0) { static char source_data[] = { 0 }; static char mask_data[] = { 0 }; GdkCursor *cursor; GdkPixmap *source, *mask; GdkColor fg = { 0, 65535, 65535, 65535 }; GdkColor bg = { 0, 0, 0, 0 }; source = gdk_bitmap_create_from_data(NULL, source_data, 1, 1); mask = gdk_bitmap_create_from_data(NULL, mask_data, 1, 1); cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 1, 1); gdk_pixmap_unref(source); gdk_pixmap_unref(mask); gdk_window_set_cursor(GTK_WIDGET(data)->window, cursor); return FALSE; } else { return TRUE; } } extern void * padbits; extern GKeyFile * keyfile; int oldx = 0; int oldy = 0; static gboolean yui_gl_hide_cursor(GtkWidget * widget, GdkEventMotion * event, gpointer user_data) { if (PerGetId(padbits) == PERMOUSE) { int x = event->x; int y = event->y; double speed = g_key_file_get_double(keyfile, "General", "MouseSpeed", NULL); PerMouseMove(padbits, speed * (x - oldx), -speed * (y - oldy)); oldx = x; oldy = y; } if (beforehiding == 0) { gdk_window_set_cursor(widget->window, NULL); g_timeout_add(1000, gonna_hide, widget); } beforehiding = 2; return FALSE; } static gboolean yui_gl_button_press(GtkWidget * widget, GdkEventButton * event, gpointer user_data) { if (PerGetId(padbits) == PERMOUSE) { switch(event->button) { case 1: PerMouseLeftPressed(padbits); break; case 2: PerMouseMiddlePressed(padbits); break; case 3: PerMouseRightPressed(padbits); break; } } return FALSE; } static gboolean yui_gl_button_release(GtkWidget * widget, GdkEventButton * event, gpointer user_data) { if (PerGetId(padbits) == PERMOUSE) { switch(event->button) { case 1: PerMouseLeftReleased(padbits); break; case 2: PerMouseMiddleReleased(padbits); break; case 3: PerMouseRightReleased(padbits); break; } } return FALSE; } GtkWidget * yui_gl_new(void) { GtkWidget * drawingArea; #ifdef HAVE_LIBGTKGLEXT int attribs[] = { GDK_GL_RGBA, GDK_GL_RED_SIZE, 1, GDK_GL_GREEN_SIZE, 1, GDK_GL_BLUE_SIZE, 1, GDK_GL_DOUBLEBUFFER, GDK_GL_DEPTH_SIZE ,1, GDK_GL_STENCIL_SIZE ,8, GDK_GL_ATTRIB_LIST_NONE }; #endif drawingArea = GTK_WIDGET(g_object_new(yui_gl_get_type(), NULL)); YUI_GL(drawingArea)->is_init = 0; #ifdef HAVE_LIBGTKGLEXT gtk_widget_set_gl_capability(drawingArea, gdk_gl_config_new(attribs), NULL, TRUE, GDK_GL_RGBA_TYPE); #endif g_signal_connect (GTK_OBJECT(drawingArea),"configure_event", GTK_SIGNAL_FUNC(yui_gl_resize),0); gtk_widget_set_events(drawingArea, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); g_signal_connect(GTK_OBJECT(drawingArea), "motion-notify-event", GTK_SIGNAL_FUNC(yui_gl_hide_cursor),0); g_signal_connect(GTK_OBJECT(drawingArea), "button-press-event", GTK_SIGNAL_FUNC(yui_gl_button_press),0); g_signal_connect(GTK_OBJECT(drawingArea), "button-release-event", GTK_SIGNAL_FUNC(yui_gl_button_release),0); return drawingArea; } void yui_gl_dump_screen(YuiGl * glxarea) { #ifdef HAVE_LIBGTKGLEXT int size; glxarea->pixels_width = GTK_WIDGET(glxarea)->allocation.width; glxarea->pixels_height = GTK_WIDGET(glxarea)->allocation.height; glxarea->pixels_rowstride = glxarea->pixels_width * 3; glxarea->pixels_rowstride += (glxarea->pixels_rowstride % 4)? (4 - (glxarea->pixels_rowstride % 4)): 0; size = glxarea->pixels_rowstride * glxarea->pixels_height; if (glxarea->pixels) free(glxarea->pixels); glxarea->pixels = malloc(sizeof(GLubyte) * size); if (glxarea->pixels == NULL) return; glReadPixels(0, 0, glxarea->pixels_width, glxarea->pixels_height, GL_RGB, GL_UNSIGNED_BYTE, glxarea->pixels); #else int buf_width, buf_height; int i, j; int size; int cur = 0; u8 * pixels; u8 * buffer; VIDCore->GetGlSize( &buf_width, &buf_height ); size = buf_width * buf_height * 3; glxarea->pixels_width = buf_width; glxarea->pixels_height = buf_height; glxarea->pixels_rowstride = glxarea->pixels_width * 3; glxarea->pixels_rowstride += (glxarea->pixels_rowstride % 4)? (4 - (glxarea->pixels_rowstride % 4)): 0; if (! glxarea->pixels) glxarea->pixels = malloc(sizeof(u8) * size); pixels = (u8 *)glxarea->pixels; pixels += size - (buf_width * 3); buffer = (u8 *)dispbuffer; for(i = 0;i < buf_height;i++) { for(j = 0;j < buf_width;j++) { *pixels++ = buffer[cur]; *pixels++ = buffer[cur + 1]; *pixels++ = buffer[cur + 2]; cur += 4; } pixels -= buf_width * 6; } #endif } GType yui_gl_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiGlClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_gl_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiGl), 0, (GInstanceInitFunc) yui_gl_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "YuiGl", &yfe_info, 0); } return yfe_type; } static void yui_gl_class_init (UNUSED YuiGlClass * klass) { } static void yui_gl_init (YuiGl * y) { y->pixels = NULL; } yabause-0.9.13.1/src/gtk/main.c000644 001750 001750 00000040606 12256006140 020054 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2006 Fabien Coulon Copyright 2005 Joost Peters This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gtkglwidget.h" #include "yuiwindow.h" #include "../yabause.h" #include "../yui.h" #include "../peripheral.h" #include "../sh2core.h" #include "../sh2int.h" #include "../vidogl.h" #include "../vidsoft.h" #include "../cs0.h" #include "../cs2.h" #include "../cdbase.h" #include "../scsp.h" #include "../sndsdl.h" #include "../sndal.h" #include "../persdljoy.h" #ifdef ARCH_IS_LINUX #include "../perlinuxjoy.h" #endif #include "../debug.h" #include "../m68kcore.h" #include "../m68kc68k.h" #include "pergtk.h" #include "settings.h" static char biospath[256] = "\0"; static char cdpath[256] = "\0"; static char buppath[256] = "\0"; static char mpegpath[256] = "\0"; static char cartpath[256] = "\0"; M68K_struct * M68KCoreList[] = { &M68KDummy, #ifdef HAVE_C68K &M68KC68K, #endif #ifdef HAVE_Q68 &M68KQ68, #endif NULL }; SH2Interface_struct *SH2CoreList[] = { &SH2Interpreter, &SH2DebugInterpreter, #ifdef TEST_PSP_SH2 &SH2PSP, #endif #ifdef SH2_DYNAREC &SH2Dynarec, #endif NULL }; PerInterface_struct *PERCoreList[] = { &PERDummy, &PERGTK, #ifdef HAVE_LIBSDL &PERSDLJoy, #endif #ifdef ARCH_IS_LINUX &PERLinuxJoy, #endif NULL }; CDInterface *CDCoreList[] = { &DummyCD, &ISOCD, #ifndef UNKNOWN_ARCH &ArchCD, #endif NULL }; SoundInterface_struct *SNDCoreList[] = { &SNDDummy, #ifdef HAVE_LIBSDL &SNDSDL, #endif #ifdef HAVE_LIBAL &SNDAL, #endif NULL }; VideoInterface_struct *VIDCoreList[] = { &VIDDummy, #ifdef HAVE_LIBGTKGLEXT &VIDOGL, #endif &VIDSoft, NULL }; #ifdef YAB_PORT_OSD OSD_struct *OSDCoreList[] = { &OSDDummy, #ifdef HAVE_LIBGLUT &OSDGlut, #endif &OSDSoft, NULL }; #endif GtkWidget * yui; GKeyFile * keyfile; yabauseinit_struct yinit; static int yui_main(gpointer data) { PERCore->HandleEvents(); return TRUE; } static GtkWidget * yui_new(void) { yui = yui_window_new(NULL, G_CALLBACK(YabauseInit), &yinit, yui_main, G_CALLBACK(YabauseReset)); gtk_widget_show(yui); return yui; } static void yui_settings_init(void) { yinit.m68kcoretype = M68KCORE_C68K; yinit.percoretype = PERCORE_GTK; yinit.sh2coretype = SH2CORE_DEFAULT; #ifdef HAVE_LIBGTKGLEXT yinit.vidcoretype = VIDCORE_OGL; #else yinit.vidcoretype = VIDCORE_SOFT; #endif yinit.sndcoretype = SNDCORE_DUMMY; yinit.cdcoretype = CDCORE_DEFAULT; yinit.carttype = CART_NONE; yinit.regionid = 0; yinit.biospath = biospath; yinit.cdpath = cdpath; yinit.buppath = buppath; yinit.mpegpath = mpegpath; yinit.cartpath = cartpath; yinit.videoformattype = VIDEOFORMATTYPE_NTSC; yinit.osdcoretype = OSDCORE_DEFAULT; } gchar * inifile; static int safe_strcmp(const char * s1, const char * s2) { if (s1) { if (s2) { return strcmp(s1, s2); } else { return 1; } } else { if (s2) { return -1; } else { return 0; } } } extern void * padbits; void * padbits; static gboolean yui_settings_load(void) { int i, tmp; long tmptime; gchar * stmp; gboolean mustRestart = FALSE; g_key_file_load_from_file(keyfile, inifile, G_KEY_FILE_NONE, 0); /* bios */ stmp = g_key_file_get_value(keyfile, "General", "BiosPath", 0); if (stmp && !*stmp) stmp = NULL; if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.biospath)) { mustRestart = TRUE; } if (stmp) { g_strlcpy(biospath, stmp, 256); yinit.biospath = biospath; } else yinit.biospath = NULL; /* cd core */ stmp = g_key_file_get_value(keyfile, "General", "CDROMDrive", 0); if (stmp && !*stmp) stmp = NULL; if((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.cdpath)) { Cs2ChangeCDCore(yinit.cdcoretype, stmp); } if (stmp) g_strlcpy(cdpath, stmp, 256); tmp = yinit.cdcoretype; yinit.cdcoretype = g_key_file_get_integer(keyfile, "General", "CDROMCore", 0); if((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.cdcoretype)) { Cs2ChangeCDCore(yinit.cdcoretype, yinit.cdpath); } /* region */ { char * region = g_key_file_get_value(keyfile, "General", "Region", 0); tmp = yinit.regionid; if ((region == 0) || !strcmp(region, "Auto")) { yinit.regionid = 0; } else { switch(region[0]) { case 'J': yinit.regionid = 1; break; case 'T': yinit.regionid = 2; break; case 'U': yinit.regionid = 4; break; case 'B': yinit.regionid = 5; break; case 'K': yinit.regionid = 6; break; case 'A': yinit.regionid = 0xA; break; case 'E': yinit.regionid = 0xC; break; case 'L': yinit.regionid = 0xD; break; } } if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.regionid)) { mustRestart = TRUE; } } /* cart */ stmp = g_key_file_get_value(keyfile, "General", "CartPath", 0); if (stmp && !*stmp) stmp = NULL; if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.cartpath)) { mustRestart = TRUE; } if (stmp) { g_strlcpy(cartpath, stmp, 256); yinit.cartpath = cartpath; } else yinit.cartpath = NULL; tmp = yinit.carttype; yinit.carttype = g_key_file_get_integer(keyfile, "General", "CartType", 0); if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.carttype)) { mustRestart = TRUE; } /* backup ram */ stmp = g_key_file_get_value(keyfile, "General", "BackupRamPath", 0); if (stmp && !*stmp) stmp = NULL; if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.buppath)) { mustRestart = TRUE; } if (stmp) { g_strlcpy(buppath, stmp, 256); yinit.buppath = buppath; } else yinit.buppath = NULL; /* mpeg rom */ stmp = g_key_file_get_value(keyfile, "General", "MpegRomPath", 0); if (stmp && !*stmp) stmp = NULL; if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && safe_strcmp(stmp, yinit.mpegpath)) { mustRestart = TRUE; } if (stmp) { g_strlcpy(mpegpath, stmp, 256); yinit.mpegpath = mpegpath; } else yinit.mpegpath = NULL; /* sh2 */ tmp = yinit.sh2coretype; yinit.sh2coretype = g_key_file_get_integer(keyfile, "General", "SH2Int", 0); if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.sh2coretype)) { mustRestart = TRUE; } /* m68k */ tmp = yinit.m68kcoretype; yinit.m68kcoretype = g_key_file_get_integer(keyfile, "General", "M68kInt", 0); if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.m68kcoretype)) { mustRestart = TRUE; } /* video core */ tmp = yinit.vidcoretype; yinit.vidcoretype = g_key_file_get_integer(keyfile, "General", "VideoCore", 0); if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.vidcoretype)) { VideoChangeCore(yinit.vidcoretype); VIDCore->Resize( GTK_WIDGET(YUI_WINDOW(yui)->area)->allocation.width, GTK_WIDGET(YUI_WINDOW(yui)->area)->allocation.height, FALSE); } /* osd core */ tmp = yinit.osdcoretype; yinit.osdcoretype = g_key_file_get_integer(keyfile, "General", "OSDCore", 0); if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.osdcoretype)) { OSDChangeCore(yinit.osdcoretype); } /* sound core */ tmp = yinit.sndcoretype; yinit.sndcoretype = g_key_file_get_integer(keyfile, "General", "SoundCore", 0); if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.sndcoretype)) { ScspChangeSoundCore(yinit.sndcoretype); } ScspSetVolume(g_key_file_get_integer(keyfile, "General", "Volume", NULL)); /* peripheral core */ yinit.percoretype = g_key_file_get_integer(keyfile, "General", "PerCore", 0); /* audio sync */ tmp = g_key_file_get_boolean(keyfile, "General", "AudioSync", 0); ScspSetFrameAccurate(tmp); /* clock sync */ tmp = yinit.clocksync; yinit.clocksync = g_key_file_get_boolean(keyfile, "General", "ClockSync", 0); if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.clocksync)) { mustRestart = TRUE; } tmptime = yinit.basetime; tmp = g_key_file_get_boolean(keyfile, "General", "FixedBaseTime", 0); if (tmp && yinit.clocksync) { /* Find timestamp of 1998-01-01 12:00 in the local time zone */ time_t utc = 883656000; // 1998-01-01 12:00 UTC struct tm tm; long local, noon; localtime_r(&utc, &tm); local = tm.tm_hour*3600 + tm.tm_min*60 + tm.tm_sec; if (tm.tm_mday == 2) // 1998-01-02 local += 86400; else if (tm.tm_mday == 31) // 1997-12-31 local -= 86400; noon = 12*3600 + 0*60 + 0; yinit.basetime = (long)utc + (noon - local); } else { yinit.basetime = 0; } if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmptime != yinit.basetime)) { mustRestart = TRUE; } /* threads */ tmp = g_key_file_get_boolean(keyfile, "General", "UseThreads", 0); if ((YUI_WINDOW(yui)->state & YUI_IS_INIT) && (tmp != yinit.usethreads)) { mustRestart = TRUE; } yinit.usethreads = tmp; PerInit(yinit.percoretype); PerPortReset(); switch(g_key_file_get_integer(keyfile, "General", "PerType", NULL)) { case PERMOUSE: padbits = PerMouseAdd(&PORTDATA1); i = 0; while(PerMouseNames[i]) { char tmp[100]; u32 key; sprintf(tmp, "Mouse.%s.1", PerMouseNames[i]); key = g_key_file_get_integer(keyfile, PERCore->Name, tmp, 0); PerSetKey(key, i + 13, padbits); i++; } break; case PERPAD: default: padbits = PerPadAdd(&PORTDATA1); i = 0; while(PerPadNames[i]) { char tmp[100]; u32 key; sprintf(tmp, "Pad.%s.1", PerPadNames[i]); key = g_key_file_get_integer(keyfile, PERCore->Name, tmp, 0); PerSetKey(key, i, padbits); i++; } } yui_resize(g_key_file_get_integer(keyfile, "General", "Width", 0), g_key_file_get_integer(keyfile, "General", "Height", 0), g_key_file_get_integer(keyfile, "General", "Fullscreen", 0)); yinit.videoformattype = g_key_file_get_integer(keyfile, "General", "VideoFormat", 0); yui_window_set_frameskip(YUI_WINDOW(yui), g_key_file_get_integer(keyfile, "General", "Frameskip", NULL)); return mustRestart; } void YuiErrorMsg(const char * string) { GtkWidget* warningDlg = gtk_message_dialog_new (GTK_WINDOW(yui), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, string, NULL); gtk_dialog_run (GTK_DIALOG(warningDlg)); gtk_widget_destroy (warningDlg); } int main(int argc, char *argv[]) { #ifndef NO_CLI int i; #endif LogStart(); LogChangeOutput( DEBUG_STDERR, NULL ); inifile = g_build_filename(g_get_user_config_dir(), "yabause", "gtk", "yabause.ini", NULL); if (! g_file_test(inifile, G_FILE_TEST_EXISTS)) { // no inifile found, but it could be in the old location gchar * oldinifile = g_build_filename(g_get_user_config_dir(), "yabause.ini", NULL); // we need to create the directory for the new file anyways gchar * xdgpath = g_build_filename(g_get_user_config_dir(), "yabause", "gtk", NULL); if (! g_file_test(xdgpath, G_FILE_TEST_EXISTS)) g_mkdir_with_parents(xdgpath, 0755); g_free(xdgpath); if (g_file_test(oldinifile, G_FILE_TEST_EXISTS)) { // ok, we found an old .ini file, let's copy the content gchar * data; g_file_get_contents(oldinifile, &data, NULL, NULL); g_file_set_contents(inifile, data, -1, NULL); } g_free(oldinifile); } keyfile = g_key_file_new(); gtk_init(&argc, &argv); #ifdef HAVE_LIBGTKGLEXT gtk_gl_init(&argc, &argv); #endif yui_settings_init(); #ifdef HAVE_LIBMINI18N mini18n_set_domain(YTSDIR); g_key_file_load_from_file(keyfile, inifile, G_KEY_FILE_NONE, 0); mini18n_set_locale(g_key_file_get_value(keyfile, "General", "TranslationPath", NULL)); #endif yui = yui_new(); yui_settings_load(); gtk_window_move(GTK_WINDOW(yui), g_key_file_get_integer(keyfile, "Gtk", "X", 0), g_key_file_get_integer(keyfile, "Gtk", "Y", 0)); #ifndef NO_CLI //handle command line arguments for (i = 1; i < argc; ++i) { if (argv[i]) { //show usage if (0 == strcmp(argv[i], "-h") || 0 == strcmp(argv[i], "-?") || 0 == strcmp(argv[i], "--help")) { print_usage(argv[0]); return 0; } //set bios if (0 == strcmp(argv[i], "-b") && argv[i + 1]) { g_strlcpy(biospath, argv[i + 1], 256); yinit.biospath = biospath; } else if (strstr(argv[i], "--bios=")) { g_strlcpy(biospath, argv[i] + strlen("--bios="), 256); yinit.biospath = biospath; } //set iso else if (0 == strcmp(argv[i], "-i") && argv[i + 1]) { g_strlcpy(cdpath, argv[i + 1], 256); yinit.cdcoretype = 1; } else if (strstr(argv[i], "--iso=")) { g_strlcpy(cdpath, argv[i] + strlen("--iso="), 256); yinit.cdcoretype = 1; } //set cdrom else if (0 == strcmp(argv[i], "-c") && argv[i + 1]) { g_strlcpy(cdpath, argv[i + 1], 256); yinit.cdcoretype = 2; } else if (strstr(argv[i], "--cdrom=")) { g_strlcpy(cdpath, argv[i] + strlen("--cdrom="), 256); yinit.cdcoretype = 2; } // Set sound else if (strcmp(argv[i], "-ns") == 0 || strcmp(argv[i], "--nosound") == 0) { yinit.sndcoretype = 0; } // Autoload else if (strcmp(argv[i], "--autoload") == 0) { yui_window_start(YUI_WINDOW(yui)); YuiLoadState(); yui_window_run(YUI_WINDOW(yui)); } // Autostart else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--autostart") == 0) { yui_window_run(YUI_WINDOW(yui)); } // Fullscreen else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--fullscreen") == 0) { yui_window_set_fullscreen(YUI_WINDOW(yui), TRUE); } // Auto frame skip else if (strstr(argv[i], "--autoframeskip=")) { int fscount; int fsenable; fscount = sscanf(argv[i] + strlen("--autoframeskip="), "%d", &fsenable); if (fscount > 0) yui_window_set_frameskip(YUI_WINDOW(yui), fsenable); } // Binary else if (strstr(argv[i], "--binary=")) { char binname[1024]; unsigned int binaddress; int bincount; bincount = sscanf(argv[i] + strlen("--binary="), "%[^:]:%x", binname, &binaddress); if (bincount > 0) { if (bincount < 2) binaddress = 0x06004000; yui_window_run(YUI_WINDOW(yui)); MappedMemoryLoadExec(binname, binaddress); } } } } #endif gtk_main (); YabauseDeInit(); LogStop(); return 0; } void YuiSwapBuffers(void) { yui_window_update(YUI_WINDOW(yui)); } void yui_conf(void) { gint result; GtkWidget * dialog; dialog = create_dialog1(); result = gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); switch(result) { case GTK_RESPONSE_OK: { gboolean mustRestart; g_file_set_contents(inifile, g_key_file_to_data(keyfile, 0, 0), -1, 0); mustRestart = yui_settings_load(); if (mustRestart) { GtkWidget* warningDlg = gtk_message_dialog_new (GTK_WINDOW(yui), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "You must restart Yabause before the changes take effect."); gtk_dialog_run (GTK_DIALOG(warningDlg)); gtk_widget_destroy (warningDlg); } break; } case GTK_RESPONSE_CANCEL: g_key_file_load_from_file(keyfile, inifile, G_KEY_FILE_NONE, 0); break; } } void yui_resize(guint width, guint height, gboolean fullscreen) { if (width <= 0) width = 320; if (height <= 0) height = 224; if (g_key_file_get_integer(keyfile, "General", "KeepRatio", 0)) { GdkGeometry geometry; geometry.min_aspect = (float) width / height; geometry.max_aspect = (float) width / height; gtk_window_set_geometry_hints(GTK_WINDOW(yui), YUI_WINDOW(yui)->area, &geometry, GDK_HINT_ASPECT); } else { gtk_window_set_geometry_hints(GTK_WINDOW(yui), YUI_WINDOW(yui)->area, NULL, 0); } gtk_window_resize(GTK_WINDOW(yui), width, height + YUI_WINDOW(yui)->menu->allocation.height); yui_window_set_fullscreen(YUI_WINDOW(yui), fullscreen); } yabause-0.9.13.1/src/gtk/yuiresolution.c000644 001750 001750 00000014060 12256006140 022055 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "yuiresolution.h" static void yui_resolution_class_init (YuiResolutionClass *klass); static void yui_resolution_init (YuiResolution *yie); static void yui_resolution_width_changed (GtkWidget * w, YuiResolution * yr); static void yui_resolution_height_changed (GtkWidget * w, YuiResolution * yr); static void yui_resolution_options_changed (GtkWidget * w, YuiResolution * yr); GType yui_resolution_get_type (void) { static GType yie_type = 0; if (!yie_type) { static const GTypeInfo yie_info = { sizeof (YuiResolutionClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_resolution_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiResolution), 0, (GInstanceInitFunc) yui_resolution_init, NULL, }; yie_type = g_type_register_static (GTK_TYPE_HBOX, "YuiResolution", &yie_info, 0); } return yie_type; } #define PROP_KEYFILE 1 #define PROP_GROUP 2 static void yui_resolution_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { switch(property_id) { case PROP_KEYFILE: YUI_RESOLUTION(object)->keyfile = g_value_get_pointer(value); break; case PROP_GROUP: YUI_RESOLUTION(object)->group = g_value_get_pointer(value); break; } } static void yui_resolution_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { } static void yui_resolution_class_init (YuiResolutionClass *klass) { GParamSpec * param; G_OBJECT_CLASS(klass)->set_property = yui_resolution_set_property; G_OBJECT_CLASS(klass)->get_property = yui_resolution_get_property; param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); } static void yui_resolution_init(YuiResolution * yr) { GtkWidget * label_w; GtkWidget * label_h; gtk_container_set_border_width (GTK_CONTAINER (yr), 10); label_w = gtk_label_new ("Width"); gtk_box_pack_start(GTK_BOX(yr), label_w, TRUE, TRUE, 0); yr->entry_w = gtk_entry_new (); gtk_entry_set_width_chars(GTK_ENTRY(yr->entry_w), 8); gtk_box_pack_start(GTK_BOX(yr), yr->entry_w, TRUE, TRUE, 0); label_h = gtk_label_new ("Height"); gtk_box_pack_start(GTK_BOX(yr), label_h, TRUE, TRUE, 0); yr->entry_h = gtk_entry_new (); gtk_entry_set_width_chars(GTK_ENTRY(yr->entry_h), 8); gtk_box_pack_start(GTK_BOX(yr), yr->entry_h, TRUE, TRUE, 0); yr->options = gtk_combo_box_new_text(); gtk_combo_box_append_text(GTK_COMBO_BOX(yr->options), "Window"); gtk_combo_box_append_text(GTK_COMBO_BOX(yr->options), "Fullscreen"); gtk_combo_box_append_text(GTK_COMBO_BOX(yr->options), "Keep ratio"); gtk_box_pack_start(GTK_BOX(yr), yr->options, TRUE, TRUE, 0); g_signal_connect(yr->entry_w, "changed", G_CALLBACK(yui_resolution_width_changed), yr); g_signal_connect(yr->entry_h, "changed", G_CALLBACK(yui_resolution_height_changed), yr); g_signal_connect(yr->options, "changed", G_CALLBACK(yui_resolution_options_changed), yr); } GtkWidget* yui_resolution_new(GKeyFile * keyfile, const gchar * group) { GtkWidget * widget; YuiResolution * yr; gchar *widthText, *heightText; widget = GTK_WIDGET(g_object_new(yui_resolution_get_type(), "key-file", keyfile, "group", group, NULL)); yr = YUI_RESOLUTION(widget); widthText = g_key_file_get_value(yr->keyfile, yr->group, "Width", 0); if ( !widthText ) widthText = ""; heightText = g_key_file_get_value(yr->keyfile, yr->group, "Height", 0); if ( !heightText ) heightText = ""; gtk_entry_set_text(GTK_ENTRY(yr->entry_w), widthText ); gtk_entry_set_text(GTK_ENTRY(yr->entry_h), heightText ); if (g_key_file_get_integer(yr->keyfile, yr->group, "Fullscreen", 0) == 1) gtk_combo_box_set_active(GTK_COMBO_BOX(yr->options), 1); else if (g_key_file_get_integer(yr->keyfile, yr->group, "KeepRatio", 0) == 1) gtk_combo_box_set_active(GTK_COMBO_BOX(yr->options), 2); else gtk_combo_box_set_active(GTK_COMBO_BOX(yr->options), 0); return widget; } static void yui_resolution_width_changed(GtkWidget * w, YuiResolution * yr) { g_key_file_set_value(yr->keyfile, yr->group, "Width", gtk_entry_get_text(GTK_ENTRY(w))); } static void yui_resolution_height_changed(GtkWidget * w, YuiResolution * yr) { g_key_file_set_value(yr->keyfile, yr->group, "Height", gtk_entry_get_text(GTK_ENTRY(w))); } static void yui_resolution_options_changed(GtkWidget * w, YuiResolution * yr) { gint active = gtk_combo_box_get_active(GTK_COMBO_BOX(yr->options)); switch(active) { case 0: g_key_file_set_integer(yr->keyfile, yr->group, "Fullscreen", 0); g_key_file_set_integer(yr->keyfile, yr->group, "KeepRatio", 0); break; case 1: g_key_file_set_integer(yr->keyfile, yr->group, "Fullscreen", 1); g_key_file_set_integer(yr->keyfile, yr->group, "KeepRatio", 0); break; case 2: g_key_file_set_integer(yr->keyfile, yr->group, "Fullscreen", 0); g_key_file_set_integer(yr->keyfile, yr->group, "KeepRatio", 1); break; } } yabause-0.9.13.1/src/gtk/yabause.desktop.in000644 001750 001750 00000000243 12256006140 022406 0ustar00guillaumeguillaume000000 000000 [Desktop Entry] Type=Application Name=Yabause (Gtk port) Comment=Sega Saturn emulator TryExec=yabause Exec=@YAB_PORT_NAME@ Icon=yabause Categories=GNOME;GTK;Game; yabause-0.9.13.1/src/gtk/menu.c000644 001750 001750 00000021406 12256006140 020071 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "settings.h" #include "yuiwindow.h" #include "yuivdp1.h" #include "yuivdp2.h" #include "yuish.h" #include "yuitransfer.h" #include "yuim68k.h" #include "yuiscudsp.h" #include "yuiscsp.h" #include "yuimem.h" #include "yuiscreenshot.h" static void openAboutDialog(GtkWidget * w, gpointer data) { gtk_show_about_dialog(data, "name", "Yabause", "version", VERSION, "website", "http://yabause.org", NULL); } void YuiSaveState(void) { char * dir = g_key_file_get_value(keyfile, "General", "StatePath", NULL); YabSaveStateSlot(dir, 1); } void YuiLoadState(void) { char * dir = g_key_file_get_value(keyfile, "General", "StatePath", NULL); YabLoadStateSlot(dir, 1); } GtkWidget* create_menu(YuiWindow * window1) { GtkWidget *menubar1; GtkWidget *menuitem1; GtkWidget *menuitem1_menu; GtkWidget *new1; GtkWidget *view1; GtkWidget *view1_menu; GtkWidget *fps1; GtkWidget *frameLimiter; GtkWidget *layer1; GtkWidget *layer1_menu; GtkWidget *log; GtkWidget *menuitem3; GtkWidget *menuitem3_menu; GtkWidget *msh; GtkWidget *ssh; GtkWidget *vdp2; GtkWidget *vdp1; GtkWidget *m68k; GtkWidget *scudsp; GtkWidget *scsp; GtkWidget *menuitem4; GtkWidget *menuitem4_menu; GtkWidget *about1; GtkWidget *transfer; GtkWidget *memory; GtkWidget *screenshot; menubar1 = gtk_menu_bar_new (); menuitem1 = gtk_menu_item_new_with_mnemonic ("_Yabause"); gtk_container_add (GTK_CONTAINER (menubar1), menuitem1); menuitem1_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem1), menuitem1_menu); new1 = gtk_image_menu_item_new_from_stock("gtk-preferences", NULL); g_signal_connect(new1, "activate", yui_conf, 0); gtk_container_add (GTK_CONTAINER (menuitem1_menu), new1); gtk_container_add(GTK_CONTAINER(menuitem1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "run"))); gtk_container_add(GTK_CONTAINER(menuitem1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "pause"))); gtk_container_add(GTK_CONTAINER(menuitem1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "reset"))); transfer = gtk_menu_item_new_with_mnemonic (_("Transfer")); gtk_container_add (GTK_CONTAINER (menuitem1_menu), transfer); g_signal_connect_swapped(transfer, "activate", G_CALLBACK(yui_transfer_new), window1); screenshot = gtk_menu_item_new_with_mnemonic (_("Screenshot")); gtk_container_add (GTK_CONTAINER (menuitem1_menu), screenshot); g_signal_connect_swapped(screenshot, "activate", G_CALLBACK(yui_screenshot_new), window1); frameLimiter = gtk_check_menu_item_new_with_mnemonic (_("Frame Skip/Limiter")); { GtkAction * action = gtk_action_group_get_action(window1->action_group, "frameskip"); gtk_action_connect_proxy(action, frameLimiter); } gtk_container_add (GTK_CONTAINER (menuitem1_menu), frameLimiter); { GtkWidget * savestate_menu; GtkWidget * savestate; GtkWidget * savestate_save; GtkWidget * savestate_load; savestate = gtk_menu_item_new_with_mnemonic(_("Save State")); gtk_container_add(GTK_CONTAINER(menuitem1_menu), savestate); savestate_menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(savestate), savestate_menu); savestate_save = gtk_menu_item_new_with_mnemonic(_("Save")); gtk_container_add(GTK_CONTAINER(savestate_menu), savestate_save); g_signal_connect_swapped(savestate_save, "activate", G_CALLBACK(YuiSaveState), NULL); savestate_load = gtk_menu_item_new_with_mnemonic(_("Load")); gtk_container_add(GTK_CONTAINER(savestate_menu), savestate_load); g_signal_connect_swapped(savestate_load, "activate", G_CALLBACK(YuiLoadState), NULL); } gtk_container_add (GTK_CONTAINER (menuitem1_menu), gtk_separator_menu_item_new ()); gtk_container_add(GTK_CONTAINER(menuitem1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "quit"))); view1 = gtk_menu_item_new_with_mnemonic (_("_View")); gtk_container_add (GTK_CONTAINER (menubar1), view1); view1_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (view1), view1_menu); fps1 = gtk_check_menu_item_new_with_mnemonic (_("FPS")); g_signal_connect(fps1, "activate", G_CALLBACK(ToggleFPS), NULL); gtk_container_add (GTK_CONTAINER (view1_menu), fps1); layer1 = gtk_menu_item_new_with_mnemonic (_("Layer")); gtk_container_add (GTK_CONTAINER (view1_menu), layer1); layer1_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (layer1), layer1_menu); gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_vdp1"))); gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_nbg0"))); gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_nbg1"))); gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_nbg2"))); gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_nbg3"))); gtk_container_add(GTK_CONTAINER(layer1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "toggle_rbg0"))); gtk_container_add(GTK_CONTAINER(view1_menu), gtk_action_create_menu_item(gtk_action_group_get_action(window1->action_group, "fullscreen"))); log = gtk_menu_item_new_with_mnemonic (_("Log")); g_signal_connect_swapped(log, "activate", G_CALLBACK(yui_window_show_log), window1); gtk_container_add(GTK_CONTAINER(view1_menu), log); menuitem3 = gtk_menu_item_new_with_mnemonic (_("_Debug")); gtk_container_add (GTK_CONTAINER (menubar1), menuitem3); menuitem3_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem3), menuitem3_menu); msh = gtk_menu_item_new_with_mnemonic ("MSH2"); gtk_container_add (GTK_CONTAINER (menuitem3_menu), msh); g_signal_connect_swapped(msh, "activate", G_CALLBACK(yui_msh_new), window1); ssh = gtk_menu_item_new_with_mnemonic ("SSH2"); gtk_container_add (GTK_CONTAINER (menuitem3_menu), ssh); g_signal_connect_swapped(ssh, "activate", G_CALLBACK(yui_ssh_new), window1); vdp2 = gtk_menu_item_new_with_mnemonic ("Vdp1"); gtk_container_add (GTK_CONTAINER (menuitem3_menu), vdp2); g_signal_connect_swapped(vdp2, "activate", G_CALLBACK(yui_vdp1_new), window1); vdp1 = gtk_menu_item_new_with_mnemonic ("Vdp2"); gtk_container_add (GTK_CONTAINER (menuitem3_menu), vdp1); g_signal_connect_swapped(vdp1, "activate", G_CALLBACK(yui_vdp2_new), window1); m68k = gtk_menu_item_new_with_mnemonic ("M68K"); gtk_container_add (GTK_CONTAINER (menuitem3_menu), m68k); g_signal_connect_swapped(m68k, "activate", G_CALLBACK(yui_m68k_new), window1); scudsp = gtk_menu_item_new_with_mnemonic ("SCU-DSP"); gtk_container_add (GTK_CONTAINER (menuitem3_menu), scudsp); g_signal_connect_swapped(scudsp, "activate", G_CALLBACK(yui_scudsp_new), window1); scsp = gtk_menu_item_new_with_mnemonic ("SCSP"); gtk_container_add (GTK_CONTAINER (menuitem3_menu), scsp); g_signal_connect_swapped(scsp, "activate", G_CALLBACK(yui_scsp_new), window1); gtk_container_add (GTK_CONTAINER (menuitem3_menu), gtk_separator_menu_item_new ()); memory = gtk_menu_item_new_with_mnemonic (_("Memory dump")); gtk_container_add (GTK_CONTAINER (menuitem3_menu), memory); g_signal_connect_swapped(memory, "activate", G_CALLBACK(yui_mem_new), window1); menuitem4 = gtk_menu_item_new_with_mnemonic (_("_Help")); gtk_container_add (GTK_CONTAINER (menubar1), menuitem4); menuitem4_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem4), menuitem4_menu); about1 = gtk_image_menu_item_new_from_stock ("gtk-about", NULL); gtk_container_add (GTK_CONTAINER (menuitem4_menu), about1); g_signal_connect(about1, "activate", G_CALLBACK(openAboutDialog), window1); return menubar1; } yabause-0.9.13.1/src/gtk/yuipage.h000644 001750 001750 00000003477 12256006135 020611 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_PAGE_H #define YUI_PAGE_H #include #include #include G_BEGIN_DECLS #define YUI_PAGE_TYPE (yui_page_get_type ()) #define YUI_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_PAGE_TYPE, YuiPage)) #define YUI_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_PAGE_TYPE, YuiPageClass)) #define IS_YUI_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_PAGE_TYPE)) #define IS_YUI_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_PAGE_TYPE)) #define YUI_FILE_SETTING 1 #define YUI_RANGE_SETTING 2 #define YUI_RESOLUTION_SETTING 3 typedef struct _YuiPage YuiPage; typedef struct _YuiPageClass YuiPageClass; struct _YuiPage { GtkVBox vbox; GKeyFile * keyfile; }; struct _YuiPageClass { GtkHBoxClass parent_class; void (* yui_page) (YuiPage * yfe); }; GType yui_page_get_type (void); GtkWidget * yui_page_new (GKeyFile * keyfile); GtkWidget * yui_page_add (YuiPage * yp, const gchar * name); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuiscsp.c000644 001750 001750 00000011553 12256006135 020632 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "yuiscsp.h" #include "../scsp.h" #include "../yabause.h" #include "settings.h" static void yui_scsp_class_init (YuiScspClass * klass); static void yui_scsp_init (YuiScsp * yfe); static void yui_scsp_spin_cursor_changed(GtkWidget * spin, YuiScsp * scsp); static void yui_scsp_clear(YuiScsp * scsp); GType yui_scsp_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiScspClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_scsp_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiScsp), 0, (GInstanceInitFunc) yui_scsp_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiScsp", &yfe_info, 0); } return yfe_type; } static void yui_scsp_class_init (UNUSED YuiScspClass * klass) { } static void yui_scsp_init (YuiScsp * yv) { gtk_window_set_title(GTK_WINDOW(yv), "SCSP"); yv->vbox = gtk_vbox_new(FALSE, 2); gtk_container_set_border_width(GTK_CONTAINER(yv->vbox), 4); gtk_container_add(GTK_CONTAINER(yv), yv->vbox); yv->spin = gtk_spin_button_new_with_range(0, 31, 1); gtk_spin_button_set_range(GTK_SPIN_BUTTON(yv->spin), 0, 31); gtk_box_pack_start(GTK_BOX(yv->vbox), yv->spin, FALSE, FALSE, 4); g_signal_connect(G_OBJECT(yv->spin), "value-changed", GTK_SIGNAL_FUNC(yui_scsp_spin_cursor_changed), yv); g_signal_connect(G_OBJECT(yv), "delete-event", GTK_SIGNAL_FUNC(yui_scsp_destroy), NULL); { GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); GtkWidget * text = gtk_text_view_new(); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE); yv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)); gtk_container_add(GTK_CONTAINER(scroll), text); gtk_box_pack_start(GTK_BOX(yv->vbox), scroll, TRUE, TRUE, 4); } yv->hbox = gtk_hbutton_box_new(); gtk_box_set_spacing(GTK_BOX(yv->hbox), 4); gtk_box_pack_start(GTK_BOX(yv->vbox ), yv->hbox, FALSE, FALSE, 4); yv->cursor = 0; } static gulong paused_handler; static gulong running_handler; static YuiWindow * yui; GtkWidget * yui_scsp_new(YuiWindow * y) { GtkWidget * dialog; YuiScsp * yv; yui = y; dialog = GTK_WIDGET(g_object_new(yui_scsp_get_type(), NULL)); yv = YUI_SCSP(dialog); { GtkWidget * but2, * but3, * but4; but2 = gtk_button_new(); gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "run"), but2); gtk_box_pack_start(GTK_BOX(yv->hbox), but2, FALSE, FALSE, 2); but3 = gtk_button_new(); gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "pause"), but3); gtk_box_pack_start(GTK_BOX(yv->hbox), but3, FALSE, FALSE, 2); but4 = gtk_button_new_from_stock("gtk-close"); g_signal_connect_swapped(but4, "clicked", G_CALLBACK(yui_scsp_destroy), dialog); gtk_box_pack_start(GTK_BOX(yv->hbox), but4, FALSE, FALSE, 2); } paused_handler = g_signal_connect_swapped(yui, "paused", G_CALLBACK(yui_scsp_update), yv); running_handler = g_signal_connect_swapped(yui, "running", G_CALLBACK(yui_scsp_clear), yv); if ((yui->state & (YUI_IS_RUNNING | YUI_IS_INIT)) == YUI_IS_INIT) yui_scsp_update(yv); gtk_widget_show_all(GTK_WIDGET(yv)); return dialog; } void yui_scsp_fill(YuiScsp * scsp) { gchar nameTemp[1024]; yui_scsp_clear(scsp); ScspSlotDebugStats(scsp->cursor, nameTemp ); gtk_text_buffer_set_text(scsp->buffer, g_strstrip(nameTemp), -1); } static void yui_scsp_spin_cursor_changed(GtkWidget * spin, YuiScsp * scsp) { scsp->cursor = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); yui_scsp_fill(scsp); } void yui_scsp_update(YuiScsp * scsp) { yui_scsp_fill(scsp); } void yui_scsp_destroy(YuiScsp * scsp) { g_signal_handler_disconnect(yui, running_handler); g_signal_handler_disconnect(yui, paused_handler); gtk_widget_destroy(GTK_WIDGET(scsp)); } static void yui_scsp_clear(YuiScsp * scsp) { gtk_text_buffer_set_text(scsp->buffer, "", -1); } yabause-0.9.13.1/src/gtk/CMakeLists.txt000644 001750 001750 00000003632 12256006140 021522 0ustar00guillaumeguillaume000000 000000 project(yabause-gtk) yab_port_start() find_package(GTK2 2.10 COMPONENTS gtk) if (NOT GTK2_FOUND) return() endif (NOT GTK2_FOUND) set(PORT_INCLUDE_DIRS ${GTK2_INCLUDE_DIRS}) set(PORT_LIBRARIES ${GTK2_LIBRARIES}) if (OPENGL_FOUND) find_path(GDKGLEXT_CONFIG_INCLUDE_DIR gdkglext-config.h PATHS ${CMAKE_SYSTEM_PREFIX_PATH} PATH_SUFFIXES lib/gtkglext-1.0/include) find_path(GTKGLEXT_INCLUDE_DIR gtk/gtkgl.h PATH_SUFFIXES gtkglext-1.0) find_library(GDKGLEXT_LIBRARY gdkglext-x11-1.0) find_library(GTKGLEXT_LIBRARY gtkglext-x11-1.0) if (NOT GDKGLEXT_CONFIG_INCLUDE_DIR OR NOT GTKGLEXT_INCLUDE_DIR OR NOT GDKGLEXT_LIBRARY OR NOT GTKGLEXT_LIBRARY) message(STATUS "Found OpenGL and Gtk+ but not libgtkglext, skipping Gtk+ port.") return() endif () set(PORT_INCLUDE_DIRS ${PORT_INCLUDE_DIRS} ${GTKGLEXT_INCLUDE_DIR} ${GDKGLEXT_CONFIG_INCLUDE_DIR}) set(PORT_LIBRARIES ${PORT_LIBRARIES} ${GTKGLEXT_LIBRARY} ${GDKGLEXT_LIBRARY}) add_definitions(-DHAVE_LIBGTKGLEXT=1) endif (OPENGL_FOUND) include_directories(${PORT_INCLUDE_DIRS}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I/usr/include/gdk-pixbuf-2.0") set(yabause_gtk_SOURCES gtk-compat.c gtkglwidget.c main.c menu.c pergtk.c settings.c yuicheckbutton.c yuifileentry.c yuiinputentry.c yuim68k.c yuimem.c yuipage.c yuirange.c yuiresolution.c yuiscreenshot.c yuiscsp.c yuiscudsp.c yuish.c yuitransfer.c yuivdp1.c yuivdp2.c yuiviewer.c yuiwindow.c) add_executable(yabause-gtk ${yabause_gtk_SOURCES}) target_link_libraries(yabause-gtk yabause ${YABAUSE_LIBRARIES} ${PORT_LIBRARIES}) yab_port_success(yabause-gtk) configure_file(yabause.desktop.in ${YAB_PORT_NAME}.desktop) install(TARGETS yabause-gtk DESTINATION "bin") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${YAB_PORT_NAME}.desktop DESTINATION "share/applications") install(FILES "doc/yabause.1" DESTINATION "${YAB_MAN_DIR}/man1" RENAME "${YAB_PORT_NAME}.1") install(FILES "yabause.png" DESTINATION "share/pixmaps") yabause-0.9.13.1/src/gtk/yuiscudsp.c000644 001750 001750 00000037433 12256006140 021164 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "yuiscudsp.h" #include "../scu.h" #include "../yabause.h" #include "settings.h" static void yui_scudsp_class_init (YuiScudspClass * klass); static void yui_scudsp_init (YuiScudsp * yfe); static void yui_scudsp_clear(YuiScudsp * scudsp); static void yui_scudsp_editedReg( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiScudsp *scudsp); static void yui_scudsp_editedBp( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiScudsp *scudsp); static void yui_scudsp_step( GtkWidget* widget, YuiScudsp * scudsp ); static void yui_scudsp_breakpoint_handler (u32 addr); static YuiScudsp *yui_scudsp; static YuiWindow * yui; GType yui_scudsp_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiScudspClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_scudsp_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiScudsp), 0, (GInstanceInitFunc) yui_scudsp_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiScudsp", &yfe_info, 0); } return yfe_type; } static void yui_scudsp_class_init (UNUSED YuiScudspClass * klass) { } static void yui_scudsp_init (YuiScudsp * scudsp) { gtk_window_set_title(GTK_WINDOW(scudsp), "SCU-DSP"); gtk_window_set_resizable( GTK_WINDOW(scudsp), FALSE ); scudsp->vbox = gtk_vbox_new(FALSE, 2); gtk_container_set_border_width( GTK_CONTAINER( scudsp->vbox ),4 ); gtk_container_add (GTK_CONTAINER (scudsp), scudsp->vbox); scudsp->hboxmain = gtk_hbox_new(FALSE, 2); gtk_container_set_border_width( GTK_CONTAINER( scudsp->hboxmain ),4 ); gtk_box_pack_start( GTK_BOX( scudsp->vbox ), scudsp->hboxmain, FALSE, FALSE, 4 ); scudsp->hbox = gtk_hbutton_box_new(); gtk_container_set_border_width( GTK_CONTAINER( scudsp->hbox ),4 ); gtk_box_pack_start( GTK_BOX( scudsp->vbox ), scudsp->hbox, FALSE, FALSE, 4 ); scudsp->vboxmain = gtk_vbox_new(FALSE, 2); gtk_container_set_border_width( GTK_CONTAINER( scudsp->vboxmain ),4 ); gtk_box_pack_start( GTK_BOX( scudsp->hboxmain ), scudsp->vboxmain, FALSE, FALSE, 4 ); /* unassembler frame */ scudsp->uFrame = gtk_frame_new("Disassembled code"); gtk_box_pack_start( GTK_BOX( scudsp->vboxmain ), scudsp->uFrame, FALSE, FALSE, 4 ); scudsp->uLabel = gtk_label_new(NULL); gtk_container_add (GTK_CONTAINER (scudsp->uFrame), scudsp->uLabel ); /* Register list */ scudsp->regListStore = gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING); scudsp->regList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(scudsp->regListStore) ); scudsp->regListRenderer1 = gtk_cell_renderer_text_new(); scudsp->regListRenderer2 = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(scudsp->regListRenderer2), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); scudsp->regListColumn1 = gtk_tree_view_column_new_with_attributes("Register", scudsp->regListRenderer1, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(scudsp->regList), scudsp->regListColumn1); scudsp->regListColumn2 = gtk_tree_view_column_new_with_attributes("Value", scudsp->regListRenderer2, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(scudsp->regList), scudsp->regListColumn2); gtk_box_pack_start( GTK_BOX( scudsp->hboxmain ), scudsp->regList, FALSE, FALSE, 4 ); g_signal_connect(G_OBJECT(scudsp->regListRenderer2), "edited", GTK_SIGNAL_FUNC(yui_scudsp_editedReg), scudsp ); /* breakpoint list */ scudsp->bpListStore = gtk_list_store_new(1,G_TYPE_STRING); scudsp->bpList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(scudsp->bpListStore) ); scudsp->bpListRenderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(scudsp->bpListRenderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); scudsp->bpListColumn = gtk_tree_view_column_new_with_attributes("Breakpoints", scudsp->bpListRenderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(scudsp->bpList), scudsp->bpListColumn); gtk_box_pack_start( GTK_BOX( scudsp->hboxmain ), scudsp->bpList, FALSE, FALSE, 4 ); g_signal_connect(G_OBJECT(scudsp->bpListRenderer), "edited", GTK_SIGNAL_FUNC(yui_scudsp_editedBp), scudsp ); g_signal_connect(G_OBJECT(scudsp), "delete-event", GTK_SIGNAL_FUNC(yui_scudsp_destroy), NULL); } GtkWidget * yui_scudsp_new(YuiWindow * y) { GtkWidget * dialog; GClosure *closureF7; GtkAccelGroup *accelGroup; const scucodebreakpoint_struct *cbp; gint i; yui = y; if ( yui_scudsp ) return GTK_WIDGET(yui_scudsp); dialog = GTK_WIDGET(g_object_new(yui_scudsp_get_type(), NULL)); yui_scudsp = YUI_SCUDSP(dialog); if (!( yui->state & YUI_IS_INIT )) { yui_window_run(yui); yui_window_pause(yui); } ScuDspSetBreakpointCallBack(&yui_scudsp_breakpoint_handler); for (i = 0; i < 23 ; i++) { GtkTreeIter iter; gtk_list_store_append( GTK_LIST_STORE( yui_scudsp->regListStore ), &iter ); } cbp = ScuDspGetBreakpointList(); for (i = 0; i < MAX_BREAKPOINTS; i++) { GtkTreeIter iter; yui_scudsp->cbp[i] = cbp[i].addr; gtk_list_store_append( GTK_LIST_STORE( yui_scudsp->bpListStore ), &iter ); if (cbp[i].addr != 0xFFFFFFFF) { gchar tempstr[20]; sprintf(tempstr, "%08X", (int)cbp[i].addr); gtk_list_store_set( GTK_LIST_STORE( yui_scudsp->bpListStore ), &iter, 0, tempstr, -1 ); } else gtk_list_store_set( GTK_LIST_STORE( yui_scudsp->bpListStore ), &iter, 0, "", -1 ); } { GtkWidget * but2, * but3, * but4; yui_scudsp->buttonStep = gtk_button_new_with_label( "Step [F7]" ); gtk_box_pack_start( GTK_BOX( yui_scudsp->hbox ), yui_scudsp->buttonStep, FALSE, FALSE, 2 ); g_signal_connect( yui_scudsp->buttonStep, "clicked", G_CALLBACK(yui_scudsp_step), yui_scudsp ); but2 = gtk_button_new(); gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "run"), but2); gtk_box_pack_start(GTK_BOX(yui_scudsp->hbox), but2, FALSE, FALSE, 2); but3 = gtk_button_new(); gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "pause"), but3); gtk_box_pack_start(GTK_BOX(yui_scudsp->hbox), but3, FALSE, FALSE, 2); but4 = gtk_button_new_from_stock("gtk-close"); g_signal_connect_swapped(but4, "clicked", G_CALLBACK(yui_scudsp_destroy), dialog); gtk_box_pack_start(GTK_BOX(yui_scudsp->hbox), but4, FALSE, FALSE, 2); } yui_scudsp->paused_handler = g_signal_connect_swapped(yui, "paused", G_CALLBACK(yui_scudsp_update), yui_scudsp); yui_scudsp->running_handler = g_signal_connect_swapped(yui, "running", G_CALLBACK(yui_scudsp_clear), yui_scudsp); accelGroup = gtk_accel_group_new (); closureF7 = g_cclosure_new (G_CALLBACK (yui_scudsp_step), yui_scudsp, NULL); gtk_accel_group_connect( accelGroup, GDK_F7, 0, 0, closureF7 ); gtk_window_add_accel_group( GTK_WINDOW( dialog ), accelGroup ); yui_scudsp_update(yui_scudsp); if ( yui->state & YUI_IS_RUNNING ) yui_scudsp_clear(yui_scudsp); gtk_widget_show_all(GTK_WIDGET(yui_scudsp)); return dialog; } static void yui_scudsp_update_reglist( YuiScudsp *scudsp, scudspregs_struct *regs) { /* refrescudsp the registery list */ GtkTreeIter iter; char valuestr[32]; gtk_tree_model_get_iter_first( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); sprintf(valuestr, "%d", regs->ProgControlPort.part.PR); gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, "PR", 1, valuestr, -1 ); #define SCUDSPUPDATEREGLISTp(rreg,format) \ gtk_tree_model_iter_next( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); \ sprintf(valuestr, #format, (int)regs->ProgControlPort.part.rreg); \ gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, #rreg, 1, valuestr, -1 ); #define SCUDSPUPDATEREGLIST(rreg,format) \ gtk_tree_model_iter_next( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); \ sprintf(valuestr, #format, (int)regs->rreg); \ gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, #rreg, 1, valuestr, -1 ); #define SCUDSPUPDATEREGLISTx(rreg,vreg,format) \ gtk_tree_model_iter_next( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); \ sprintf(valuestr, #format, (int)(vreg)); \ gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, #rreg, 1, valuestr, -1 ); SCUDSPUPDATEREGLISTp(EP,%d); SCUDSPUPDATEREGLISTp(T0,%d); SCUDSPUPDATEREGLISTp(S,%d); SCUDSPUPDATEREGLISTp(Z,%d); SCUDSPUPDATEREGLISTp(C,%d); SCUDSPUPDATEREGLISTp(V,%d); SCUDSPUPDATEREGLISTp(E,%d); SCUDSPUPDATEREGLISTp(ES,%d); SCUDSPUPDATEREGLISTp(EX,%d); SCUDSPUPDATEREGLISTp(LE,%d); SCUDSPUPDATEREGLISTp(P,%02X); SCUDSPUPDATEREGLIST(TOP,%02X); SCUDSPUPDATEREGLIST(LOP,%02X); gtk_tree_model_iter_next( GTK_TREE_MODEL( scudsp->regListStore ), &iter ); sprintf(valuestr, "%08X", (int)(((u32)(regs->CT[0]))<<24 | ((u32)(regs->CT[1]))<<16 | ((u32)(regs->CT[2]))<<8 | ((u32)(regs->CT[3]))) ); gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 0, "CT", 1, valuestr, -1 ); SCUDSPUPDATEREGLISTx(RA,regs->RA0,%08X); SCUDSPUPDATEREGLISTx(WA,regs->WA0,%08X); SCUDSPUPDATEREGLIST(RX,%08X); SCUDSPUPDATEREGLIST(RY,%08X); SCUDSPUPDATEREGLISTx(PH,regs->P.part.H & 0xFFFF,%04X); SCUDSPUPDATEREGLISTx(PL,regs->P.part.L & 0xFFFFFFFF,%08X); SCUDSPUPDATEREGLISTx(ACH,regs->AC.part.H & 0xFFFF,%04X); SCUDSPUPDATEREGLISTx(ACL,regs->AC.part.L & 0xFFFFFFFF,%08X); } static void scudspsetRegister( YuiScudsp *scudsp, int nReg, u32 value ) { /* set register number to value in proc */ scudspregs_struct scudspregs; ScuDspGetRegisters(&scudspregs); switch ( nReg ) { case 0: scudspregs.ProgControlPort.part.PR = value; break; case 1: scudspregs.ProgControlPort.part.EP = value; break; case 2: scudspregs.ProgControlPort.part.T0 = value; break; case 3: scudspregs.ProgControlPort.part.S = value; break; case 4: scudspregs.ProgControlPort.part.Z = value; break; case 5: scudspregs.ProgControlPort.part.C = value; break; case 6: scudspregs.ProgControlPort.part.V = value; break; case 7: scudspregs.ProgControlPort.part.E = value; break; case 8: scudspregs.ProgControlPort.part.ES = value; break; case 9: scudspregs.ProgControlPort.part.EX = value; break; case 10: scudspregs.ProgControlPort.part.LE = value; break; case 11: scudspregs.ProgControlPort.part.P = value; break; case 12: scudspregs.TOP = value; break; case 13: scudspregs.LOP = value; break; case 14: scudspregs.CT[0] = (value>>24) & 0xff; scudspregs.CT[1] = (value>>16) & 0xff; scudspregs.CT[2] = (value>>8) & 0xff; scudspregs.CT[3] = (value) & 0xff; break; case 15: scudspregs.RA0 = value; break; case 16: scudspregs.WA0 = value; break; case 17: scudspregs.RX = value; break; case 18: scudspregs.RY = value; break; case 19: scudspregs.P.part.H = value; break; case 20: scudspregs.P.part.L = value; break; case 21: scudspregs.AC.part.H = value; break; case 22: scudspregs.AC.part.L = value; break; } ScuDspSetRegisters(&scudspregs); } static void yui_scudsp_update_codelist( YuiScudsp *scudsp, u32 addr) { /* refresh the assembler view. points the line to be highlighted. */ int i; static char tagPC[] = ""; static char tagEnd[] = "\n"; char buf[100*24+40]; char *curs = buf; char lineBuf[100]; u32 offset; if ( addr - scudsp->lastCode >= 20 ) offset = addr - 8; else offset = scudsp->lastCode; scudsp->lastCode = offset; for (i=0; i < 24; i++) { if ( offset + i == addr ) { strcpy( curs, tagPC ); curs += strlen(tagPC); } ScuDspDisasm(offset+i, lineBuf); strcpy( curs, lineBuf ); curs += strlen(lineBuf); if ( offset + i == addr ) { strcpy( curs, tagEnd ); curs += strlen(tagEnd); } else { strcpy( curs, "\n" ); curs += 1;} } *curs = 0; gtk_label_set_markup( GTK_LABEL(scudsp->uLabel), buf ); } static void yui_scudsp_step( GtkWidget* widget, YuiScudsp * scudsp ) { ScuDspStep(); yui_window_invalidate( yui ); /* update all dialogs, including us */ } static void yui_scudsp_editedReg( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiScudsp *scudsp) { /* registry number value has been set to */ GtkTreeIter iter; char bptext[10]; char *endptr; int i = atoi(arg1); u32 addr; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( scudsp->regListStore ), &iter, arg1 ); addr = strtoul(arg2, &endptr, 16 ); if ( endptr - arg2 == strlen(arg2) ) { sprintf(bptext, "%08X", (int)addr); scudspsetRegister( scudsp, i, addr ); gtk_list_store_set( GTK_LIST_STORE( scudsp->regListStore ), &iter, 1, bptext, -1 ); } yui_window_invalidate( yui ); } static void yui_scudsp_editedBp( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiScudsp *scudsp) { /* breakpoint has been set to address */ GtkTreeIter iter; char bptext[10]; char *endptr; int i = atoi(arg1); u32 addr; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( scudsp->bpListStore ), &iter, arg1 ); addr = strtoul(arg2, &endptr, 16 ); if ((endptr - arg2 < strlen(arg2)) || (!addr)) addr = 0xFFFFFFFF; if ( scudsp->cbp[i] != 0xFFFFFFFF) ScuDspDelCodeBreakpoint(scudsp->cbp[i]); scudsp->cbp[i] = 0xFFFFFFFF; if ((addr!=0xFFFFFFFF)&&(ScuDspAddCodeBreakpoint(addr) == 0)) { sprintf(bptext, "%08X", (int)addr); scudsp->cbp[i] = addr; } else strcpy(bptext,""); gtk_list_store_set( GTK_LIST_STORE( scudsp->bpListStore ), &iter, 0, bptext, -1 ); } static void debugPauseLoop(void) { /* secondary gtk event loop for the "breakpoint pause" state */ while ( !(yui->state & YUI_IS_RUNNING) ) if ( gtk_main_iteration() ) return; } static void yui_scudsp_breakpoint_handler (u32 addr) { yui_window_pause(yui); { scudspregs_struct scudspregs; YuiScudsp* scudsp = YUI_SCUDSP(yui_scudsp_new( yui )); ScuDspGetRegisters(&scudspregs); yui_scudsp_update_reglist(scudsp, &scudspregs); yui_scudsp_update_codelist(scudsp, scudspregs.PC); } debugPauseLoop(); /* execution is suspended inside a normal cycle - enter secondary gtk loop */ } void yui_scudsp_update(YuiScudsp * scudsp) { scudspregs_struct scudspregs; ScuDspGetRegisters(&scudspregs); yui_scudsp_update_codelist(scudsp,scudspregs.PC); yui_scudsp_update_reglist(scudsp, &scudspregs); gtk_widget_set_sensitive(scudsp->uLabel, TRUE); gtk_widget_set_sensitive(scudsp->bpList, TRUE); gtk_widget_set_sensitive(scudsp->regList, TRUE); gtk_widget_set_sensitive(scudsp->buttonStep, TRUE); } void yui_scudsp_destroy(YuiScudsp * scudsp) { g_signal_handler_disconnect(yui, scudsp->running_handler); g_signal_handler_disconnect(yui, scudsp->paused_handler); yui_scudsp = NULL; gtk_widget_destroy(GTK_WIDGET(scudsp)); } static void yui_scudsp_clear(YuiScudsp * scudsp) { gtk_widget_set_sensitive(scudsp->uLabel, FALSE); gtk_widget_set_sensitive(scudsp->bpList, FALSE); gtk_widget_set_sensitive(scudsp->regList, FALSE); gtk_widget_set_sensitive(scudsp->buttonStep, FALSE); } yabause-0.9.13.1/src/gtk/yuimem.h000644 001750 001750 00000003626 12256006135 020447 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_MEM_H #define YUI_MEM_H #include #include #include #include "yuiwindow.h" G_BEGIN_DECLS #define YUI_MEM_TYPE (yui_mem_get_type ()) #define YUI_MEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_MEM_TYPE, YuiMem)) #define YUI_MEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_MEM_TYPE, YuiMemClass)) #define IS_YUI_MEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_MEM_TYPE)) #define IS_YUI_MEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_MEM_TYPE)) typedef struct _YuiMem YuiMem; typedef struct _YuiMemClass YuiMemClass; struct _YuiMem { GtkWindow dialog; GtkWidget * toolbar; GtkListStore * store; GtkWidget * quickCombo; guint wLine; guint32 address; gulong paused_handler; gulong running_handler; YuiWindow * yui; }; struct _YuiMemClass { GtkWindowClass parent_class; void (* yui_mem) (YuiMem * yv); }; GType yui_mem_get_type(void); GtkWidget * yui_mem_new (YuiWindow * yui); void yui_mem_fill (YuiMem * vdp1); void yui_mem_destroy (YuiMem * vdp1); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuiscreenshot.c000644 001750 001750 00000007730 12256006135 022041 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006-2007 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "yuiscreenshot.h" #include "gtkglwidget.h" #include "yuiviewer.h" #include "../core.h" static void yui_screenshot_class_init (YuiScreenshotClass * klass); static void yui_screenshot_init (YuiScreenshot * yfe); static void yui_screenshot_update (YuiScreenshot * ys, gpointer data); static gboolean yui_screenshot_draw(YuiScreenshot * ys); GType yui_screenshot_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiScreenshotClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_screenshot_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiScreenshot), 0, (GInstanceInitFunc) yui_screenshot_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiScreenshot", &yfe_info, 0); } return yfe_type; } static void yui_screenshot_class_init (UNUSED YuiScreenshotClass * klass) { } static YuiWindow * yui; static void yui_screenshot_init (YuiScreenshot * yv) { GtkWidget * box; GtkWidget * button_box; GtkWidget * button; gtk_window_set_title(GTK_WINDOW(yv), "Screenshot"); gtk_container_set_border_width(GTK_CONTAINER(yv), 4); box = gtk_vbox_new(FALSE, 4); gtk_container_add(GTK_CONTAINER(yv), box); yv->image = yui_viewer_new(); gtk_box_pack_start(GTK_BOX(box), yv->image, FALSE, FALSE, 0); gtk_widget_set_size_request(GTK_WIDGET(yv->image), 320, 224); button_box = gtk_hbutton_box_new(); gtk_box_pack_start(GTK_BOX(box), button_box, FALSE, FALSE, 0); button = gtk_button_new_from_stock(GTK_STOCK_REFRESH); gtk_box_pack_start(GTK_BOX(button_box), button, FALSE, FALSE, 0); g_signal_connect_swapped(button, "clicked", G_CALLBACK(yui_screenshot_update), yv); button = gtk_button_new_from_stock(GTK_STOCK_SAVE); gtk_box_pack_start(GTK_BOX(button_box), button, FALSE, FALSE, 0); g_signal_connect_swapped(button, "clicked", G_CALLBACK(yui_viewer_save), yv->image); button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); gtk_box_pack_start(GTK_BOX(button_box), button, FALSE, FALSE, 0); g_signal_connect_swapped(button, "clicked", G_CALLBACK(gtk_widget_destroy), yv); } GtkWidget * yui_screenshot_new(YuiWindow * y) { GtkWidget * dialog; YuiScreenshot * yv; yui = y; dialog = GTK_WIDGET(g_object_new(yui_screenshot_get_type(), NULL)); yv = YUI_SCREENSHOT(dialog); gtk_widget_show_all(dialog); yui_gl_dump_screen(YUI_GL(yui->area)); yui_screenshot_draw(yv); return dialog; } static void yui_screenshot_update(YuiScreenshot * ys, UNUSED gpointer data) { yui_gl_dump_screen(YUI_GL(yui->area)); yui_screenshot_draw(ys); } static gboolean yui_screenshot_draw(YuiScreenshot * ys) { GdkPixbuf * pixbuf, * correct; pixbuf = gdk_pixbuf_new_from_data((const guchar *) YUI_GL(yui->area)->pixels, GDK_COLORSPACE_RGB, FALSE, 8, YUI_GL(yui->area)->pixels_width, YUI_GL(yui->area)->pixels_height, YUI_GL(yui->area)->pixels_rowstride, NULL, NULL); correct = gdk_pixbuf_flip(pixbuf, FALSE); yui_viewer_draw_pixbuf(YUI_VIEWER(ys->image), correct, YUI_GL(yui->area)->pixels_width, YUI_GL(yui->area)->pixels_height); g_object_unref(pixbuf); g_object_unref(correct); return TRUE; } yabause-0.9.13.1/src/gtk/yuimem.c000644 001750 001750 00000023326 12256006135 020441 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "yuimem.h" #include "settings.h" static void yui_mem_class_init (YuiMemClass * klass); static void yui_mem_init (YuiMem * yfe); static void yui_mem_clear (YuiMem * vdp1); static void yui_mem_address_changed (GtkWidget * w, YuiMem * ym); static void yui_mem_content_changed ( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiMem *ym); static gboolean yui_mem_pagedown_pressed (GtkWidget *w, gpointer func, gpointer data, gpointer data2, YuiMem *ym); static gboolean yui_mem_pageup_pressed (GtkWidget *w, gpointer func, gpointer data, gpointer data2, YuiMem *ym); static void yui_mem_update (YuiMem * ym); static void yui_mem_combo_changed(GtkWidget * w, YuiMem * ym); static void yui_mem_pagedown_clicked (GtkToolButton * button, YuiMem * ym); static void yui_mem_pageup_clicked (GtkToolButton * button, YuiMem * ym); struct { gchar* name; u32 address; } quickAddress[] = { {"VDP2_VRAM_A0", 0x25E00000 }, {"VDP2_VRAM_A1", 0x25E20000 }, {"VDP2_VRAM_B0", 0x25E40000 }, {"VDP2_VRAM_B1", 0x25E60000 }, {"VDP2_CRAM", 0x25F00000 }, {"LWRAM", 0x20200000 }, {"HWRAM", 0x26000000 }, {"SpriteVRAM", 0x25C00000 }, {0, 0 } }; GType yui_mem_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiMemClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_mem_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiMem), 0, (GInstanceInitFunc) yui_mem_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiMem", &yfe_info, 0); } return yfe_type; } static void yui_mem_class_init (UNUSED YuiMemClass * klass) { } static void yui_mem_init (YuiMem * yv) { GtkWidget * view; GtkCellRenderer * renderer; GtkTreeViewColumn * column; GtkAccelGroup *accelGroup; GtkToolItem * comboItem, * upbutton, * downbutton; GtkWidget * testbox, * vbox; gint i; gtk_window_set_title(GTK_WINDOW(yv), "Memory dump"); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(yv), vbox); yv->toolbar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(yv->toolbar), GTK_TOOLBAR_ICONS); gtk_box_pack_start(GTK_BOX(vbox), yv->toolbar, FALSE, FALSE, 0); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), gtk_separator_tool_item_new(), 0); comboItem = gtk_tool_item_new(); gtk_tool_item_set_expand(comboItem, FALSE); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), comboItem, 1); downbutton = gtk_tool_button_new_from_stock(GTK_STOCK_GO_DOWN); g_signal_connect(downbutton, "clicked", G_CALLBACK(yui_mem_pagedown_clicked), yv); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), downbutton, 2); upbutton = gtk_tool_button_new_from_stock(GTK_STOCK_GO_UP); g_signal_connect(upbutton, "clicked", G_CALLBACK(yui_mem_pageup_clicked), yv); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), upbutton, 3); yv->quickCombo = gtk_combo_box_entry_new_text(); gtk_entry_set_width_chars(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(yv->quickCombo))), 8); for ( i = 0 ; quickAddress[i].name ; i++ ) gtk_combo_box_insert_text( GTK_COMBO_BOX( yv->quickCombo ), i, quickAddress[i].name ); gtk_combo_box_set_active( GTK_COMBO_BOX(yv->quickCombo), 0 ); g_signal_connect(yv->quickCombo, "changed", G_CALLBACK(yui_mem_combo_changed), yv ); g_signal_connect(gtk_bin_get_child(GTK_BIN(yv->quickCombo)), "activate", G_CALLBACK(yui_mem_address_changed), yv ); testbox = gtk_vbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(testbox), yv->quickCombo, TRUE, FALSE, 0); gtk_container_add(GTK_CONTAINER(comboItem), testbox); yv->store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); view = gtk_tree_view_new_with_model(GTK_TREE_MODEL (yv->store)); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("Address", renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("Dump", renderer, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); g_object_set(G_OBJECT(renderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); g_signal_connect(G_OBJECT(renderer), "edited", GTK_SIGNAL_FUNC(yui_mem_content_changed), yv ); gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0); g_signal_connect(G_OBJECT(yv), "delete-event", GTK_SIGNAL_FUNC(yui_mem_destroy), NULL); accelGroup = gtk_accel_group_new (); gtk_accel_group_connect( accelGroup, GDK_Page_Up, 0, 0, g_cclosure_new (G_CALLBACK(yui_mem_pageup_pressed), yv, NULL) ); gtk_accel_group_connect( accelGroup, GDK_Page_Down, 0, 0, g_cclosure_new (G_CALLBACK(yui_mem_pagedown_pressed), yv, NULL) ); gtk_window_add_accel_group( GTK_WINDOW( yv ), accelGroup ); yv->address = 0; yv->wLine = 8; gtk_window_set_default_size(GTK_WINDOW(yv), 300, -1); } GtkWidget * yui_mem_new(YuiWindow * y) { GtkWidget * dialog; YuiMem * yv; dialog = GTK_WIDGET(g_object_new(yui_mem_get_type(), NULL)); yv = YUI_MEM(dialog); yv->yui = y; if (!( yv->yui->state & YUI_IS_INIT )) { yui_window_run(yv->yui); yui_window_pause(yv->yui); } { GtkToolItem * play_button, * pause_button; play_button = gtk_tool_button_new_from_stock("run"); gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "run"), GTK_WIDGET(play_button)); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(play_button), 0); pause_button = gtk_tool_button_new_from_stock("pause"); gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "pause"), GTK_WIDGET(pause_button)); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(pause_button), 1); } yv->paused_handler = g_signal_connect_swapped(yv->yui, "paused", G_CALLBACK(yui_mem_update), yv); yv->running_handler = g_signal_connect_swapped(yv->yui, "running", G_CALLBACK(yui_mem_clear), yv); if ((yv->yui->state & (YUI_IS_RUNNING | YUI_IS_INIT)) == YUI_IS_INIT) yui_mem_update(yv); gtk_widget_show_all(GTK_WIDGET(yv)); return dialog; } void yui_mem_destroy(YuiMem * ym) { g_signal_handler_disconnect(ym->yui, ym->running_handler); g_signal_handler_disconnect(ym->yui, ym->paused_handler); gtk_widget_destroy(GTK_WIDGET(ym)); } static void yui_mem_clear(YuiMem * vdp1) { } static void yui_mem_address_changed(GtkWidget * w, YuiMem * ym) { sscanf(gtk_entry_get_text(GTK_ENTRY(w)), "%x", &ym->address); yui_mem_update(ym); } static void yui_mem_combo_changed(GtkWidget * w, YuiMem * ym) { gint i = gtk_combo_box_get_active( GTK_COMBO_BOX(w) ); if (i > -1) { ym->address = quickAddress[i].address; yui_mem_update(ym); } } static gint hexaDigitToInt( gchar c ) { if (( c >= '0' )&&( c <= '9' )) return c-'0'; if (( c >= 'a' )&&( c <= 'f' )) return c-'a' + 0xA; if (( c >= 'A' )&&( c <= 'F' )) return c-'A' + 0xA; return -1; } static gboolean yui_mem_pageup_pressed(GtkWidget *w, gpointer func, gpointer data, gpointer data2, YuiMem *ym) { ym->address -= 2*ym->wLine; yui_mem_update(ym); return TRUE; } static gboolean yui_mem_pagedown_pressed(GtkWidget *w, gpointer func, gpointer data, gpointer data2, YuiMem *ym) { ym->address += 2*ym->wLine; yui_mem_update(ym); return TRUE; } static void yui_mem_pagedown_clicked (GtkToolButton * button, YuiMem * ym) { ym->address += 2*ym->wLine; yui_mem_update(ym); } static void yui_mem_pageup_clicked (GtkToolButton * button, YuiMem * ym) { ym->address -= 2*ym->wLine; yui_mem_update(ym); } static void yui_mem_content_changed( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiMem *ym) { /* dump line has been modified - new content is */ GtkTreeIter iter; gint i = atoi(arg1); gint j,k; gchar *curs; u32 addr = ym->address + i*ym->wLine; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( ym->store ), &iter, arg1 ); /* check the format : wLine*2 hexa digits */ for ( curs = arg2, j=0 ; *curs ; curs++ ) if ( hexaDigitToInt( *curs ) != -1 ) j++; if ( j != ym->wLine * 2 ) return; /* convert */ for ( curs = arg2, k=-1 ; *curs ; curs++ ) { if ( hexaDigitToInt( *curs )!=-1 ) { if ( k==-1 ) k = hexaDigitToInt( *curs ); else { MappedMemoryWriteByte( addr++, 16*k + hexaDigitToInt( *curs ) ); k = -1; } } } yui_window_invalidate(ym->yui); } static void yui_mem_update(YuiMem * ym) { int i, j; GtkTreeIter iter; char address[10]; char dump[30]; gtk_list_store_clear(ym->store); for(i = 0;i < 6;i++) { sprintf(address, "%08x", ym->address + (8 * i)); for(j = 0;j < 8;j++) { sprintf(dump + (j * 3), "%02x ", MappedMemoryReadByte(ym->address + (8 * i) + j)); } gtk_list_store_append(ym->store, &iter); gtk_list_store_set(GTK_LIST_STORE(ym->store ), &iter, 0, address, 1, dump, -1); } sprintf( address, "%08X", ym->address ); gtk_entry_set_text( GTK_ENTRY(gtk_bin_get_child(GTK_BIN(ym->quickCombo))), address ); } yabause-0.9.13.1/src/gtk/yuivdp2.c000644 001750 001750 00000024133 12256006135 020533 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "yuivdp2.h" #include "../vdp2.h" #include "../yabause.h" #include "settings.h" #include "../vdp2debug.h" #include "yuiviewer.h" static void yui_vdp2_sync(GtkAction * action, YuiVdp2 * yv); static const char * yui_vdp2_action_names[] = { NULL, "toggle_nbg0", "toggle_nbg1", "toggle_nbg2", "toggle_nbg3", "toggle_rbg0" }; static void yui_vdp2_class_init (YuiVdp2Class * klass); static void yui_vdp2_init (YuiVdp2 * yfe); static void yui_vdp2_clear(YuiVdp2 * vdp2); static void yui_vdp2_view_cursor_changed(GtkWidget * view, YuiVdp2 * vdp2); static void yui_vdp2_draw(YuiVdp2 * vdp2, u32 * texture, int w, int h); GType yui_vdp2_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiVdp2Class), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_vdp2_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiVdp2), 0, (GInstanceInitFunc) yui_vdp2_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiVdp2", &yfe_info, 0); } return yfe_type; } static void yui_vdp2_class_init (UNUSED YuiVdp2Class * klass) { } static void yui_vdp2_toggle(GtkCellRendererToggle * crt, const gchar * path, YuiVdp2 * yv) { int val; GtkAction * action = NULL; sscanf(path, "%d", &val); if (! yui_vdp2_action_names[val]) return; action = gtk_action_group_get_action(yv->yui->action_group, yui_vdp2_action_names[val]); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), ! gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action))); } static void yui_vdp2_sync(GtkAction * action, YuiVdp2 * yv) { GtkTreeIter iter; const gchar * name; name = gtk_action_get_name(action) + 7; if (!strcmp("nbg0", name)) gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "1"); else if (!strcmp("nbg1", name)) gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "2"); else if (!strcmp("nbg2", name)) gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "3"); else if (!strcmp("nbg3", name)) gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "4"); else if (!strcmp("rbg0", name)) gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(yv->store), &iter, "5"); gtk_list_store_set(yv->store, &iter, 1, gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)), -1); } static void yui_vdp2_init (YuiVdp2 * yv) { GtkWidget * text; GtkWidget * scroll; GtkWidget * box, * box2; GtkWidget * hpane; GtkWidget * view; const char * screens[] = { "General", "NBG0/RBG1", "NBG1", "NBG2", "NBG3", "RBG0" }; unsigned int i; gtk_window_set_title(GTK_WINDOW(yv), "VDP2"); box = gtk_vbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(box), 0); gtk_container_add(GTK_CONTAINER(yv), box); yv->toolbar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(yv->toolbar), GTK_TOOLBAR_ICONS); gtk_box_pack_start(GTK_BOX(box), yv->toolbar, FALSE, FALSE, 0); hpane = gtk_hpaned_new(); gtk_container_add(GTK_CONTAINER(box), hpane); yv->store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_BOOLEAN); view = gtk_tree_view_new_with_model(GTK_TREE_MODEL (yv->store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); { GtkWidget * scroll; GtkCellRenderer *renderer; GtkTreeViewColumn *column; scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("Command", renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); renderer = gtk_cell_renderer_toggle_new(); gtk_cell_renderer_toggle_set_activatable(GTK_CELL_RENDERER_TOGGLE(renderer), TRUE); g_signal_connect(renderer, "toggled", G_CALLBACK(yui_vdp2_toggle), yv); column = gtk_tree_view_column_new_with_attributes("Command", renderer, "active", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); gtk_container_add(GTK_CONTAINER(scroll), view); gtk_paned_pack1(GTK_PANED(hpane), scroll, FALSE, TRUE); } g_signal_connect(view, "cursor-changed", G_CALLBACK(yui_vdp2_view_cursor_changed), yv); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_paned_pack2(GTK_PANED(hpane), scroll, TRUE, TRUE); box2 = gtk_vpaned_new(); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), box2); for(i = 0;i < (sizeof(screens) / sizeof(screens[0]));i++) { GtkTreeIter iter; gtk_list_store_append(yv->store, &iter); gtk_list_store_set(yv->store, &iter, 0, screens[i], -1); } text = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE); { GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), text); gtk_paned_pack1(GTK_PANED(box2), scroll, FALSE, TRUE); } yv->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text)); yv->image = yui_viewer_new(); { GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), yv->image); gtk_paned_pack2(GTK_PANED(box2), scroll, TRUE, TRUE); } gtk_window_set_default_size(GTK_WINDOW(yv), 500, 450); gtk_paned_set_position(GTK_PANED(hpane), 120); g_signal_connect(G_OBJECT(yv), "delete-event", GTK_SIGNAL_FUNC(yui_vdp2_destroy), NULL); } GtkWidget * yui_vdp2_new(YuiWindow * y) { GtkWidget * dialog; YuiVdp2 * yv; int i; dialog = GTK_WIDGET(g_object_new(yui_vdp2_get_type(), NULL)); yv = YUI_VDP2(dialog); yv->yui = y; if (!( yv->yui->state & YUI_IS_INIT )) { yui_window_run(yv->yui); yui_window_pause(yv->yui); } { GtkToolItem * play_button, * pause_button; play_button = gtk_tool_button_new_from_stock("run"); gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "run"), GTK_WIDGET(play_button)); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(play_button), -1); pause_button = gtk_tool_button_new_from_stock("pause"); gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "pause"), GTK_WIDGET(pause_button)); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(pause_button), -1); } yv->paused_handler = g_signal_connect_swapped(yv->yui, "paused", G_CALLBACK(yui_vdp2_update), yv); yv->running_handler = g_signal_connect_swapped(yv->yui, "running", G_CALLBACK(yui_vdp2_clear), yv); for(i = 0;i < (sizeof(yui_vdp2_action_names) / sizeof(yui_vdp2_action_names[0]));i++) { GtkAction * action; if (! yui_vdp2_action_names[i]) continue; action = gtk_action_group_get_action(yv->yui->action_group, yui_vdp2_action_names[i]); yui_vdp2_sync(action, yv); g_signal_connect(action, "toggled", G_CALLBACK(yui_vdp2_sync), yv); } if ((yv->yui->state & (YUI_IS_RUNNING | YUI_IS_INIT)) == YUI_IS_INIT) yui_vdp2_update(yv); gtk_widget_show_all(GTK_WIDGET(yv)); return dialog; } void yui_vdp2_update(YuiVdp2 * vdp2) { gchar nameTemp[VDP2_DEBUG_STRING_SIZE]; gboolean isscrenabled; yui_viewer_clear(YUI_VIEWER(vdp2->image)); switch(vdp2->cursor) { case 0: Vdp2DebugStatsGeneral(nameTemp, &isscrenabled); break; case 1: Vdp2DebugStatsNBG0(nameTemp, &isscrenabled); break; case 2: Vdp2DebugStatsNBG1(nameTemp, &isscrenabled); break; case 3: Vdp2DebugStatsNBG2(nameTemp, &isscrenabled); break; case 4: Vdp2DebugStatsNBG3(nameTemp, &isscrenabled); break; case 5: Vdp2DebugStatsRBG0(nameTemp, &isscrenabled); break; } if (vdp2->cursor > 0) { u32 * texture; int w, h; texture = Vdp2DebugTexture(vdp2->cursor - 1, &w, &h); yui_vdp2_draw(vdp2, texture, w, h); } if (isscrenabled) { gtk_text_buffer_set_text(vdp2->buffer, nameTemp, -1); } else { gtk_text_buffer_set_text(vdp2->buffer, "", -1); } } void yui_vdp2_destroy(YuiVdp2 * vdp2) { g_signal_handler_disconnect(vdp2->yui, vdp2->paused_handler); g_signal_handler_disconnect(vdp2->yui, vdp2->running_handler); gtk_widget_destroy(GTK_WIDGET(vdp2)); } static void yui_vdp2_clear(YuiVdp2 * vdp2) { gtk_text_buffer_set_text(vdp2->buffer, "", -1); } void yui_vdp2_view_cursor_changed(GtkWidget * view, YuiVdp2 * vdp2) { GtkTreePath * path; gchar * strpath; int i; gtk_tree_view_get_cursor(GTK_TREE_VIEW(view), &path, NULL); if (path) { strpath = gtk_tree_path_to_string(path); sscanf(strpath, "%i", &i); vdp2->cursor = i; yui_vdp2_update(vdp2); g_free(strpath); gtk_tree_path_free(path); } } static void yui_vdp2_draw(YuiVdp2 * vdp2, u32 * texture, int w, int h) { GdkPixbuf * pixbuf; int rowstride; if ((texture != NULL) && (w > 0) && (h > 0)) { rowstride = w * 4; rowstride += (rowstride % 4)? (4 - (rowstride % 4)): 0; pixbuf = gdk_pixbuf_new_from_data((const guchar *) texture, GDK_COLORSPACE_RGB, TRUE, 8, w, h, rowstride, yui_texture_free, NULL); yui_viewer_draw_pixbuf(YUI_VIEWER(vdp2->image), pixbuf, w, h); g_object_unref(pixbuf); } } yabause-0.9.13.1/src/gtk/yuifileentry.h000644 001750 001750 00000004075 12256006140 021665 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_FILE_ENTRY_H #define YUI_FILE_ENTRY_H #include #include #include G_BEGIN_DECLS #define YUI_FILE_ENTRY_TYPE (yui_file_entry_get_type ()) #define YUI_FILE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_FILE_ENTRY_TYPE, YuiFileEntry)) #define YUI_FILE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_FILE_ENTRY_TYPE, YuiFileEntryClass)) #define IS_YUI_FILE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_FILE_ENTRY_TYPE)) #define IS_YUI_FILE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_FILE_ENTRY_TYPE)) #define YUI_FILE_ENTRY_BROWSE 1 #define YUI_FILE_ENTRY_DIRECTORY 2 typedef struct _YuiFileEntry YuiFileEntry; typedef struct _YuiFileEntryClass YuiFileEntryClass; struct _YuiFileEntry { GtkHBox hbox; GtkWidget * entry; GtkWidget * button; GKeyFile * keyfile; gchar * group; gchar * key; int flags; }; struct _YuiFileEntryClass { GtkHBoxClass parent_class; void (* yui_file_entry) (YuiFileEntry * yfe); }; GType yui_file_entry_get_type (void); GtkWidget* yui_file_entry_new (GKeyFile *, const gchar *, const gchar *, gint flags, const gchar * label); G_END_DECLS #endif /* YUI_FILE_ENTRY_H */ yabause-0.9.13.1/src/gtk/yuicheckbutton.h000644 001750 001750 00000004172 12256006135 022177 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_CHECK_BUTTON_H #define YUI_CHECK_BUTTON_H #include #include #include G_BEGIN_DECLS #define YUI_CHECK_BUTTON_TYPE (yui_check_button_get_type ()) #define YUI_CHECK_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_CHECK_BUTTON_TYPE, YuiCheckButton)) #define YUI_CHECK_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_CHECK_BUTTON_TYPE, YuiCheckButtonClass)) #define IS_YUI_CHECK_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_CHECK_BUTTON_TYPE)) #define IS_YUI_CHECK_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_CHECK_BUTTON_TYPE)) typedef struct _YuiCheckButton YuiCheckButton; typedef struct _YuiCheckButtonClass YuiCheckButtonClass; struct _YuiCheckButton { GtkCheckButton button; GKeyFile * keyfile; gchar * group; gchar * key; }; struct _YuiCheckButtonClass { GtkCheckButtonClass parent_class; void (* yui_check_button_change) (YuiCheckButton * ycb); }; GType yui_check_button_get_type (void); GtkWidget * yui_check_button_new (const gchar * label, GKeyFile * keyfile, const gchar * group, const gchar * key); gboolean yui_check_button_get_active (YuiCheckButton * ycb); G_END_DECLS #endif /* YUI_CHECK_BUTTON_H */ yabause-0.9.13.1/src/gtk/yuivdp1.h000644 001750 001750 00000004027 12256006135 020537 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_VDP1_H #define YUI_VDP1_H #include #include "yuiwindow.h" #include "../core.h" G_BEGIN_DECLS #define YUI_VDP1_TYPE (yui_vdp1_get_type ()) #define YUI_VDP1(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_VDP1_TYPE, YuiVdp1)) #define YUI_VDP1_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_VDP1_TYPE, YuiVdp1Class)) #define IS_YUI_VDP1(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_VDP1_TYPE)) #define IS_YUI_VDP1_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_VDP1_TYPE)) #define MAX_VDP1_COMMAND 4000 typedef struct _YuiVdp1 YuiVdp1; typedef struct _YuiVdp1Class YuiVdp1Class; struct _YuiVdp1 { GtkWindow dialog; GtkWidget * image; GtkWidget * toolbar; GtkListStore * store; GtkTextBuffer * buffer; gint cursor; u32 * texture; int w; int h; gulong paused_handler; gulong running_handler; YuiWindow * yui; }; struct _YuiVdp1Class { GtkWindowClass parent_class; void (* yui_vdp1) (YuiVdp1 * yv); }; GType yui_vdp1_get_type (void); GtkWidget * yui_vdp1_new (YuiWindow * yui); void yui_vdp1_fill (YuiVdp1 * vdp1); void yui_vdp1_update (YuiVdp1 * vdp1); void yui_vdp1_destroy (YuiVdp1 * vdp1); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuiinputentry.c000644 001750 001750 00000015425 12256006135 022105 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "yuiinputentry.h" #include "settings.h" static void yui_input_entry_class_init (YuiInputEntryClass *klass); static void yui_input_entry_init (YuiInputEntry *yie); gboolean yui_input_entry_keypress(GtkWidget * widget, GdkEventKey * event, gpointer name); gboolean yui_input_entry_focus_in(GtkWidget * widget, GdkEventFocus * event, gpointer user_data); gboolean yui_input_entry_focus_out(GtkWidget * widget, GdkEventFocus * event, gpointer user_data); GType yui_input_entry_get_type (void) { static GType yie_type = 0; if (!yie_type) { static const GTypeInfo yie_info = { sizeof (YuiInputEntryClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_input_entry_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiInputEntry), 0, (GInstanceInitFunc) yui_input_entry_init, NULL, }; yie_type = g_type_register_static (GTK_TYPE_TABLE, "YuiInputEntry", &yie_info, 0); } return yie_type; } #define PROP_KEYFILE 1 #define PROP_GROUP 2 static void yui_input_entry_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { switch(property_id) { case PROP_KEYFILE: YUI_INPUT_ENTRY(object)->keyfile = g_value_get_pointer(value); break; case PROP_GROUP: YUI_INPUT_ENTRY(object)->group = g_value_get_pointer(value); break; } } static void yui_input_entry_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { } static void yui_input_entry_class_init (YuiInputEntryClass *klass) { GParamSpec * param; G_OBJECT_CLASS(klass)->set_property = yui_input_entry_set_property; G_OBJECT_CLASS(klass)->get_property = yui_input_entry_get_property; param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); } static void yui_input_entry_init(YuiInputEntry *yie) { gtk_container_set_border_width(GTK_CONTAINER(yie), 0); gtk_table_set_row_spacings(GTK_TABLE(yie), 10); gtk_table_set_col_spacings(GTK_TABLE(yie), 10); } GtkWidget* yui_input_entry_new(GKeyFile * keyfile, const gchar * group, const gchar * keys[]) { GtkWidget * widget; GtkWidget * label; GtkWidget * entry; guint keyName; int row = 0; widget = GTK_WIDGET(g_object_new(yui_input_entry_get_type(), "key-file", keyfile, "group", group, NULL)); while(keys[row]) { char tmp[100]; gtk_table_resize(GTK_TABLE(widget), row + 1, 2); label = gtk_label_new(keys[row]); gtk_table_attach(GTK_TABLE(widget), label, 0, 1, row , row + 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); entry = gtk_entry_new (); gtk_entry_set_width_chars(GTK_ENTRY(entry), 10); sprintf(tmp, "%s.%s.1", group, keys[row]); keyName = g_key_file_get_integer(keyfile, PERCore->Name, tmp, 0); if (keyName > 0) { char buffer[50]; PERCore->KeyName(keyName, buffer, 50); gtk_entry_set_text(GTK_ENTRY(entry), buffer); } if (PERCore) { //if (PERCore->canScan) g_signal_connect(entry, "focus-in-event", G_CALLBACK(yui_input_entry_focus_in), (gpointer) keys[row]); g_signal_connect(entry, "focus-out-event", G_CALLBACK(yui_input_entry_focus_out), NULL); //else g_signal_connect(entry, "key-press-event", G_CALLBACK(yui_input_entry_keypress), (gpointer) keys[row]); } else { gtk_widget_set_sensitive(entry, FALSE); } gtk_table_attach(GTK_TABLE(widget), entry, 1, 2, row, row + 1, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 0); row++; } return widget; } gboolean yui_input_entry_keypress(GtkWidget * widget, GdkEventKey * event, gpointer name) { char tmp[100]; if (PERCore->canScan) return FALSE; PERCore->KeyName(event->keyval, tmp, 100); gtk_entry_set_text(GTK_ENTRY(widget), tmp); sprintf(tmp, "%s.%s.1", YUI_INPUT_ENTRY(gtk_widget_get_parent(widget))->group, (char *)name); g_key_file_set_integer(YUI_INPUT_ENTRY(gtk_widget_get_parent(widget))->keyfile, PERCore->Name, tmp, event->keyval); return TRUE; } gboolean is_watching = FALSE; GtkEntry * entry_hack = NULL; static gboolean watch_joy(gpointer name) { u32 i; if (! is_watching) return TRUE; if (! PERCore->canScan) { is_watching = FALSE; return TRUE; } i = PERCore->Scan(PERSF_KEY | PERSF_BUTTON | PERSF_HAT); if (i == 0) { return TRUE; } else { char tmp[100]; sprintf(tmp, "Pad.%s.1", (char *)name); // should be group.name g_key_file_set_integer(keyfile, PERCore->Name, tmp, i); PERCore->KeyName(i, tmp, 100); gtk_entry_set_text(entry_hack, tmp); is_watching = FALSE; return FALSE; } } gboolean yui_input_entry_focus_in(GtkWidget * widget, GdkEventFocus * event, gpointer name) { if (! PERCore->canScan) return TRUE; PERCore->Flush(); entry_hack = GTK_ENTRY(widget); if (!is_watching) { g_timeout_add(100, watch_joy, name); is_watching = TRUE; } return FALSE; } gboolean yui_input_entry_focus_out(GtkWidget * widget, GdkEventFocus * event, gpointer name) { is_watching = FALSE; return FALSE; } void yui_input_entry_update(YuiInputEntry * yie) { GList * wlist = gtk_container_get_children(GTK_CONTAINER(yie)); u32 key; GtkEntry * entry = NULL; char tmp[100]; while(wlist) { if (strcmp(gtk_widget_get_name(wlist->data), "GtkEntry") == 0) { entry = wlist->data; } if (strcmp(gtk_widget_get_name(wlist->data), "GtkLabel") == 0) { sprintf(tmp, "%s.%s.1", yie->group, gtk_label_get_text(wlist->data)); key = g_key_file_get_integer(yie->keyfile, PERCore->Name, tmp, 0); if (key > 0) { PERCore->KeyName(key, tmp, 100); gtk_entry_set_text(entry, tmp); } else { gtk_entry_set_text(entry, ""); } } wlist = g_list_next(wlist); } } yabause-0.9.13.1/src/gtk/yuivdp1.c000644 001750 001750 00000021362 12256006135 020533 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006-2007 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "yuiviewer.h" #include "yuivdp1.h" #include "../vdp1.h" #include "../yabause.h" #include "settings.h" static void yui_vdp1_class_init (YuiVdp1Class * klass); static void yui_vdp1_init (YuiVdp1 * yfe); static void yui_vdp1_view_cursor_changed(GtkWidget * view, YuiVdp1 * vdp1); static void yui_vdp1_clear(YuiVdp1 * vdp1); static void yui_vdp1_draw(YuiVdp1 * vdp1); GType yui_vdp1_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiVdp1Class), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_vdp1_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiVdp1), 0, (GInstanceInitFunc) yui_vdp1_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiVdp1", &yfe_info, 0); } return yfe_type; } static void yui_vdp1_class_init (UNUSED YuiVdp1Class * klass) { } static void yui_vdp1_init (YuiVdp1 * yv) { GtkWidget * hbox, * vbox, * vbox2, * view; gtk_window_set_title(GTK_WINDOW(yv), "VDP1"); vbox = gtk_vbox_new(FALSE, 0); gtk_container_set_border_width(GTK_CONTAINER(vbox), 0); gtk_container_add(GTK_CONTAINER(yv), vbox); yv->toolbar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(yv->toolbar), GTK_TOOLBAR_ICONS); gtk_box_pack_start(GTK_BOX(vbox), yv->toolbar, FALSE, FALSE, 0); hbox = gtk_hpaned_new(); gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4); yv->store = gtk_list_store_new(2, G_TYPE_STRING, GDK_TYPE_PIXBUF); view = gtk_tree_view_new_with_model(GTK_TREE_MODEL (yv->store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); { GtkWidget * scroll; GtkCellRenderer *renderer; GtkCellRenderer *icon; GtkTreeViewColumn *column; scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("Command", renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); icon = gtk_cell_renderer_pixbuf_new(); g_object_set(icon, "xalign", 0, NULL); column = gtk_tree_view_column_new_with_attributes("Icon", icon, "pixbuf", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (view), column); gtk_container_add(GTK_CONTAINER(scroll), view); gtk_paned_pack1(GTK_PANED(hbox), scroll, FALSE, TRUE); } g_signal_connect(view, "cursor-changed", G_CALLBACK(yui_vdp1_view_cursor_changed), yv); g_signal_connect(G_OBJECT(yv), "delete-event", GTK_SIGNAL_FUNC(yui_vdp1_destroy), NULL); vbox2 = gtk_vpaned_new(); gtk_paned_pack2(GTK_PANED(hbox), vbox2, TRUE, TRUE); { GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); GtkWidget * text = gtk_text_view_new(); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text), FALSE); yv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text)); gtk_container_add(GTK_CONTAINER(scroll), text); gtk_paned_pack1(GTK_PANED(vbox2), scroll, FALSE, TRUE); } yv->image = yui_viewer_new(); { GtkWidget * scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), yv->image); gtk_paned_pack2(GTK_PANED(vbox2), scroll, TRUE, TRUE); } yv->cursor = 0; yv->texture = NULL; gtk_window_set_default_size(GTK_WINDOW(yv), 500, 450); gtk_paned_set_position(GTK_PANED(hbox), 250); gtk_paned_set_position(GTK_PANED(vbox2), 200); } GtkWidget * yui_vdp1_new(YuiWindow * y) { GtkWidget * dialog; YuiVdp1 * yv; dialog = GTK_WIDGET(g_object_new(yui_vdp1_get_type(), NULL)); yv = YUI_VDP1(dialog); yv->yui = y; if (!( yv->yui->state & YUI_IS_INIT )) { yui_window_run(yv->yui); yui_window_pause(yv->yui); } { GtkToolItem * play_button, * pause_button; play_button = gtk_tool_button_new_from_stock("run"); gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "run"), GTK_WIDGET(play_button)); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(play_button), -1); pause_button = gtk_tool_button_new_from_stock("pause"); gtk_action_connect_proxy(gtk_action_group_get_action(yv->yui->action_group, "pause"), GTK_WIDGET(pause_button)); gtk_toolbar_insert(GTK_TOOLBAR(yv->toolbar), GTK_TOOL_ITEM(pause_button), -1); } yv->paused_handler = g_signal_connect_swapped(yv->yui, "paused", G_CALLBACK(yui_vdp1_update), yv); yv->running_handler = g_signal_connect_swapped(yv->yui, "running", G_CALLBACK(yui_vdp1_clear), yv); if ((yv->yui->state & (YUI_IS_RUNNING | YUI_IS_INIT)) == YUI_IS_INIT) yui_vdp1_update(yv); gtk_widget_show_all(GTK_WIDGET(yv)); return dialog; } void yui_vdp1_fill(YuiVdp1 * vdp1) { gint j; gchar * string; gchar nameTemp[1024]; GtkTreeIter iter; yui_vdp1_clear(vdp1); j = 0; string = Vdp1DebugGetCommandNumberName(j); while(string && (j < MAX_VDP1_COMMAND)) { gtk_list_store_append(vdp1->store, &iter); gtk_list_store_set(vdp1->store, &iter, 0, string, -1); { u32 * icontext; int wtext, htext; int rowstride; GdkPixbuf * pixbuftext, * resized; float ratio; icontext = Vdp1DebugTexture(j, &wtext, &htext); if ((icontext != NULL) && (wtext > 0) && (htext > 0)) { rowstride = wtext * 4; rowstride += (rowstride % 4)? (4 - (rowstride % 4)): 0; pixbuftext = gdk_pixbuf_new_from_data((const guchar *) icontext, GDK_COLORSPACE_RGB, TRUE, 8, wtext, htext, rowstride, yui_texture_free, NULL); ratio = (float) 16 / htext; if (htext > 16) { resized = gdk_pixbuf_scale_simple(pixbuftext, wtext * ratio, 16, GDK_INTERP_BILINEAR); } else { resized = gdk_pixbuf_scale_simple(pixbuftext, wtext, htext, GDK_INTERP_BILINEAR); } gtk_list_store_set(vdp1->store, &iter, 1, resized, -1); g_object_unref(pixbuftext); g_object_unref(resized); } } j++; string = Vdp1DebugGetCommandNumberName(j); } Vdp1DebugCommand(vdp1->cursor, nameTemp); gtk_text_buffer_set_text(vdp1->buffer, g_strstrip(nameTemp), -1); vdp1->texture = Vdp1DebugTexture(vdp1->cursor, &vdp1->w, &vdp1->h); yui_vdp1_draw(vdp1); } static void yui_vdp1_view_cursor_changed(GtkWidget * view, YuiVdp1 * vdp1) { GtkTreePath * path; gchar * strpath; int i; gtk_tree_view_get_cursor(GTK_TREE_VIEW(view), &path, NULL); if (path) { gchar nameTemp[1024]; yui_viewer_clear(YUI_VIEWER(vdp1->image)); strpath = gtk_tree_path_to_string(path); sscanf(strpath, "%i", &i); vdp1->cursor = i; Vdp1DebugCommand(i, nameTemp); gtk_text_buffer_set_text(vdp1->buffer, g_strstrip(nameTemp), -1); vdp1->texture = Vdp1DebugTexture(i, &vdp1->w, &vdp1->h); yui_vdp1_draw(vdp1); g_free(strpath); gtk_tree_path_free(path); } } void yui_vdp1_update(YuiVdp1 * vdp1) { gint i; for(i = 0 ; i < MAX_VDP1_COMMAND ; i++ ) if ( !Vdp1DebugGetCommandNumberName(i)) break; vdp1->cursor = 0; yui_vdp1_fill(vdp1); } void yui_vdp1_destroy(YuiVdp1 * vdp1) { g_signal_handler_disconnect(vdp1->yui, vdp1->running_handler); g_signal_handler_disconnect(vdp1->yui, vdp1->paused_handler); gtk_widget_destroy(GTK_WIDGET(vdp1)); } static void yui_vdp1_clear(YuiVdp1 * vdp1) { gtk_list_store_clear(vdp1->store); gtk_text_buffer_set_text(vdp1->buffer, "", -1); yui_viewer_clear(YUI_VIEWER(vdp1->image)); } static void yui_vdp1_draw(YuiVdp1 * vdp1) { GdkPixbuf * pixbuf; int rowstride; if ((vdp1->texture != NULL) && (vdp1->w > 0) && (vdp1->h > 0)) { rowstride = vdp1->w * 4; rowstride += (rowstride % 4)? (4 - (rowstride % 4)): 0; pixbuf = gdk_pixbuf_new_from_data((const guchar *) vdp1->texture, GDK_COLORSPACE_RGB, TRUE, 8, vdp1->w, vdp1->h, rowstride, yui_texture_free, NULL); yui_viewer_draw_pixbuf(YUI_VIEWER(vdp1->image), pixbuf, vdp1->w, vdp1->h); g_object_unref(pixbuf); } } yabause-0.9.13.1/src/gtk/pergtk.c000644 001750 001750 00000004110 12256006140 020412 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Guillaume Duhamel Copyright 2005-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "pergtk.h" #include #include "../yabause.h" #include "../vdp2.h" #include "../yui.h" int PERGTKInit(void); void PERGTKDeInit(void); int PERGTKHandleEvents(void); u32 PERGTKScan(u32 flags); void PERGTKFlush(void); void PERGTKKeyName(u32 key, char * name, int size); PerInterface_struct PERGTK = { PERCORE_GTK, "GTK Input Interface", PERGTKInit, PERGTKDeInit, PERGTKHandleEvents, PERGTKScan, 0, PERGTKFlush, PERGTKKeyName }; ////////////////////////////////////////////////////////////////////////////// int PERGTKInit(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// void PERGTKDeInit(void) { } ////////////////////////////////////////////////////////////////////////////// int PERGTKHandleEvents(void) { YabauseExec(); return 0; } ////////////////////////////////////////////////////////////////////////////// u32 PERGTKScan(u32 flags) { g_print("this is wrong, the gtk peripheral can't scan\n"); return 1; } ////////////////////////////////////////////////////////////////////////////// void PERGTKFlush(void) { } ////////////////////////////////////////////////////////////////////////////// void PERGTKKeyName(u32 key, char * name, int size) { g_strlcpy(name, gdk_keyval_name(key), size); } yabause-0.9.13.1/src/gtk/yuirange.h000644 001750 001750 00000004063 12256006140 020755 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_RANGE_H #define YUI_RANGE_H #include #include #include G_BEGIN_DECLS #define YUI_RANGE_TYPE (yui_range_get_type ()) #define YUI_RANGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_RANGE_TYPE, YuiRange)) #define YUI_RANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_RANGE_TYPE, YuiRangeClass)) #define IS_YUI_RANGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_RANGE_TYPE)) #define IS_YUI_RANGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_RANGE_TYPE)) typedef struct _YuiRangeItem YuiRangeItem; typedef struct _YuiRange YuiRange; typedef struct _YuiRangeClass YuiRangeClass; struct _YuiRangeItem { const gchar * value; const gchar * name; }; struct _YuiRange { GtkHBox hbox; GtkWidget * combo; GKeyFile * keyfile; gchar * group; gchar * key; YuiRangeItem * items; }; struct _YuiRangeClass { GtkHBoxClass parent_class; void (* yui_range_change) (YuiRange * yfe); }; GType yui_range_get_type (void); GtkWidget * yui_range_new (GKeyFile * keyfile, const gchar * group, const gchar * key, YuiRangeItem * items); gint yui_range_get_active (YuiRange * range); G_END_DECLS #endif /* YUI_RANGE_H */ yabause-0.9.13.1/src/gtk/gtk-compat.c000644 001750 001750 00000002120 12256006140 021163 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gtk-compat.h" #if !GLIB_CHECK_VERSION(2, 8, 0) gboolean g_file_set_contents(const gchar * filename, const gchar * contents, gssize len, GError ** error) { FILE * file = fopen(filename, "w"); if (len == -1) fprintf(file, "%s", contents); else fwrite(contents, 1, len, file); fclose(file); } #endif yabause-0.9.13.1/src/gtk/yuim68k.c000644 001750 001750 00000032314 12256006140 020441 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "yuim68k.h" #include "../m68kd.h" #include "../yabause.h" #include "settings.h" static void yui_m68k_class_init (YuiM68kClass * klass); static void yui_m68k_init (YuiM68k * yfe); static void yui_m68k_clear(YuiM68k * m68k); static void yui_m68k_editedReg( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiM68k *m68k); static void yui_m68k_editedBp( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiM68k *m68k); static void yui_m68k_step( GtkWidget* widget, YuiM68k * m68k ); static void yui_m68k_breakpoint_handler(u32 addr); static YuiM68k *yui_m68k; static YuiWindow * yui; GType yui_m68k_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiM68kClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_m68k_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiM68k), 0, (GInstanceInitFunc) yui_m68k_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiM68k", &yfe_info, 0); } return yfe_type; } static void yui_m68k_class_init (UNUSED YuiM68kClass * klass) { } static void yui_m68k_init (YuiM68k * m68k) { gtk_window_set_title(GTK_WINDOW(m68k), "M68K"); gtk_window_set_resizable( GTK_WINDOW(m68k), FALSE ); m68k->vbox = gtk_vbox_new(FALSE, 2); gtk_container_set_border_width( GTK_CONTAINER( m68k->vbox ),4 ); gtk_container_add (GTK_CONTAINER (m68k), m68k->vbox); m68k->hboxmain = gtk_hbox_new(FALSE, 2); gtk_container_set_border_width( GTK_CONTAINER( m68k->hboxmain ),4 ); gtk_box_pack_start( GTK_BOX( m68k->vbox ), m68k->hboxmain, FALSE, FALSE, 4 ); m68k->hbox = gtk_hbutton_box_new(); gtk_container_set_border_width( GTK_CONTAINER( m68k->hbox ),4 ); gtk_box_pack_start( GTK_BOX( m68k->vbox ), m68k->hbox, FALSE, FALSE, 4 ); m68k->vboxmain = gtk_vbox_new(FALSE, 2); gtk_container_set_border_width( GTK_CONTAINER( m68k->vboxmain ),4 ); gtk_box_pack_start( GTK_BOX( m68k->hboxmain ), m68k->vboxmain, FALSE, FALSE, 4 ); /* unassembler frame */ m68k->uFrame = gtk_frame_new("Disassembled code"); gtk_box_pack_start( GTK_BOX( m68k->vboxmain ), m68k->uFrame, FALSE, FALSE, 4 ); m68k->uLabel = gtk_label_new(NULL); gtk_container_add (GTK_CONTAINER (m68k->uFrame), m68k->uLabel ); /* Register list */ m68k->regListStore = gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING); m68k->regList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(m68k->regListStore) ); m68k->regListRenderer1 = gtk_cell_renderer_text_new(); m68k->regListRenderer2 = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(m68k->regListRenderer2), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); m68k->regListColumn1 = gtk_tree_view_column_new_with_attributes("Register", m68k->regListRenderer1, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(m68k->regList), m68k->regListColumn1); m68k->regListColumn2 = gtk_tree_view_column_new_with_attributes("Value", m68k->regListRenderer2, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(m68k->regList), m68k->regListColumn2); gtk_box_pack_start( GTK_BOX( m68k->hboxmain ), m68k->regList, FALSE, FALSE, 4 ); g_signal_connect(G_OBJECT(m68k->regListRenderer2), "edited", GTK_SIGNAL_FUNC(yui_m68k_editedReg), m68k ); /* breakpoint list */ m68k->bpListStore = gtk_list_store_new(1,G_TYPE_STRING); m68k->bpList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(m68k->bpListStore) ); m68k->bpListRenderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(m68k->bpListRenderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); m68k->bpListColumn = gtk_tree_view_column_new_with_attributes("Breakpoints", m68k->bpListRenderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(m68k->bpList), m68k->bpListColumn); gtk_box_pack_start( GTK_BOX( m68k->hboxmain ), m68k->bpList, FALSE, FALSE, 4 ); g_signal_connect(G_OBJECT(m68k->bpListRenderer), "edited", GTK_SIGNAL_FUNC(yui_m68k_editedBp), m68k ); g_signal_connect(G_OBJECT(m68k), "delete-event", GTK_SIGNAL_FUNC(yui_m68k_destroy), NULL); } GtkWidget * yui_m68k_new(YuiWindow * y) { GtkWidget * dialog; GClosure *closureF7; GtkAccelGroup *accelGroup; const m68kcodebreakpoint_struct *cbp; gint i; yui = y; if ( yui_m68k ) return GTK_WIDGET(yui_m68k); dialog = GTK_WIDGET(g_object_new(yui_m68k_get_type(), NULL)); yui_m68k = YUI_M68K(dialog); if (!( yui->state & YUI_IS_INIT )) { yui_window_run(yui); yui_window_pause(yui); } M68KSetBreakpointCallBack(&yui_m68k_breakpoint_handler); for (i = 0; i < 23 ; i++) { GtkTreeIter iter; gtk_list_store_append( GTK_LIST_STORE( yui_m68k->regListStore ), &iter ); } cbp = M68KGetBreakpointList(); for (i = 0; i < MAX_BREAKPOINTS ; i++) { GtkTreeIter iter; yui_m68k->cbp[i] = cbp[i].addr; gtk_list_store_append( GTK_LIST_STORE( yui_m68k->bpListStore ), &iter ); if (cbp[i].addr != 0xFFFFFFFF) { gchar tempstr[20]; sprintf(tempstr, "%08X", (int)cbp[i].addr); gtk_list_store_set( GTK_LIST_STORE( yui_m68k->bpListStore ), &iter, 0, tempstr, -1 ); } else gtk_list_store_set( GTK_LIST_STORE( yui_m68k->bpListStore ), &iter, 0, "", -1 ); } { GtkWidget * but2, * but3, * but4; yui_m68k->buttonStep = gtk_button_new_with_label( "Step [F7]" ); gtk_box_pack_start( GTK_BOX( yui_m68k->hbox ), yui_m68k->buttonStep, FALSE, FALSE, 2 ); g_signal_connect( yui_m68k->buttonStep, "clicked", G_CALLBACK(yui_m68k_step), yui_m68k ); but2 = gtk_button_new(); gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "run"), but2); gtk_box_pack_start(GTK_BOX(yui_m68k->hbox), but2, FALSE, FALSE, 2); but3 = gtk_button_new(); gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "pause"), but3); gtk_box_pack_start(GTK_BOX(yui_m68k->hbox), but3, FALSE, FALSE, 2); but4 = gtk_button_new_from_stock("gtk-close"); g_signal_connect_swapped(but4, "clicked", G_CALLBACK(yui_m68k_destroy), dialog); gtk_box_pack_start(GTK_BOX(yui_m68k->hbox), but4, FALSE, FALSE, 2); } yui_m68k->paused_handler = g_signal_connect_swapped(yui, "paused", G_CALLBACK(yui_m68k_update), yui_m68k); yui_m68k->running_handler = g_signal_connect_swapped(yui, "running", G_CALLBACK(yui_m68k_clear), yui_m68k); accelGroup = gtk_accel_group_new (); closureF7 = g_cclosure_new (G_CALLBACK (yui_m68k_step), yui_m68k, NULL); gtk_accel_group_connect( accelGroup, GDK_F7, 0, 0, closureF7 ); gtk_window_add_accel_group( GTK_WINDOW( dialog ), accelGroup ); yui_m68k_update(yui_m68k); if ( yui->state & YUI_IS_RUNNING ) yui_m68k_clear(yui_m68k); gtk_widget_show_all(GTK_WIDGET(yui_m68k)); return dialog; } static void yui_m68k_update_reglist( YuiM68k *m68k, m68kregs_struct *regs) { /* refresh the registery list */ GtkTreeIter iter; char regstr[32]; char valuestr[32]; int i; for ( i = 0 ; i < 8 ; i++ ) { if ( i==0 ) gtk_tree_model_get_iter_first( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); else gtk_tree_model_iter_next( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); sprintf(regstr, "D%d", i ); sprintf(valuestr, "%08x", (int)regs->D[i]); gtk_list_store_set( GTK_LIST_STORE( yui_m68k->regListStore ), &iter, 0, regstr, 1, valuestr, -1 ); } for ( i = 0 ; i < 8 ; i++ ) { gtk_tree_model_iter_next( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); sprintf(regstr, "A%d", i ); sprintf(valuestr, "%08x", (int)regs->A[i]); gtk_list_store_set( GTK_LIST_STORE( yui_m68k->regListStore ), &iter, 0, regstr, 1, valuestr, -1 ); } gtk_tree_model_iter_next( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); sprintf(valuestr, "%08x", (int)regs->SR); gtk_list_store_set( GTK_LIST_STORE( yui_m68k->regListStore ), &iter, 0, "SR", 1, valuestr, -1 ); gtk_tree_model_iter_next( GTK_TREE_MODEL( yui_m68k->regListStore ), &iter ); sprintf(valuestr, "%08x", (int)regs->PC); gtk_list_store_set( GTK_LIST_STORE( yui_m68k->regListStore ), &iter, 0, "PC", 1, valuestr, -1 ); } static void m68ksetRegister( YuiM68k *m68k, int nReg, u32 value ) { /* set register number to value in proc */ m68kregs_struct m68kregs; M68KGetRegisters(&m68kregs); if ( nReg < 8 ) m68kregs.D[nReg] = value; else if ( nReg < 16 ) m68kregs.A[nReg-8] = value; if ( nReg == 16 ) m68kregs.SR = value; if ( nReg == 17 ) m68kregs.PC = value; M68KSetRegisters(&m68kregs); } static void yui_m68k_update_codelist( YuiM68k *m68k, u32 addr) { /* refrem68k the assembler view. points the line to be highlighted. */ int i; static char tagPC[] = ""; static char tagEnd[] = "\n"; char buf[64*24+40]; char *curs = buf; char lineBuf[64]; u8 bOnPC = 0; u32 offset; if ( addr - m68k->lastCode >= 22 ) offset = addr; else offset = m68k->lastCode; m68k->lastCode = offset; for (i=0; i < 24; i++) { if ( offset == addr ) { bOnPC = 1; strcpy( curs, tagPC ); curs += strlen(tagPC); } offset = M68KDisasm(offset, lineBuf); strcpy( curs, lineBuf ); curs += strlen(lineBuf); if ( bOnPC ) { bOnPC = 0; strcpy( curs, tagEnd ); curs += strlen(tagEnd); } else { strcpy( curs, "\n" ); curs += 1;} } *curs = 0; gtk_label_set_markup( GTK_LABEL(m68k->uLabel), buf ); } static void yui_m68k_step( GtkWidget* widget, YuiM68k * m68k ) { M68KStep(); yui_window_invalidate( yui ); /* update all dialogs, including us */ } static void yui_m68k_editedReg( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiM68k *m68k) { /* registry number value has been set to */ GtkTreeIter iter; char bptext[10]; char *endptr; int i = atoi(arg1); u32 addr; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( m68k->regListStore ), &iter, arg1 ); addr = strtoul(arg2, &endptr, 16 ); if ( endptr - arg2 == strlen(arg2) ) { sprintf(bptext, "%08X", (int)addr); m68ksetRegister( m68k, i, addr ); gtk_list_store_set( GTK_LIST_STORE( m68k->regListStore ), &iter, 1, bptext, -1 ); } yui_window_invalidate( yui ); } static void yui_m68k_editedBp( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiM68k *m68k) { /* breakpoint has been set to address */ GtkTreeIter iter; char bptext[10]; char *endptr; int i = atoi(arg1); u32 addr; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( m68k->bpListStore ), &iter, arg1 ); addr = strtoul(arg2, &endptr, 16 ); if ((endptr - arg2 < strlen(arg2)) || (!addr)) addr = 0xFFFFFFFF; if ( m68k->cbp[i] != 0xFFFFFFFF) M68KDelCodeBreakpoint(m68k->cbp[i]); m68k->cbp[i] = 0xFFFFFFFF; if ((addr!=0xFFFFFFFF)&&(M68KAddCodeBreakpoint(addr) == 0)) { sprintf(bptext, "%08X", (int)addr); m68k->cbp[i] = addr; } else strcpy(bptext,""); gtk_list_store_set( GTK_LIST_STORE( m68k->bpListStore ), &iter, 0, bptext, -1 ); } static void debugPauseLoop(void) { /* secondary gtk event loop for the "breakpoint pause" state */ while ( !(yui->state & YUI_IS_RUNNING) ) if ( gtk_main_iteration() ) return; } static void yui_m68k_breakpoint_handler (u32 addr) { yui_window_pause(yui); { m68kregs_struct regs; YuiM68k* m68k = YUI_M68K(yui_m68k_new( yui )); M68KGetRegisters(®s); yui_m68k_update_codelist(m68k,regs.PC); yui_m68k_update_reglist(m68k,®s); } debugPauseLoop(); /* execution is suspended inside a normal cycle - enter secondary gtk loop */ } void yui_m68k_update(YuiM68k * m68k) { m68kregs_struct m68kregs; M68KGetRegisters(&m68kregs); yui_m68k_update_codelist(m68k,m68kregs.PC); yui_m68k_update_reglist(m68k, &m68kregs); gtk_widget_set_sensitive(m68k->uLabel, TRUE); gtk_widget_set_sensitive(m68k->bpList, TRUE); gtk_widget_set_sensitive(m68k->regList, TRUE); gtk_widget_set_sensitive(m68k->buttonStep, TRUE); } void yui_m68k_destroy(YuiM68k * m68k) { g_signal_handler_disconnect(yui, m68k->running_handler); g_signal_handler_disconnect(yui, m68k->paused_handler); yui_m68k = NULL; gtk_widget_destroy(GTK_WIDGET(m68k)); } static void yui_m68k_clear(YuiM68k * m68k) { gtk_widget_set_sensitive(m68k->uLabel, FALSE); gtk_widget_set_sensitive(m68k->bpList, FALSE); gtk_widget_set_sensitive(m68k->regList, FALSE); gtk_widget_set_sensitive(m68k->buttonStep, FALSE); } yabause-0.9.13.1/src/gtk/yuivdp2.h000644 001750 001750 00000003714 12256006135 020542 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_VDP2_H #define YUI_VDP2_H #include #include #include #include "yuiwindow.h" G_BEGIN_DECLS #define YUI_VDP2_TYPE (yui_vdp2_get_type ()) #define YUI_VDP2(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_VDP2_TYPE, YuiVdp2)) #define YUI_VDP2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_VDP2_TYPE, YuiVdp2Class)) #define IS_YUI_VDP2(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_VDP2_TYPE)) #define IS_YUI_VDP2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_VDP2_TYPE)) typedef struct _YuiVdp2 YuiVdp2; typedef struct _YuiVdp2Class YuiVdp2Class; struct _YuiVdp2 { GtkWindow dialog; GtkWidget * image; GtkTextBuffer * buffer; GtkWidget * toolbar; GtkListStore * store; gint cursor; gulong paused_handler; gulong running_handler; YuiWindow * yui; }; struct _YuiVdp2Class { GtkWindowClass parent_class; }; GType yui_vdp2_get_type (void); GtkWidget * yui_vdp2_new (YuiWindow * yui); void yui_vdp2_fill (YuiVdp2 * vdp2); void yui_vdp2_update (YuiVdp2 * vdp2); void yui_vdp2_destroy (YuiVdp2 * vdp2); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuish.c000644 001750 001750 00000107475 12256006135 020305 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Fabien Coulon Copyright 2008 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "yuish.h" #include "../sh2core.h" #include "../sh2d.h" #include "../yabause.h" #include "settings.h" static void yui_sh_class_init (YuiShClass * klass); static void yui_sh_init (YuiSh * yfe); static void yui_sh_clear(YuiSh * sh); static void yui_sh_editedReg( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiSh *sh2); static void yui_sh_editedBp( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiSh *sh2); static void yui_sh_editedMbp( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiSh *sh2); static void yui_sh_editedMbpFlags( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiSh *sh2); static void yui_sh_step( GtkWidget* widget, YuiSh * sh2 ); static void SH2BreakpointHandler (SH2_struct *context, u32 addr, void *userdata); static gint yui_sh_popup(GtkWidget * widget, GdkEvent * event, gpointer data); static gint yui_sh_bp_popup(GtkWidget * widget, GdkEventButton * event, gpointer data); static gint yui_sh_mbp_popup(GtkWidget * widget, GdkEventButton * event, gpointer data); static void yui_sh_popup_add_bp(GtkMenuItem * menuitem, gpointer user_data); static void yui_sh_popup_del_bp(GtkMenuItem * menuitem, gpointer user_data); static void SH2UpdateBreakpointList(YuiSh * sh2); static void SH2UpdateMemoryBreakpointList(YuiSh * sh2); static void yui_sh_bp_add(GtkEntry * entry, gpointer user_data); static void yui_sh_button_bp_add(GtkWidget * widget, gpointer user_data); static void yui_sh_mbp_add(GtkEntry * entry, gpointer user_data); static void yui_sh_mbp_toggle_flag(GtkWidget * menuitem, gpointer user_data); static void yui_sh_mbp_remove(GtkWidget * menuitem, gpointer user_data); static void yui_sh_mbp_clear(GtkWidget * menuitem, gpointer user_data); static void yui_sh_bp_remove(GtkWidget * menuitem, gpointer user_data); static void yui_sh_bp_clear(GtkWidget * menuitem, gpointer user_data); static YuiSh *yui_msh, *yui_ssh; static YuiWindow * yui; GType yui_sh_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiShClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_sh_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiSh), 0, (GInstanceInitFunc) yui_sh_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiSh", &yfe_info, 0); } return yfe_type; } static void yui_sh_class_init (UNUSED YuiShClass * klass) { } static void yui_sh_init (YuiSh * sh2) { //GtkWidget *vboxBp; sh2->breakpointEnabled = MSH2->breakpointEnabled; gtk_window_set_title(GTK_WINDOW(sh2), "SH"); sh2->vbox = gtk_vbox_new(FALSE, 0); gtk_container_set_border_width( GTK_CONTAINER( sh2->vbox ), 0); gtk_container_add (GTK_CONTAINER (sh2), sh2->vbox); sh2->toolbar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(sh2->toolbar), GTK_TOOLBAR_ICONS); gtk_box_pack_start(GTK_BOX(sh2->vbox), sh2->toolbar, FALSE, FALSE, 0); sh2->hboxmain = gtk_hbox_new(FALSE, 4); gtk_box_pack_start( GTK_BOX( sh2->vbox ), sh2->hboxmain, FALSE, FALSE, 0); /* Register list */ sh2->regListStore = gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING); sh2->regList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(sh2->regListStore) ); sh2->regListRenderer1 = gtk_cell_renderer_text_new(); sh2->regListRenderer2 = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(sh2->regListRenderer2), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); sh2->regListColumn1 = gtk_tree_view_column_new_with_attributes(_("Register"), sh2->regListRenderer1, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->regList), sh2->regListColumn1); sh2->regListColumn2 = gtk_tree_view_column_new_with_attributes(_("Value"), sh2->regListRenderer2, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->regList), sh2->regListColumn2); gtk_box_pack_start( GTK_BOX( sh2->hboxmain ), sh2->regList, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(sh2->regListRenderer2), "edited", GTK_SIGNAL_FUNC(yui_sh_editedReg), sh2 ); sh2->store = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); sh2->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL (sh2->store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(sh2->view), FALSE); { GtkWidget * scroll; GtkCellRenderer *renderer; GtkCellRenderer *icon; GtkTreeViewColumn *column; scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); icon = gtk_cell_renderer_pixbuf_new(); g_object_set(icon, "xalign", 0, NULL); column = gtk_tree_view_column_new_with_attributes("Icon", icon, "stock-id", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (sh2->view), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("Address", renderer, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (sh2->view), column); column = gtk_tree_view_column_new_with_attributes("Command", renderer, "text", 2, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (sh2->view), column); gtk_container_add(GTK_CONTAINER(scroll), sh2->view); gtk_box_pack_start(GTK_BOX(sh2->hboxmain), scroll, TRUE, TRUE, 0); } if (sh2->breakpointEnabled) { GtkWidget * menu = gtk_menu_new(); GtkWidget * item; item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD, NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); g_signal_connect(item, "activate", G_CALLBACK(yui_sh_popup_add_bp), sh2); item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); g_signal_connect(item, "activate", G_CALLBACK(yui_sh_popup_del_bp), sh2); gtk_widget_show_all(menu); g_signal_connect(sh2->view, "button-press-event", G_CALLBACK(yui_sh_popup), menu); } /* breakpoint list */ sh2->vboxBp = gtk_vbox_new(FALSE, 0); gtk_box_pack_start( GTK_BOX( sh2->hboxmain ), sh2->vboxBp, FALSE, FALSE, 0 ); sh2->bpListStore = gtk_list_store_new(1,G_TYPE_STRING); sh2->bpList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(sh2->bpListStore) ); sh2->bpListRenderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(sh2->bpListRenderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); sh2->bpListColumn = gtk_tree_view_column_new_with_attributes("Code breaks", sh2->bpListRenderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->bpList), sh2->bpListColumn); gtk_box_pack_start( GTK_BOX( sh2->vboxBp ), sh2->bpList, TRUE, TRUE, 0 ); g_signal_connect(G_OBJECT(sh2->bpListRenderer), "edited", GTK_SIGNAL_FUNC(yui_sh_editedBp), sh2 ); { GtkWidget * bp_form_box; GtkWidget * bp_input; GtkWidget * bp_add; bp_form_box = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(sh2->vboxBp), bp_form_box, FALSE, FALSE, 0); bp_input = gtk_entry_new(); gtk_entry_set_width_chars(GTK_ENTRY(bp_input), 8); g_signal_connect(bp_input, "activate", G_CALLBACK(yui_sh_bp_add), sh2); gtk_box_pack_start(GTK_BOX(bp_form_box), bp_input, TRUE, TRUE, 0); bp_add = gtk_button_new(); gtk_container_add(GTK_CONTAINER(bp_add), gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON)); gtk_button_set_relief(GTK_BUTTON(bp_add), GTK_RELIEF_NONE); g_signal_connect(bp_add, "clicked", G_CALLBACK(yui_sh_button_bp_add), bp_input); gtk_box_pack_start(GTK_BOX(bp_form_box), bp_add, FALSE, FALSE, 0); } { GtkWidget * bp_item; sh2->bp_menu = gtk_menu_new(); bp_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, NULL); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->bp_menu), bp_item); g_signal_connect(bp_item, "activate", G_CALLBACK(yui_sh_bp_remove), sh2); bp_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->bp_menu), bp_item); g_signal_connect(bp_item, "activate", G_CALLBACK(yui_sh_bp_clear), sh2); gtk_widget_show_all(sh2->bp_menu); g_signal_connect(sh2->bpList, "button-press-event", G_CALLBACK(yui_sh_bp_popup), sh2); } /* memory breakpoint list */ sh2->mbpListStore = gtk_list_store_new(2,G_TYPE_STRING,G_TYPE_STRING); sh2->mbpList = gtk_tree_view_new_with_model( GTK_TREE_MODEL(sh2->mbpListStore) ); sh2->mbpListRenderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(sh2->mbpListRenderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL ); sh2->mbpListColumn = gtk_tree_view_column_new_with_attributes("Memory breaks", sh2->mbpListRenderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->mbpList), sh2->mbpListColumn); { GtkCellRenderer * flags_renderer; GtkTreeViewColumn * flags_column; flags_renderer = gtk_cell_renderer_text_new(); g_object_set(G_OBJECT(flags_renderer), "editable", TRUE, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL); g_signal_connect(G_OBJECT(flags_renderer), "edited", GTK_SIGNAL_FUNC(yui_sh_editedMbpFlags), sh2 ); flags_column = gtk_tree_view_column_new_with_attributes("Flags", flags_renderer, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(sh2->mbpList), flags_column); } gtk_box_pack_start( GTK_BOX( sh2->vboxBp ), sh2->mbpList, TRUE, TRUE, 0 ); g_signal_connect(G_OBJECT(sh2->mbpListRenderer), "edited", GTK_SIGNAL_FUNC(yui_sh_editedMbp), sh2 ); { GtkWidget * mbp_item; sh2->mbp_menu = gtk_menu_new(); sh2->mbp_menu_item[0] = gtk_check_menu_item_new_with_label("Byte read"); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[0]); g_signal_connect(sh2->mbp_menu_item[0], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); sh2->mbp_menu_item[1] = gtk_check_menu_item_new_with_label("Word read"); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[1]); g_signal_connect(sh2->mbp_menu_item[1], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); sh2->mbp_menu_item[2] = gtk_check_menu_item_new_with_label("Long read"); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[2]); g_signal_connect(sh2->mbp_menu_item[2], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); sh2->mbp_menu_item[3] = gtk_check_menu_item_new_with_label("Byte write"); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[3]); g_signal_connect(sh2->mbp_menu_item[3], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); sh2->mbp_menu_item[4] = gtk_check_menu_item_new_with_label("Word write"); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[4]); g_signal_connect(sh2->mbp_menu_item[4], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); sh2->mbp_menu_item[5] = gtk_check_menu_item_new_with_label("Long write"); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), sh2->mbp_menu_item[5]); g_signal_connect(sh2->mbp_menu_item[5], "activate", G_CALLBACK(yui_sh_mbp_toggle_flag), sh2); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), gtk_separator_menu_item_new()); mbp_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REMOVE, NULL); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), mbp_item); g_signal_connect(mbp_item, "activate", G_CALLBACK(yui_sh_mbp_remove), sh2); mbp_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL); gtk_menu_shell_append(GTK_MENU_SHELL(sh2->mbp_menu), mbp_item); g_signal_connect(mbp_item, "activate", G_CALLBACK(yui_sh_mbp_clear), sh2); gtk_widget_show_all(sh2->mbp_menu); g_signal_connect(sh2->mbpList, "button-press-event", G_CALLBACK(yui_sh_mbp_popup), sh2); } { GtkWidget * bp_form_box; GtkWidget * bp_input; GtkWidget * bp_add; bp_form_box = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(sh2->vboxBp), bp_form_box, FALSE, FALSE, 0); bp_input = gtk_entry_new(); gtk_entry_set_width_chars(GTK_ENTRY(bp_input), 8); g_signal_connect(bp_input, "activate", G_CALLBACK(yui_sh_mbp_add), sh2); gtk_box_pack_start(GTK_BOX(bp_form_box), bp_input, TRUE, TRUE, 0); bp_add = gtk_button_new(); gtk_container_add(GTK_CONTAINER(bp_add), gtk_image_new_from_stock(GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON)); gtk_button_set_relief(GTK_BUTTON(bp_add), GTK_RELIEF_NONE); g_signal_connect(bp_add, "clicked", G_CALLBACK(yui_sh_button_bp_add), bp_input); gtk_box_pack_start(GTK_BOX(bp_form_box), bp_add, FALSE, FALSE, 0); } g_signal_connect(G_OBJECT(sh2), "delete-event", GTK_SIGNAL_FUNC(yui_sh_destroy), NULL); gtk_window_set_default_size(GTK_WINDOW(sh2), 650, -1); } static GtkWidget * yui_sh_new(YuiWindow * y, gboolean bMaster) { GtkWidget * dialog; GClosure *closureF7; //, *closureF8; GtkAccelGroup *accelGroup; YuiSh * sh2; gint i; yui = y; if (!( yui->state & YUI_IS_INIT )) { yui_window_run(yui); yui_window_pause(yui); } if ( bMaster && yui_msh ) return GTK_WIDGET(yui_msh); if ( !bMaster && yui_ssh ) return GTK_WIDGET(yui_ssh); dialog = GTK_WIDGET(g_object_new(yui_sh_get_type(), NULL)); sh2 = YUI_SH(dialog); //sh2->breakpointEnabled = MSH2->breakpointEnabled; /* if ( !sh2->breakpointEnabled ) gtk_box_pack_start( GTK_BOX( sh2->vboxmain ), gtk_label_new("Breakpoints are disabled (fast interpreter)"), FALSE, FALSE, 4 ); */ sh2->bMaster = bMaster; sh2->debugsh = bMaster ? MSH2 : SSH2; SH2SetBreakpointCallBack(sh2->debugsh, (void (*)(void *, u32, void *))SH2BreakpointHandler, NULL); gtk_window_set_title(GTK_WINDOW(sh2), bMaster?"Master SH2":"Slave SH2"); for (i = 0; i < 23 ; i++) { GtkTreeIter iter; gtk_list_store_append( GTK_LIST_STORE( sh2->regListStore ), &iter ); } SH2UpdateBreakpointList(sh2); SH2UpdateMemoryBreakpointList(sh2); { GtkToolItem * play_button, * pause_button; play_button = gtk_tool_button_new_from_stock("run"); gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "run"), GTK_WIDGET(play_button)); gtk_toolbar_insert(GTK_TOOLBAR(sh2->toolbar), GTK_TOOL_ITEM(play_button), 0); pause_button = gtk_tool_button_new_from_stock("pause"); gtk_action_connect_proxy(gtk_action_group_get_action(yui->action_group, "pause"), GTK_WIDGET(pause_button)); gtk_toolbar_insert(GTK_TOOLBAR(sh2->toolbar), GTK_TOOL_ITEM(pause_button), 1); sh2->buttonStep = gtk_tool_button_new_from_stock(GTK_STOCK_GO_DOWN); g_signal_connect(sh2->buttonStep, "clicked", G_CALLBACK(yui_sh_step), sh2); gtk_toolbar_insert(GTK_TOOLBAR(sh2->toolbar), GTK_TOOL_ITEM(sh2->buttonStep), 2); #if GTK_CHECK_VERSION(2,12,0) gtk_widget_set_tooltip_text(GTK_WIDGET(sh2->buttonStep), "step"); #endif } sh2->paused_handler = g_signal_connect_swapped(yui, "paused", G_CALLBACK(yui_sh_update), sh2); sh2->running_handler = g_signal_connect_swapped(yui, "running", G_CALLBACK(yui_sh_clear), sh2); accelGroup = gtk_accel_group_new (); closureF7 = g_cclosure_new (G_CALLBACK (yui_sh_step), sh2, NULL); gtk_accel_group_connect( accelGroup, GDK_F7, 0, 0, closureF7 ); gtk_window_add_accel_group( GTK_WINDOW( dialog ), accelGroup ); yui_sh_update(sh2); if ( yui->state & YUI_IS_RUNNING ) yui_sh_clear(sh2); gtk_widget_show_all(GTK_WIDGET(sh2)); if ( !sh2->breakpointEnabled ) { /* gtk_widget_hide( sh2->bpList ); gtk_widget_hide( sh2->mbpList ); */ gtk_widget_hide(sh2->vboxBp); } return dialog; } GtkWidget * yui_msh_new(YuiWindow * y) { return GTK_WIDGET( yui_msh = YUI_SH(yui_sh_new( y, TRUE )) ); } GtkWidget * yui_ssh_new(YuiWindow * y) { return GTK_WIDGET( yui_ssh = YUI_SH(yui_sh_new( y, FALSE )) ); } static void SH2UpdateRegList( YuiSh *sh2, sh2regs_struct *regs) { /* refresh the registery list */ GtkTreeIter iter; char regstr[32]; char valuestr[32]; int i; for (i = 0; i < 16; i++) { sprintf(regstr, "R%02d", i); sprintf(valuestr, "%08X", (int)regs->R[i] ); if ( !i ) gtk_tree_model_get_iter_first( GTK_TREE_MODEL( sh2->regListStore ), &iter ); else gtk_tree_model_iter_next( GTK_TREE_MODEL( sh2->regListStore ), &iter ); gtk_list_store_set( GTK_LIST_STORE( sh2->regListStore ), &iter, 0, regstr, 1, valuestr, -1 ); } #define SH2UPDATEREGLIST(rreg) \ gtk_tree_model_iter_next( GTK_TREE_MODEL( sh2->regListStore ), &iter ); \ sprintf(valuestr, "%08X", (int)regs->rreg); \ gtk_list_store_set( GTK_LIST_STORE( sh2->regListStore ), &iter, 0, #rreg, 1, valuestr, -1 ); SH2UPDATEREGLIST(SR.all); SH2UPDATEREGLIST(GBR); SH2UPDATEREGLIST(VBR); SH2UPDATEREGLIST(MACH); SH2UPDATEREGLIST(MACL); SH2UPDATEREGLIST(PR); SH2UPDATEREGLIST(PC); } static void sh2setRegister( YuiSh *sh2, int nReg, u32 value ) { /* set register number to value in proc */ sh2regs_struct sh2regs; SH2GetRegisters(sh2->debugsh, &sh2regs); if ( nReg < 16 ) sh2regs.R[nReg] = value; switch ( nReg ) { case 16: sh2regs.SR.all = value; break; case 17: sh2regs.GBR = value; break; case 18: sh2regs.VBR = value; break; case 19: sh2regs.MACH = value; break; case 20: sh2regs.MACL = value; break; case 21: sh2regs.PR = value; break; case 22: sh2regs.PC = value; break; } SH2SetRegisters(sh2->debugsh, &sh2regs); } void SH2UpdateBreakpointList(YuiSh * sh2) { const codebreakpoint_struct *cbp; int i; gtk_list_store_clear(GTK_LIST_STORE( sh2->bpListStore )); cbp = SH2GetBreakpointList(sh2->debugsh); for (i = 0; i < MAX_BREAKPOINTS-1; i++) { if (cbp[i].addr != 0xFFFFFFFF) { gchar tempstr[20]; GtkTreeIter iter; gtk_list_store_append( GTK_LIST_STORE( sh2->bpListStore ), &iter ); sprintf(tempstr, "%08X", (int)cbp[i].addr); gtk_list_store_set( GTK_LIST_STORE( sh2->bpListStore ), &iter, 0, tempstr, -1 ); } } } void SH2UpdateMemoryBreakpointList(YuiSh * sh2) { const memorybreakpoint_struct *cmbp; int i; gtk_list_store_clear( sh2->mbpListStore ); cmbp = SH2GetMemoryBreakpointList(sh2->debugsh); for (i = 0; i < MAX_BREAKPOINTS; i++) { if (cmbp[i].addr != 0xFFFFFFFF) { gchar tempstr[30]; gchar flagstr[30]; gchar *curs = flagstr; u32 flags = cmbp[i].flags; GtkTreeIter iter; gtk_list_store_append( GTK_LIST_STORE( sh2->mbpListStore ), &iter ); sprintf(tempstr, "%08X", (int)cmbp[i].addr); if ( flags & BREAK_BYTEREAD ) *(curs++) = 'b'; if ( flags & BREAK_WORDREAD ) *(curs++) = 'w'; if ( flags & BREAK_LONGREAD ) *(curs++) = 'l'; if ( flags & BREAK_BYTEWRITE ) *(curs++) = 'B'; if ( flags & BREAK_WORDWRITE ) *(curs++) = 'W'; if ( flags & BREAK_LONGWRITE ) *(curs++) = 'L'; *curs = 0; gtk_list_store_set( GTK_LIST_STORE( sh2->mbpListStore ), &iter, 0, tempstr, -1 ); gtk_list_store_set( GTK_LIST_STORE( sh2->mbpListStore ), &iter, 1, flagstr, -1 ); } } } static void SH2UpdateCodeList( YuiSh *sh2, u32 addr) { /* refresh the assembler view. points the line to be highlighted. */ int i, j; char lineBuf[64]; u32 offset; GtkTreeIter iter; unsigned int address; char address_s[20]; char command_s[64]; codebreakpoint_struct *cbp; gtk_list_store_clear(sh2->store); if ( addr - sh2->lastCode >= 20*2 ) offset = addr - (8*2); else offset = sh2->lastCode; sh2->lastCode = offset; cbp = SH2GetBreakpointList(sh2->debugsh); for (i = 0; i < 24; i++) { SH2Disasm(offset+2*i, MappedMemoryReadWord(offset+2*i), 0, lineBuf); sscanf(lineBuf, "0x%8X: %[^\n]", &address, command_s); sprintf(address_s, "0x%08X", address); gtk_list_store_append(sh2->store, &iter); if ( offset + 2*i == addr ) { gtk_list_store_set(sh2->store, &iter, 0, GTK_STOCK_GO_FORWARD, -1); } else { for (j = 0;j < MAX_BREAKPOINTS - 1;j++) { if (cbp[j].addr == address) { gtk_list_store_set(sh2->store, &iter, 0, GTK_STOCK_STOP, -1); } } } gtk_list_store_set(sh2->store, &iter, 1, address_s, -1); gtk_list_store_set(sh2->store, &iter, 2, command_s, -1); } } static void yui_sh_step( GtkWidget* widget, YuiSh * sh2 ) { SH2Step(sh2->debugsh); yui_window_invalidate( yui ); /* update all dialogs, including us */ } static void yui_sh_editedReg( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiSh *sh2) { /* registry number value has been set to */ GtkTreeIter iter; char bptext[10]; char *endptr; int i = atoi(arg1); u32 addr; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( sh2->regListStore ), &iter, arg1 ); addr = strtoul(arg2, &endptr, 16 ); if ( endptr - arg2 == strlen(arg2) ) { sprintf(bptext, "%08X", (int)addr); sh2setRegister( sh2, i, addr ); gtk_list_store_set( GTK_LIST_STORE( sh2->regListStore ), &iter, 1, bptext, -1 ); } yui_window_invalidate( yui ); } static void yui_sh_editedBp( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiSh *sh2) { /* breakpoint has been set to address */ GtkTreeIter iter; char *endptr; unsigned int addr; gchar * oldaddr_s; unsigned int oldaddr; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( sh2->bpListStore ), &iter, arg1 ); gtk_tree_model_get(GTK_TREE_MODEL( sh2->bpListStore ), &iter, 0, &oldaddr_s, -1); sscanf(oldaddr_s, "%8X", &oldaddr); g_free(oldaddr_s); SH2DelCodeBreakpoint(sh2->debugsh, oldaddr); addr = strtoul(arg2, &endptr, 16 ); if ((endptr - arg2 < strlen(arg2)) || (!addr)) addr = 0xFFFFFFFF; if (addr != 0xFFFFFFFF) { SH2AddCodeBreakpoint(sh2->debugsh, addr); } { sh2regs_struct sh2regs; SH2GetRegisters(sh2->debugsh, &sh2regs); SH2UpdateCodeList(sh2,sh2regs.PC); SH2UpdateBreakpointList(sh2); } } static void yui_sh_editedMbp( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiSh *sh2) { /* breakpoint has been set to address */ GtkTreeIter iter; gchar *endptr; unsigned int addr; gchar * oldaddr_s, * flags_s; unsigned int oldaddr; u32 flags; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( sh2->mbpListStore ), &iter, arg1 ); gtk_tree_model_get(GTK_TREE_MODEL( sh2->mbpListStore ), &iter, 0, &oldaddr_s, -1); sscanf(oldaddr_s, "%8X", &oldaddr); g_free(oldaddr_s); gtk_tree_model_get(GTK_TREE_MODEL( sh2->mbpListStore ), &iter, 1, &flags_s, -1); SH2DelMemoryBreakpoint(sh2->debugsh, oldaddr); addr = strtoul(arg2, &endptr, 16 ); if (!addr) addr = 0xFFFFFFFF; if (addr!=0xFFFFFFFF) { flags = 0; endptr = flags_s; while ( *endptr ) { switch (*endptr) { case 'b': flags |= BREAK_BYTEREAD; break; case 'w': flags |= BREAK_WORDREAD; break; case 'l': flags |= BREAK_LONGREAD; break; case 'B': flags |= BREAK_BYTEWRITE; break; case 'W': flags |= BREAK_WORDWRITE; break; case 'L': flags |= BREAK_LONGWRITE; break; } endptr++; } if ( !flags ) flags = BREAK_BYTEREAD|BREAK_WORDREAD|BREAK_LONGREAD|BREAK_BYTEWRITE|BREAK_WORDWRITE|BREAK_LONGWRITE; SH2AddMemoryBreakpoint(sh2->debugsh, addr, flags); } SH2UpdateMemoryBreakpointList(sh2); } static void yui_sh_editedMbpFlags( GtkCellRendererText *cellrenderertext, gchar *arg1, gchar *arg2, YuiSh *sh2) { /* breakpoint has been set to address */ GtkTreeIter iter; gchar *endptr; unsigned int addr; gchar * addr_s; u32 flags = 0; gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( sh2->mbpListStore ), &iter, arg1 ); gtk_tree_model_get(GTK_TREE_MODEL( sh2->mbpListStore ), &iter, 0, &addr_s, -1); sscanf(addr_s, "%8X", &addr); g_free(addr_s); SH2DelMemoryBreakpoint(sh2->debugsh, addr); endptr = arg2; while ( *endptr ) { switch (*endptr) { case 'b': flags |= BREAK_BYTEREAD; break; case 'w': flags |= BREAK_WORDREAD; break; case 'l': flags |= BREAK_LONGREAD; break; case 'B': flags |= BREAK_BYTEWRITE; break; case 'W': flags |= BREAK_WORDWRITE; break; case 'L': flags |= BREAK_LONGWRITE; break; } endptr++; } SH2AddMemoryBreakpoint(sh2->debugsh, addr, flags); SH2UpdateMemoryBreakpointList(sh2); } static void debugPauseLoop(void) { /* secondary gtk event loop for the "breakpoint pause" state */ while ( !(yui->state & YUI_IS_RUNNING) ) if ( gtk_main_iteration() ) return; } static void SH2BreakpointHandler (SH2_struct *context, u32 addr, void *userdata) { yui_window_pause(yui); { sh2regs_struct sh2regs; YuiSh* sh2 = YUI_SH(yui_sh_new( yui, context == MSH2 )); SH2GetRegisters(sh2->debugsh, &sh2regs); SH2UpdateRegList(sh2, &sh2regs); SH2UpdateCodeList(sh2, sh2regs.PC); } debugPauseLoop(); /* execution is suspended inside a normal cycle - enter secondary gtk loop */ } void yui_sh_update(YuiSh * sh) { sh2regs_struct sh2regs; SH2GetRegisters(sh->debugsh, &sh2regs); SH2UpdateCodeList(sh,sh2regs.PC); SH2UpdateRegList(sh, &sh2regs); gtk_widget_set_sensitive(sh->bpList, TRUE); gtk_widget_set_sensitive(sh->mbpList, TRUE); gtk_widget_set_sensitive(sh->regList, TRUE); gtk_widget_set_sensitive(GTK_WIDGET(sh->buttonStep), !sh->debugsh->isIdle && !(( sh->debugsh == SSH2 )&&( !yabsys.IsSSH2Running ))); } void yui_sh_destroy(YuiSh * sh) { g_signal_handler_disconnect(yui, sh->running_handler); g_signal_handler_disconnect(yui, sh->paused_handler); if ( sh->bMaster ) yui_msh = NULL; else yui_ssh = NULL; gtk_widget_destroy(GTK_WIDGET(sh)); } static void yui_sh_clear(YuiSh * sh) { gtk_widget_set_sensitive(sh->bpList, FALSE); gtk_widget_set_sensitive(sh->mbpList, FALSE); gtk_widget_set_sensitive(sh->regList, FALSE); gtk_widget_set_sensitive(GTK_WIDGET(sh->buttonStep), FALSE); gtk_list_store_clear(sh->store); } gint yui_sh_popup(GtkWidget * widget, GdkEvent * event, gpointer data) { GtkMenu *menu; GdkEventButton *event_button; g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (GTK_IS_MENU (data), FALSE); g_return_val_if_fail (event != NULL, FALSE); menu = GTK_MENU(data); if (event->type == GDK_BUTTON_PRESS) { event_button = (GdkEventButton *) event; if (event_button->button == 3) { gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time); } } return FALSE; } void yui_sh_popup_add_bp(GtkMenuItem * menuitem, gpointer user_data) { YuiSh * sh2 = user_data; GtkTreeView * view = GTK_TREE_VIEW(sh2->view); GtkTreeSelection * selection; GtkTreeModel * model; GtkTreeIter iter; gchar * address_s; unsigned int address; selection = gtk_tree_view_get_selection(view); gtk_tree_selection_get_selected(selection, &model, &iter); gtk_tree_model_get(model, &iter, 1, &address_s, -1); sscanf(address_s, "0x%08X", &address); SH2AddCodeBreakpoint(sh2->debugsh, address); g_free(address_s); { sh2regs_struct sh2regs; SH2GetRegisters(sh2->debugsh, &sh2regs); SH2UpdateCodeList(sh2,sh2regs.PC); SH2UpdateBreakpointList(sh2); } } void yui_sh_popup_del_bp(GtkMenuItem * menuitem, gpointer user_data) { YuiSh * sh2 = user_data; GtkTreeView * view = GTK_TREE_VIEW(sh2->view); GtkTreeSelection * selection; GtkTreeModel * model; GtkTreeIter iter; gchar * address_s; unsigned int address; selection = gtk_tree_view_get_selection(view); gtk_tree_selection_get_selected(selection, &model, &iter); gtk_tree_model_get(model, &iter, 1, &address_s, -1); sscanf(address_s, "0x%08X", &address); SH2DelCodeBreakpoint(sh2->debugsh, address); g_free(address_s); { sh2regs_struct sh2regs; SH2GetRegisters(sh2->debugsh, &sh2regs); SH2UpdateCodeList(sh2,sh2regs.PC); SH2UpdateBreakpointList(sh2); } } static void yui_sh_bp_add(GtkEntry * entry, gpointer user_data) { YuiSh * sh2 = user_data; const gchar * address_s; unsigned int address; address_s = gtk_entry_get_text(entry); if (*address_s == 0) return; sscanf(address_s, "%8X", &address); SH2AddCodeBreakpoint(sh2->debugsh, address); gtk_entry_set_text(entry, ""); { sh2regs_struct sh2regs; SH2GetRegisters(sh2->debugsh, &sh2regs); SH2UpdateCodeList(sh2,sh2regs.PC); SH2UpdateBreakpointList(sh2); } } static void yui_sh_button_bp_add(GtkWidget * widget, gpointer user_data) { g_signal_emit_by_name(user_data, "activate"); } static void yui_sh_mbp_add(GtkEntry * entry, gpointer user_data) { YuiSh * sh2 = user_data; const gchar * address_s; unsigned int address; address_s = gtk_entry_get_text(entry); if (*address_s == 0) return; sscanf(address_s, "%8X", &address); SH2AddMemoryBreakpoint(sh2->debugsh, address, BREAK_BYTEREAD|BREAK_WORDREAD|BREAK_LONGREAD|BREAK_BYTEWRITE|BREAK_WORDWRITE|BREAK_LONGWRITE); gtk_entry_set_text(entry, ""); SH2UpdateMemoryBreakpointList(sh2); } gint yui_sh_mbp_popup(GtkWidget * widget, GdkEventButton * event, gpointer data) { GtkMenu *menu; GdkEventButton *event_button; YuiSh * sh2 = data; GtkTreeView * view; GtkTreeSelection * selection; GtkTreeIter iter; GtkTreeModel * model; gchar * flags_s; char *endptr; int i; guint signal_id; g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (event != NULL, FALSE); view = GTK_TREE_VIEW(sh2->mbpList); menu = GTK_MENU(sh2->mbp_menu); if (event->type == GDK_BUTTON_PRESS) { event_button = (GdkEventButton *) event; if (event_button->button == 3) { GtkTreePath *path; selection = gtk_tree_view_get_selection(view); if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), event->x, event->y, &path, NULL, NULL, NULL)) { gtk_tree_selection_unselect_all(selection); gtk_tree_selection_select_path(selection, path); gtk_tree_path_free(path); } gtk_tree_selection_get_selected(selection, &model, &iter); if (gtk_tree_selection_count_selected_rows(selection) == 0) return FALSE; gtk_tree_model_get(model, &iter, 1, &flags_s, -1); signal_id = g_signal_lookup("activate", GTK_TYPE_CHECK_MENU_ITEM); for(i = 0;i < 6;i++) g_signal_handlers_block_matched(sh2->mbp_menu_item[i], G_SIGNAL_MATCH_DATA, signal_id, 0, NULL, NULL, sh2); for(i = 0;i < 6;i++) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[i]), FALSE); endptr = flags_s; while ( *endptr ) { switch (*endptr) { case 'b': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[0]), TRUE); break; case 'w': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[1]), TRUE); break; case 'l': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[2]), TRUE); break; case 'B': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[3]), TRUE); break; case 'W': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[4]), TRUE); break; case 'L': gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(sh2->mbp_menu_item[5]), TRUE); break; } endptr++; } for(i = 0;i < 6;i++) g_signal_handlers_unblock_matched(sh2->mbp_menu_item[i], G_SIGNAL_MATCH_DATA, signal_id, 0, NULL, NULL, sh2); gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time); } } return FALSE; } void yui_sh_mbp_toggle_flag(GtkWidget * menuitem, gpointer user_data) { GtkTreeSelection * selection; YuiSh * sh2 = user_data; GtkTreeIter iter; GtkTreeModel * model; gchar * address_s, * flags_s; unsigned int address; u32 flags; GtkTreeView * view; char *endptr; view = GTK_TREE_VIEW(sh2->mbpList); selection = gtk_tree_view_get_selection(view); gtk_tree_selection_get_selected(selection, &model, &iter); gtk_tree_model_get(model, &iter, 0, &address_s, -1); gtk_tree_model_get(model, &iter, 1, &flags_s, -1); sscanf(address_s, "%8X", &address); SH2DelMemoryBreakpoint(sh2->debugsh, address); flags = 0; endptr = flags_s; while ( *endptr ) { switch (*endptr) { case 'b': flags |= BREAK_BYTEREAD; break; case 'w': flags |= BREAK_WORDREAD; break; case 'l': flags |= BREAK_LONGREAD; break; case 'B': flags |= BREAK_BYTEWRITE; break; case 'W': flags |= BREAK_WORDWRITE; break; case 'L': flags |= BREAK_LONGWRITE; break; } endptr++; } if (menuitem == sh2->mbp_menu_item[0]) flags = (flags & ~BREAK_BYTEREAD) | (~flags & BREAK_BYTEREAD); if (menuitem == sh2->mbp_menu_item[1]) flags = (flags & ~BREAK_WORDREAD) | (~flags & BREAK_WORDREAD); if (menuitem == sh2->mbp_menu_item[2]) flags = (flags & ~BREAK_LONGREAD) | (~flags & BREAK_LONGREAD); if (menuitem == sh2->mbp_menu_item[3]) flags = (flags & ~BREAK_BYTEWRITE) | (~flags & BREAK_BYTEWRITE); if (menuitem == sh2->mbp_menu_item[4]) flags = (flags & ~BREAK_WORDWRITE) | (~flags & BREAK_WORDWRITE); if (menuitem == sh2->mbp_menu_item[5]) flags = (flags & ~BREAK_LONGWRITE) | (~flags & BREAK_LONGWRITE); SH2AddMemoryBreakpoint(sh2->debugsh, address, flags); SH2UpdateMemoryBreakpointList(sh2); } void yui_sh_mbp_remove(GtkWidget * menuitem, gpointer user_data) { GtkTreeSelection * selection; YuiSh * sh2 = user_data; GtkTreeIter iter; GtkTreeModel * model; gchar * address_s; unsigned int address; GtkTreeView * view; view = GTK_TREE_VIEW(sh2->mbpList); selection = gtk_tree_view_get_selection(view); gtk_tree_selection_get_selected(selection, &model, &iter); gtk_tree_model_get(model, &iter, 0, &address_s, -1); sscanf(address_s, "%8X", &address); SH2DelMemoryBreakpoint(sh2->debugsh, address); SH2UpdateMemoryBreakpointList(sh2); } void yui_sh_mbp_clear(GtkWidget * menuitem, gpointer user_data) { YuiSh * sh2 = user_data; SH2ClearMemoryBreakpoints(sh2->debugsh); SH2UpdateMemoryBreakpointList(sh2); } gint yui_sh_bp_popup(GtkWidget * widget, GdkEventButton * event, gpointer data) { GtkMenu *menu; GdkEventButton *event_button; YuiSh * sh2 = data; g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (event != NULL, FALSE); menu = GTK_MENU(sh2->bp_menu); if (event->type == GDK_BUTTON_PRESS) { event_button = (GdkEventButton *) event; if (event_button->button == 3) { gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time); } } return FALSE; } void yui_sh_bp_remove(GtkWidget * menuitem, gpointer user_data) { GtkTreeSelection * selection; YuiSh * sh2 = user_data; GtkTreeIter iter; GtkTreeModel * model; gchar * address_s; unsigned int address; GtkTreeView * view; view = GTK_TREE_VIEW(sh2->bpList); selection = gtk_tree_view_get_selection(view); gtk_tree_selection_get_selected(selection, &model, &iter); gtk_tree_model_get(model, &iter, 0, &address_s, -1); sscanf(address_s, "%8X", &address); SH2DelCodeBreakpoint(sh2->debugsh, address); { sh2regs_struct sh2regs; SH2GetRegisters(sh2->debugsh, &sh2regs); SH2UpdateCodeList(sh2,sh2regs.PC); SH2UpdateBreakpointList(sh2); } } void yui_sh_bp_clear(GtkWidget * menuitem, gpointer user_data) { YuiSh * sh2 = user_data; SH2ClearCodeBreakpoints(sh2->debugsh); { sh2regs_struct sh2regs; SH2GetRegisters(sh2->debugsh, &sh2regs); SH2UpdateCodeList(sh2,sh2regs.PC); SH2UpdateBreakpointList(sh2); } } yabause-0.9.13.1/src/gtk/yuiscreenshot.h000644 001750 001750 00000003371 12256006135 022043 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_SCREENSHOT_H #define YUI_SCREENSHOT_H #include #include "yuiwindow.h" G_BEGIN_DECLS #define YUI_SCREENSHOT_TYPE (yui_screenshot_get_type ()) #define YUI_SCREENSHOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_SCREENSHOT_TYPE, YuiScreenshot)) #define YUI_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_SCREENSHOT_TYPE, YuiScreenshotClass)) #define IS_YUI_SCREENSHOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_SCREENSHOT_TYPE)) #define IS_YUI_SCREENSHOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_SCREENSHOT_TYPE)) typedef struct _YuiScreenshot YuiScreenshot; typedef struct _YuiScreenshotClass YuiScreenshotClass; struct _YuiScreenshot { GtkWindow dialog; GtkWidget * image; }; struct _YuiScreenshotClass { GtkWindowClass parent_class; }; GType yui_screenshot_get_type (void); GtkWidget * yui_screenshot_new (YuiWindow * yui); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuim68k.h000644 001750 001750 00000004500 12256006140 020442 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_M68K_H #define YUI_M68K_H #include #include #include #include "../scsp.h" #include "yuiwindow.h" G_BEGIN_DECLS #define YUI_M68K_TYPE (yui_m68k_get_type ()) #define YUI_M68K(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_M68K_TYPE, YuiM68k)) #define YUI_M68K_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_M68K_TYPE, YuiM68kClass)) #define IS_YUI_M68K(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_M68K_TYPE)) #define IS_YUI_M68K_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_M68K_TYPE)) typedef struct _YuiM68k YuiM68k; typedef struct _YuiM68kClass YuiM68kClass; struct _YuiM68k { GtkWindow dialog; GtkWidget *vbox, *vboxmain; GtkWidget *hbox, *hboxmain; GtkWidget * buttonStep, * buttonStepOver; GtkWidget * bpList, *regList, *uLabel, *uFrame; GtkListStore *bpListStore, *regListStore; GtkCellRenderer *bpListRenderer, *regListRenderer1, *regListRenderer2; GtkTreeViewColumn *bpListColumn, *regListColumn1, *regListColumn2; u32 cbp[MAX_BREAKPOINTS]; /* the list of breakpoint positions, as they can be found in the list widget */ u32 lastCode; /* offset of last unassembly. Try to reuse it to prevent sliding. */ gulong paused_handler; gulong running_handler; }; struct _YuiM68kClass { GtkWindowClass parent_class; void (* yui_m68k) (YuiM68k * yv); }; GType yui_m68k_get_type (void); GtkWidget* yui_m68k_new(YuiWindow * y); void yui_m68k_update (YuiM68k * m68k); void yui_m68k_destroy (YuiM68k * sh); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuiviewer.c000644 001750 001750 00000011215 12256006140 021152 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006-2007 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "yuiviewer.h" #include "gtkglwidget.h" #include "../core.h" static void yui_viewer_class_init (YuiViewerClass * klass); static void yui_viewer_init (YuiViewer * yfe); static void yui_viewer_expose (GtkWidget * widget, GdkEventExpose *event, gpointer data); GType yui_viewer_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiViewerClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_viewer_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiViewer), 0, (GInstanceInitFunc) yui_viewer_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_DRAWING_AREA, "YuiViewer", &yfe_info, 0); } return yfe_type; } static void yui_viewer_class_init (UNUSED YuiViewerClass * klass) { } static gint my_popup_handler (GtkWidget *widget, GdkEvent *event) { GtkMenu *menu; GdkEventButton *event_button; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_MENU (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); /* The "widget" is the menu that was supplied when * * g_signal_connect_swapped() was called. * */ menu = GTK_MENU (widget); if (event->type == GDK_BUTTON_PRESS) { event_button = (GdkEventButton *) event; if (event_button->button == 3) { gtk_menu_popup (menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time); return TRUE; } } return FALSE; } static void yui_viewer_init (YuiViewer * yv) { GtkWidget * menu; GtkWidget * item; gtk_widget_set_events(GTK_WIDGET(yv), GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); menu = gtk_menu_new(); item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show_all(menu); g_signal_connect_swapped(yv, "button-press-event", G_CALLBACK(my_popup_handler), menu); g_signal_connect(yv, "expose-event", G_CALLBACK(yui_viewer_expose), NULL); g_signal_connect_swapped(item, "activate", G_CALLBACK(yui_viewer_save), yv); yv->pixbuf = NULL; } GtkWidget * yui_viewer_new(void) { GtkWidget * dialog; dialog = GTK_WIDGET(g_object_new(yui_viewer_get_type(), NULL)); return dialog; } static void yui_viewer_expose(GtkWidget * widget, GdkEventExpose *event, gpointer data) { YuiViewer * yv = YUI_VIEWER(widget); if (yv->pixbuf != NULL) { gdk_draw_pixbuf(widget->window, NULL, yv->pixbuf, 0, 0, 0, 0, yv->w, yv->h, GDK_RGB_DITHER_NONE, 0, 0); } } void yui_viewer_save(YuiViewer * yv) { GtkWidget * file_selector; gint result; char * filename; int rowstride; file_selector = gtk_file_chooser_dialog_new ("Please choose a file", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); gtk_widget_show(file_selector); result = gtk_dialog_run(GTK_DIALOG(file_selector)); switch(result) { case GTK_RESPONSE_ACCEPT: rowstride = yv->w * 4; rowstride += (rowstride % 4)? (4 - (rowstride % 4)): 0; filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_selector)); gdk_pixbuf_save(yv->pixbuf, filename, "png", NULL, NULL); break; case GTK_RESPONSE_CANCEL: break; } gtk_widget_destroy(file_selector); } void yui_viewer_draw_pixbuf(YuiViewer * yv, GdkPixbuf * pixbuf, int w, int h) { if (yv->pixbuf) { g_object_unref(yv->pixbuf); } yv->pixbuf = g_object_ref(pixbuf); yv->w = w; yv->h = h; gdk_window_clear(GTK_WIDGET(yv)->window); gtk_widget_queue_draw_area(GTK_WIDGET(yv), 0, 0, w, h); } void yui_viewer_clear(YuiViewer * yv) { if (GTK_WIDGET(yv)->window != NULL) { gdk_window_clear(GTK_WIDGET(yv)->window); } } yabause-0.9.13.1/src/gtk/yuiviewer.h000644 001750 001750 00000003476 12256006140 021171 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_VIEWER_H #define YUI_VIEWER_H #include G_BEGIN_DECLS #define YUI_VIEWER_TYPE (yui_viewer_get_type ()) #define YUI_VIEWER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_VIEWER_TYPE, YuiViewer)) #define YUI_VIEWER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_VIEWER_TYPE, YuiViewerClass)) #define IS_YUI_VIEWER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_VIEWER_TYPE)) #define IS_YUI_VIEWER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_VIEWER_TYPE)) typedef struct _YuiViewer YuiViewer; typedef struct _YuiViewerClass YuiViewerClass; struct _YuiViewer { GtkDrawingArea parent; int w; int h; GdkPixbuf * pixbuf; }; struct _YuiViewerClass { GtkDrawingAreaClass parent_class; }; GType yui_viewer_get_type (void); GtkWidget * yui_viewer_new (void); void yui_viewer_draw_pixbuf(YuiViewer * yv, GdkPixbuf * pixbuf, int w, int h); void yui_viewer_save (YuiViewer * yv); void yui_viewer_clear (YuiViewer * yv); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuicheckbutton.c000644 001750 001750 00000011000 12256006135 022156 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "yuicheckbutton.h" static void yui_check_button_class_init(YuiCheckButtonClass * klass); static void yui_check_button_init (YuiCheckButton * ycb); static void yui_check_button_toggled (GtkToggleButton * button, YuiCheckButton * ycb); GType yui_check_button_get_type (void) { static GType ycb_type = 0; if (!ycb_type) { static const GTypeInfo ycb_info = { sizeof (YuiCheckButtonClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_check_button_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiCheckButton), 0, (GInstanceInitFunc) yui_check_button_init, NULL, }; ycb_type = g_type_register_static(GTK_TYPE_CHECK_BUTTON, "YuiCheckButton", &ycb_info, 0); } return ycb_type; } #define PROP_KEYFILE 1 #define PROP_GROUP 2 #define PROP_KEY 3 static void yui_check_button_set_property(GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { switch(property_id) { case PROP_KEYFILE: YUI_CHECK_BUTTON(object)->keyfile = g_value_get_pointer(value); break; case PROP_GROUP: YUI_CHECK_BUTTON(object)->group = g_value_get_pointer(value); break; case PROP_KEY: YUI_CHECK_BUTTON(object)->key = g_value_get_pointer(value); break; } } static void yui_check_button_get_property(GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { } enum { YUI_CHECK_BUTTON_CHANGED_SIGNAL, LAST_SIGNAL }; static guint yui_check_button_signals[LAST_SIGNAL] = { 0 }; static void yui_check_button_class_init (YuiCheckButtonClass * klass) { GParamSpec * param; G_OBJECT_CLASS(klass)->set_property = yui_check_button_set_property; G_OBJECT_CLASS(klass)->get_property = yui_check_button_get_property; param = g_param_spec_pointer("key-file", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEYFILE, param); param = g_param_spec_pointer("group", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_GROUP, param); param = g_param_spec_pointer("key", 0, 0, G_PARAM_READABLE | G_PARAM_WRITABLE); g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_KEY, param); yui_check_button_signals[YUI_CHECK_BUTTON_CHANGED_SIGNAL] = g_signal_new ("changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(YuiCheckButtonClass, yui_check_button_change), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void yui_check_button_init (YuiCheckButton * ycb) { } GtkWidget * yui_check_button_new(const gchar * label, GKeyFile * keyfile, const gchar * group, const gchar * key) { GtkWidget * button; YuiCheckButton * ycb; gboolean current; button = GTK_WIDGET(g_object_new(yui_check_button_get_type(), "label", label, "key-file", keyfile, "group", group, "key", key, NULL)); ycb = YUI_CHECK_BUTTON(button); gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(ycb), TRUE); current = g_key_file_get_boolean(ycb->keyfile, ycb->group, ycb->key, NULL); gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(ycb), current); g_signal_connect(GTK_TOGGLE_BUTTON(ycb), "toggled", G_CALLBACK(yui_check_button_toggled), ycb); return button; } static void yui_check_button_toggled(GtkToggleButton * button, YuiCheckButton * ycb) { g_key_file_set_boolean(ycb->keyfile, ycb->group, ycb->key, gtk_toggle_button_get_active(button)); g_signal_emit(G_OBJECT(ycb), yui_check_button_signals[YUI_CHECK_BUTTON_CHANGED_SIGNAL], 0); } gboolean yui_check_button_get_active(YuiCheckButton * ycb) { return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ycb)); } yabause-0.9.13.1/src/gtk/gtk-compat.h000644 001750 001750 00000002023 12256006140 021172 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_GTK_COMPAT_H #define YUI_GTK_COMPAT_H #include #if ((GLIB_MAJOR_VERSION < 2) || ((GLIB_MAJOR_VERSION == 2) && (GLIB_MINOR_VERSION < 8))) gboolean g_file_set_contents(const gchar *, const gchar *, gssize, GError **); #endif #endif yabause-0.9.13.1/src/gtk/yuitransfer.h000644 001750 001750 00000004044 12256006135 021510 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_TRANSFERT_H #define YUI_TRANSFERT_H #include #include #include #include "yuiwindow.h" G_BEGIN_DECLS #define YUI_TRANSFERT_TYPE (yui_transfer_get_type ()) #define YUI_TRANSFERT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_TRANSFERT_TYPE, YuiTransfer)) #define YUI_TRANSFERT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_TRANSFERT_TYPE, YuiTransferClass)) #define IS_YUI_TRANSFERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_TRANSFERT_TYPE)) #define IS_YUI_TRANSFERT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_TRANSFERT_TYPE)) #define YUI_TRANSFER_LOAD 1 #define YUI_TRANSFER_LOAD_EXEC 2 #define YUI_TRANSFER_STORE 3 typedef struct _YuiTransfer YuiTransfer; typedef struct _YuiTransferClass YuiTransferClass; struct _YuiTransfer { GtkWindow window; GtkWidget * file_entry; GtkWidget * from_entry; GtkWidget * to_label; GtkWidget * to_entry; GtkWidget * transfer_button; int mode; }; struct _YuiTransferClass { GtkWindowClass parent_class; void (* yui_transfer) (YuiTransfer * yfe); }; GType yui_transfer_get_type (void); GtkWidget* yui_transfer_new (YuiWindow * yw); G_END_DECLS #endif yabause-0.9.13.1/src/gtk/yuiwindow.c000644 001750 001750 00000032756 12256006140 021175 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "yuiwindow.h" #include "gtkglwidget.h" #include "../yabause.h" #include "settings.h" static void yui_window_class_init (YuiWindowClass * klass); static void yui_window_init (YuiWindow * yfe); static gboolean yui_window_keypress(GtkWidget *widget, GdkEventKey *event, gpointer user_data); static gboolean yui_window_keyrelease(GtkWidget *widget, GdkEventKey *event, gpointer user_data); static void yui_window_keep_clean(GtkWidget * widget, GdkEventExpose * event, YuiWindow * yui); static void yui_window_toggle_fullscreen(GtkWidget * w, YuiWindow * yui); static void yui_window_toggle_frameskip(GtkWidget * w, YuiWindow * yui); static void yui_window_create_actions(YuiWindow * yw) { GtkAction * action; GtkToggleAction * taction; action = gtk_action_new("run", _("Run"), _("start emulation"), "gtk-media-play"); gtk_action_group_add_action_with_accel(yw->action_group, action, "r"); g_signal_connect_swapped(action, "activate", G_CALLBACK(yui_window_run), yw); action = gtk_action_new("pause", _("Pause"), _("pause emulation"), "gtk-media-pause"); gtk_action_group_add_action_with_accel(yw->action_group, action, "p"); g_signal_connect_swapped(action, "activate", G_CALLBACK(yui_window_pause), yw); action = gtk_action_new("reset", _("Reset"), _("reset emulation"), NULL); gtk_action_group_add_action_with_accel(yw->action_group, action, NULL); g_signal_connect_swapped(action, "activate", G_CALLBACK(yui_window_reset), yw); taction = gtk_toggle_action_new("fullscreen", _("Fullscreen"), NULL, "gtk-fullscreen"); gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), "f"); g_signal_connect(taction, "activate", G_CALLBACK(yui_window_toggle_fullscreen), yw); taction = gtk_toggle_action_new("frameskip", _("Frame Skip/Limiter"), NULL, NULL); gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); g_signal_connect(taction, "activate", G_CALLBACK(yui_window_toggle_frameskip), yw); action = gtk_action_new("quit", _("Quit"), NULL, "gtk-quit"); gtk_action_group_add_action_with_accel(yw->action_group, action, "q"); g_signal_connect(action, "activate", G_CALLBACK(gtk_main_quit), yw); taction = gtk_toggle_action_new("toggle_vdp1", _("VDP1"), NULL, NULL); gtk_toggle_action_set_active(taction, TRUE); gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); g_signal_connect(taction, "activate", G_CALLBACK(ToggleVDP1), NULL); taction = gtk_toggle_action_new("toggle_nbg0", _("NBG0"), NULL, NULL); gtk_toggle_action_set_active(taction, TRUE); gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); g_signal_connect(taction, "activate", G_CALLBACK(ToggleNBG0), NULL); taction = gtk_toggle_action_new("toggle_nbg1", _("NBG1"), NULL, NULL); gtk_toggle_action_set_active(taction, TRUE); gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); g_signal_connect(taction, "activate", G_CALLBACK(ToggleNBG1), NULL); taction = gtk_toggle_action_new("toggle_nbg2", _("NBG2"), NULL, NULL); gtk_toggle_action_set_active(taction, TRUE); gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); g_signal_connect(taction, "activate", G_CALLBACK(ToggleNBG2), NULL); taction = gtk_toggle_action_new("toggle_nbg3", _("NBG3"), NULL, NULL); gtk_toggle_action_set_active(taction, TRUE); gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); g_signal_connect(taction, "activate", G_CALLBACK(ToggleNBG3), NULL); taction = gtk_toggle_action_new("toggle_rbg0", _("RBG0"), NULL, NULL); gtk_toggle_action_set_active(taction, TRUE); gtk_action_group_add_action_with_accel(yw->action_group, GTK_ACTION(taction), NULL); g_signal_connect(taction, "activate", G_CALLBACK(ToggleRBG0), NULL); } GType yui_window_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiWindowClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_window_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiWindow), 0, (GInstanceInitFunc) yui_window_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_WINDOW, "YuiWindow", &yfe_info, 0); } return yfe_type; } enum { YUI_WINDOW_RUNNING_SIGNAL, YUI_WINDOW_PAUSED_SIGNAL, LAST_SIGNAL }; static guint yui_window_signals[LAST_SIGNAL] = { 0, 0 }; static void yui_window_class_init (YuiWindowClass * klass) { yui_window_signals[YUI_WINDOW_RUNNING_SIGNAL] = g_signal_new ("running", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(YuiWindowClass, yui_window_running), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); yui_window_signals[YUI_WINDOW_PAUSED_SIGNAL] = g_signal_new ("paused", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(YuiWindowClass, yui_window_paused), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void yui_set_accel_group(gpointer action, gpointer group) { gtk_action_set_accel_group(action, group); } static gboolean yui_window_log_delete(GtkWidget *widget, GdkEvent *event, YuiWindow *yw ) { yui_window_show_log( yw ); return TRUE; /* hide instead of killing */ } extern gchar * inifile; static void yui_window_destroy(GtkWidget * window) { gint x, y; char buffer[512]; gtk_window_get_position(GTK_WINDOW(window), &x, &y); sprintf(buffer, "%d", x); g_key_file_set_value(keyfile, "Gtk", "X", buffer); sprintf(buffer, "%d", y); g_key_file_set_value(keyfile, "Gtk", "Y", buffer); g_file_set_contents(inifile, g_key_file_to_data(keyfile, 0, 0), -1, 0); gtk_main_quit(); } static void yui_window_init (YuiWindow * yw) { GtkAccelGroup * accel_group = gtk_accel_group_new(); GtkWidget * scroll; yw->action_group = gtk_action_group_new("yui"); yui_window_create_actions(yw); gtk_action_set_sensitive(gtk_action_group_get_action(yw->action_group, "pause"), FALSE); gtk_action_set_sensitive(gtk_action_group_get_action(yw->action_group, "reset"), FALSE); { GList * list = gtk_action_group_list_actions(yw->action_group); g_list_foreach(list, yui_set_accel_group, accel_group); } gtk_window_add_accel_group(GTK_WINDOW(yw), accel_group); { const gchar * const * data_dir; gboolean pngfound = FALSE; gchar * pngfile; data_dir = g_get_system_data_dirs(); while (!pngfound && (*data_dir != NULL)) { pngfile = g_build_filename(*data_dir, "pixmaps", "yabause.png", NULL); if (g_file_test(pngfile, G_FILE_TEST_EXISTS)) { gtk_window_set_icon(GTK_WINDOW(yw), gdk_pixbuf_new_from_file(pngfile, NULL)); pngfound = TRUE; } data_dir++; } if (!pngfound) { gtk_window_set_icon(GTK_WINDOW(yw), gdk_pixbuf_new_from_file("yabause.png", NULL)); } } gtk_window_set_title (GTK_WINDOW(yw), "Yabause"); yw->box = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(yw), yw->box); yw->menu = create_menu(yw); gtk_box_pack_start(GTK_BOX(yw->box), yw->menu, FALSE, FALSE, 0); yw->area = yui_gl_new(); gtk_box_pack_start(GTK_BOX(yw->box), yw->area, TRUE, TRUE, 0); gtk_widget_set_size_request(GTK_WIDGET(yw->area), 320, 224); g_signal_connect(G_OBJECT(yw), "delete-event", G_CALLBACK(yui_window_destroy), NULL); g_signal_connect(G_OBJECT(yw), "key-press-event", G_CALLBACK(yui_window_keypress), yw); g_signal_connect(G_OBJECT(yw), "key-release-event", G_CALLBACK(yui_window_keyrelease), yw); yw->logpopup = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title( GTK_WINDOW( yw->logpopup ), "Yabause Logs" ); gtk_widget_set_size_request( yw->logpopup, 500, 300 ); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(yw->logpopup), scroll); g_signal_connect(G_OBJECT(yw->logpopup), "delete-event", G_CALLBACK(yui_window_log_delete), yw); yw->log = gtk_text_view_new(); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll), yw->log); gtk_widget_show(yw->box); gtk_widget_show_all(yw->menu); gtk_widget_show(yw->area); yw->clean_handler = g_signal_connect(yw->area, "expose-event", G_CALLBACK(yui_window_keep_clean), yw); yw->state = 0; } GtkWidget * yui_window_new(YuiAction * act, GCallback ifunc, gpointer idata, GSourceFunc rfunc, GCallback resetfunc) { GtkWidget * widget; YuiWindow * yw; widget = GTK_WIDGET(g_object_new(yui_window_get_type(), NULL)); yw = YUI_WINDOW(widget); yw->actions = act; yw->init_func = ifunc; yw->init_data = idata; yw->run_func = rfunc; yw->reset_func = resetfunc; return widget; } void yui_window_toggle_fullscreen(GtkWidget * w, YuiWindow * yui) { GtkAction * action = gtk_action_group_get_action(yui->action_group, "fullscreen"); static unsigned int beforefswidth = 1; static unsigned int beforefsheight = 1; if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action))) { beforefswidth = GTK_WIDGET(yui)->allocation.width; beforefsheight = GTK_WIDGET(yui)->allocation.height; gtk_widget_hide(yui->menu); gtk_window_fullscreen(GTK_WINDOW(yui)); } else { gtk_window_unfullscreen(GTK_WINDOW(yui)); gtk_widget_show(yui->menu); gtk_window_resize(GTK_WINDOW(yui), beforefswidth, beforefsheight); } } void yui_window_toggle_frameskip(GtkWidget * w, YuiWindow * yui) { GtkAction * action = gtk_action_group_get_action(yui->action_group, "frameskip"); gboolean active = gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(action)); if (active) EnableAutoFrameSkip (); else DisableAutoFrameSkip (); } static gboolean yui_window_keypress(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { PerKeyDown(event->keyval); return FALSE; } static gboolean yui_window_keyrelease(GtkWidget *widget, GdkEventKey *event, gpointer user_data) { PerKeyUp(event->keyval); return FALSE; } void yui_window_update(YuiWindow * yui) { if (!(yui->state & YUI_IS_RUNNING)) yui_gl_draw_pause(YUI_GL(yui->area)); else yui_gl_draw(YUI_GL(yui->area)); } void yui_window_log(YuiWindow * yui, const char * message) { GtkTextBuffer * buffer; GtkTextIter iter; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(yui->log)); gtk_text_buffer_get_start_iter(buffer, &iter); gtk_text_buffer_insert(buffer, &iter, message, -1); } void yui_window_show_log(YuiWindow * yui) { static int i = 0; if (i) gtk_widget_hide(yui->logpopup); else gtk_widget_show_all(yui->logpopup); i = !i; } static void yui_window_keep_clean(GtkWidget * widget, GdkEventExpose * event, YuiWindow * yui) { #ifdef HAVE_LIBGTKGLEXT glClear(GL_COLOR_BUFFER_BIT); #endif yui_window_update(yui); } void yui_window_start(YuiWindow * yui) { if ((yui->state & YUI_IS_INIT) == 0) { if (((int (*)(gpointer)) yui->init_func)(yui->init_data) == 0) { yui->state |= YUI_IS_INIT; gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "reset"), TRUE); VIDCore->Resize(GTK_WIDGET(yui->area)->allocation.width, GTK_WIDGET(yui->area)->allocation.height, FALSE); } } } void yui_window_run(YuiWindow * yui) { yui_window_start(yui); if ((yui->state & YUI_IS_INIT) && ((yui->state & YUI_IS_RUNNING) == 0)) { ScspUnMuteAudio(SCSP_MUTE_SYSTEM); g_idle_add(yui->run_func, GINT_TO_POINTER(1)); g_signal_emit(G_OBJECT(yui), yui_window_signals[YUI_WINDOW_RUNNING_SIGNAL], 0); yui->state |= YUI_IS_RUNNING; gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "run"), FALSE); gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "pause"), TRUE); } } void yui_window_pause(YuiWindow * yui) { if (yui->state & YUI_IS_RUNNING) { yui_gl_dump_screen(YUI_GL(yui->area)); ScspMuteAudio(SCSP_MUTE_SYSTEM); g_idle_remove_by_data(GINT_TO_POINTER(1)); g_signal_emit(G_OBJECT(yui), yui_window_signals[YUI_WINDOW_PAUSED_SIGNAL], 0); yui->state &= ~YUI_IS_RUNNING; gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "run"), TRUE); gtk_action_set_sensitive(gtk_action_group_get_action(yui->action_group, "pause"), FALSE); } } void yui_window_reset(YuiWindow * yui) { if (yui->state & YUI_IS_INIT) { yui->reset_func(); } } void yui_window_invalidate(YuiWindow * yui) { /* Emit a pause signal while already in pause means refresh all debug views */ if ( !(yui->state & YUI_IS_RUNNING )) g_signal_emit(G_OBJECT(yui), yui_window_signals[YUI_WINDOW_PAUSED_SIGNAL], 0); } void yui_window_set_fullscreen(YuiWindow * yui, gboolean f) { GtkAction * action = gtk_action_group_get_action(yui->action_group, "fullscreen"); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), f); } void yui_window_set_frameskip(YuiWindow * yui, gboolean f) { GtkAction * action = gtk_action_group_get_action(yui->action_group, "frameskip"); gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), f); } yabause-0.9.13.1/src/gtk/yabause.png000644 001750 001750 00000004760 12256006140 021124 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDR }JbgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe< ‚IDATxÚbüÿÿ?2 &!&Óåäqú@ ÛÝÔE@ªÖîùÿÿv_å€bq€‚ÿø'ˆ @Œèf Ø fÙÿòÊþ Ó÷°Ów Fàû^}ûù €z€üûO¿ý3O>|ë@ edVýÏÄbüj“0§ìRŸ‘Iæ?@ÁlÝäÜO/:„Ò @qF€bÚÁ 4òú¿¿ý€–ÿ©ïÞÿ‹e™§î6WžûO?g ° 0 Ò Å01€Âð: F êF&ñõÿÿ½dØæÂ ò'—÷>žO É¿¿Ï0ØV™ÿª:“þ³2Ë5¼±ÙYdÉ@ €à[ïÈíÀÈp™AM×ñ\QÝF;׺~ü~l² €@F¾ôØyƒó6³¬ã¬åWöÿûû—ašóú㱎ÿo¾ÿ¢ @ øáÙôæ{wŸým«ÝÁÄøßüëç O¿üÐ °/€Öœª1‡:|0ð#ÜwÞ{ €P¼ Š%¨iŒ01€+J8 yÿPÑô0Á¢î@ÁmJ¾gf5`fÓf`f¼ÅðýˉÀpsJùñv¯=_O ˆ¥1|ûúƒá××E !ò¬²«îÝ{@ðh†› ‡¬ (ª¬œÿ÷ ß‹6í±ù¿¹Ã\°T=+s,&mù Θ_+Zv1HH1pqs€m5©”áΕý‡&3\úûÿ¿53scÆñ‡”@ÕA© ¨™ Í¿ÿ'Ì96 «!ŽáÍýó®Ólyw[û1¼¹~‰ááÍk ‡Ÿ¿Ÿ @0Dõ,âç@ rÅ¥?ÿþücæ’dû÷ÍÈbÑïþy^}ói0¾ûñ{{ßÍ7^„’\‘Ã]¶IY³o20]åä±øô»=6µD09D±Ê/ \®€,J·X‘@W@,@âPò>0Ó!+>LD Àû-løÙ˜Îÿf€¨ùÿ˜ÿ±u‚ ËÂáÆÀÅç â;S/ž Äû¾®eæÔË11Ë€4ƒRéVB€‚‡3‹l?‹L¤l0Û†{7æ‚3 Ð5 g¿×6ªaøøá3Ó{AEÈðo Ø âo«3÷¢¿¿Ÿ0pr~ePRÑaàã—y%¤Ùŧ–ARJ˜áõó¥ å…ÀD÷d'óÌ n | òÊí«sÄ€I8)gH|¾ˆ˜<ƒŽ¾ÃÏï÷€øÍ‡®<¡zþ‚€%e §nƒJ+ Éÿ¶öË)¤Lbæ«|÷öCM¾ ÃBV›?~÷ýþ÷ÿŽ,/g7+ów€¹ ˆ{@K ¿ Ý?ÇðèÁ^>n0žÔàÌËÔ $*~Ä= ØLC˜7êÊÛOçzþ(Šœ€ø Zü‚ï¿ñ%0ÿÕó ­–ÌodååXX˜ÎÂÀŬ ’ Îq%0 9ÑÒοçOßüâ' Œ ¥Ü¬Lï\<Çpcï6†Ÿ?1|üù”F–_r¯â¹ ¢öóïÿ¼Ül¬ŠLÿÿdù6@üìÂëþÌüöëïwFFFó¦Ë//< )Rõ@Ì^îýúÇ0˜'ýü=‰Ff‚V‰ìû¿Öɨ—‚òˆýÝkÝbÀpaÅ–™gn„áÈ‹M@Qœˆb(kW€2Ü}ôò PØ$ aÏXvæ‡ðíÏA9ƒy Hþƒ±@ê>@Íú@èUçz À,2XØ‘ŒÿÃð÷ç6¦ÿøYÃÛrí J@ ¢¥ø÷ô›¿žn~ü{°ÙÃÀÎíÏðï"Aýù¹Ÿù߯-Ÿ~<ò…‰F¤@a`çòf`å``gce`cgeøøf+°Ž»’š,’@IT,½&ϿВg>»0ƒ¤\,ï_~ýüÍðãç7†o72üÿt<cØr;.P9JÎÿúþÖ4à.«(qìÕß“_ÿü—ã4c—vdàäbuì Oîocxúè(HÙ`:Bݤ D%t´ ¾ÿ Ư_\bxþh4È Ž~ Íýÿ¡Å!@!gDPÜ€Š= ÎâÕÀRÝd°€ƒ‰U&7s0<¾ŒáðÞ™0ƒA5-{[ç, Å?¾~ùÁpòðL†OÏ¡8j3Ô ôÇ @°¢”Iu¸ˆ­8<ë‘ZDûAlÿðvE`aÏÃà †¿0<z›AAYŸáÏï ß¿ý`xÿî=ÃÒ¹ Þ=e–Å+í9üþûO˜™?r²0½÷Úuó/Ô1*ÏA–ûq ³±ÐòÛhé”ØÎƒj_[ç¿Ð\¸#@¥ëŸ?Á–?¼aÑÌ `ëŒáC‘K–"÷ÿR^.C.66†ß¿2|ªyýý×K`Û¨B‘{ /ó_€b‚VéÉ@¬JDè–CK!P{QÈl8¼w ôž4Œt³iͰåLŒ çZ±Æj ³O³¶±6ôž¸”Á"§’AI]›AZ€—A‘SêóÏ¿ú˜ªƒ䀯Ðv0¨x÷‹®BèˆF}ÿÎEp-÷úÕ{†—/Þ‚éWN2øWvÆj  Ìÿþð½{xáRg Ãí³Þ<{Œ¦ß Iîÿø÷ä!€‚¥} {3Ëñ/ ^mR\ºò'ZtLU¿Y¥ëXYY^}|Ÿïó­Û’ç[ÿʲý²`ddðš¿õþ‡oâÿÿg ƒŸ'ÿ€ÞûtÀËo¿Þ}ûµS’—#¦äìÓ7„œ @¡á m]š yäˆÏP\Û€ZP€ PËlÖûŸ¿o>úøÝøÝ¯ßfÿþýg&Â+BlÊÎ=}S @8ë ƒ„€”:4[ªCK<˜…ß]¯—¿ÿ³Þù_äÙ©_ú,¯œ’{óáHí$`T!¶. ’+#`€¢ˆ‹×X©3šL¿~>eøýóˆïˆ­mŠ  X+ˆøö§'b@ xu `^öÑ«¾“ÚIEND®B`‚yabause-0.9.13.1/src/gtk/pergtk.h000644 001750 001750 00000001623 12256006140 020425 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PERGTK_H #define PERGTK_H #include "../peripheral.h" #define PERCORE_GTK 2 extern PerInterface_struct PERGTK; #endif yabause-0.9.13.1/src/gtk/yuipage.c000644 001750 001750 00000004616 12256006135 020600 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "yuifileentry.h" #include "yuirange.h" #include "yuiresolution.h" #include "yuipage.h" #include "../core.h" static void yui_page_class_init (YuiPageClass * klass); static void yui_page_init (YuiPage * yfe); GType yui_page_get_type (void) { static GType yfe_type = 0; if (!yfe_type) { static const GTypeInfo yfe_info = { sizeof (YuiPageClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) yui_page_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (YuiPage), 0, (GInstanceInitFunc) yui_page_init, NULL, }; yfe_type = g_type_register_static(GTK_TYPE_VBOX, "YuiPage", &yfe_info, 0); } return yfe_type; } static void yui_page_class_init (UNUSED YuiPageClass * klass) { } static void yui_page_init (UNUSED YuiPage * yp) { } GtkWidget * yui_page_new(GKeyFile * keyfile) { GtkWidget * widget; YuiPage * yp; widget = GTK_WIDGET(g_object_new(yui_page_get_type(), NULL)); yp = YUI_PAGE(widget); yp->keyfile = keyfile; return widget; } GtkWidget * yui_page_add(YuiPage * yp, const gchar * name) { GtkWidget * label; GtkWidget * frame; GtkWidget * box; gchar buffer[1024]; frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(yp), frame, FALSE, TRUE, 0); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); box = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), box); sprintf(buffer, "%s", name); label = gtk_label_new(buffer); gtk_frame_set_label_widget(GTK_FRAME(frame), label); gtk_label_set_use_markup(GTK_LABEL(label), TRUE); return box; } yabause-0.9.13.1/src/gtk/yuiresolution.h000644 001750 001750 00000003724 12256006140 022067 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_RESOLUTION_H #define YUI_RESOLUTION_H #include #include #include G_BEGIN_DECLS #define YUI_RESOLUTION_TYPE (yui_resolution_get_type ()) #define YUI_RESOLUTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), YUI_RESOLUTION_TYPE, YuiResolution)) #define YUI_RESOLUTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), YUI_RESOLUTION_TYPE, YuiResolutionClass)) #define IS_YUI_RESOLUTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), YUI_RESOLUTION_TYPE)) #define IS_YUI_RESOLUTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), YUI_RESOLUTION_TYPE)) typedef struct _YuiResolution YuiResolution; typedef struct _YuiResolutionClass YuiResolutionClass; struct _YuiResolution { GtkHBox table; GtkWidget * entry_w; GtkWidget * entry_h; GtkWidget * options; GKeyFile * keyfile; gchar * group; }; struct _YuiResolutionClass { GtkHBoxClass parent_class; void (* yui_resolution) (YuiResolution *yie); }; GType yui_resolution_get_type (void); GtkWidget* yui_resolution_new (GKeyFile * keyfile, const gchar * group); G_END_DECLS #endif /* YUI_RESOLUTION_H */ yabause-0.9.13.1/src/gtk/settings.c000644 001750 001750 00000044510 12256006140 020766 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006-2011 Guillaume Duhamel Copyright 2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "settings.h" #include "yuicheckbutton.h" #include "yuifileentry.h" #include "yuirange.h" #include "yuipage.h" #include "yuiinputentry.h" #include "yuiresolution.h" #include "../scsp.h" typedef struct { int id; const char *Name; } GenericInterface_struct; void cores_to_range(void * pointer, YuiRangeItem ** items) { GenericInterface_struct ** cores; GenericInterface_struct * core; int i; if (*items != NULL) return; cores = pointer; i = 0; core = cores[i]; while(core) { i++; core = cores[i]; } *items = malloc(sizeof(YuiRangeItem) * (i + 1)); i = 0; core = cores[i]; while(core) { char buffer[1024]; (*items)[i].name = strdup(core->Name); sprintf(buffer, "%d", core->id); (*items)[i].value = strdup(buffer); i++; core = cores[i]; } (*items)[i].name = NULL; (*items)[i].value = NULL; } extern CDInterface *SH2CoreList[]; extern CDInterface *M68KCoreList[]; extern CDInterface *CDCoreList[]; extern CDInterface *VIDCoreList[]; extern CDInterface *OSDCoreList[]; extern CDInterface *SNDCoreList[]; extern CDInterface *PERCoreList[]; YuiRangeItem * sh2interpreters = NULL; YuiRangeItem * m68kinterpreters = NULL; YuiRangeItem * cdcores = NULL; YuiRangeItem * vidcores = NULL; YuiRangeItem * osdcores = NULL; YuiRangeItem * sndcores = NULL; YuiRangeItem * percores = NULL; YuiRangeItem regions[] = { { "Auto" , "Auto-detect" }, { "J" , "Japan (NTSC)" }, { "T", "Asia (NTSC)" }, { "U", "North America (NTSC)" }, { "B", "Central/South America (NTSC)" }, { "K", "Korea (NTSC)" }, { "A", "Asia (PAL)" }, { "E", "Europe + others (PAL)" }, { "L", "Central/South America (PAL)" }, { 0, 0 } }; YuiRangeItem carttypes[] = { { "0", "None" }, { "1", "Pro Action Replay" }, { "2", "4 Mbit Backup Ram" }, { "3", "8 Mbit Backup Ram" }, { "4", "16 Mbit Backup Ram" }, { "5", "32 Mbit Backup Ram" }, { "6", "8 Mbit Dram" }, { "7", "32 Mbit Dram" }, { "8", "Netlink" }, { "9", "16 Mbit ROM" }, { 0, 0 } }; const gchar * keys1[] = { "Up", "Right", "Down", "Left", "R", "L", 0 }; const gchar * keys2[] = { "A", "B", "C", "X", "Y", "Z", "Start", 0 }; YuiRangeItem vidformats[] = { { "0", "NTSC" }, { "1", "PAL" }, { 0, 0 } }; YuiRangeItem mousepercores[] = { { "2", "Gtk Input Interface" }, { 0, 0 } }; static void hide_show_cart_path(YuiRange * instance, gpointer data) { gint i = yui_range_get_active(instance); if (i == 8) { gtk_widget_hide(data); } else { gtk_widget_show(data); } } static void hide_show_netlink(YuiRange * instance, gpointer data) { gint i = yui_range_get_active(instance); if (i != 8) { gtk_widget_hide(data); } else { gtk_widget_show(data); } } static void percore_changed(GtkWidget * widget, gpointer data) { const char * core_s = percores[gtk_combo_box_get_active(GTK_COMBO_BOX(widget))].value; GList * entrylist = data; int core; sscanf(core_s, "%d", &core); PerDeInit(); PerInit(core); while(entrylist) { yui_input_entry_update(entrylist->data); entrylist = g_list_next(entrylist); } } static void pertype_display_pad(GtkWidget * box) { GtkWidget * table4, * table5; GtkWidget * box_percore = gtk_vbox_new(FALSE, 10); GtkWidget * select_percore = yui_range_new(keyfile, "General", "PerCore", percores); GList * entrylist = NULL; gtk_container_set_border_width(GTK_CONTAINER(select_percore), 0); gtk_container_set_border_width(GTK_CONTAINER(box_percore), 10); gtk_box_pack_start(GTK_BOX (box_percore), select_percore, FALSE, FALSE, 0); table4 = yui_input_entry_new(keyfile, "Pad", keys1); entrylist = g_list_append(entrylist, table4); gtk_box_pack_start (GTK_BOX (box_percore), table4, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box), box_percore, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box), gtk_vseparator_new(), TRUE, TRUE, 0); table5 = yui_input_entry_new(keyfile, "Pad", keys2); entrylist = g_list_append(entrylist, table5); gtk_container_set_border_width(GTK_CONTAINER(table5), 10); gtk_box_pack_start (GTK_BOX (box), table5, TRUE, TRUE, 0); g_signal_connect(GTK_COMBO_BOX(YUI_RANGE(select_percore)->combo), "changed", G_CALLBACK(percore_changed), entrylist); gtk_widget_show_all(box); } static void mouse_speed_change(GtkWidget * range, gpointer data) { g_key_file_set_double(keyfile, "General", "MouseSpeed", gtk_range_get_value(GTK_RANGE(range))); } static void pertype_display_mouse(GtkWidget * box) { GtkWidget * scale; GtkWidget * table5; GtkWidget * box_percore = gtk_vbox_new(FALSE, 10); GtkWidget * select_percore = yui_range_new(keyfile, "General", "MousePerCore", mousepercores); GList * entrylist = NULL; gtk_container_set_border_width(GTK_CONTAINER(select_percore), 0); gtk_container_set_border_width(GTK_CONTAINER(box_percore), 10); gtk_box_pack_start(GTK_BOX (box_percore), select_percore, FALSE, FALSE, 0); scale = gtk_hscale_new_with_range(0, 10, 0.1); gtk_range_set_value(GTK_RANGE(scale), g_key_file_get_double(keyfile, "General", "MouseSpeed", NULL)); g_signal_connect(scale, "value-changed", G_CALLBACK(mouse_speed_change), NULL); gtk_box_pack_start(GTK_BOX (box_percore), scale, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (box), box_percore, TRUE, TRUE, 0); gtk_box_pack_start (GTK_BOX (box), gtk_vseparator_new(), TRUE, TRUE, 0); table5 = yui_input_entry_new(keyfile, "Mouse", PerMouseNames); entrylist = g_list_append(entrylist, table5); gtk_container_set_border_width(GTK_CONTAINER(table5), 10); gtk_box_pack_start (GTK_BOX (box), table5, TRUE, TRUE, 0); g_signal_connect(GTK_COMBO_BOX(YUI_RANGE(select_percore)->combo), "changed", G_CALLBACK(percore_changed), entrylist); gtk_widget_show_all(box); } static void pertype_changed(GtkWidget * widget, gpointer data) { GtkTreePath * path; gchar * strpath; int i; GtkWidget * box = data; GList * children; GtkWidget * child; children = gtk_container_get_children(GTK_CONTAINER(box)); for(i = 1;i < 4;i++) { child = g_list_nth_data(children, i); if (child != NULL) gtk_widget_destroy(child); } gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL); if (path) { int i; strpath = gtk_tree_path_to_string(path); sscanf(strpath, "%d", &i); switch(i) { case 0: g_key_file_set_integer(keyfile, "General", "PerType", PERPAD); pertype_display_pad(box); break; case 1: g_key_file_set_integer(keyfile, "General", "PerType", PERMOUSE); pertype_display_mouse(box); break; } g_free(strpath); gtk_tree_path_free(path); } } static void frameskip_toggled(GtkWidget * widget, gpointer data) { g_key_file_set_integer(keyfile, "General", "Frameskip", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))); } static void disable_enable_audio_sync(YuiCheckButton *audiosync) { ScspSetFrameAccurate(yui_check_button_get_active(audiosync)); } static void disable_enable_fixed_base_time(YuiCheckButton *clocksync, YuiCheckButton *fixedbasetime) { gtk_widget_set_sensitive(GTK_WIDGET(fixedbasetime), yui_check_button_get_active(clocksync)); } static void volume_changed(GtkRange * range, gpointer data) { g_key_file_set_integer(keyfile, "General", "Volume", (int) gtk_range_get_value(range)); } GtkWidget* create_dialog1(void) { GtkWidget *dialog1; GtkWidget *notebook1; GtkWidget *vbox17; GtkWidget *hbox22; GtkWidget *button11; GtkWidget *button12; GtkWidget * general, * video_sound, * cart_memory, *advanced, * sound; GtkWidget * box; u8 perid; cores_to_range(SH2CoreList, &sh2interpreters); cores_to_range(M68KCoreList, &m68kinterpreters); cores_to_range(CDCoreList, &cdcores); cores_to_range(VIDCoreList, &vidcores); cores_to_range(OSDCoreList, &osdcores); cores_to_range(SNDCoreList, &sndcores); cores_to_range(PERCoreList, &percores); dialog1 = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dialog1), "Yabause configuration"); gtk_window_set_icon_name (GTK_WINDOW (dialog1), "gtk-preferences"); gtk_window_set_type_hint (GTK_WINDOW (dialog1), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_set_resizable(GTK_WINDOW(dialog1), FALSE); notebook1 = gtk_notebook_new (); gtk_widget_show(notebook1); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog1)->vbox), notebook1, TRUE, TRUE, 0); /* * General configuration */ general = yui_page_new(keyfile); box = yui_page_add(YUI_PAGE(general), _("Bios")); gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "BiosPath", YUI_FILE_ENTRY_BROWSE, NULL)); box = yui_page_add(YUI_PAGE(general), _("Cdrom")); gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "CDROMCore", cdcores)); gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "CDROMDrive", YUI_FILE_ENTRY_BROWSE, NULL)); box = yui_page_add(YUI_PAGE(general), _("Save States")); gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "StatePath", YUI_FILE_ENTRY_BROWSE | YUI_FILE_ENTRY_DIRECTORY, NULL)); gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), general, gtk_label_new (_("General"))); gtk_widget_show_all(general); /* * Video configuration */ video_sound = yui_page_new(keyfile); box = yui_page_add(YUI_PAGE(video_sound), _("Video Core")); gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "VideoCore", vidcores)); #ifdef YAB_PORT_OSD box = yui_page_add(YUI_PAGE(video_sound), _("OSD Core")); gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "OSDCore", osdcores)); #endif box = yui_page_add(YUI_PAGE(video_sound), _("Resolution")); gtk_container_add(GTK_CONTAINER(box), yui_resolution_new(keyfile, "General")); box = yui_page_add(YUI_PAGE(video_sound), _("Video Format")); gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "VideoFormat", vidformats)); box = yui_page_add(YUI_PAGE(video_sound), _("Frame Skip/Limiter")); { GtkWidget * frameskip = gtk_check_button_new_with_label("Enable frame skipping/limiting"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(frameskip), g_key_file_get_integer(keyfile, "General", "Frameskip", NULL)); gtk_container_set_border_width(GTK_CONTAINER(frameskip), 10); g_signal_connect(frameskip, "toggled", G_CALLBACK(frameskip_toggled), NULL); gtk_container_add(GTK_CONTAINER(box), frameskip); } gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), video_sound, gtk_label_new (_("Video"))); gtk_widget_show_all(video_sound); /* * Sound configuration */ sound = yui_page_new(keyfile); box = yui_page_add(YUI_PAGE(sound), _("Sound Core")); gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "SoundCore", sndcores)); { GtkWidget * volume; box = yui_page_add(YUI_PAGE(sound), _("Volume")); gtk_container_set_border_width(GTK_CONTAINER(box), 10); volume = gtk_hscale_new_with_range(0, 100, 1); gtk_range_set_value(GTK_RANGE(volume), g_key_file_get_integer(keyfile, "General", "Volume", NULL)); g_signal_connect(volume, "value-changed", G_CALLBACK(volume_changed), NULL); gtk_container_add(GTK_CONTAINER(box), volume); } gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), sound, gtk_label_new (_("Sound"))); gtk_widget_show_all(sound); /* * Cart/Memory configuration */ cart_memory = yui_page_new(keyfile); box = yui_page_add(YUI_PAGE(cart_memory), _("Cartridge")); { GtkWidget * w1, * w2, * w3; w1 = yui_range_new(keyfile, "General", "CartType", carttypes); gtk_container_add(GTK_CONTAINER(box), w1); w2 = yui_file_entry_new(keyfile, "General", "CartPath", YUI_FILE_ENTRY_BROWSE, NULL); gtk_container_add(GTK_CONTAINER(box), w2); w3 = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(w3), yui_file_entry_new(keyfile, "General", "NetlinkHost", 0, "Host"), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(w3), yui_file_entry_new(keyfile, "General", "NetlinkPort", 0, "Port"), TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(box), w3); g_signal_connect(w1, "changed", G_CALLBACK(hide_show_cart_path), w2); g_signal_connect(w1, "changed", G_CALLBACK(hide_show_netlink), w3); box = yui_page_add(YUI_PAGE(cart_memory), _("Memory")); gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "BackupRamPath", YUI_FILE_ENTRY_BROWSE, NULL)); box = yui_page_add(YUI_PAGE(cart_memory), _("Mpeg ROM")); gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "MpegRomPath", YUI_FILE_ENTRY_BROWSE, NULL)); gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), cart_memory, gtk_label_new (_("Cart/Memory"))); gtk_widget_show_all(cart_memory); if (yui_range_get_active(YUI_RANGE(w1)) == 8) gtk_widget_hide(w2); else gtk_widget_hide(w3); } /* * Input Configuration */ vbox17 = gtk_vbox_new (FALSE, 0); hbox22 = gtk_hbox_new (FALSE, 0); { GtkWidget * controllerscroll; GtkTreeStore * controllerlist; GtkWidget * controllerlistview; GtkCellRenderer * controllerrenderer; GtkTreeViewColumn * controllercolumn; GtkTreeIter iter; GtkTreePath * path; controllerscroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(controllerscroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); controllerlist = gtk_tree_store_new(1, G_TYPE_STRING); controllerlistview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(controllerlist)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(controllerlistview), FALSE); controllerrenderer = gtk_cell_renderer_text_new(); controllercolumn = gtk_tree_view_column_new_with_attributes("Controller", controllerrenderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW (controllerlistview), controllercolumn); gtk_tree_store_append(controllerlist, &iter, NULL); gtk_tree_store_set(controllerlist, &iter, 0, "Pad", -1); gtk_tree_store_append(controllerlist, &iter, NULL); gtk_tree_store_set(controllerlist, &iter, 0, "Mouse", -1); gtk_container_add(GTK_CONTAINER(controllerscroll), controllerlistview); gtk_box_pack_start (GTK_BOX (hbox22), controllerscroll, TRUE, TRUE, 0); gtk_tree_view_expand_all(GTK_TREE_VIEW(controllerlistview)); g_signal_connect(controllerlistview, "cursor-changed", G_CALLBACK(pertype_changed), hbox22); perid = g_key_file_get_integer(keyfile, "General", "PerType", NULL); switch(perid) { case PERMOUSE: path = gtk_tree_path_new_from_string("1"); break; case PERPAD: default: path = gtk_tree_path_new_from_string("0"); break; } gtk_tree_view_set_cursor(GTK_TREE_VIEW(controllerlistview), path, NULL, FALSE); gtk_tree_path_free(path); } gtk_box_pack_start (GTK_BOX (vbox17), hbox22, TRUE, TRUE, 0); //pertype_display_pad(hbox22); gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), vbox17, gtk_label_new (_("Input"))); gtk_widget_show_all(vbox17); /* * Advanced configuration */ advanced = yui_page_new(keyfile); box = yui_page_add(YUI_PAGE(advanced), _("Region")); gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "Region", regions)); box = yui_page_add(YUI_PAGE(advanced), _("SH2 Interpreter")); gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "SH2Int", sh2interpreters)); box = yui_page_add(YUI_PAGE(advanced), _("M68k Interpreter")); gtk_container_add(GTK_CONTAINER(box), yui_range_new(keyfile, "General", "M68kInt", m68kinterpreters)); box = yui_page_add(YUI_PAGE(advanced), _("Audio Sync")); { GtkWidget *button = yui_check_button_new( _("Synchronize audio output with emulation"), keyfile, "General", "AudioSync" ); gtk_container_add(GTK_CONTAINER(box), button); g_signal_connect(button, "changed", G_CALLBACK(disable_enable_audio_sync), NULL); } box = yui_page_add(YUI_PAGE(advanced), _("Clock Sync")); { GtkWidget *button1, *button2; button1 = yui_check_button_new( _("Synchronize internal clock with emulation"), keyfile, "General", "ClockSync" ); gtk_container_add(GTK_CONTAINER(box), button1); button2 = yui_check_button_new( _("Always start from 1998-01-01 12:00"), keyfile, "General", "FixedBaseTime" ); gtk_container_add(GTK_CONTAINER(box), button2); if (!yui_check_button_get_active(YUI_CHECK_BUTTON(button1))) gtk_widget_set_sensitive(button2, FALSE); g_signal_connect(button1, "changed", G_CALLBACK(disable_enable_fixed_base_time), button2); } box = yui_page_add(YUI_PAGE(advanced), _("Threads")); { GtkWidget *button = yui_check_button_new( _("Use multithreaded emulation (EXPERIMENTAL!)"), keyfile, "General", "UseThreads" ); gtk_container_add(GTK_CONTAINER(box), button); } #ifdef HAVE_LIBMINI18N box = yui_page_add(YUI_PAGE(advanced), _("Translation")); gtk_container_add(GTK_CONTAINER(box), yui_file_entry_new(keyfile, "General", "TranslationPath", YUI_FILE_ENTRY_BROWSE, NULL)); #endif gtk_notebook_append_page(GTK_NOTEBOOK(notebook1), advanced, gtk_label_new (_("Advanced"))); gtk_widget_show_all(advanced); /* * Dialog buttons */ button11 = gtk_button_new_from_stock ("gtk-cancel"); gtk_dialog_add_action_widget (GTK_DIALOG (dialog1), button11, GTK_RESPONSE_CANCEL); GTK_WIDGET_SET_FLAGS (button11, GTK_CAN_DEFAULT); gtk_widget_show(button11); button12 = gtk_button_new_from_stock ("gtk-ok"); gtk_dialog_add_action_widget (GTK_DIALOG (dialog1), button12, GTK_RESPONSE_OK); GTK_WIDGET_SET_FLAGS (button12, GTK_CAN_DEFAULT); gtk_widget_show(button12); gtk_widget_show(dialog1); return dialog1; } void yui_texture_free(guchar *pixels, gpointer data) { free(pixels); } yabause-0.9.13.1/src/gtk/settings.h000644 001750 001750 00000002603 12256006140 020770 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel Copyright 2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YUI_SETTINGS_H #define YUI_SETTINGS_H #include #include "../vdp1.h" #include "../vdp2.h" #include "../scsp.h" #include "../yabause.h" #include "../cdbase.h" #include "../peripheral.h" #include "yuiwindow.h" extern GKeyFile * keyfile; extern yabauseinit_struct yinit; extern void YuiSaveState(void); extern void YuiLoadState(void); GtkWidget* create_dialog1(void); GtkWidget* create_menu(YuiWindow *); void yui_conf(void); void yui_resize(guint, guint, gboolean); void gtk_yui_toggle_fullscreen(void); void yui_texture_free(guchar *pixels, gpointer data); #endif yabause-0.9.13.1/src/osdcore.c000644 001750 001750 00000017471 12256006124 020007 0ustar00guillaumeguillaume000000 000000 /* Copyright 2012 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "osdcore.h" #include "vdp1.h" #include "font.h" #include "titan/titan.h" #include #include #ifndef YAB_PORT_OSD /* Heya fellow port developper :) If you're reading this, that may be because you want to use your own OSD core in your port and you're about to do it here... Don't. Please define the CPP constant YAB_PORT_OSD and define your own list of OSD cores in your port. This definition was added here to avoid breaking "everything" when we the new OSD system was added. */ OSD_struct *OSDCoreList[] = { &OSDDummy, #ifdef HAVE_LIBGLUT &OSDGlut, #endif &OSDSoft, NULL }; #else extern OSD_struct * OSDCoreList[]; #endif static OSD_struct * OSD = NULL; static OSDMessage_struct osdmessages[OSDMSG_COUNT]; int OSDInit(int coreid) { int i; for (i = 0; OSDCoreList[i] != NULL; i++) { if (OSDCoreList[i]->id == coreid) { OSD = OSDCoreList[i]; break; } } if (OSD == NULL) return -1; if (OSD->Init() != 0) return -1; memset(osdmessages, 0, sizeof(osdmessages)); osdmessages[OSDMSG_FPS].hidden = 1; osdmessages[OSDMSG_DEBUG].hidden = 1; return 0; } void OSDDeInit() { if (OSD) OSD->DeInit(); OSD = NULL; } int OSDChangeCore(int coreid) { int preservefps, fpshidden, dbghidden; preservefps = (OSD != NULL); fpshidden = osdmessages[OSDMSG_FPS].hidden; dbghidden = osdmessages[OSDMSG_DEBUG].hidden; OSDDeInit(); OSDInit(coreid); if (preservefps) { osdmessages[OSDMSG_FPS].hidden = fpshidden; osdmessages[OSDMSG_DEBUG].hidden = dbghidden; } return 0; } void OSDPushMessage(int msgtype, int ttl, const char * format, ...) { va_list arglist; char message[1024]; if (ttl == 0) return; va_start(arglist, format); vsprintf(message, format, arglist); va_end(arglist); osdmessages[msgtype].type = msgtype; osdmessages[msgtype].message = strdup(message); osdmessages[msgtype].timetolive = ttl; osdmessages[msgtype].timeleft = ttl; } int OSDDisplayMessages(pixel_t * buffer, int w, int h) { int i = 0; int somethingnew = 0; if (OSD == NULL) return somethingnew; for(i = 0;i < OSDMSG_COUNT;i++) if (osdmessages[i].timeleft > 0) { if (osdmessages[i].hidden == 0) { somethingnew = 1; OSD->DisplayMessage(osdmessages + i, buffer, w, h); } osdmessages[i].timeleft--; if (osdmessages[i].timeleft == 0) free(osdmessages[i].message); } return somethingnew; } void OSDToggle(int what) { if ((what < 0) || (what >= OSDMSG_COUNT)) return; osdmessages[what].hidden = 1 - osdmessages[what].hidden; } int OSDIsVisible(int what) { if ((what < 0) || (what >= OSDMSG_COUNT)) return -1; return 1 - osdmessages[what].hidden; } void OSDSetVisible(int what, int visible) { if ((what < 0) || (what >= OSDMSG_COUNT)) return; visible = visible == 0 ? 0 : 1; osdmessages[what].hidden = 1 - visible; } int OSDUseBuffer(void) { if (OSD == NULL) return 0; return OSD->UseBuffer(); } void ToggleFPS() { OSDToggle(OSDMSG_FPS); } int GetOSDToggle(void) { return OSDIsVisible(OSDMSG_FPS); } void SetOSDToggle(int toggle) { OSDSetVisible(OSDMSG_FPS, toggle); } void DisplayMessage(const char* str) { OSDPushMessage(OSDMSG_STATUS, 120, str); } static int OSDDummyInit(void); static void OSDDummyDeInit(void); static void OSDDummyReset(void); static void OSDDummyDisplayMessage(OSDMessage_struct * message, pixel_t * buffer, int w, int h); static int OSDDummyUseBuffer(void); OSD_struct OSDDummy = { OSDCORE_DUMMY, "Dummy OSD Interface", OSDDummyInit, OSDDummyDeInit, OSDDummyReset, OSDDummyDisplayMessage, OSDDummyUseBuffer, }; int OSDDummyInit(void) { return 0; } void OSDDummyDeInit(void) { } void OSDDummyReset(void) { } void OSDDummyDisplayMessage(OSDMessage_struct * message, pixel_t * buffer, int w, int h) { } int OSDDummyUseBuffer(void) { return 0; } #ifdef HAVE_LIBGLUT #ifdef __APPLE__ #include #else #include #endif static int OSDGlutInit(void); static void OSDGlutDeInit(void); static void OSDGlutReset(void); static void OSDGlutDisplayMessage(OSDMessage_struct * message, pixel_t * buffer, int w, int h); static int OSDGlutUseBuffer(void); OSD_struct OSDGlut = { OSDCORE_GLUT, "Glut OSD Interface", OSDGlutInit, OSDGlutDeInit, OSDGlutReset, OSDGlutDisplayMessage, OSDGlutUseBuffer }; int OSDGlutInit(void) { int fake_argc = 1; char * fake_argv[] = { "yabause" }; static int glutinited = 0; if (!glutinited) { glutInit(&fake_argc, fake_argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_STENCIL); glutinited = 1; } return 0; } void OSDGlutDeInit(void) { } void OSDGlutReset(void) { } void OSDGlutDisplayMessage(OSDMessage_struct * message, pixel_t * buffer, int w, int h) { int LeftX=9; int Width=500; int TxtY=11; int Height=13; int i, msglength; int vidwidth, vidheight; VIDCore->GetGlSize(&vidwidth, &vidheight); Width = vidwidth - 2 * LeftX; switch(message->type) { case OSDMSG_STATUS: TxtY = vidheight - (Height + TxtY); break; } msglength = strlen(message->message); glBegin(GL_POLYGON); glColor3f(0, 0, 0); glVertex2i(LeftX, TxtY); glVertex2i(LeftX + Width, TxtY); glVertex2i(LeftX + Width, TxtY + Height); glVertex2i(LeftX, TxtY + Height); glEnd(); glColor3f(1.0f, 1.0f, 1.0f); glRasterPos2i(10, TxtY + 11); for (i = 0; i < msglength; i++) { glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, message->message[i]); } glColor3f(1, 1, 1); } int OSDGlutUseBuffer(void) { return 0; } #endif static int OSDSoftInit(void); static void OSDSoftDeInit(void); static void OSDSoftReset(void); static void OSDSoftDisplayMessage(OSDMessage_struct * message, pixel_t * buffer, int w, int h); static int OSDSoftUseBuffer(void); OSD_struct OSDSoft = { OSDCORE_SOFT, "Software OSD Interface", OSDSoftInit, OSDSoftDeInit, OSDSoftReset, OSDSoftDisplayMessage, OSDSoftUseBuffer }; int OSDSoftInit(void) { return 0; } void OSDSoftDeInit(void) { } void OSDSoftReset(void) { } void OSDSoftDisplayMessage(OSDMessage_struct * message, pixel_t * buffer, int w, int h) { int i; char * c; int loffset = 0; if (buffer == NULL) return; switch (message->type) { case OSDMSG_STATUS: loffset = h - 48; break; } c = message->message; i = 0; while(*c) { if (*c >= 47) { int first_line, l, p; first_line = *c * 10; for(l = 0;l < 10;l++) { for(p = 0;p < 9;p++) { if (font[first_line + l][p] == '.') TitanWriteColor(buffer, w, (i * 8) + 20 + p, loffset + l + 20, 0xFF000000); else if (font[first_line + l][p] == '#') TitanWriteColor(buffer, w, (i * 8) + 20 + p, loffset + l + 20, 0xFFFFFFFF); } } } c++; i++; } } int OSDSoftUseBuffer(void) { return 1; } yabause-0.9.13.1/src/cs0.c000644 001750 001750 00000110040 12256006201 017014 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004-2005 Theo Berkau Copyright 2006 Ex-Cyber Copyright 2005 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "cs0.h" #include "error.h" #include "japmodem.h" #include "netlink.h" cartridge_struct *CartridgeArea; ////////////////////////////////////////////////////////////////////////////// // Dummy/No Cart Functions ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL DummyCs0ReadByte(UNUSED u32 addr) { return 0xFF; } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL DummyCs0ReadWord(UNUSED u32 addr) { return 0xFFFF; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL DummyCs0ReadLong(UNUSED u32 addr) { return 0xFFFFFFFF; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs0WriteByte(UNUSED u32 addr, UNUSED u8 val) { } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs0WriteWord(UNUSED u32 addr, UNUSED u16 val) { } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs0WriteLong(UNUSED u32 addr, UNUSED u32 val) { } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL DummyCs1ReadByte(UNUSED u32 addr) { return 0xFF; } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL DummyCs1ReadWord(UNUSED u32 addr) { return 0xFFFF; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL DummyCs1ReadLong(UNUSED u32 addr) { return 0xFFFFFFFF; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs1WriteByte(UNUSED u32 addr, UNUSED u8 val) { } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs1WriteWord(UNUSED u32 addr, UNUSED u16 val) { } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs1WriteLong(UNUSED u32 addr, UNUSED u32 val) { } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL DummyCs2ReadByte(UNUSED u32 addr) { return 0xFF; } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL DummyCs2ReadWord(UNUSED u32 addr) { return 0xFFFF; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL DummyCs2ReadLong(UNUSED u32 addr) { return 0xFFFFFFFF; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs2WriteByte(UNUSED u32 addr, UNUSED u8 val) { } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs2WriteWord(UNUSED u32 addr, UNUSED u16 val) { } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DummyCs2WriteLong(UNUSED u32 addr, UNUSED u32 val) { } ////////////////////////////////////////////////////////////////////////////// // Action Replay 4M Plus funcions ////////////////////////////////////////////////////////////////////////////// typedef enum { FL_READ, FL_SDP, FL_CMD, FL_ID, FL_IDSDP, FL_IDCMD, FL_WRITEBUF, FL_WRITEARRAY } flashstate; u8 flreg0 = 0; u8 flreg1 = 0; flashstate flstate0; flashstate flstate1; u8 flbuf0[128]; u8 flbuf1[128]; ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL FlashCs0ReadByte(u32 addr) { flashstate* state; u8* reg; if (addr & 1) { state = &flstate1; reg = &flreg1; } else { state = &flstate0; reg = &flreg0; } switch (*state) { case FL_ID: case FL_IDSDP: case FL_IDCMD: if (addr & 2) return 0xD5; else return 0x1F; case FL_WRITEARRAY: *reg ^= 0x02; case FL_WRITEBUF: return *reg; case FL_SDP: case FL_CMD: *state = FL_READ; case FL_READ: default: return T2ReadByte(CartridgeArea->rom, addr); } } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL FlashCs0ReadWord(u32 addr) { return ((u16)(FlashCs0ReadByte(addr) << 8) | (u16)(FlashCs0ReadByte(addr+1))); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL FlashCs0ReadLong(u32 addr) { return ((u32)FlashCs0ReadWord(addr) << 16) |(u32) FlashCs0ReadWord(addr + 2); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL FlashCs0WriteByte(u32 addr, u8 val) { flashstate* state; u8* reg; u8* buf; if (addr & 1) { state = &flstate1; reg = &flreg1; buf = flbuf1; } else { state = &flstate0; reg = &flreg0; buf = flbuf0; } switch (*state) { case FL_READ: if (((addr & 0xfffe) == 0xaaaa) && (val == 0xaa)) *state = FL_SDP; return; case FL_WRITEBUF: buf[(addr >> 1) & 0x7f] = val; if (((addr >> 1) & 0x7f) == 0x7f) { int i; int j = addr & 0x1; addr &= 0xffffff00; for (i = 0; i <= 127; i++) { T2WriteByte(CartridgeArea->rom, (addr + i*2 + j), buf[i]); } *state = FL_READ; } return; case FL_SDP: if (((addr & 0xfffe) == 0x5554) && (val == 0x55)) *state = FL_CMD; else *state = FL_READ; return; case FL_ID: if (((addr & 0xfffe) == 0xaaaa) && (val == 0xaa)) *state = FL_IDSDP; else *state = FL_ID; return; case FL_IDSDP: if (((addr & 0xfffe) == 0x5554) && (val == 0x55)) *state = FL_READ; else *state=FL_ID; return; case FL_IDCMD: if (((addr & 0xfffe) == 0xaaaa) && (val == 0xf0)) *state = FL_READ; else *state = FL_ID; return; case FL_CMD: if ((addr & 0xfffe) != 0xaaaa) { *state = FL_READ; return; } switch (val) { case 0xa0: *state = FL_WRITEBUF; return; case 0x90: *state = FL_ID; return; default: *state = FL_READ; return; } default: break; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL FlashCs0WriteWord(u32 addr, u16 val) { FlashCs0WriteByte(addr, (u8)(val >> 8)); FlashCs0WriteByte(addr + 1, (u8)(val & 0xff)); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL FlashCs0WriteLong(u32 addr, u32 val) { FlashCs0WriteWord(addr, (u16)(val >> 16)); FlashCs0WriteWord(addr + 2, (u16)(val & 0xffff)); } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL AR4MCs0ReadByte(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x00: { if ((addr & 0x80000) == 0) // EEPROM return FlashCs0ReadByte(addr); // return biosarea->getByte(addr); // else // Outport // fprintf(stderr, "Commlink Outport Byte read\n"); break; } case 0x01: { // if ((addr & 0x80000) == 0) // Commlink Status flag // fprintf(stderr, "Commlink Status Flag read\n"); // else // Inport for Commlink // fprintf(stderr, "Commlink Inport Byte read\n"); break; } case 0x04: case 0x05: case 0x06: case 0x07: // Dram area return T1ReadByte(CartridgeArea->dram, addr & 0x3FFFFF); default: // The rest doesn't matter break; } return 0xFF; } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL AR4MCs0ReadWord(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x00: { if ((addr & 0x80000) == 0) // EEPROM return FlashCs0ReadWord(addr); // else // Outport // fprintf(stderr, "Commlink Outport Word read\n"); break; } case 0x01: { // if ((addr & 0x80000) == 0) // Commlink Status flag // fprintf(stderr, "Commlink Status Flag read\n"); // else // Inport for Commlink // fprintf(stderr, "Commlink Inport Word read\n"); break; } case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area return T1ReadWord(CartridgeArea->dram, addr & 0x3FFFFF); case 0x12: case 0x1E: if (0x80000) return 0xFFFD; break; case 0x13: case 0x16: case 0x17: case 0x1A: case 0x1B: case 0x1F: return 0xFFFD; default: // The rest doesn't matter break; } return 0xFFFF; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL AR4MCs0ReadLong(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x00: { if ((addr & 0x80000) == 0) // EEPROM return FlashCs0ReadLong(addr); // else // Outport // fprintf(stderr, "Commlink Outport Long read\n"); break; } case 0x01: { // if ((addr & 0x80000) == 0) // Commlink Status flag // fprintf(stderr, "Commlink Status Flag read\n"); // else // Inport for Commlink // fprintf(stderr, "Commlink Inport Long read\n"); break; } case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area return T1ReadLong(CartridgeArea->dram, addr & 0x3FFFFF); case 0x12: case 0x1E: if (0x80000) return 0xFFFDFFFD; break; case 0x13: case 0x16: case 0x17: case 0x1A: case 0x1B: case 0x1F: return 0xFFFDFFFD; default: // The rest doesn't matter break; } return 0xFFFFFFFF; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL AR4MCs0WriteByte(u32 addr, u8 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x00: { if ((addr & 0x80000) == 0) // EEPROM FlashCs0WriteByte(addr, val); // else // Outport // fprintf(stderr, "Commlink Outport byte write\n"); break; } case 0x01: { // if ((addr & 0x80000) == 0) // Commlink Status flag // fprintf(stderr, "Commlink Status Flag write\n"); // else // Inport for Commlink // fprintf(stderr, "Commlink Inport Byte write\n"); break; } case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area T1WriteByte(CartridgeArea->dram, addr & 0x3FFFFF, val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL AR4MCs0WriteWord(u32 addr, u16 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x00: { if ((addr & 0x80000) == 0) // EEPROM FlashCs0WriteWord(addr, val); // else // Outport // fprintf(stderr, "Commlink Outport Word write\n"); break; } case 0x01: { // if ((addr & 0x80000) == 0) // Commlink Status flag // fprintf(stderr, "Commlink Status Flag write\n"); // else // Inport for Commlink // fprintf(stderr, "Commlink Inport Word write\n"); break; } case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area T1WriteWord(CartridgeArea->dram, addr & 0x3FFFFF, val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL AR4MCs0WriteLong(u32 addr, u32 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x00: { if ((addr & 0x80000) == 0) // EEPROM FlashCs0WriteLong(addr, val); // else // Outport // fprintf(stderr, "Commlink Outport Long write\n"); break; } case 0x01: { // if ((addr & 0x80000) == 0) // Commlink Status flag // fprintf(stderr, "Commlink Status Flag write\n"); // else // Inport for Commlink // fprintf(stderr, "Commlink Inport Long write\n"); break; } case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area T1WriteLong(CartridgeArea->dram, addr & 0x3FFFFF, val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// // 8 Mbit Dram ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL DRAM8MBITCs0ReadByte(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: // Dram area return T1ReadByte(CartridgeArea->dram, addr & 0x7FFFF); case 0x06: // Dram area return T1ReadByte(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF)); default: // The rest doesn't matter break; } return 0xFF; } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL DRAM8MBITCs0ReadWord(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: // Dram area return T1ReadWord(CartridgeArea->dram, addr & 0x7FFFF); case 0x06: // Dram area return T1ReadWord(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF)); default: // The rest doesn't matter break; } return 0xFFFF; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL DRAM8MBITCs0ReadLong(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: // Dram area return T1ReadLong(CartridgeArea->dram, addr & 0x7FFFF); case 0x06: // Dram area return T1ReadLong(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF)); default: // The rest doesn't matter break; } return 0xFFFFFFFF; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DRAM8MBITCs0WriteByte(u32 addr, u8 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: // Dram area T1WriteByte(CartridgeArea->dram, addr & 0x7FFFF, val); break; case 0x06: // Dram area T1WriteByte(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF), val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DRAM8MBITCs0WriteWord(u32 addr, u16 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: // Dram area T1WriteWord(CartridgeArea->dram, addr & 0x7FFFF, val); break; case 0x06: // Dram area T1WriteWord(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF), val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DRAM8MBITCs0WriteLong(u32 addr, u32 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: // Dram area T1WriteLong(CartridgeArea->dram, addr & 0x7FFFF, val); break; case 0x06: // Dram area T1WriteLong(CartridgeArea->dram, 0x80000 | (addr & 0x7FFFF), val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// // 32 Mbit Dram ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL DRAM32MBITCs0ReadByte(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: case 0x05: case 0x06: case 0x07: // Dram area return T1ReadByte(CartridgeArea->dram, addr & 0x3FFFFF); default: // The rest doesn't matter break; } return 0xFF; } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL DRAM32MBITCs0ReadWord(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area return T1ReadWord(CartridgeArea->dram, addr & 0x3FFFFF); default: // The rest doesn't matter break; } return 0xFFFF; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL DRAM32MBITCs0ReadLong(u32 addr) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area return T1ReadLong(CartridgeArea->dram, addr & 0x3FFFFF); default: // The rest doesn't matter break; } return 0xFFFFFFFF; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DRAM32MBITCs0WriteByte(u32 addr, u8 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area T1WriteByte(CartridgeArea->dram, addr & 0x3FFFFF, val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DRAM32MBITCs0WriteWord(u32 addr, u16 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area T1WriteWord(CartridgeArea->dram, addr & 0x3FFFFF, val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL DRAM32MBITCs0WriteLong(u32 addr, u32 val) { addr &= 0x1FFFFFF; switch (addr >> 20) { case 0x04: case 0x05: case 0x06: case 0x07: // Ram cart area T1WriteLong(CartridgeArea->dram, addr & 0x3FFFFF, val); break; default: // The rest doesn't matter break; } } ////////////////////////////////////////////////////////////////////////////// // 4 Mbit Backup Ram ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL BUP4MBITCs1ReadByte(u32 addr) { return T1ReadByte(CartridgeArea->bupram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL BUP4MBITCs1ReadWord(u32 addr) { return T1ReadWord(CartridgeArea->bupram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL BUP4MBITCs1ReadLong(u32 addr) { return T1ReadLong(CartridgeArea->bupram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP4MBITCs1WriteByte(u32 addr, u8 val) { T1WriteByte(CartridgeArea->bupram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP4MBITCs1WriteWord(u32 addr, u16 val) { T1WriteWord(CartridgeArea->bupram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP4MBITCs1WriteLong(u32 addr, u32 val) { T1WriteLong(CartridgeArea->bupram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// // 8 Mbit Backup Ram ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL BUP8MBITCs1ReadByte(u32 addr) { return T1ReadByte(CartridgeArea->bupram, addr & 0x1FFFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL BUP8MBITCs1ReadWord(u32 addr) { return T1ReadWord(CartridgeArea->bupram, addr & 0x1FFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL BUP8MBITCs1ReadLong(u32 addr) { return T1ReadLong(CartridgeArea->bupram, addr & 0x1FFFFF); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP8MBITCs1WriteByte(u32 addr, u8 val) { T1WriteByte(CartridgeArea->bupram, addr & 0x1FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP8MBITCs1WriteWord(u32 addr, u16 val) { T1WriteWord(CartridgeArea->bupram, addr & 0x1FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP8MBITCs1WriteLong(u32 addr, u32 val) { T1WriteLong(CartridgeArea->bupram, addr & 0x1FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// // 16 Mbit Backup Ram ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL BUP16MBITCs1ReadByte(u32 addr) { return T1ReadByte(CartridgeArea->bupram, addr & 0x3FFFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL BUP16MBITCs1ReadWord(u32 addr) { return T1ReadWord(CartridgeArea->bupram, addr & 0x3FFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL BUP16MBITCs1ReadLong(u32 addr) { return T1ReadLong(CartridgeArea->bupram, addr & 0x3FFFFF); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP16MBITCs1WriteByte(u32 addr, u8 val) { T1WriteByte(CartridgeArea->bupram, addr & 0x3FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP16MBITCs1WriteWord(u32 addr, u16 val) { T1WriteWord(CartridgeArea->bupram, addr & 0x3FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP16MBITCs1WriteLong(u32 addr, u32 val) { T1WriteLong(CartridgeArea->bupram, addr & 0x3FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// // 32 Mbit Backup Ram ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL BUP32MBITCs1ReadByte(u32 addr) { return T1ReadByte(CartridgeArea->bupram, addr & 0x7FFFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL BUP32MBITCs1ReadWord(u32 addr) { return T1ReadWord(CartridgeArea->bupram, addr & 0x7FFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL BUP32MBITCs1ReadLong(u32 addr) { return T1ReadLong(CartridgeArea->bupram, addr & 0x7FFFFF); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP32MBITCs1WriteByte(u32 addr, u8 val) { T1WriteByte(CartridgeArea->bupram, addr & 0x7FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP32MBITCs1WriteWord(u32 addr, u16 val) { T1WriteWord(CartridgeArea->bupram, addr & 0x7FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BUP32MBITCs1WriteLong(u32 addr, u32 val) { T1WriteLong(CartridgeArea->bupram, addr & 0x7FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// // 16 Mbit Rom ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL ROM16MBITCs0ReadByte(u32 addr) { return T1ReadByte(CartridgeArea->rom, addr & 0x1FFFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL ROM16MBITCs0ReadWord(u32 addr) { return T1ReadWord(CartridgeArea->rom, addr & 0x1FFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL ROM16MBITCs0ReadLong(u32 addr) { return T1ReadLong(CartridgeArea->rom, addr & 0x1FFFFF); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL ROM16MBITCs0WriteByte(u32 addr, u8 val) { T1WriteByte(CartridgeArea->rom, addr & 0x1FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL ROM16MBITCs0WriteWord(u32 addr, u16 val) { T1WriteWord(CartridgeArea->rom, addr & 0x1FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL ROM16MBITCs0WriteLong(u32 addr, u32 val) { T1WriteLong(CartridgeArea->rom, addr & 0x1FFFFF, val); } ////////////////////////////////////////////////////////////////////////////// // General Cart functions ////////////////////////////////////////////////////////////////////////////// int CartInit(const char * filename, int type) { if ((CartridgeArea = (cartridge_struct *)calloc(1, sizeof(cartridge_struct))) == NULL) return -1; CartridgeArea->carttype = type; CartridgeArea->filename = filename; // Setup default mappings CartridgeArea->Cs0ReadByte = &DummyCs0ReadByte; CartridgeArea->Cs0ReadWord = &DummyCs0ReadWord; CartridgeArea->Cs0ReadLong = &DummyCs0ReadLong; CartridgeArea->Cs0WriteByte = &DummyCs0WriteByte; CartridgeArea->Cs0WriteWord = &DummyCs0WriteWord; CartridgeArea->Cs0WriteLong = &DummyCs0WriteLong; CartridgeArea->Cs1ReadByte = &DummyCs1ReadByte; CartridgeArea->Cs1ReadWord = &DummyCs1ReadWord; CartridgeArea->Cs1ReadLong = &DummyCs1ReadLong; CartridgeArea->Cs1WriteByte = &DummyCs1WriteByte; CartridgeArea->Cs1WriteWord = &DummyCs1WriteWord; CartridgeArea->Cs1WriteLong = &DummyCs1WriteLong; CartridgeArea->Cs2ReadByte = &DummyCs2ReadByte; CartridgeArea->Cs2ReadWord = &DummyCs2ReadWord; CartridgeArea->Cs2ReadLong = &DummyCs2ReadLong; CartridgeArea->Cs2WriteByte = &DummyCs2WriteByte; CartridgeArea->Cs2WriteWord = &DummyCs2WriteWord; CartridgeArea->Cs2WriteLong = &DummyCs2WriteLong; switch(type) { case CART_PAR: // Action Replay 4M Plus(or equivalent) { if ((CartridgeArea->rom = T2MemoryInit(0x40000)) == NULL) return -1; if ((CartridgeArea->dram = T1MemoryInit(0x400000)) == NULL) return -1; // Use 32 Mbit Dram id CartridgeArea->cartid = 0x5C; // Load AR firmware to memory if (T123Load(CartridgeArea->rom, 0x40000, 2, filename) != 0) return -1; flstate0 = FL_READ; flstate1 = FL_READ; // Setup Functions CartridgeArea->Cs0ReadByte = &AR4MCs0ReadByte; CartridgeArea->Cs0ReadWord = &AR4MCs0ReadWord; CartridgeArea->Cs0ReadLong = &AR4MCs0ReadLong; CartridgeArea->Cs0WriteByte = &AR4MCs0WriteByte; CartridgeArea->Cs0WriteWord = &AR4MCs0WriteWord; CartridgeArea->Cs0WriteLong = &AR4MCs0WriteLong; break; } case CART_BACKUPRAM4MBIT: // 4 Mbit Backup Ram { if ((CartridgeArea->bupram = T1MemoryInit(0x100000)) == NULL) return -1; CartridgeArea->cartid = 0x21; // Load Backup Ram data from file if (T123Load(CartridgeArea->bupram, 0x100000, 1, filename) != 0) FormatBackupRam(CartridgeArea->bupram, 0x100000); // Setup Functions CartridgeArea->Cs1ReadByte = &BUP4MBITCs1ReadByte; CartridgeArea->Cs1ReadWord = &BUP4MBITCs1ReadWord; CartridgeArea->Cs1ReadLong = &BUP4MBITCs1ReadLong; CartridgeArea->Cs1WriteByte = &BUP4MBITCs1WriteByte; CartridgeArea->Cs1WriteWord = &BUP4MBITCs1WriteWord; CartridgeArea->Cs1WriteLong = &BUP4MBITCs1WriteLong; break; } case CART_BACKUPRAM8MBIT: // 8 Mbit Backup Ram { if ((CartridgeArea->bupram = T1MemoryInit(0x200000)) == NULL) return -1; CartridgeArea->cartid = 0x22; // Load Backup Ram data from file if (T123Load(CartridgeArea->bupram, 0x200000, 1, filename) != 0) FormatBackupRam(CartridgeArea->bupram, 0x200000); // Setup Functions CartridgeArea->Cs1ReadByte = &BUP8MBITCs1ReadByte; CartridgeArea->Cs1ReadWord = &BUP8MBITCs1ReadWord; CartridgeArea->Cs1ReadLong = &BUP8MBITCs1ReadLong; CartridgeArea->Cs1WriteByte = &BUP8MBITCs1WriteByte; CartridgeArea->Cs1WriteWord = &BUP8MBITCs1WriteWord; CartridgeArea->Cs1WriteLong = &BUP8MBITCs1WriteLong; break; } case CART_BACKUPRAM16MBIT: // 16 Mbit Backup Ram { if ((CartridgeArea->bupram = T1MemoryInit(0x400000)) == NULL) return -1; CartridgeArea->cartid = 0x23; // Load Backup Ram data from file if (T123Load(CartridgeArea->bupram, 0x400000, 1, filename) != 0) FormatBackupRam(CartridgeArea->bupram, 0x400000); // Setup Functions CartridgeArea->Cs1ReadByte = &BUP16MBITCs1ReadByte; CartridgeArea->Cs1ReadWord = &BUP16MBITCs1ReadWord; CartridgeArea->Cs1ReadLong = &BUP16MBITCs1ReadLong; CartridgeArea->Cs1WriteByte = &BUP16MBITCs1WriteByte; CartridgeArea->Cs1WriteWord = &BUP16MBITCs1WriteWord; CartridgeArea->Cs1WriteLong = &BUP16MBITCs1WriteLong; break; } case CART_BACKUPRAM32MBIT: // 32 Mbit Backup Ram { if ((CartridgeArea->bupram = T1MemoryInit(0x800000)) == NULL) return -1; CartridgeArea->cartid = 0x24; // Load Backup Ram data from file if (T123Load(CartridgeArea->bupram, 0x800000, 1, filename) != 0) FormatBackupRam(CartridgeArea->bupram, 0x800000); // Setup Functions CartridgeArea->Cs1ReadByte = &BUP32MBITCs1ReadByte; CartridgeArea->Cs1ReadWord = &BUP32MBITCs1ReadWord; CartridgeArea->Cs1ReadLong = &BUP32MBITCs1ReadLong; CartridgeArea->Cs1WriteByte = &BUP32MBITCs1WriteByte; CartridgeArea->Cs1WriteWord = &BUP32MBITCs1WriteWord; CartridgeArea->Cs1WriteLong = &BUP32MBITCs1WriteLong; break; } case CART_DRAM8MBIT: // 8 Mbit Dram Cart { if ((CartridgeArea->dram = T1MemoryInit(0x100000)) == NULL) return -1; CartridgeArea->cartid = 0x5A; // Setup Functions CartridgeArea->Cs0ReadByte = &DRAM8MBITCs0ReadByte; CartridgeArea->Cs0ReadWord = &DRAM8MBITCs0ReadWord; CartridgeArea->Cs0ReadLong = &DRAM8MBITCs0ReadLong; CartridgeArea->Cs0WriteByte = &DRAM8MBITCs0WriteByte; CartridgeArea->Cs0WriteWord = &DRAM8MBITCs0WriteWord; CartridgeArea->Cs0WriteLong = &DRAM8MBITCs0WriteLong; break; } case CART_DRAM32MBIT: // 32 Mbit Dram Cart { if ((CartridgeArea->dram = T1MemoryInit(0x400000)) == NULL) return -1; CartridgeArea->cartid = 0x5C; // Setup Functions CartridgeArea->Cs0ReadByte = &DRAM32MBITCs0ReadByte; CartridgeArea->Cs0ReadWord = &DRAM32MBITCs0ReadWord; CartridgeArea->Cs0ReadLong = &DRAM32MBITCs0ReadLong; CartridgeArea->Cs0WriteByte = &DRAM32MBITCs0WriteByte; CartridgeArea->Cs0WriteWord = &DRAM32MBITCs0WriteWord; CartridgeArea->Cs0WriteLong = &DRAM32MBITCs0WriteLong; break; } case CART_NETLINK: { CartridgeArea->cartid = 0xFF; CartridgeArea->Cs2ReadByte = &NetlinkReadByte; CartridgeArea->Cs2WriteByte = &NetlinkWriteByte; break; } case CART_ROM16MBIT: // 16 Mbit Rom Cart { if ((CartridgeArea->rom = T1MemoryInit(0x200000)) == NULL) return -1; CartridgeArea->cartid = 0xFF; // I have no idea what the real id is // Load Rom to memory if (T123Load(CartridgeArea->rom, 0x200000, 1, filename) != 0) return -1; // Setup Functions CartridgeArea->Cs0ReadByte = &ROM16MBITCs0ReadByte; CartridgeArea->Cs0ReadWord = &ROM16MBITCs0ReadWord; CartridgeArea->Cs0ReadLong = &ROM16MBITCs0ReadLong; CartridgeArea->Cs0WriteByte = &ROM16MBITCs0WriteByte; CartridgeArea->Cs0WriteWord = &ROM16MBITCs0WriteWord; CartridgeArea->Cs0WriteLong = &ROM16MBITCs0WriteLong; break; } case CART_JAPMODEM: // Sega Saturn Modem(Japanese) { CartridgeArea->cartid = 0xFF; CartridgeArea->Cs0ReadByte = &JapModemCs0ReadByte; CartridgeArea->Cs0ReadWord = &JapModemCs0ReadWord; CartridgeArea->Cs0ReadLong = &JapModemCs0ReadLong; CartridgeArea->Cs1ReadByte = &JapModemCs1ReadByte; CartridgeArea->Cs1ReadWord = &JapModemCs1ReadWord; CartridgeArea->Cs1ReadLong = &JapModemCs1ReadLong; CartridgeArea->Cs1WriteByte = &JapModemCs1WriteByte; CartridgeArea->Cs1WriteWord = &JapModemCs1WriteWord; CartridgeArea->Cs1WriteLong = &JapModemCs1WriteLong; CartridgeArea->Cs2ReadByte = &JapModemCs2ReadByte; CartridgeArea->Cs2WriteByte = &JapModemCs2WriteByte; break; } default: // No Cart { CartridgeArea->cartid = 0xFF; break; } } return 0; } ////////////////////////////////////////////////////////////////////////////// void CartDeInit(void) { if (CartridgeArea) { if (CartridgeArea->carttype == CART_PAR) { if (CartridgeArea->rom) { if (T123Save(CartridgeArea->rom, 0x40000, 2, CartridgeArea->filename) != 0) YabSetError(YAB_ERR_FILEWRITE, (void *)CartridgeArea->filename); T2MemoryDeInit(CartridgeArea->rom); } } else { if (CartridgeArea->rom) T1MemoryDeInit(CartridgeArea->rom); } if (CartridgeArea->bupram) { u32 size=0; switch (CartridgeArea->carttype) { case CART_BACKUPRAM4MBIT: // 4 Mbit Backup Ram { size = 0x100000; break; } case CART_BACKUPRAM8MBIT: // 8 Mbit Backup Ram { size = 0x200000; break; } case CART_BACKUPRAM16MBIT: // 16 Mbit Backup Ram { size = 0x400000; break; } case CART_BACKUPRAM32MBIT: // 32 Mbit Backup Ram { size = 0x800000; break; } } if (size != 0) { if (T123Save(CartridgeArea->bupram, size, 1, CartridgeArea->filename) != 0) YabSetError(YAB_ERR_FILEWRITE, (void *)CartridgeArea->filename); T1MemoryDeInit(CartridgeArea->bupram); } } if (CartridgeArea->dram) T1MemoryDeInit(CartridgeArea->dram); free(CartridgeArea); } CartridgeArea = NULL; } ////////////////////////////////////////////////////////////////////////////// int CartSaveState(FILE * fp) { int offset; offset = StateWriteHeader(fp, "CART", 1); // Write cart type fwrite((void *)&CartridgeArea->carttype, 4, 1, fp); // Write the areas associated with the cart type here return StateFinishHeader(fp, offset); } ////////////////////////////////////////////////////////////////////////////// int CartLoadState(FILE * fp, UNUSED int version, int size) { int newtype; // Read cart type fread((void *)&newtype, 4, 1, fp); // Check to see if old cart type and new cart type match, if they don't, // reallocate memory areas // Read the areas associated with the cart type here return size; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/sndsdl.c000644 001750 001750 00000015004 12256006135 017630 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_LIBSDL #include #if defined(__APPLE__) || defined(GEKKO) #include #else #include "SDL.h" #endif #include "error.h" #include "scsp.h" #include "sndsdl.h" #include "debug.h" static int SNDSDLInit(void); static void SNDSDLDeInit(void); static int SNDSDLReset(void); static int SNDSDLChangeVideoFormat(int vertfreq); static void sdlConvert32uto16s(s32 *srcL, s32 *srcR, s16 *dst, u32 len); static void SNDSDLUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples); static u32 SNDSDLGetAudioSpace(void); static void SNDSDLMuteAudio(void); static void SNDSDLUnMuteAudio(void); static void SNDSDLSetVolume(int volume); SoundInterface_struct SNDSDL = { SNDCORE_SDL, "SDL Sound Interface", SNDSDLInit, SNDSDLDeInit, SNDSDLReset, SNDSDLChangeVideoFormat, SNDSDLUpdateAudio, SNDSDLGetAudioSpace, SNDSDLMuteAudio, SNDSDLUnMuteAudio, SNDSDLSetVolume }; #define NUMSOUNDBLOCKS 4 static u16 *stereodata16; static u32 soundoffset; static volatile u32 soundpos; static u32 soundlen; static u32 soundbufsize; static SDL_AudioSpec audiofmt; static u8 soundvolume; static int muted = 0; ////////////////////////////////////////////////////////////////////////////// static void MixAudio(UNUSED void *userdata, Uint8 *stream, int len) { int i; Uint8* soundbuf = (Uint8*)stereodata16; // original code for (i = 0; i < len; i++) { if (soundpos >= soundbufsize) soundpos = 0; stream[i] = muted ? audiofmt.silence : soundbuf[soundpos]; soundpos++; } } ////////////////////////////////////////////////////////////////////////////// static int SNDSDLInit(void) { //samples should be a power of 2 according to SDL-doc //so normalize it to the nearest power of 2 here u32 normSamples = 512; #if defined (_MSC_VER) && SDL_VERSION_ATLEAST(2,0,0) SDL_SetMainReady(); #endif SDL_InitSubSystem(SDL_INIT_AUDIO); // if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0); // return -1; audiofmt.freq = 44100; audiofmt.format = AUDIO_S16SYS; audiofmt.channels = 2; audiofmt.samples = (audiofmt.freq / 60) * 2; audiofmt.callback = MixAudio; audiofmt.userdata = NULL; while (normSamples < audiofmt.samples) normSamples <<= 1; audiofmt.samples = normSamples; soundlen = audiofmt.freq / 60; // 60 for NTSC or 50 for PAL. Initially assume it's going to be NTSC. soundbufsize = soundlen * NUMSOUNDBLOCKS * 2 * 2; soundvolume = SDL_MIX_MAXVOLUME; if (SDL_OpenAudio(&audiofmt, NULL) != 0) { YabSetError(YAB_ERR_SDL, (void *)SDL_GetError()); return -1; } if ((stereodata16 = (u16 *)malloc(soundbufsize)) == NULL) return -1; memset(stereodata16, 0, soundbufsize); soundpos = 0; SDL_PauseAudio(0); return 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDSDLDeInit(void) { SDL_CloseAudio(); if (stereodata16) free(stereodata16); } ////////////////////////////////////////////////////////////////////////////// static int SNDSDLReset(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// static int SNDSDLChangeVideoFormat(int vertfreq) { soundlen = audiofmt.freq / vertfreq; soundbufsize = soundlen * NUMSOUNDBLOCKS * 2 * 2; if (stereodata16) free(stereodata16); if ((stereodata16 = (u16 *)malloc(soundbufsize)) == NULL) return -1; memset(stereodata16, 0, soundbufsize); return 0; } ////////////////////////////////////////////////////////////////////////////// static void sdlConvert32uto16s(s32 *srcL, s32 *srcR, s16 *dst, u32 len) { u32 i; for (i = 0; i < len; i++) { // Left Channel *srcL = ( *srcL *soundvolume ) /SDL_MIX_MAXVOLUME; if (*srcL > 0x7FFF) *dst = 0x7FFF; else if (*srcL < -0x8000) *dst = -0x8000; else *dst = *srcL; srcL++; dst++; // Right Channel *srcR = ( *srcR *soundvolume ) /SDL_MIX_MAXVOLUME; if (*srcR > 0x7FFF) *dst = 0x7FFF; else if (*srcR < -0x8000) *dst = -0x8000; else *dst = *srcR; srcR++; dst++; } } static void SNDSDLUpdateAudio(u32 *leftchanbuffer, u32 *rightchanbuffer, u32 num_samples) { u32 copy1size=0, copy2size=0; SDL_LockAudio(); if ((soundbufsize - soundoffset) < (num_samples * sizeof(s16) * 2)) { copy1size = (soundbufsize - soundoffset); copy2size = (num_samples * sizeof(s16) * 2) - copy1size; } else { copy1size = (num_samples * sizeof(s16) * 2); copy2size = 0; } sdlConvert32uto16s((s32 *)leftchanbuffer, (s32 *)rightchanbuffer, (s16 *)(((u8 *)stereodata16)+soundoffset), copy1size / sizeof(s16) / 2); if (copy2size) sdlConvert32uto16s((s32 *)leftchanbuffer + (copy1size / sizeof(s16) / 2), (s32 *)rightchanbuffer + (copy1size / sizeof(s16) / 2), (s16 *)stereodata16, copy2size / sizeof(s16) / 2); soundoffset += copy1size + copy2size; soundoffset %= soundbufsize; SDL_UnlockAudio(); } ////////////////////////////////////////////////////////////////////////////// static u32 SNDSDLGetAudioSpace(void) { u32 freespace=0; if (soundoffset > soundpos) freespace = soundbufsize - soundoffset + soundpos; else freespace = soundpos - soundoffset; return (freespace / sizeof(s16) / 2); } ////////////////////////////////////////////////////////////////////////////// static void SNDSDLMuteAudio(void) { muted = 1; } ////////////////////////////////////////////////////////////////////////////// static void SNDSDLUnMuteAudio(void) { muted = 0; } ////////////////////////////////////////////////////////////////////////////// static void SNDSDLSetVolume(int volume) { soundvolume = ( (double)SDL_MIX_MAXVOLUME /(double)100 ) *volume; } ////////////////////////////////////////////////////////////////////////////// #endif yabause-0.9.13.1/src/error.c000644 001750 001750 00000011115 12256006143 017470 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "error.h" #include "yui.h" ////////////////////////////////////////////////////////////////////////////// static void AllocAmendPrintString(const char *string1, const char *string2) { char *string; if ((string = (char *)malloc(strlen(string1) + strlen(string2) + 2)) == NULL) return; sprintf(string, "%s%s\n", string1, string2); YuiErrorMsg(string); free(string); } ////////////////////////////////////////////////////////////////////////////// void YabSetError(int type, const void *extra) { char tempstr[512]; SH2_struct *sh; switch (type) { case YAB_ERR_FILENOTFOUND: AllocAmendPrintString(_("File not found: "), extra); break; case YAB_ERR_MEMORYALLOC: YuiErrorMsg(_("Error allocating memory\n")); break; case YAB_ERR_FILEREAD: AllocAmendPrintString(_("Error reading file: "), extra); break; case YAB_ERR_FILEWRITE: AllocAmendPrintString(_("Error writing file: "), extra); break; case YAB_ERR_CANNOTINIT: AllocAmendPrintString(_("Cannot initialize "), extra); break; case YAB_ERR_SH2INVALIDOPCODE: sh = (SH2_struct *)extra; SH2GetRegisters(sh, &sh->regs); sprintf(tempstr, "%s SH2 invalid opcode\n\n" "R0 = %08lX\tR12 = %08lX\n" "R1 = %08lX\tR13 = %08lX\n" "R2 = %08lX\tR14 = %08lX\n" "R3 = %08lX\tR15 = %08lX\n" "R4 = %08lX\tSR = %08lX\n" "R5 = %08lX\tGBR = %08lX\n" "R6 = %08lX\tVBR = %08lX\n" "R7 = %08lX\tMACH = %08lX\n" "R8 = %08lX\tMACL = %08lX\n" "R9 = %08lX\tPR = %08lX\n" "R10 = %08lX\tPC = %08lX\n" "R11 = %08lX\n", sh->isslave ? "Slave" : "Master", (long)sh->regs.R[0], (long)sh->regs.R[12], (long)sh->regs.R[1], (long)sh->regs.R[13], (long)sh->regs.R[2], (long)sh->regs.R[14], (long)sh->regs.R[3], (long)sh->regs.R[15], (long)sh->regs.R[4], (long)sh->regs.SR.all, (long)sh->regs.R[5], (long)sh->regs.GBR, (long)sh->regs.R[6], (long)sh->regs.VBR, (long)sh->regs.R[7], (long)sh->regs.MACH, (long)sh->regs.R[8], (long)sh->regs.MACL, (long)sh->regs.R[9], (long)sh->regs.PR, (long)sh->regs.R[10], (long)sh->regs.PC, (long)sh->regs.R[11]); YuiErrorMsg(tempstr); break; case YAB_ERR_SH2READ: YuiErrorMsg(_("SH2 read error\n")); // fix me break; case YAB_ERR_SH2WRITE: YuiErrorMsg(_("SH2 write error\n")); // fix me break; case YAB_ERR_SDL: AllocAmendPrintString(_("SDL Error: "), extra); break; case YAB_ERR_OTHER: YuiErrorMsg((char *)extra); break; case YAB_ERR_UNKNOWN: default: YuiErrorMsg(_("Unknown error occurred\n")); break; } } ////////////////////////////////////////////////////////////////////////////// void YabErrorMsg(const char * format, ...) { va_list l; int n; char * buffer; va_start(l, format); n = vsnprintf(NULL, 0, format, l); va_end(l); buffer = malloc(n + 1); va_start(l, format); vsprintf(buffer, format, l); va_end(l); YuiErrorMsg(buffer); free(buffer); } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/coffelf.c000644 001750 001750 00000024423 12256006106 017750 0ustar00guillaumeguillaume000000 000000 /* Copyright 2007 Theo Berkau Copyright 2009 Lawrence Sebald This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "core.h" #include "debug.h" #include "sh2core.h" #include "yabause.h" #include "coffelf.h" typedef struct { u8 magic[2]; u16 numsections; u32 timedate; u32 symtabptr; u32 numsymtabs; u16 optheader; u16 flags; } coff_header_struct; typedef struct { u8 magic[2]; u16 versionstamp; u32 textsize; u32 datasize; u32 bsssize; u32 entrypoint; u32 textaddr; u32 dataaddr; } aout_header_struct; typedef struct { s8 name[8]; u32 physaddr; u32 virtaddr; u32 sectionsize; u32 sectionptr; u32 relptr; u32 linenoptr; u16 numreloc; u16 numlineno; u32 flags; } section_header_struct; typedef struct { u8 ident[16]; u16 type; u16 machine; u32 version; u32 entry; u32 phdr; u32 shdr; u32 flags; u16 hdrsize; u16 phdrsize; u16 phdrcount; u16 shdrsize; u16 shdrcount; u16 shdrstridx; } elf_header_struct; #define ELF_MACHINE_SH 42 typedef struct { u32 name; u32 type; u32 flags; u32 addr; u32 offs; u32 size; u32 link; u32 inf; u32 align; u32 esize; } elf_section_header_struct; #define ELF_SECTION_TYPE_NODATA 8 #define ELF_SECTION_FLAG_ALLOC 2 #define WordSwap(x) x = ((x & 0xFF00) >> 8) + ((x & 0x00FF) << 8); #define DoubleWordSwap(x) x = (((x & 0xFF000000) >> 24) + \ ((x & 0x00FF0000) >> 8) + \ ((x & 0x0000FF00) << 8) + \ ((x & 0x000000FF) << 24)); ////////////////////////////////////////////////////////////////////////////// int MappedMemoryLoadCoff(const char *filename) { coff_header_struct coff_header; aout_header_struct aout_header; section_header_struct *section_headers=NULL; FILE *fp; u8 *buffer; u32 i, j; if ((fp = fopen(filename, "rb")) == NULL) return -1; fread((void *)&coff_header, sizeof(coff_header), 1, fp); #ifndef WORDS_BIGENDIAN WordSwap(coff_header.numsections); DoubleWordSwap(coff_header.timedate); DoubleWordSwap(coff_header.timedate); DoubleWordSwap(coff_header.symtabptr); DoubleWordSwap(coff_header.numsymtabs); WordSwap(coff_header.optheader); WordSwap(coff_header.flags); #endif if (coff_header.magic[0] != 0x05 || coff_header.magic[1] != 0x00 || coff_header.optheader != sizeof(aout_header)) { // Not SH COFF or is missing the optional header fclose(fp); return -1; } fread((void *)&aout_header, sizeof(aout_header), 1, fp); #ifndef WORDS_BIGENDIAN WordSwap(aout_header.versionstamp); DoubleWordSwap(aout_header.textsize); DoubleWordSwap(aout_header.datasize); DoubleWordSwap(aout_header.bsssize); DoubleWordSwap(aout_header.entrypoint); DoubleWordSwap(aout_header.textaddr); DoubleWordSwap(aout_header.dataaddr); #endif // Read in each section header if ((section_headers = (section_header_struct *)malloc(sizeof(section_header_struct) * coff_header.numsections)) == NULL) { fclose(fp); return -2; } // read in section headers for (i = 0; i < coff_header.numsections; i++) { fread((void *)§ion_headers[i], sizeof(section_header_struct), 1, fp); #ifndef WORDS_BIGENDIAN DoubleWordSwap(section_headers[i].physaddr); DoubleWordSwap(section_headers[i].virtaddr); DoubleWordSwap(section_headers[i].sectionsize); DoubleWordSwap(section_headers[i].sectionptr); DoubleWordSwap(section_headers[i].relptr); DoubleWordSwap(section_headers[i].linenoptr); WordSwap(section_headers[i].numreloc); WordSwap(section_headers[i].numlineno); DoubleWordSwap(section_headers[i].flags); #endif } YabauseResetNoLoad(); // Setup the vector table area, etc. YabauseSpeedySetup(); // Read in sections, load them to ram for (i = 0; i < coff_header.numsections; i++) { if (section_headers[i].sectionsize == 0 || section_headers[i].sectionptr == 0) // Skip to the next section continue; if ((buffer = (u8 *)malloc(section_headers[i].sectionsize)) == NULL) { fclose(fp); free(section_headers); return -2; } fseek(fp, section_headers[i].sectionptr, SEEK_SET); fread((void *)buffer, 1, section_headers[i].sectionsize, fp); for (j = 0; j < section_headers[i].sectionsize; j++) MappedMemoryWriteByte(section_headers[i].physaddr+j, buffer[j]); SH2WriteNotify(section_headers[i].physaddr, section_headers[i].sectionsize); free(buffer); } // Clean up free(section_headers); fclose(fp); SH2GetRegisters(MSH2, &MSH2->regs); MSH2->regs.PC = aout_header.entrypoint; SH2SetRegisters(MSH2, &MSH2->regs); return 0; } ////////////////////////////////////////////////////////////////////////////// int MappedMemoryLoadElf(const char *filename) { elf_header_struct elf_hdr; elf_section_header_struct *sections = NULL; FILE *fp; u16 i; u32 j; u8 *buffer; fp = fopen(filename, "rb"); if(fp == NULL) return -1; fread(&elf_hdr, sizeof(elf_header_struct), 1, fp); if(elf_hdr.ident[0] != 0x7F || elf_hdr.ident[1] != 'E' || elf_hdr.ident[2] != 'L' || elf_hdr.ident[3] != 'F' || elf_hdr.ident[4] != 1) { /* Doesn't appear to be a valid ELF file. */ fclose(fp); return -1; } if(elf_hdr.ident[5] != 2) { /* Doesn't appear to be a big-endian file. */ fclose(fp); return -1; } #ifndef WORDS_BIGENDIAN WordSwap(elf_hdr.type); WordSwap(elf_hdr.machine); DoubleWordSwap(elf_hdr.version); DoubleWordSwap(elf_hdr.entry); DoubleWordSwap(elf_hdr.phdr); DoubleWordSwap(elf_hdr.shdr); DoubleWordSwap(elf_hdr.flags); WordSwap(elf_hdr.hdrsize); WordSwap(elf_hdr.phdrsize); WordSwap(elf_hdr.phdrcount); WordSwap(elf_hdr.shdrsize); WordSwap(elf_hdr.shdrcount); WordSwap(elf_hdr.shdrstridx); #endif LOG("Loading ELF file %s\n", filename); LOG("Type: %d\n", elf_hdr.type); LOG("Machine code: %d\n", elf_hdr.machine); LOG("Version: %d\n", elf_hdr.version); LOG("Entry point: 0x%08X\n", elf_hdr.entry); LOG("Program header offset: %d\n", elf_hdr.phdr); LOG("Section header offset: %d\n", elf_hdr.shdr); LOG("Flags: %d\n", elf_hdr.flags); LOG("ELF Header Size: %d\n", elf_hdr.hdrsize); LOG("Program header size: %d\n", elf_hdr.phdrsize); LOG("Program header count: %d\n", elf_hdr.phdrcount); LOG("Section header size: %d\n", elf_hdr.shdrsize); LOG("Section header count: %d\n", elf_hdr.shdrcount); LOG("String table section: %d\n", elf_hdr.shdrstridx); if(elf_hdr.machine != ELF_MACHINE_SH) { /* Not a SuperH ELF file. */ fclose(fp); return -1; } /* Allocate space for the section headers. */ sections = (elf_section_header_struct *)malloc(sizeof(elf_section_header_struct) * elf_hdr.shdrcount); if(sections == NULL) { fclose(fp); return -2; } /* Look at the actual section headers. */ fseek(fp, elf_hdr.shdr, SEEK_SET); /* Read in each section header. */ for(i = 0; i < elf_hdr.shdrcount; ++i) { fread(sections + i, sizeof(elf_section_header_struct), 1, fp); #ifndef WORDS_BIGENDIAN DoubleWordSwap(sections[i].name); DoubleWordSwap(sections[i].type); DoubleWordSwap(sections[i].flags); DoubleWordSwap(sections[i].addr); DoubleWordSwap(sections[i].offs); DoubleWordSwap(sections[i].size); DoubleWordSwap(sections[i].link); DoubleWordSwap(sections[i].inf); DoubleWordSwap(sections[i].align); DoubleWordSwap(sections[i].esize); #endif LOG("Section header %d:\n", i); LOG("Name index: %d\n", sections[i].name); LOG("Type: %d\n", sections[i].type); LOG("Flags: 0x%X\n", sections[i].flags); LOG("In-memory address: 0x%08X\n", sections[i].addr); LOG("In-file offset: %d\n", sections[i].offs); LOG("Size: %d\n", sections[i].size); LOG("Link field: %d\n", sections[i].link); LOG("Info field: %d\n", sections[i].inf); LOG("Alignment: %d\n", sections[i].align); LOG("Entry size: %d\n", sections[i].esize); } YabauseResetNoLoad(); /* Set up the vector table area, etc. */ YabauseSpeedySetup(); /* Read in the sections and load them to RAM. */ for(i = 0; i < elf_hdr.shdrcount; ++i) { /* Does the header request actual storage for this section? */ if(sections[i].flags & ELF_SECTION_FLAG_ALLOC) { /* Check if the section contains data, or if its just a marker for a section of zero bytes. */ if(sections[i].type == ELF_SECTION_TYPE_NODATA) { for(j = 0; j < sections[i].size; ++j) { MappedMemoryWriteByte(sections[i].addr + j, 0); } } else { buffer = (u8 *)malloc(sections[i].size); if(buffer == NULL) { fclose(fp); free(sections); return -2; } fseek(fp, sections[i].offs, SEEK_SET); fread(buffer, 1, sections[i].size, fp); for(j = 0; j < sections[i].size; ++j) { MappedMemoryWriteByte(sections[i].addr + j, buffer[j]); } free(buffer); } } } /* Clean up. */ free(sections); fclose(fp); /* Set up our entry point. */ SH2GetRegisters(MSH2, &MSH2->regs); MSH2->regs.PC = elf_hdr.entry; SH2SetRegisters(MSH2, &MSH2->regs); return 0; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/netlink.c000644 001750 001750 00000103373 12256006161 020013 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006, 2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "cs2.h" #include "error.h" #include "netlink.h" #include "debug.h" #include "scu.h" #ifdef USESOCKET #include "sock.h" #include "threads.h" #endif Netlink *NetlinkArea = NULL; static volatile u8 netlink_listener_thread_running; static volatile u8 netlink_connect_thread_running; static volatile u8 netlink_client_thread_running; static int NetworkInit(const char *port); static void NetworkDeInit(void); static void NetworkStopClient(); static void NetworkStopConnect(); static void NetworkConnect(const char *ip, const char *port); static int NetworkWaitForConnect(); static int NetworkSend(const void *buffer, int length); static int NetworkReceive(void *buffer, int maxlength); ////////////////////////////////////////////////////////////////////////////// UNUSED static void NetlinkLSRChange(u8 val) { // If IER bit 2 is set and if any of the error or alarms bits are set(and // they weren't previously), trigger an interrupt if ((NetlinkArea->reg.IER & 0x4) && ((NetlinkArea->reg.LSR ^ val) & val & 0x1E)) { NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x6; ScuSendExternalInterrupt12(); } NetlinkArea->reg.LSR = val; } ////////////////////////////////////////////////////////////////////////////// #ifndef USESOCKET UNUSED #endif static void NetlinkMSRChange(u8 set, u8 clear) { u8 change; change = ((NetlinkArea->reg.MSR >> 4) ^ set) & set; change |= (((NetlinkArea->reg.MSR >> 4) ^ 0xFF) ^ clear) & clear; // If IER bit 3 is set and CTS/DSR/RI/RLSD changes, trigger interrupt if ((NetlinkArea->reg.IER & 0x8) && change) { NetlinkArea->reg.IIR = NetlinkArea->reg.IIR & 0xF0; ScuSendExternalInterrupt12(); } NetlinkArea->reg.MSR &= ~(clear << 4); NetlinkArea->reg.MSR |= (set << 4) | change; } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL NetlinkReadByte(u32 addr) { u8 ret; switch (addr & 0xFFFFF) { case 0x95001: // Receiver Buffer/Divisor Latch Low Byte { if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch Low Byte return NetlinkArea->reg.DLL; else // Receiver Buffer { if (NetlinkArea->outbuffersize == 0) { #ifdef USESOCKET YabThreadWake(YAB_THREAD_NETLINKCLIENT); #endif return 0x00; } ret = NetlinkArea->outbuffer[NetlinkArea->outbufferstart]; NetlinkArea->outbufferstart++; NetlinkArea->outbuffersize--; // If the buffer is empty now, make sure the data available // bit in LSR is cleared if (NetlinkArea->outbuffersize == 0) { NetlinkArea->outbufferstart = NetlinkArea->outbufferend = 0; NetlinkArea->reg.LSR &= ~0x01; } // If interrupt has been triggered because of RBR having data, reset it if ((NetlinkArea->reg.IER & 0x1) && (NetlinkArea->reg.IIR & 0xF) == 0x4) NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1; return ret; } return 0; } case 0x95005: // Interrupt Enable Register/Divisor Latch High Byte { if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch High Byte return NetlinkArea->reg.DLM; else // Interrupt Enable Register return NetlinkArea->reg.IER; } case 0x95009: // Interrupt Identification Register { // If interrupt has been triggered because THB is empty, reset it if ((NetlinkArea->reg.IER & 0x2) && (NetlinkArea->reg.IIR & 0xF) == 0x2) NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1; return NetlinkArea->reg.IIR; } case 0x9500D: // Line Control Register { return NetlinkArea->reg.LCR; } case 0x95011: // Modem Control Register { return NetlinkArea->reg.MCR; } case 0x95015: // Line Status Register { return NetlinkArea->reg.LSR; } case 0x95019: // Modem Status Register { // If interrupt has been triggered because of MSR change, reset it if ((NetlinkArea->reg.IER & 0x8) && (NetlinkArea->reg.IIR & 0xF) == 0) NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1; ret = NetlinkArea->reg.MSR; NetlinkArea->reg.MSR &= 0xF0; return ret; } case 0x9501D: // Scratch { return NetlinkArea->reg.SCR; } default: break; } NETLINK_LOG("Unimplemented Netlink byte read: %08X\n", addr); return 0xFF; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL NetlinkDoATResponse(const char *string) { strcpy((char *)&NetlinkArea->outbuffer[NetlinkArea->outbufferend], string); NetlinkArea->outbufferend += (u32)strlen(string); NetlinkArea->outbuffersize += (u32)strlen(string); } ////////////////////////////////////////////////////////////////////////////// static int FASTCALL NetlinkFetchATParameter(u8 val, u32 *offset) { if (val >= '0' && val <= '9') { (*offset)++; return (val - 0x30); } else return 0; } ////////////////////////////////////////////////////////////////////////////// void NetlinkUpdateReceivedDataInt() { if (NetlinkArea->outbuffersize > 0) { // Set Data available bit in LSR NetlinkArea->reg.LSR |= 0x01; // Trigger Interrrupt NetlinkArea->reg.IIR = 0x4; ScuSendExternalInterrupt12(); } } ////////////////////////////////////////////////////////////////////////////// void remove_all_chars(char* str, char c) { char *pr = str, *pw = str; while (*pr) { *pw = *pr++; pw += (*pw != c); } *pw = '\0'; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL NetlinkWriteByte(u32 addr, u8 val) { switch (addr & 0xFFFFF) { case 0x2503D: // ??? { return; } case 0x95001: // Transmitter Holding Buffer/Divisor Latch Low Byte { if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch Low Byte { NetlinkArea->reg.DLL = val; } else // Transmitter Holding Buffer { if (NetlinkArea->thb_write_time == 0xFFFFFFFF) NetlinkArea->thb_write_time = 0; if (val == '+') { // Possible escape sequence? if (NetlinkArea->escape_count == 0 && NetlinkArea->thb_write_time >= 1000000) { // Start of sequence NetlinkArea->escape_count++; } else if (NetlinkArea->escape_count >= 1) { // Middle/possible tail NetlinkArea->escape_count++; } } else NetlinkArea->escape_count = 0; NetlinkArea->inbuffer[NetlinkArea->inbufferend] = val; NetlinkArea->thb_write_time = 0; NetlinkArea->inbufferend++; if (NetlinkArea->inbufferend == NETLINK_BUFFER_SIZE) { NetlinkArea->inbufferstart=0; NetlinkArea->inbufferend=1; } NetlinkArea->inbuffersize++; // If interrupt has been triggered because THB is empty, reset it if ((NetlinkArea->reg.IER & 0x2) && (NetlinkArea->reg.IIR & 0xF) == 0x2) NetlinkArea->reg.IIR = (NetlinkArea->reg.IIR & 0xF0) | 0x1; if (NetlinkArea->modemstate == NL_MODEMSTATE_COMMAND) { if (val == 0x0D && (strncmp((char *)&NetlinkArea->inbuffer[NetlinkArea->inbufferstart], "AT", 2) == 0 || strncmp((char *)&NetlinkArea->inbuffer[NetlinkArea->inbufferstart], "at", 2) == 0)) // fix me { u32 i=NetlinkArea->inbufferstart+2; enum NL_RESULTCODE resultcode=NL_RESULTCODE_OK; int parameter; NETLINK_LOG("Program issued %s\n", NetlinkArea->inbuffer); // If echo is enabled, do it if (NetlinkArea->isechoenab) NetlinkDoATResponse((char *)NetlinkArea->inbuffer); // Handle AT command while(NetlinkArea->inbuffer[i] != 0xD) { switch (toupper(NetlinkArea->inbuffer[i])) { case ' ': // Whitespace break; case '%': break; case '&': // Figure out second part of command i++; switch (toupper(NetlinkArea->inbuffer[i])) { case 'C': // Data Carrier Detect Options NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; case 'D': // Data Terminal Ready Options NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; case 'F': // Factory reset NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; case 'K': // Local Flow Control Options NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; case 'Q': // Communications Mode Options NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; case 'S': // Data Set Ready Options NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; default: break; } break; case 'S': { // Status Registers int reg; char *str; char *inbuffer = (char *) NetlinkArea->inbuffer; i++; reg = strtoul(inbuffer+i, &str, 10); i = str-inbuffer; if (inbuffer[i] == '=') { NetlinkArea->reg.SREG[reg & 0xFF] = strtoul(inbuffer+i+1, &str, 10); i = str-inbuffer; } i-=1; switch (reg) { case 7: // Wait Time for Carrier, Silence, or Dial Tone NetlinkArea->connect_timeout = NetlinkArea->reg.SREG[reg] * 1000000; break; default: break; } break; } case ')': case '*': case ':': case '?': case '@': break; case '\\': i++; if (toupper(NetlinkArea->inbuffer[i]) == 'N') { // linefeed } break; case 'A': // Answer Command(no other commands should follow) NETLINK_LOG("Starting answer\n"); break; case 'D': { // Dial Command char *p; int j; char *inbuffer = (char *) NetlinkArea->inbuffer; NetlinkArea->connectstatus = NL_CONNECTSTATUS_CONNECT; NetlinkArea->internet_enable = 0; if ((p = strchr(inbuffer+i+2, '*')) != NULL) { // Fetch IP char ipstring[45]; sscanf(p+1, "%[^\r]\r", ipstring); // replace ',' with '.' for (j = 0; ipstring[j] != '\0'; j++) if (ipstring[j] == ',') ipstring[j] = '.'; // Get port string if necessary if ((p = strchr(ipstring, '*')) != NULL) { p[0] = '\0'; strcpy(NetlinkArea->portstring, p+1); } strcpy(NetlinkArea->ipstring, ipstring); } else { // If we're using Sega's old network, just assume we're using internet mode char number[45]; sscanf(inbuffer+i+2, "%[^\r]\r", number); remove_all_chars(number, '-'); if (strcmp(number, "18007798852") == 0 || strcmp(number, "8007798852") == 0) NetlinkArea->internet_enable = 1; } if (!NetlinkArea->internet_enable) { #ifdef USESOCKET NetworkConnect(NetlinkArea->ipstring, NetlinkArea->portstring); #endif NetlinkArea->connect_time = 0; NETLINK_LOG("Starting dial %s\n", NetlinkArea->ipstring); } i = strchr(inbuffer+i, '\r')-inbuffer-1; break; } case 'E': // Command State Character Echo Selection parameter = NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); // Parameter can only be 0 or 1 if (parameter < 2) NetlinkArea->isechoenab = parameter; else resultcode = NL_RESULTCODE_ERROR; break; case 'I': // Internal Memory Tests switch(NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i)) { case 0: NetlinkDoATResponse("\r\n28800\r\n"); break; default: break; } break; case 'L': // Speaker Volume Level Selection NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; case 'M': // Speaker On/Off Selection NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; case 'V': // Result Code Format Options NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; case 'W': // Negotiation Progress Message Selection NetlinkFetchATParameter(NetlinkArea->inbuffer[i+1], &i); break; default: NETLINK_LOG("Unsupported AT command %c", NetlinkArea->inbuffer[i]); break; } i++; } switch (resultcode) { case NL_RESULTCODE_OK: // OK NetlinkDoATResponse("\r\nOK\r\n"); break; case NL_RESULTCODE_CONNECT: // CONNECT NetlinkDoATResponse("\r\nCONNECT\r\n"); break; case NL_RESULTCODE_RING: // RING NetlinkDoATResponse("\r\nRING\r\n"); break; case NL_RESULTCODE_NOCARRIER: // NO CARRIER NetlinkDoATResponse("\r\nNO CARRIER\r\n"); break; case NL_RESULTCODE_ERROR: // ERROR NetlinkDoATResponse("\r\nERROR\r\n"); break; case NL_RESULTCODE_CONNECT1200: // CONNECT 1200 NetlinkDoATResponse("\r\nCONNECT 1200\r\n"); break; case NL_RESULTCODE_NODIALTONE: // NO DIALTONE NetlinkDoATResponse("\r\nNO DIALTONE\r\n"); break; case NL_RESULTCODE_BUSY: // BUSY NetlinkDoATResponse("\r\nBUSY\r\n"); break; case NL_RESULTCODE_NOANSWER: // NO ANSWER NetlinkDoATResponse("\r\nNO ANSWER\r\n"); break; default: break; } memset((void *) NetlinkArea->inbuffer, 0, NetlinkArea->inbuffersize); NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0; if (NetlinkArea->outbuffersize > 0) { // Set Data available bit in LSR NetlinkArea->reg.LSR |= 0x01; // Trigger Interrrupt NetlinkArea->reg.IIR = 0x4; ScuSendExternalInterrupt12(); } } } else if (NetlinkArea->connectstatus == NL_CONNECTSTATUS_LOGIN1 && NetlinkArea->modemstate == NL_MODEMSTATE_DATA && val == 0x0D) { // Internet login name NetlinkArea->connectstatus = NL_CONNECTSTATUS_LOGIN2; NETLINK_LOG("login response: %s", NetlinkArea->inbuffer+NetlinkArea->inbufferstart); NetlinkDoATResponse("\r\npassword:"); NetlinkUpdateReceivedDataInt(); } else if (NetlinkArea->connectstatus == NL_CONNECTSTATUS_LOGIN2 && NetlinkArea->modemstate == NL_MODEMSTATE_DATA && val == 0x0D) { // Internet password NetlinkArea->connectstatus = NL_CONNECTSTATUS_LOGIN3; NETLINK_LOG("password response: %s", NetlinkArea->inbuffer+NetlinkArea->inbufferstart); NetlinkDoATResponse("\r\n$"); NetlinkUpdateReceivedDataInt(); } else if (NetlinkArea->connectstatus == NL_CONNECTSTATUS_LOGIN3 && NetlinkArea->modemstate == NL_MODEMSTATE_DATA && val == 0x0D) { // Internet password NETLINK_LOG("shell response: %s", NetlinkArea->inbuffer+NetlinkArea->inbufferstart); NetlinkArea->connectstatus = NL_CONNECTSTATUS_CONNECTED; } } return; } case 0x95005: // Interrupt Enable Register/Divisor Latch High Byte { if (NetlinkArea->reg.LCR & 0x80) // Divisor Latch High Byte { NetlinkArea->reg.DLM = val; } else // Interrupt Enable Register { NetlinkArea->reg.IER = val; } return; } case 0x95009: // FIFO Control Register { NetlinkArea->reg.FCR = val; if (val & 0x1) // set FIFO enabled bits NetlinkArea->reg.IIR |= 0xC0; else // clear FIFO enabled bits NetlinkArea->reg.IIR &= ~0xC0; return; } case 0x9500D: // Line Control Register { NetlinkArea->reg.LCR = val; return; } case 0x95011: // Modem Control Register { NetlinkArea->reg.MCR = val; return; } case 0x95019: // Modem Status Register(read-only) return; case 0x9501D: // Scratch { NetlinkArea->reg.SCR = val; return; } default: break; } NETLINK_LOG("Unimplemented Netlink byte write: %08X\n", addr); } ////////////////////////////////////////////////////////////////////////////// int NetlinkInit(const char *setting) { if ((NetlinkArea = malloc(sizeof(Netlink))) == NULL) { Cs2Area->carttype = CART_NONE; YabSetError(YAB_ERR_CANNOTINIT, (void *)"Netlink"); return 0; } memset((void *) NetlinkArea->inbuffer, 0, NETLINK_BUFFER_SIZE); memset((void *) NetlinkArea->outbuffer, 0, NETLINK_BUFFER_SIZE); NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0; NetlinkArea->inbufferupdate = 0; NetlinkArea->outbufferstart = NetlinkArea->outbufferend = NetlinkArea->outbuffersize = 0; NetlinkArea->outbufferupdate = 0; NetlinkArea->isechoenab = 1; NetlinkArea->cycles = 0; NetlinkArea->thb_write_time = 0xFFFFFFFF; NetlinkArea->modemstate = NL_MODEMSTATE_COMMAND; NetlinkArea->escape_count = 0; NetlinkArea->reg.RBR = 0x00; NetlinkArea->reg.IER = 0x00; NetlinkArea->reg.DLL = 0x00; NetlinkArea->reg.DLM = 0x00; NetlinkArea->reg.IIR = 0x01; // NetlinkArea->reg.FCR = 0x??; // have no idea NetlinkArea->reg.LCR = 0x00; NetlinkArea->reg.MCR = 0x00; NetlinkArea->reg.LSR = 0x60; NetlinkArea->reg.MSR = 0x30; NetlinkArea->reg.SCR = 0x01; NetlinkArea->reg.SREG[7] = 50; NetlinkArea->connect_timeout = NetlinkArea->reg.SREG[7] * 1000000; if (setting == NULL || strcmp(setting, "") == 0) { // Use Loopback ip and port 1337 sprintf(NetlinkArea->ipstring, "127.0.0.1"); sprintf(NetlinkArea->portstring, "31337"); } else { char *p; p = strchr(setting, '\n'); if (p == NULL) { strcpy(NetlinkArea->ipstring, setting); sprintf(NetlinkArea->portstring, "1337"); } else { memcpy(NetlinkArea->ipstring, setting, (int)(p - setting)); NetlinkArea->ipstring[(p - setting)] = '\0'; if (strlen(p+1) == 0) sprintf(NetlinkArea->portstring, "1337"); else strcpy(NetlinkArea->portstring, p+1); } } #ifdef USESOCKET return NetworkInit(NetlinkArea->portstring); #else return 0; #endif } ////////////////////////////////////////////////////////////////////////////// void NetlinkDeInit(void) { #ifdef USESOCKET NetworkDeInit(); #endif if (NetlinkArea) free(NetlinkArea); } ////////////////////////////////////////////////////////////////////////////// void NetlinkExec(u32 timing) { NetlinkArea->cycles += timing; NetlinkArea->connect_time += timing; if (NetlinkArea->thb_write_time != 0xFFFFFFFF) NetlinkArea->thb_write_time += timing; if (NetlinkArea->cycles >= 20000) { NetlinkArea->cycles -= 20000; if (NetlinkArea->escape_count == 3 && NetlinkArea->thb_write_time >= 1000000) { // Switch back to command mode NETLINK_LOG("Detected escape sequence, switching back to command mode\n"); NetlinkArea->modemstate = NL_MODEMSTATE_COMMAND; } switch(NetlinkArea->connectstatus) { case NL_CONNECTSTATUS_IDLE: { #ifdef USESOCKET if (NetworkWaitForConnect() == 0) { NetlinkArea->connectstatus = NL_CONNECTSTATUS_CONNECTED; NetlinkArea->modemstate = NL_MODEMSTATE_DATA; // This is probably wrong, but let's give it a try anyways NetlinkDoATResponse("\r\nRING\r\n\r\nCONNECT\r\n"); NetlinkMSRChange(0x08, 0x00); NetlinkUpdateReceivedDataInt(); NETLINK_LOG("Connected via listener\n"); } #endif break; } case NL_CONNECTSTATUS_CONNECT: { #ifdef USESOCKET if (NetlinkArea->internet_enable || NetworkWaitForConnect() == 0) { NetlinkArea->connectstatus = NL_CONNECTSTATUS_CONNECTED; NetlinkArea->modemstate = NL_MODEMSTATE_DATA; NetlinkDoATResponse("\r\nCONNECT 28800\r\n"); NetlinkMSRChange(0x08, 0x00); NetlinkUpdateReceivedDataInt(); NETLINK_LOG("Connected via remote ip connect\n"); if (NetlinkArea->internet_enable) { NetlinkArea->connectstatus = NL_CONNECTSTATUS_LOGIN1; NetlinkDoATResponse("\r\nlogin:"); } } else if (NetlinkArea->connect_time >= NetlinkArea->connect_timeout) { // Kill connect attempt NetworkStopConnect(); NetlinkDoATResponse("\r\nNO ANSWER\r\n"); NetlinkUpdateReceivedDataInt(); NetlinkArea->connectstatus = NL_CONNECTSTATUS_IDLE; } #endif break; } case NL_CONNECTSTATUS_CONNECTED: { #ifdef USESOCKET if (NetlinkArea->outbufferupdate) { NetlinkMSRChange(0x08, 0x00); NetlinkUpdateReceivedDataInt(); //NETLINK_LOG("Received %d byte(s) from external source\n", NetlinkArea->bytes_read); NetlinkArea->outbufferupdate = 0; } #endif break; } default: break; } } } ////////////////////////////////////////////////////////////////////////////// #ifdef USESOCKET void netlink_client(void *data) { netlink_thread *client=(netlink_thread *)data; netlink_client_thread_running = 1; while (netlink_client_thread_running) { int bytes; if (YabSockSelect(client->sock, 1, 1) != 0) { continue; } if (NetlinkArea->modemstate == NL_MODEMSTATE_DATA && NetlinkArea->inbuffersize > 0 && YabSockIsWriteSet(client->sock) && NetlinkArea->thb_write_time > 1000) { //NETLINK_LOG("Sending to external source..."); // Send via network connection if ((bytes = YabSockSend(client->sock, (void *) &NetlinkArea->inbuffer[NetlinkArea->inbufferstart], NetlinkArea->inbufferend-NetlinkArea->inbufferstart, 0)) >= 0) { //NETLINK_LOG("Successfully sent %d byte(s)\n", bytes); if (NetlinkArea->inbufferend > bytes) { NetlinkArea->inbufferstart += bytes; NetlinkArea->inbuffersize -= bytes; } else NetlinkArea->inbufferstart = NetlinkArea->inbufferend = NetlinkArea->inbuffersize = 0; NetlinkArea->inbufferupdate = 1; } else { //NETLINK_LOG("failed.\n"); } } if (YabSockIsReadSet(client->sock)) { //NETLINK_LOG("Data is ready from external source..."); if ((bytes = YabSockReceive(client->sock, (void *) &NetlinkArea->outbuffer[NetlinkArea->outbufferend], sizeof(NetlinkArea->outbuffer)-1-NetlinkArea->outbufferend, 0)) > 0) { //NETLINK_LOG("Successfully received %d byte(s)\n", bytes); NetlinkArea->outbufferend += bytes; NetlinkArea->outbuffersize += bytes; NetlinkArea->outbufferupdate = 1; } } //YabThreadSleep(); } free(data); } ////////////////////////////////////////////////////////////////////////////// static void NetworkStopClient() { if (netlink_client_thread_running) { if (NetlinkArea->clientsocket != -1) YabSockCloseSocket(NetlinkArea->clientsocket); NetlinkArea->clientsocket = -1; netlink_client_thread_running = 0; YabThreadWait(YAB_THREAD_NETLINKCLIENT); } } ////////////////////////////////////////////////////////////////////////////// void netlink_listener(void *data) { YabSock Listener=(YabSock)(pointer)data; netlink_thread *client; netlink_listener_thread_running = 1; while(netlink_listener_thread_running) { NetlinkArea->clientsocket = YabSockAccept(Listener); if (NetlinkArea->clientsocket == -1) { perror("accept failed\n"); continue; } client = malloc(sizeof(netlink_thread)); client->sock = NetlinkArea->clientsocket; YabThreadStart(YAB_THREAD_NETLINKCLIENT, netlink_client, (void *)client); } return; } ////////////////////////////////////////////////////////////////////////////// static void NetworkStopListener() { if (netlink_listener_thread_running) { if (NetlinkArea->listensocket != -1) YabSockCloseSocket(NetlinkArea->listensocket); NetlinkArea->listensocket = -1; netlink_listener_thread_running = 0; YabThreadWait(YAB_THREAD_NETLINKLISTENER); } } ////////////////////////////////////////////////////////////////////////////// int NetworkRestartListener(int port) { int ret; if ((ret = YabSockListenSocket(port, &NetlinkArea->listensocket)) != 0) return ret; YabThreadStart(YAB_THREAD_NETLINKLISTENER, netlink_listener, (void *)(pointer)NetlinkArea->listensocket); return ret; } ////////////////////////////////////////////////////////////////////////////// static int NetworkInit(const char *port) { int ret; YabSockInit(); NetlinkArea->clientsocket = NetlinkArea->listensocket = NetlinkArea->connectsocket = -1; if (ret = NetworkRestartListener(atoi(port)) != 0) return ret; NetlinkArea->connectstatus = NL_CONNECTSTATUS_IDLE; return 0; } ////////////////////////////////////////////////////////////////////////////// void netlink_connect(void *data) { netlink_thread *connect=(netlink_thread *)data; netlink_connect_thread_running = 1; while(netlink_connect_thread_running) { if (YabSockConnectSocket(connect->ip, connect->port, &connect->sock) == 0) { NetlinkArea->connectsocket = connect->sock; YabThreadStart(YAB_THREAD_NETLINKCLIENT, netlink_client, (void *)connect); return; } else YabThreadYield(); } NetworkRestartListener(connect->port); free(data); } ////////////////////////////////////////////////////////////////////////////// static void NetworkStopConnect() { if (netlink_connect_thread_running) { if (NetlinkArea->connectsocket != -1) YabSockCloseSocket(NetlinkArea->connectsocket); NetlinkArea->connectsocket = -1; netlink_connect_thread_running = 0; YabThreadWait(YAB_THREAD_NETLINKCONNECT); } } ////////////////////////////////////////////////////////////////////////////// static void NetworkConnect(const char *ip, const char *port) { netlink_thread *connect; connect = malloc(sizeof(netlink_thread)); strcpy(connect->ip, ip); connect->port = atoi(port); NetworkStopListener(); YabThreadStart(YAB_THREAD_NETLINKCONNECT, netlink_connect, connect); } ////////////////////////////////////////////////////////////////////////////// static int NetworkWaitForConnect() { if (netlink_client_thread_running) return 0; else return -1; } ////////////////////////////////////////////////////////////////////////////// static void NetworkDeInit(void) { NetworkStopListener(); NetworkStopConnect(); NetworkStopClient(); YabSockDeInit(); } ////////////////////////////////////////////////////////////////////////////// #endif yabause-0.9.13.1/src/persdljoy.h000644 001750 001750 00000001646 12256006174 020373 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PERSDLJOY_H #define PERSDLJOY_H #include "peripheral.h" /** @addtogroup peripheral * @{ */ #define PERCORE_SDLJOY 3 extern PerInterface_struct PERSDLJoy; /** @} */ #endif yabause-0.9.13.1/src/sh2_dynarec/assem_arm.h000644 001750 001750 00000002173 12256006124 022517 0ustar00guillaumeguillaume000000 000000 #define HOST_REGS 13 #define HOST_CCREG 10 #define EXCLUDE_REG 11 #define SLAVERA_REG 8 #define HOST_IMM8 1 #define HAVE_CMOV_IMM 1 #define CORTEX_A8_BRANCH_PREDICTION_HACK 1 #define USE_MINI_HT 1 //#define REG_PREFETCH 1 /* ARM calling convention: r0-r3, r12: caller-save r4-r11: callee-save */ #define ARG1_REG 0 #define ARG2_REG 1 #define ARG3_REG 2 #define ARG4_REG 3 /* GCC register naming convention: r10 = sl (base) r11 = fp (frame pointer) r12 = ip (scratch) r13 = sp (stack pointer) r14 = lr (link register) r15 = pc (program counter) */ #define FP 11 #define LR 14 #define HOST_TEMPREG 14 // Note: FP is set to &dynarec_local when executing generated code. // Thus the local variables are actually global and not on the stack. extern u8 sh2_dynarec_target[16777216]; extern u32 memory_map[1048576]; // 32-bit //#define BASE_ADDR 0x6000000 // Code generator target address #define BASE_ADDR ((u32)&sh2_dynarec_target) // Code generator target address #define TARGET_SIZE_2 24 // 2^24 = 16 megabytes //#define TARGET_SIZE_2 25 // 2^25 = 32 megabytes #ifdef ANDROID #define __clear_cache clear_cache #endif yabause-0.9.13.1/src/sh2_dynarec/linkage_x64.s000644 001750 001750 00000044261 12256006124 022702 0ustar00guillaumeguillaume000000 000000 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Yabause - linkage_x64.s * * Copyright (C) 2009-2011 Ari64 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .file "linkage_x86_64.s" .bss .align 4 .section .rodata .text .globl YabauseDynarecOneFrameExec .type YabauseDynarecOneFrameExec, @function YabauseDynarecOneFrameExec: /* (arg1/edi - m68kcycles) */ /* (arg2/esi - m68kcenticycles) */ push %rbp mov %rsp, %rbp mov master_ip, %rax xor %ecx, %ecx push %rbx push %r12 push %r13 push %r14 push %r15 push %rcx /* zero */ push %rcx push %rcx push %rcx call .+5 mov %esi,-60(%rbp) /* m68kcenticycles */ mov %edi,-64(%rbp) /* m68kcycles */ mov %rax,-80(%rbp) /* overwrite return address */ /* Stack frame: return address (0) rbp (8/0) save rbx (16/8) save r12 (24/16) save r13 (32/24) save r14 (40/32) save r15 (48/40) decilinecount (52/44) decilinecycles (56/48) sh2cycles (60/52) scucycles (64/56) m68kcenticycles (68/60) m68kcycles (72/64) space for alignment (80/72) ret address/master_ip (88/80) (alternate rsp at call) save %rax (96/88) save %rcx (104/96) save %rdx (112/104) save %rsi (120/112) save %rdi (128/120) space for alignment (136/128) (rsp at call) next return address (144/136) total = 144 */ /* usecinc? cyclesinc?*/ newline: /* const u32 decilinecycles = yabsys.DecilineStop >> YABSYS_TIMING_BITS; */ /* const u32 cyclesinc = yabsys.DecilineStop * 10; */ mov decilinestop_p, %rax mov yabsys_timing_bits, %ecx mov (%rax), %eax lea (%eax,%eax,4), %ebx /* decilinestop*5 */ shr %cl, %eax /* decilinecycles */ shl %ebx /* cyclesinc=decilinestop*10 */ lea (%eax,%eax,8), %edx /* decilinecycles*9 */ /* yabsys.SH2CycleFrac += cyclesinc;*/ /* sh2cycles = (yabsys.SH2CycleFrac >> (YABSYS_TIMING_BITS + 1)) << 1;*/ /* yabsys.SH2CycleFrac &= ((YABSYS_TIMING_MASK << 1) | 1);*/ mov SH2CycleFrac_p, %rsi mov yabsys_timing_mask, %edi inc %ecx /* yabsys_timing_bits+1 */ add (%rsi), %ebx /* SH2CycleFrac */ stc adc %edi, %edi /* ((YABSYS_TIMING_MASK << 1) | 1) */ mov %eax, -48(%rbp) /* decilinecycles */ and %ebx, %edi mov %edi, (%rsi) /* SH2CycleFrac */ shr %cl, %ebx mov %ebx, -56(%rbp) /* scucycles */ add %ebx, %ebx /* sh2cycles */ mov MSH2, %rax mov NumberOfInterruptsOffset, %ecx sub %edx, %ebx /* sh2cycles(full line) - decilinecycles*9 */ mov %rax, CurrentSH2 mov %ebx, -52(%rbp) /* sh2cycles */ cmp $0, (%rax, %rcx) jne master_handle_interrupts mov master_cc, %esi sub %ebx, %esi ret /* jmp master_ip */ .size YabauseDynarecOneFrameExec, .-YabauseDynarecOneFrameExec .globl master_handle_interrupts .type master_handle_interrupts, @function master_handle_interrupts: mov -80(%rbp), %rax /* get return address */ mov %rax, master_ip call DynarecMasterHandleInterrupts mov master_ip, %rax mov master_cc, %esi mov %rax,-80(%rbp) /* overwrite return address */ sub %ebx, %esi ret /* jmp master_ip */ .size master_handle_interrupts, .-master_handle_interrupts .globl slave_entry .type slave_entry, @function slave_entry: mov 28(%rsp), %ebx /* sh2cycles */ mov %esi, master_cc mov %ebx, %edi call FRTExec mov %ebx, %edi call WDTExec mov slave_ip, %rdx test %edx, %edx je cc_interrupt_master /* slave not running */ mov SSH2, %rax mov NumberOfInterruptsOffset, %ecx mov %rax, CurrentSH2 cmp $0, (%rax, %rcx) jne slave_handle_interrupts mov slave_cc, %esi sub %ebx, %esi jmp *%rdx /* jmp *slave_ip */ .size slave_entry, .-slave_entry .globl slave_handle_interrupts .type slave_handle_interrupts, @function slave_handle_interrupts: call DynarecSlaveHandleInterrupts mov slave_ip, %rdx mov slave_cc, %esi sub %ebx, %esi jmp *%rdx /* jmp *slave_ip */ .size slave_handle_interrupts, .-slave_handle_interrupts .globl cc_interrupt .type cc_interrupt, @function cc_interrupt: /* slave */ mov 28(%rsp), %ebx /* sh2cycles */ mov %rbp, slave_ip mov %esi, slave_cc mov %ebx, %edi call FRTExec mov %ebx, %edi call WDTExec .size cc_interrupt, .-cc_interrupt .globl cc_interrupt_master .type cc_interrupt_master, @function cc_interrupt_master: lea 80(%rsp), %rbp mov -44(%rbp), %eax /* decilinecount */ mov -48(%rbp), %ebx /* decilinecycles */ inc %eax cmp $9, %eax ja .A3 mov %eax, -44(%rbp) /* decilinecount++ */ je .A2 mov %ebx, -52(%rbp) /* sh2cycles */ .A1: mov master_cc, %esi mov MSH2, %rax mov NumberOfInterruptsOffset, %ecx mov %rax, CurrentSH2 cmpl $0, (%rax, %rcx) jne master_handle_interrupts sub %ebx, %esi ret /* jmp master_ip */ .A2: call Vdp2HBlankIN jmp .A1 .A3: mov -56(%rbp), %edi /* scucycles */ call ScuExec call M68KSync call Vdp2HBlankOUT call ScspExec mov linecount_p, %rbx mov maxlinecount_p, %rax mov vblanklinecount_p, %rcx mov (%rbx), %edx mov (%rax), %eax mov (%rcx), %ecx inc %edx andl $0, -44(%rbp) /* decilinecount=0 */ cmp %eax, %edx /* max ? */ je nextframe mov %edx, (%rbx) /* linecount++ */ cmp %ecx, %edx /* vblank ? */ je vblankin nextline: call finishline jmp newline finishline: /*const u32 usecinc = yabsys.DecilineUsec * 10;*/ mov decilineusec_p, %rax mov UsecFrac_p, %rbx mov yabsys_timing_bits, %ecx mov (%rax), %eax mov (%rbx), %edx lea (%eax,%eax,4), %edi add %edi, %edi /*yabsys.UsecFrac += usecinc;*/ add %edx, %edi add $-8, %rsp /* Align stack */ /*SmpcExec(yabsys.UsecFrac >> YABSYS_TIMING_BITS); /*Cs2Exec(yabsys.UsecFrac >> YABSYS_TIMING_BITS); /*yabsys.UsecFrac &= YABSYS_TIMING_MASK;*/ mov %edi, (%rbx) /* UsecFrac */ shr %cl, %edi call SmpcExec /* SmpcExec may modify UsecFrac; must reload it */ mov yabsys_timing_mask, %r12d mov (%rbx), %edi /* UsecFrac */ mov yabsys_timing_bits, %ecx and %edi, %r12d shr %cl, %edi call Cs2Exec mov %r12d, (%rbx) /* UsecFrac */ mov saved_centicycles, %ecx mov -60(%rbp), %ebx /* m68kcenticycles */ mov -64(%rbp), %edi /* m68kcycles */ add %ebx, %ecx mov %ecx, %ebx add $-100, %ecx cmovnc %ebx, %ecx adc $0, %edi mov %ecx, saved_centicycles call M68KExec add $8, %rsp /* Align stack */ ret vblankin: call SmpcINTBACKEnd call Vdp2VBlankIN call CheatDoPatches jmp nextline nextframe: call Vdp2VBlankOUT andl $0, (%rbx) /* linecount = 0 */ call finishline call M68KSync mov rccount, %esi inc %esi andl $0, invalidate_count and $0x3f, %esi cmpl $0, restore_candidate(,%esi,4) mov %esi, rccount jne .A5 .A4: mov (%rsp), %rax add $40, %rsp mov %rax, master_ip pop %r15 /* restore callee-save registers */ pop %r14 pop %r13 pop %r12 pop %rbx pop %rbp ret .A5: /* Move 'dirty' blocks to the 'clean' list */ mov restore_candidate(,%esi,4), %ebx mov %esi, %ebp andl $0, restore_candidate(,%esi,4) shl $5, %ebp .A6: shr $1, %ebx jnc .A7 mov %ebp, %edi call clean_blocks .A7: inc %ebp test $31, %ebp jne .A6 jmp .A4 .size cc_interrupt_master, .-cc_interrupt_master .globl dyna_linker .type dyna_linker, @function dyna_linker: /* eax = virtual target address */ /* ebx = instruction to patch */ mov %eax, %ecx mov $1023, %edx shr $12, %ecx and %ecx, %edx and $0xDFFFF, %ecx or $1024, %edx cmp %edx, %ecx cmova %edx, %ecx /* jump_in lookup */ movq jump_in(,%ecx,8), %r12 .B1: test %r12, %r12 je .B3 mov (%r12), %edi xor %eax, %edi je .B2 movq 16(%r12), %r12 jmp .B1 .B2: mov (%ebx), %edi mov %esi, %ebp lea 4(%ebx,%edi,1), %esi mov %eax, %edi call add_link mov 8(%r12), %edi mov %ebp, %esi lea -4(%edi), %edx subl %ebx, %edx movl %edx, (%ebx) jmp *%rdi .B3: /* hash_table lookup */ mov %eax, %edi shr $16, %edi xor %eax, %edi movzwl %di, %edi shl $4, %edi cmp hash_table(%edi), %eax jne .B5 .B4: mov hash_table+4(%edi), %edx jmp *%rdx .B5: cmp hash_table+8(%edi), %eax lea 8(%edi), %edi je .B4 /* jump_dirty lookup */ movq jump_dirty(,%ecx,8), %r12 .B6: test %r12, %r12 je .B8 mov (%r12), %ecx xor %eax, %ecx je .B7 movq 16(%r12), %r12 jmp .B6 .B7: movl 8(%r12), %edx /* hash_table insert */ mov hash_table-8(%edi), %ebx mov hash_table-4(%edi), %ecx mov %eax, hash_table-8(%edi) mov %edx, hash_table-4(%edi) mov %ebx, hash_table(%edi) mov %ecx, hash_table+4(%edi) jmp *%rdx .B8: mov %eax, %edi mov %eax, %ebp /* Note: assumes %rbx and %rbp are callee-saved */ mov %esi, %r12d call sh2_recompile_block test %eax, %eax mov %ebp, %eax mov %r12d, %esi je dyna_linker /* shouldn't happen */ int3 .size dyna_linker, .-dyna_linker .globl jump_vaddr_eax_master .type jump_vaddr_eax_master, @function jump_vaddr_eax_master: mov %eax, %edi jmp jump_vaddr_edi_master .size jump_vaddr_eax_master, .-jump_vaddr_eax_master .globl jump_vaddr_ecx_master .type jump_vaddr_ecx_master, @function jump_vaddr_ecx_master: mov %ecx, %edi jmp jump_vaddr_edi_master .size jump_vaddr_ecx_master, .-jump_vaddr_ecx_master .globl jump_vaddr_edx_master .type jump_vaddr_edx_master, @function jump_vaddr_edx_master: mov %edx, %edi jmp jump_vaddr_edi_master .size jump_vaddr_edx_master, .-jump_vaddr_edx_master .globl jump_vaddr_ebx_master .type jump_vaddr_ebx_master, @function jump_vaddr_ebx_master: mov %ebx, %edi jmp jump_vaddr_edi_master .size jump_vaddr_ebx_master, .-jump_vaddr_ebx_master .globl jump_vaddr_ebp_master .type jump_vaddr_ebp_master, @function jump_vaddr_ebp_master: mov %ebp, %edi jmp jump_vaddr_edi_master .size jump_vaddr_ebp_master, .-jump_vaddr_ebp_master .globl jump_vaddr_eax_slave .type jump_vaddr_eax_slave, @function jump_vaddr_eax_slave: mov %eax, %edi jmp jump_vaddr_edi_slave .size jump_vaddr_eax_slave, .-jump_vaddr_eax_slave .globl jump_vaddr_ecx_slave .type jump_vaddr_ecx_slave, @function jump_vaddr_ecx_slave: mov %ecx, %edi jmp jump_vaddr_edi_slave .size jump_vaddr_ecx_slave, .-jump_vaddr_ecx_slave .globl jump_vaddr_edx_slave .type jump_vaddr_edx_slave, @function jump_vaddr_edx_slave: mov %edx, %edi jmp jump_vaddr_edi_slave .size jump_vaddr_edx_slave, .-jump_vaddr_edx_slave .globl jump_vaddr_ebx_slave .type jump_vaddr_ebx_slave, @function jump_vaddr_ebx_slave: mov %ebx, %edi jmp jump_vaddr_edi_slave .size jump_vaddr_ebx_slave, .-jump_vaddr_ebx_slave .globl jump_vaddr_ebp_slave .type jump_vaddr_ebp_slave, @function jump_vaddr_ebp_slave: mov %ebp, %edi .size jump_vaddr_ebp_slave, .-jump_vaddr_ebp_slave .globl jump_vaddr_edi_slave .type jump_vaddr_edi_slave, @function jump_vaddr_edi_slave: or $1, %edi .size jump_vaddr_edi_slave, .-jump_vaddr_edi_slave .globl jump_vaddr_edi_master .type jump_vaddr_edi_master, @function jump_vaddr_edi_master: mov %edi, %eax .size jump_vaddr_edi_master, .-jump_vaddr_edi_master .globl jump_vaddr .type jump_vaddr, @function jump_vaddr: /* Check hash table */ shr $16, %eax xor %edi, %eax movzwl %ax, %eax shl $4, %eax cmp hash_table(%eax), %edi jne .C2 .C1: mov hash_table+4(%eax), %edi jmp *%rdi .C2: cmp hash_table+8(%eax), %edi lea 8(%eax), %eax je .C1 /* No hit on hash table, call compiler */ mov %esi, %ebx /* CCREG */ call get_addr mov %ebx, %esi jmp *%rax .size jump_vaddr, .-jump_vaddr .globl verify_code .type verify_code, @function verify_code: /* rax = source */ /* ebx = target */ /* ecx = length */ /* r12d = instruction pointer */ mov -4(%rax,%rcx,1), %edi xor -4(%ebx,%ecx,1), %edi jne .D4 mov %ecx, %edx add $-4, %ecx je .D3 test $4, %edx cmove %edx, %ecx .D2: mov -8(%rax,%rcx,1), %rdi cmp -8(%ebx,%ecx,1), %rdi jne .D4 add $-8, %ecx jne .D2 .D3: ret .D4: add $8, %rsp /* pop return address, we're not returning */ mov %r12d, %edi mov %esi, %ebx call get_addr mov %ebx, %esi jmp *%rax .size verify_code, .-verify_code .globl WriteInvalidateLong .type WriteInvalidateLong, @function WriteInvalidateLong: mov %edi, %ecx shr $12, %ecx bt %ecx, cached_code jnc MappedMemoryWriteLong /*push %rax*/ /*push %rcx*/ push %rdx /* unused, for stack alignment */ push %rsi push %rdi call invalidate_addr pop %rdi pop %rsi pop %rdx /* unused, for stack alignment */ /*pop %rcx*/ /*pop %rax*/ jmp MappedMemoryWriteLong .size WriteInvalidateLong, .-WriteInvalidateLong .globl WriteInvalidateWord .type WriteInvalidateWord, @function WriteInvalidateWord: mov %edi, %ecx shr $12, %ecx bt %ecx, cached_code jnc MappedMemoryWriteWord /*push %rax*/ /*push %rcx*/ push %rdx /* unused, for stack alignment */ push %rsi push %rdi call invalidate_addr pop %rdi pop %rsi pop %rdx /* unused, for stack alignment */ /*pop %rcx*/ /*pop %rax*/ jmp MappedMemoryWriteWord .size WriteInvalidateWord, .-WriteInvalidateWord .globl WriteInvalidateByteSwapped .type WriteInvalidateByteSwapped, @function WriteInvalidateByteSwapped: xor $1, %edi .size WriteInvalidateByteSwapped, .-WriteInvalidateByteSwapped .globl WriteInvalidateByte .type WriteInvalidateByte, @function WriteInvalidateByte: mov %edi, %ecx shr $12, %ecx bt %ecx, cached_code jnc MappedMemoryWriteByte /*push %rax*/ /*push %rcx*/ push %rdx /* unused, for stack alignment */ push %rsi push %rdi call invalidate_addr pop %rdi pop %rsi pop %rdx /* unused, for stack alignment */ /*pop %rcx*/ /*pop %rax*/ jmp MappedMemoryWriteByte .size WriteInvalidateByte, .-WriteInvalidateByte .globl div1 .type div1, @function div1: /* eax = dividend */ /* ecx = divisor */ /* edx = sr */ bt $9, %edx /* M bit */ jc div1_negative_divisor bts $0, %edx /* Get T bit and set */ adc %eax, %eax /* rn=(rn<<1)+T */ adc %ebx, %ebx /* New Q in ebx */ mov %ecx, %ebp btr $8, %edx /* Get Q bit and clear it */ cmc sbb %edi, %edi /* 0xFFFFFFFF if old_Q clear, 0 otherwise */ sbb $0, %ebp xor %edi, %ebp add %ebp, %eax /* rn+rm if old_Q, rn-rm if !old_Q */ /* carry set if rn < old_rn */ adc %edi, %ebx /* low bit = (rn=old_rn)^new_Q */ not %edi /* if old_Q clear, edi=0 */ or %ebp, %edi /* zero if old_Q==0 && rn==old_rn */ neg %edi /* clear carry if edi==0 */ adc $-1, %ebx /* invert result for old_Q==0 && rn==old_rn */ and $1, %ebx xor %ebx, %edx /* New T = (Q==M) */ shl $8, %ebx or %ebx, %edx /* save new Q */ /* push %edx push %eax push %ecx call debug_division pop %ecx pop %eax pop %edx */ ret div1_negative_divisor: btr $0, %edx /* Get T bit and clear */ adc %eax, %eax /* rn=(rn<<1)+T */ adc %ebx, %ebx /* New Q in ebx */ mov %ecx, %ebp btr $8, %edx /* Get Q bit and clear it */ sbb %edi, %edi /* 0xFFFFFFFF if old_Q set, 0 otherwise */ sbb $0, %ebp xor %edi, %ebp not %edi /* if old_Q clear, edi=-1 */ add %ebp, %eax /* rn+rm if !old_Q, rn-rm if old_Q */ /* carry set if rn < old_rn */ adc %edi, %ebx /* low bit = (rn=old_rn)^new_Q */ or %ebp, %edi /* zero if old_Q==1 && rn==old_rn */ neg %edi /* clear carry if edi==0 */ adc $-1, %ebx /* invert result for old_Q==1 && rn==old_rn */ and $1, %ebx xor %ebx, %edx /* New T = (Q==M) */ shl $8, %ebx or %ebx, %edx /* save new Q */ ret .size div1, .-div1 .globl macl .type macl, @function macl: /* ebx = sr */ /* ebp = multiplicand address */ /* edi = multiplicand address */ /* eax = return MACL */ /* edx = return MACH */ mov %edx, %r12d /* MACH */ mov %eax, %r13d /* MACL */ mov %ebp, %r14d mov %edi, %r15d call MappedMemoryReadLong mov %eax, %esi mov %r14d, %edi call MappedMemoryReadLong lea 4(%r14), %ebp lea 4(%r15), %edi imul %esi add %r13d, %eax /* MACL */ adc %r12d, %edx /* MACH */ test $0x2, %bl jne macl_saturation ret macl_saturation: mov $0xFFFF8000, %esi xor %ecx, %ecx cmp %esi, %edx cmovl %esi, %edx cmovl %ecx, %eax not %esi not %ecx cmp %esi, %edx cmovg %esi, %edx cmovg %ecx, %eax ret .size macl, .-macl .globl macw .type macw, @function macw: /* ebx = sr */ /* ebp = multiplicand address */ /* edi = multiplicand address */ /* eax = return MACL */ /* edx = return MACH */ mov %edx, %r12d /* MACH */ mov %eax, %r13d /* MACL */ mov %ebp, %r14d mov %edi, %r15d call MappedMemoryReadWord movswl %ax, %esi mov %r14d, %edi call MappedMemoryReadWord movswl %ax, %eax lea 2(%r14), %ebp lea 2(%r15), %edi imul %esi test $0x2, %bl jne macw_saturation add %r13d, %eax /* MACL */ adc %r12d, %edx /* MACH */ ret macw_saturation: mov %r13d, %esi sar $31, %esi add %r13d, %eax /* MACL */ adc %esi, %edx mov $0x80000000, %esi mov $0x7FFFFFFF, %ecx add %eax, %esi adc $0, %edx cmovne %ecx, %eax not %ecx cmovl %ecx, %eax mov %r12d, %edx ret .size macw, .-macw .globl master_handle_bios .type master_handle_bios, @function master_handle_bios: mov (%rsp), %rdx /* get return address */ mov %eax, master_pc mov %esi, master_cc mov %rdx, master_ip mov MSH2, %rdi call BiosHandleFunc mov master_ip, %rdx mov master_cc, %esi mov %rdx, (%rsp) ret /* jmp *master_ip */ .size master_handle_bios, .-master_handle_bios .globl slave_handle_bios .type slave_handle_bios, @function slave_handle_bios: pop %rdx /* get return address */ mov %eax, slave_pc mov %esi, slave_cc mov %rdx, slave_ip mov SSH2, %rdi call BiosHandleFunc mov slave_ip, %rdx mov slave_cc, %esi jmp *%rdx /* jmp *slave_ip */ .size slave_handle_bios, .-slave_handle_bios .globl breakpoint .type breakpoint, @function breakpoint: ret /* Set breakpoint here for debugging */ .size breakpoint, .-breakpoint yabause-0.9.13.1/src/sh2_dynarec/assem_x64.h000644 001750 001750 00000001374 12256006124 022363 0ustar00guillaumeguillaume000000 000000 #define HOST_REGS 8 #define HOST_CCREG 6 #define EXCLUDE_REG 4 #define SLAVERA_REG 5 //#define IMM_PREFETCH 1 #define HOST_IMM_ADDR32 1 #define INVERTED_CARRY 1 #define DESTRUCTIVE_WRITEBACK 1 #define DESTRUCTIVE_SHIFT 1 #define POINTERS_64BIT 1 #define USE_MINI_HT 1 #define BASE_ADDR 0x70000000 // Code generator target address #define TARGET_SIZE_2 25 // 2^25 = 32 megabytes #define JUMP_TABLE_SIZE 0 // Not needed for x86 /* x86-64 calling convention: func(rdi, rsi, rdx, rcx, r8, r9) {return rax;} callee-save: %rbp %rbx %r12-%r15 */ #define ARG1_REG 7 /* RDI */ #define ARG2_REG 6 /* RSI */ #define EAX 0 #define ECX 1 #define EDX 2 #define EBX 3 #define ESP 4 #define EBP 5 #define ESI 6 #define EDI 7 extern u64 memory_map[1048576]; // 64-bit yabause-0.9.13.1/src/sh2_dynarec/sh2_dynarec.h000644 001750 001750 00000000277 12256006124 022754 0ustar00guillaumeguillaume000000 000000 #ifndef SH2_DYNAREC_H #define SH2_DYNAREC_H void sh2_dynarec_init(void); int verify_dirty(pointer addr); void invalidate_all_pages(void); void YabauseDynarecOneFrameExec(int, int); #endif yabause-0.9.13.1/src/sh2_dynarec/assem_x86.c000644 001750 001750 00000231616 12256006124 022366 0ustar00guillaumeguillaume000000 000000 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Yabause - assem_x86.c * * Copyright (C) 2009-2011 Ari64 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ u32 memory_map[1048576]; ALIGNED(8) u32 mini_ht_master[32][2]; ALIGNED(8) u32 mini_ht_slave[32][2]; ALIGNED(4) u8 restore_candidate[512]; int rccount; int master_reg[22]; int master_cc; // Cycle count int master_pc; // Virtual PC void * master_ip; // Translated PC int slave_reg[22]; int slave_cc; // Cycle count int slave_pc; // Virtual PC void * slave_ip; // Translated PC void FASTCALL WriteInvalidateLong(u32 addr, u32 val); void FASTCALL WriteInvalidateWord(u32 addr, u32 val); void FASTCALL WriteInvalidateByte(u32 addr, u32 val); void FASTCALL WriteInvalidateByteSwapped(u32 addr, u32 val); u32 rmw_temp; // Temporary storage for TAS.B instruction void jump_vaddr_eax_master(); void jump_vaddr_ecx_master(); void jump_vaddr_edx_master(); void jump_vaddr_ebx_master(); void jump_vaddr_ebp_master(); void jump_vaddr_edi_master(); void jump_vaddr_eax_slave(); void jump_vaddr_ecx_slave(); void jump_vaddr_edx_slave(); void jump_vaddr_ebx_slave(); void jump_vaddr_ebp_slave(); void jump_vaddr_edi_slave(); const pointer jump_vaddr_reg[2][8] = { { (pointer)jump_vaddr_eax_master, (pointer)jump_vaddr_ecx_master, (pointer)jump_vaddr_edx_master, (pointer)jump_vaddr_ebx_master, 0, (pointer)jump_vaddr_ebp_master, 0, (pointer)jump_vaddr_edi_master },{ (pointer)jump_vaddr_eax_slave, (pointer)jump_vaddr_ecx_slave, (pointer)jump_vaddr_edx_slave, (pointer)jump_vaddr_ebx_slave, 0, (pointer)jump_vaddr_ebp_slave, 0, (pointer)jump_vaddr_edi_slave } }; // We need these for cmovcc instructions on x86 u32 const_zero=0; u32 const_one=1; /* Linker */ void set_jump_target(pointer addr,pointer target) { u8 *ptr=(u8 *)addr; if(*ptr==0x0f) { assert(ptr[1]>=0x80&&ptr[1]<=0x8f); u32 *ptr2=(u32 *)(ptr+2); *ptr2=target-(u32)ptr2-4; } else if(*ptr==0xe8||*ptr==0xe9) { u32 *ptr2=(u32 *)(ptr+1); *ptr2=target-(u32)ptr2-4; } else { assert(*ptr==0xc7); /* mov immediate (store address) */ u32 *ptr2=(u32 *)(ptr+6); *ptr2=target; } } void *kill_pointer(void *stub) { u32 *i_ptr=*((u32 **)(stub+6)); *i_ptr=(u32)stub-(u32)i_ptr-4; return i_ptr; } pointer get_pointer(void *stub) { s32 *i_ptr=*((u32 **)(stub+6)); return *i_ptr+(pointer)i_ptr+4; } // Find the "clean" entry point from a "dirty" entry point // by skipping past the call to verify_code pointer get_clean_addr(pointer addr) { u8 *ptr=(u8 *)addr; assert(ptr[20]==0xE8); // call instruction assert(ptr[25]==0x83); // pop (add esp,4) instruction if(ptr[28]==0xE9) return *(s32 *)(ptr+29)+addr+33; // follow jmp else return(addr+28); } int verify_dirty(pointer addr) { u8 *ptr=(u8 *)addr; assert(ptr[5]==0xB8); u32 source=*(u32 *)(ptr+6); u32 copy=*(u32 *)(ptr+11); u32 len=*(u32 *)(ptr+16); assert(ptr[20]==0xE8); // call instruction return !memcmp((void *)source,(void *)copy,len); } // This doesn't necessarily find all clean entry points, just // guarantees that it's not dirty int isclean(pointer addr) { u8 *ptr=(u8 *)addr; if(ptr[5]!=0xB8) return 1; // mov imm,%eax if(ptr[10]!=0xBB) return 1; // mov imm,%ebx if(ptr[15]!=0xB9) return 1; // mov imm,%ecx if(ptr[20]!=0xE8) return 1; // call instruction if(ptr[25]!=0x83) return 1; // pop (add esp,4) instruction return 0; } void get_bounds(pointer addr,u32 *start,u32 *end) { u8 *ptr=(u8 *)addr; assert(ptr[5]==0xB8); u32 source=*(u32 *)(ptr+6); //u32 copy=*(u32 *)(ptr+11); u32 len=*(u32 *)(ptr+16); assert(ptr[20]==0xE8); // call instruction if(start) *start=source; if(end) *end=source+len; } /* Register allocation */ // Note: registers are allocated clean (unmodified state) // if you intend to modify the register, you must call dirty_reg(). void alloc_reg(struct regstat *cur,int i,signed char reg) { int r,hr; int preferred_reg = (reg&3)+(reg>21)*4+(reg==24)+(reg==28)+(reg==32); if(reg==CCREG) preferred_reg=HOST_CCREG; // Don't allocate unused registers if((cur->u>>reg)&1) return; // see if it's already allocated for(hr=0;hrregmap[hr]==reg) return; } // Keep the same mapping if the register was already allocated in a loop preferred_reg = loop_reg(i,reg,preferred_reg); // Try to allocate the preferred register if(cur->regmap[preferred_reg]==-1) { cur->regmap[preferred_reg]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[preferred_reg]; if(r<64&&((cur->u>>r)&1)) { cur->regmap[preferred_reg]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==-1) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]; if(r>=0) { if((cur->u>>r)&1) if(i==0||(unneeded_reg[i-1]>>r)&1) {cur->regmap[hr]=-1;break;} } } // Try to allocate any available register, but prefer // registers that have not been used recently. if(i>0) { for(hr=0;hrregmap[hr]==-1) { if(regs[i-1].regmap[hr]!=rs1[i-1]&®s[i-1].regmap[hr]!=rs2[i-1]&®s[i-1].regmap[hr]!=rt1[i-1]&®s[i-1].regmap[hr]!=rt2[i-1]) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==-1) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); if(i>0) { // Don't evict the cycle count at entry points, otherwise the entry // stub will have to write it. if(bt[i]&&hsn[CCREG]>2) hsn[CCREG]=2; if(i>1&&hsn[CCREG]>2&&(itype[i-2]==RJUMP||itype[i-2]==UJUMP||itype[i-2]==CJUMP||itype[i-2]==SJUMP)) hsn[CCREG]=2; for(j=10;j>=3;j--) { // Alloc preferred register if available if(hsn[r=cur->regmap[preferred_reg]&63]==j) { for(hr=0;hrregmap[hr]&63)==r) { cur->regmap[hr]=-1; cur->dirty&=~(1<isdoingcp&=~(1<regmap[preferred_reg]=reg; return; } for(r=0;r<=MAXREG;r++) { if(hsn[r]==j&&r!=rs1[i-1]&&r!=rs2[i-1]&&r!=rt1[i-1]&&r!=rt2[i-1]) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==reg) return; } // Try to allocate any available register, starting with EDI, ESI, EBP... // We prefer EDI, ESI, EBP since the others are used for byte/halfword stores for(hr=HOST_REGS-1;hr>=0;hr--) { if(hr!=EXCLUDE_REG&&cur->regmap[hr]==-1) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;hr--) { r=cur->regmap[hr]; if(r>=0) { if((cur->u>>r)&1) { if(i==0||((unneeded_reg[i-1]>>r)&1)) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); if(i>0) { // Don't evict the cycle count at entry points, otherwise the entry // stub will have to write it. if(bt[i]&&hsn[CCREG]>2) hsn[CCREG]=2; if(i>1&&hsn[CCREG]>2&&(itype[i-2]==RJUMP||itype[i-2]==UJUMP||itype[i-2]==CJUMP||itype[i-2]==SJUMP)) hsn[CCREG]=2; for(j=10;j>=3;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j&&r!=rs1[i-1]&&r!=rs2[i-1]&&r!=rt1[i-1]&&r!=rt2[i-1]) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[n]==reg) { dirty=(cur->dirty>>n)&1; cur->regmap[n]=-1; } } cur->regmap[hr]=reg; cur->dirty&=~(1<dirty|=dirty<isdoingcp&=~(1<=-128&&rs<4) { output_byte(0xF6); output_modrm(3,rs,0); output_byte(imm); } else { output_byte(0xF7); output_modrm(3,rs,0); output_w32(imm); } } void emit_not(int rs,int rt) { if(rs!=rt) emit_mov(rs,rt); assem_debug("not %%%s\n",regname[rt]); output_byte(0xF7); output_modrm(3,rt,2); } void emit_and(unsigned int rs1,unsigned int rs2,unsigned int rt) { assert(rs1<8); assert(rs2<8); assert(rt<8); if(rs1==rt) { assem_debug("and %%%s,%%%s\n",regname[rs2],regname[rt]); output_byte(0x21); output_modrm(3,rs1,rs2); } else if(rs2==rt) { assem_debug("and %%%s,%%%s\n",regname[rs1],regname[rt]); output_byte(0x21); output_modrm(3,rs2,rs1); } else { emit_mov(rs1,rt); emit_and(rt,rs2,rt); } } void emit_or(unsigned int rs1,unsigned int rs2,unsigned int rt) { assert(rs1<8); assert(rs2<8); assert(rt<8); if(rs1==rt) { assem_debug("or %%%s,%%%s\n",regname[rs2],regname[rt]); output_byte(0x09); output_modrm(3,rs1,rs2); } else if(rs2==rt) { assem_debug("or %%%s,%%%s\n",regname[rs1],regname[rt]); output_byte(0x09); output_modrm(3,rs2,rs1); } else { emit_mov(rs1,rt); emit_or(rt,rs2,rt); } } void emit_or_and_set_flags(int rs1,int rs2,int rt) { emit_or(rs1,rs2,rt); } void emit_xor(unsigned int rs1,unsigned int rs2,unsigned int rt) { assert(rs1<8); assert(rs2<8); assert(rt<8); if(rs1==rt) { assem_debug("xor %%%s,%%%s\n",regname[rs2],regname[rt]); output_byte(0x31); output_modrm(3,rs1,rs2); } else if(rs2==rt) { assem_debug("xor %%%s,%%%s\n",regname[rs1],regname[rt]); output_byte(0x31); output_modrm(3,rs2,rs1); } else { emit_mov(rs1,rt); emit_xor(rt,rs2,rt); } } void emit_movimm(int imm,unsigned int rt) { assem_debug("mov $%d,%%%s\n",imm,regname[rt]); assert(rt<8); output_byte(0xB8+rt); output_w32(imm); } void emit_addimm(int rs,int imm,int rt) { if(rs==rt) { if(imm!=0) { assem_debug("add $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,0); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,0); output_w32(imm); } } } else { if(imm!=0) { assem_debug("lea %d(%%%s),%%%s\n",imm,regname[rs],regname[rt]); output_byte(0x8D); if(imm<128&&imm>=-128) { output_modrm(1,rs,rt); output_byte(imm); }else{ output_modrm(2,rs,rt); output_w32(imm); } }else{ emit_mov(rs,rt); } } } void emit_addimm_and_set_flags(int imm,int rt) { assem_debug("add $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,0); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,0); output_w32(imm); } } void emit_addimm_no_flags(int imm,int rt) { if(imm!=0) { assem_debug("lea %d(%%%s),%%%s\n",imm,regname[rt],regname[rt]); output_byte(0x8D); if(imm<128&&imm>=-128) { output_modrm(1,rt,rt); output_byte(imm); }else{ output_modrm(2,rt,rt); output_w32(imm); } } } void emit_adcimm(int imm,unsigned int rt) { assem_debug("adc $%d,%%%s\n",imm,regname[rt]); assert(rt<8); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,2); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,2); output_w32(imm); } } void emit_sbbimm(int imm,unsigned int rt) { assem_debug("sbb $%d,%%%s\n",imm,regname[rt]); assert(rt<8); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,3); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,3); output_w32(imm); } } void emit_addimm64_32(int rsh,int rsl,int imm,int rth,int rtl) { if(rsh==rth&&rsl==rtl) { assem_debug("add $%d,%%%s\n",imm,regname[rtl]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rtl,0); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rtl,0); output_w32(imm); } assem_debug("adc $%d,%%%s\n",imm>>31,regname[rth]); output_byte(0x83); output_modrm(3,rth,2); output_byte(imm>>31); } else { emit_mov(rsh,rth); emit_mov(rsl,rtl); emit_addimm64_32(rth,rtl,imm,rth,rtl); } } void emit_sbb(int rs1,int rs2) { assem_debug("sbb %%%s,%%%s\n",regname[rs1],regname[rs2]); output_byte(0x19); output_modrm(3,rs2,rs1); } void emit_andimm(int rs,int imm,int rt) { if(imm==0) { emit_zeroreg(rt); } else if(rs==rt) { assem_debug("and $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,4); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,4); output_w32(imm); } } else { emit_mov(rs,rt); emit_andimm(rt,imm,rt); } } void emit_orimm(int rs,int imm,int rt) { if(rs==rt) { if(imm!=0) { assem_debug("or $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,1); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,1); output_w32(imm); } } } else { emit_mov(rs,rt); emit_orimm(rt,imm,rt); } } void emit_xorimm(int rs,int imm,int rt) { if(rs==rt) { if(imm!=0) { assem_debug("xor $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,6); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,6); output_w32(imm); } } } else { emit_mov(rs,rt); emit_xorimm(rt,imm,rt); } } void emit_shlimm(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shl %%%s,%d\n",regname[rt],imm); assert(imm>0); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,4); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_shlimm(rt,imm,rt); } } void emit_shrimm(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shr %%%s,%d\n",regname[rt],imm); assert(imm>0); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,5); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_shrimm(rt,imm,rt); } } void emit_sarimm(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("sar %%%s,%d\n",regname[rt],imm); assert(imm>0); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,7); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_sarimm(rt,imm,rt); } } void emit_rorimm(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("ror %%%s,%d\n",regname[rt],imm); assert(imm>0); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,1); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_rorimm(rt,imm,rt); } } void emit_swapb(int rs,int rt) { if(rs==rt) { assem_debug("ror %%%s,8\n",regname[rt]+1); output_byte(0x66); output_byte(0xC1); output_modrm(3,rt,1); output_byte(8); } else { emit_mov(rs,rt); emit_swapb(rt,rt); } } void emit_shldimm(int rs,int rs2,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shld %%%s,%%%s,%d\n",regname[rt],regname[rs2],imm); assert(imm>0); output_byte(0x0F); output_byte(0xA4); output_modrm(3,rt,rs2); output_byte(imm); } else { emit_mov(rs,rt); emit_shldimm(rt,rs2,imm,rt); } } void emit_shrdimm(int rs,int rs2,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shrd %%%s,%%%s,%d\n",regname[rt],regname[rs2],imm); assert(imm>0); output_byte(0x0F); output_byte(0xAC); output_modrm(3,rt,rs2); output_byte(imm); } else { emit_mov(rs,rt); emit_shrdimm(rt,rs2,imm,rt); } } void emit_shlcl(int r) { assem_debug("shl %%%s,%%cl\n",regname[r]); output_byte(0xD3); output_modrm(3,r,4); } void emit_shrcl(int r) { assem_debug("shr %%%s,%%cl\n",regname[r]); output_byte(0xD3); output_modrm(3,r,5); } void emit_sarcl(int r) { assem_debug("sar %%%s,%%cl\n",regname[r]); output_byte(0xD3); output_modrm(3,r,7); } void emit_shldcl(int r1,int r2) { assem_debug("shld %%%s,%%%s,%%cl\n",regname[r1],regname[r2]); output_byte(0x0F); output_byte(0xA5); output_modrm(3,r1,r2); } void emit_shrdcl(int r1,int r2) { assem_debug("shrd %%%s,%%%s,%%cl\n",regname[r1],regname[r2]); output_byte(0x0F); output_byte(0xAD); output_modrm(3,r1,r2); } void emit_cmpimm(int rs,int imm) { assem_debug("cmp $%d,%%%s\n",imm,regname[rs]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rs,7); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rs,7); output_w32(imm); } } void emit_cmovne(u32 *addr,int rt) { assem_debug("cmovne %x,%%%s",(int)addr,regname[rt]); if(addr==&const_zero) assem_debug(" [zero]\n"); else if(addr==&const_one) assem_debug(" [one]\n"); else assem_debug("\n"); output_byte(0x0F); output_byte(0x45); output_modrm(0,5,rt); output_w32((int)addr); } void emit_cmovl(u32 *addr,int rt) { assem_debug("cmovl %x,%%%s",(int)addr,regname[rt]); if(addr==&const_zero) assem_debug(" [zero]\n"); else if(addr==&const_one) assem_debug(" [one]\n"); else assem_debug("\n"); output_byte(0x0F); output_byte(0x4C); output_modrm(0,5,rt); output_w32((int)addr); } void emit_cmovs(u32 *addr,int rt) { assem_debug("cmovs %x,%%%s",(int)addr,regname[rt]); if(addr==&const_zero) assem_debug(" [zero]\n"); else if(addr==&const_one) assem_debug(" [one]\n"); else assem_debug("\n"); output_byte(0x0F); output_byte(0x48); output_modrm(0,5,rt); output_w32((int)addr); } void emit_cmovne_reg(int rs,int rt) { assem_debug("cmovne %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x45); output_modrm(3,rs,rt); } void emit_cmovl_reg(int rs,int rt) { assem_debug("cmovl %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x4C); output_modrm(3,rs,rt); } void emit_cmovle_reg(int rs,int rt) { assem_debug("cmovle %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x4E); output_modrm(3,rs,rt); } void emit_cmovs_reg(int rs,int rt) { assem_debug("cmovs %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x48); output_modrm(3,rs,rt); } void emit_cmovnc_reg(int rs,int rt) { assem_debug("cmovae %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x43); output_modrm(3,rs,rt); } void emit_cmova_reg(int rs,int rt) { assem_debug("cmova %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x47); output_modrm(3,rs,rt); } void emit_cmovp_reg(int rs,int rt) { assem_debug("cmovp %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x4A); output_modrm(3,rs,rt); } void emit_cmovnp_reg(int rs,int rt) { assem_debug("cmovnp %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x4B); output_modrm(3,rs,rt); } void emit_setl(int rt) { assem_debug("setl %%%s\n",regname[rt]); output_byte(0x0F); output_byte(0x9C); output_modrm(3,rt,2); } void emit_movzbl_reg(int rs, int rt) { if(rs<4) { assem_debug("movzbl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_byte(0x0F); output_byte(0xB6); output_modrm(3,rs,rt); } else if(rt<4) { emit_mov(rs,rt); emit_movzbl_reg(rt,rt); } else { emit_andimm(rs,0xFF,rt); } } void emit_movzwl_reg(int rs, int rt) { assem_debug("movzwl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_byte(0x0F); output_byte(0xB7); output_modrm(3,rs,rt); } void emit_movsbl_reg(int rs, int rt) { if(rs<4) { assem_debug("movsbl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_byte(0x0F); output_byte(0xBE); output_modrm(3,rs,rt); } else if(rt<4) { emit_mov(rs,rt); emit_movsbl_reg(rt,rt); } else { emit_shlimm(rs,24,rt); emit_sarimm(rt,24,rt); } } void emit_movswl_reg(int rs, int rt) { assem_debug("movswl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_byte(0x0F); output_byte(0xBF); output_modrm(3,rs,rt); } void emit_slti32(int rs,int imm,int rt) { if(rs!=rt) emit_zeroreg(rt); emit_cmpimm(rs,imm); if(rt<4) { emit_setl(rt); if(rs==rt) emit_movzbl_reg(rt,rt); } else { if(rs==rt) emit_movimm(0,rt); emit_cmovl(&const_one,rt); } } void emit_sltiu32(int rs,int imm,int rt) { if(rs!=rt) emit_zeroreg(rt); emit_cmpimm(rs,imm); if(rs==rt) emit_movimm(0,rt); emit_adcimm(0,rt); } void emit_slti64_32(int rsh,int rsl,int imm,int rt) { assert(rsh!=rt); emit_slti32(rsl,imm,rt); if(imm>=0) { emit_test(rsh,rsh); emit_cmovne(&const_zero,rt); emit_cmovs(&const_one,rt); } else { emit_cmpimm(rsh,-1); emit_cmovne(&const_zero,rt); emit_cmovl(&const_one,rt); } } void emit_sltiu64_32(int rsh,int rsl,int imm,int rt) { assert(rsh!=rt); emit_sltiu32(rsl,imm,rt); if(imm>=0) { emit_test(rsh,rsh); emit_cmovne(&const_zero,rt); } else { emit_cmpimm(rsh,-1); emit_cmovne(&const_one,rt); } } void emit_cmp(int rs,int rt) { assem_debug("cmp %%%s,%%%s\n",regname[rt],regname[rs]); output_byte(0x39); output_modrm(3,rs,rt); } void emit_set_gz32(int rs, int rt) { //assem_debug("set_gz32\n"); emit_cmpimm(rs,1); emit_movimm(1,rt); emit_cmovl(&const_zero,rt); } void emit_set_nz32(int rs, int rt) { //assem_debug("set_nz32\n"); emit_cmpimm(rs,1); emit_movimm(1,rt); emit_sbbimm(0,rt); } void emit_set_gz64_32(int rsh, int rsl, int rt) { //assem_debug("set_gz64\n"); emit_set_gz32(rsl,rt); emit_test(rsh,rsh); emit_cmovne(&const_one,rt); emit_cmovs(&const_zero,rt); } void emit_set_nz64_32(int rsh, int rsl, int rt) { //assem_debug("set_nz64\n"); emit_or_and_set_flags(rsh,rsl,rt); emit_cmovne(&const_one,rt); } void emit_set_if_less32(int rs1, int rs2, int rt) { //assem_debug("set if less (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]); if(rs1!=rt&&rs2!=rt) emit_zeroreg(rt); emit_cmp(rs1,rs2); if(rs1==rt||rs2==rt) emit_movimm(0,rt); emit_cmovl(&const_one,rt); } void emit_set_if_carry32(int rs1, int rs2, int rt) { //assem_debug("set if carry (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]); if(rs1!=rt&&rs2!=rt) emit_zeroreg(rt); emit_cmp(rs1,rs2); if(rs1==rt||rs2==rt) emit_movimm(0,rt); emit_adcimm(0,rt); } void emit_set_if_less64_32(int u1, int l1, int u2, int l2, int rt) { //assem_debug("set if less64 (%%%s,%%%s,%%%s,%%%s),%%%s\n",regname[u1],regname[l1],regname[u2],regname[l2],regname[rt]); assert(u1!=rt); assert(u2!=rt); emit_cmp(l1,l2); emit_mov(u1,rt); emit_sbb(u2,rt); emit_movimm(0,rt); emit_cmovl(&const_one,rt); } void emit_set_if_carry64_32(int u1, int l1, int u2, int l2, int rt) { //assem_debug("set if carry64 (%%%s,%%%s,%%%s,%%%s),%%%s\n",regname[u1],regname[l1],regname[u2],regname[l2],regname[rt]); assert(u1!=rt); assert(u2!=rt); emit_cmp(l1,l2); emit_mov(u1,rt); emit_sbb(u2,rt); emit_movimm(0,rt); emit_adcimm(0,rt); } void emit_adc(int rs,int rt) { assem_debug("adc %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x11); output_modrm(3,rt,rs); } void emit_sh2tst(int s1, int s2, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_test(s1,s2); emit_cmovne_reg(temp,sr); } void emit_sh2tstimm(int s, int imm, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_testimm(s,imm); //emit_addimm(sr,-1,temp); assem_debug("lea -1(%%%s),%%%s\n",regname[sr],regname[temp]); output_byte(0x8D); output_modrm(1,sr,temp); output_byte(0xFF); emit_cmovne_reg(temp,sr); } void emit_cmpeq(int s1, int s2, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_cmp(s1,s2); emit_cmovne_reg(temp,sr); } void emit_cmpeqimm(int s, int imm, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_cmpimm(s,imm); emit_cmovne_reg(temp,sr); } void emit_cmpge(int s1, int s2, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_cmp(s2,s1); emit_cmovl_reg(temp,sr); } void emit_cmpgt(int s1, int s2, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_cmp(s2,s1); emit_cmovle_reg(temp,sr); } void emit_cmphi(int s1, int s2, int sr, int temp) { emit_andimm(sr,~1,sr); emit_cmp(s1,s2); emit_adcimm(0,sr); } void emit_cmphs(int s1, int s2, int sr, int temp) { emit_orimm(sr,1,sr); emit_cmp(s2,s1); emit_sbbimm(0,sr); } void emit_dt(int t, int sr) { emit_addimm(t,-2,t); emit_shrimm(sr,1,sr); emit_addimm(t,1,t); emit_adc(sr,sr); } void emit_cmppz(int s, int sr) { emit_shrimm(sr,1,sr); emit_cmpimm(s,0x80000000); emit_adc(sr,sr); } void emit_cmppl(int s, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_test(s,s); emit_cmovle_reg(temp,sr); } void emit_addc(int s, int t, int sr) { emit_shrimm(sr,1,sr); emit_adc(s,t); emit_adc(sr,sr); } void emit_subc(int s, int t, int sr) { emit_shrimm(sr,1,sr); emit_sbb(s,t); emit_adc(sr,sr); } void emit_shrsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_shrimm(t,1,t); emit_adc(sr,sr); } void emit_sarsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_sarimm(t,1,t); emit_adc(sr,sr); } void emit_shlsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_shlimm(t,1,t); emit_adc(sr,sr); } void emit_rotl(int t) { assem_debug("rol %%%s\n",regname[t]); output_byte(0xD1); output_modrm(3,t,0); } void emit_rotlsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_rotl(t); emit_adc(sr,sr); } void emit_rotr(int t) { assem_debug("ror %%%s\n",regname[t]); output_byte(0xD1); output_modrm(3,t,1); } void emit_rotrsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_rotr(t); emit_adc(sr,sr); } void emit_rotclsr(int t, int sr) { emit_shrimm(sr,1,sr); assem_debug("rcl %%%s\n",regname[t]); output_byte(0xD1); output_modrm(3,t,2); emit_adc(sr,sr); } void emit_rotcrsr(int t, int sr) { emit_shrimm(sr,1,sr); assem_debug("rcr %%%s\n",regname[t]); output_byte(0xD1); output_modrm(3,t,3); emit_adc(sr,sr); } void emit_call(int a) { assem_debug("call %x (%x+%x)\n",a,(int)out+5,a-(int)out-5); output_byte(0xe8); output_w32(a-(int)out-4); } void emit_jmp(int a) { assem_debug("jmp %x (%x+%x)\n",a,(int)out+5,a-(int)out-5); output_byte(0xe9); output_w32(a-(int)out-4); } void emit_jne(int a) { assem_debug("jne %x\n",a); output_byte(0x0f); output_byte(0x85); output_w32(a-(int)out-4); } void emit_jeq(int a) { assem_debug("jeq %x\n",a); output_byte(0x0f); output_byte(0x84); output_w32(a-(int)out-4); } void emit_js(int a) { assem_debug("js %x\n",a); output_byte(0x0f); output_byte(0x88); output_w32(a-(int)out-4); } void emit_jns(int a) { assem_debug("jns %x\n",a); output_byte(0x0f); output_byte(0x89); output_w32(a-(int)out-4); } void emit_jl(int a) { assem_debug("jl %x\n",a); output_byte(0x0f); output_byte(0x8c); output_w32(a-(int)out-4); } void emit_jge(int a) { assem_debug("jge %x\n",a); output_byte(0x0f); output_byte(0x8d); output_w32(a-(int)out-4); } void emit_jno(int a) { assem_debug("jno %x\n",a); output_byte(0x0f); output_byte(0x81); output_w32(a-(int)out-4); } void emit_jc(int a) { assem_debug("jc %x\n",a); output_byte(0x0f); output_byte(0x82); output_w32(a-(int)out-4); } void emit_pushimm(int imm) { assem_debug("push $%x\n",imm); output_byte(0x68); output_w32(imm); } void emit_pushmem(int addr) { assem_debug("push *%x\n",addr); output_byte(0xFF); output_modrm(0,5,6); output_w32(addr); } void emit_pusha() { assem_debug("pusha\n"); output_byte(0x60); } void emit_popa() { assem_debug("popa\n"); output_byte(0x61); } void emit_pushreg(unsigned int r) { assem_debug("push %%%s\n",regname[r]); assert(r<8); output_byte(0x50+r); } void emit_popreg(unsigned int r) { assem_debug("pop %%%s\n",regname[r]); assert(r<8); output_byte(0x58+r); } void emit_callreg(unsigned int r) { assem_debug("call *%%%s\n",regname[r]); assert(r<8); output_byte(0xFF); output_modrm(3,r,2); } void emit_jmpreg(unsigned int r) { assem_debug("jmp *%%%s\n",regname[r]); assert(r<8); output_byte(0xFF); output_modrm(3,r,4); } void emit_jmpmem_indexed(u32 addr,unsigned int r) { assem_debug("jmp *%x(%%%s)\n",addr,regname[r]); assert(r<8); output_byte(0xFF); output_modrm(2,r,4); output_w32(addr); } void emit_cmpstr(int s1, int s2, int sr, int temp) { // Compare s1 and s2. If any byte is equal, set T. // Calculates the xor of the strings, then checks if any byte is // zero by subtracting 1 from each byte. If there is a carry/borrow // then a byte was zero. assert(temp>=0); emit_pushreg(s2); emit_xor(s1,s2,s2); emit_shrimm(sr,1,sr); emit_mov(s2,temp); emit_addimm_and_set_flags(0-0x01010101,temp); emit_adcimm(-1,temp); emit_not(s2,s2); emit_xor(temp,s2,temp); emit_andimm(temp,0x01010101,temp); emit_addimm_and_set_flags(-1,temp); emit_adc(sr,sr); emit_popreg(s2); } void emit_negc(int rs, int rt, int sr) { assert(rs>=0&&rs<8); if(rt<0) { emit_shrimm(sr,1,sr); // Get C flag emit_jc((pointer)out+10); // 6 emit_neg(rs,rs); // 2 emit_neg(rs,rs); // 2 emit_adc(sr,sr); // Save C flag }else{ if(rs!=rt) emit_mov(rs,rt); emit_shrimm(sr,1,sr); // Get C flag emit_jc((pointer)out+9); // 6 emit_addimm(rt,-1,rt); // 3 emit_adc(sr,sr); // Save C flag emit_not(rt,rt); } } void emit_readword(int addr, int rt) { assem_debug("mov %x,%%%s\n",addr,regname[rt]); output_byte(0x8B); output_modrm(0,5,rt); output_w32(addr); } void emit_readword_indexed(int addr, int rs, int rt) { assem_debug("mov %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x8B); if(addr<128&&addr>=-128) { output_modrm(1,rs,rt); if(rs==ESP) output_sib(0,4,4); output_byte(addr); } else { output_modrm(2,rs,rt); if(rs==ESP) output_sib(0,4,4); output_w32(addr); } } void emit_readword_map(int addr, int map, int rt) { if(map<0) emit_readword(addr, rt); else { assem_debug("mov (%x,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x8B); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_readword_indexed_map(int addr, int rs, int map, int rt) { assert(map>=0); if(map<0) emit_readword_indexed(addr, rs, rt); else { assem_debug("mov %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assert(rs!=ESP); output_byte(0x8B); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(2,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(2,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(2,map,rs); output_w32(addr); } } } void emit_movmem_indexedx4(int addr, int rs, int rt) { assem_debug("mov (%x,%%%s,4),%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x8B); output_modrm(0,4,rt); output_sib(2,rs,5); output_w32(addr); } void emit_movsbl(int addr, int rt) { assem_debug("movsbl %x,%%%s\n",addr,regname[rt]); output_byte(0x0F); output_byte(0xBE); output_modrm(0,5,rt); output_w32(addr); } void emit_movsbl_indexed(int addr, int rs, int rt) { assem_debug("movsbl %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x0F); output_byte(0xBE); output_modrm(2,rs,rt); output_w32(addr); } void emit_movsbl_map(int addr, int map, int rt) { if(map<0) emit_movsbl(addr, rt); else { assem_debug("movsbl (%x,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x0F); output_byte(0xBE); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_movsbl_indexed_map(int addr, int rs, int map, int rt) { if(map<0) emit_movsbl_indexed(addr, rs, rt); else { assem_debug("movsbl %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assert(rs!=ESP); output_byte(0x0F); output_byte(0xBE); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(2,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(2,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(2,map,rs); output_w32(addr); } } } void emit_movswl(int addr, int rt) { assem_debug("movswl %x,%%%s\n",addr,regname[rt]); output_byte(0x0F); output_byte(0xBF); output_modrm(0,5,rt); output_w32(addr); } void emit_movswl_indexed(int addr, int rs, int rt) { assem_debug("movswl %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x0F); output_byte(0xBF); output_modrm(2,rs,rt); output_w32(addr); } void emit_movswl_map(int addr, int map, int rt) { if(map<0) emit_movswl(addr, rt); else { assem_debug("movswl (%x,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x0F); output_byte(0xBF); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_movswl_indexed_map(int addr, int rs, int map, int rt) { assert(map>=0); if(map<0) emit_movswl_indexed(addr, rs, rt); else { assem_debug("movswl %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assert(rs!=ESP); output_byte(0x0F); output_byte(0xBF); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(2,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(2,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(2,map,rs); output_w32(addr); } } } void emit_movzbl(int addr, int rt) { assem_debug("movzbl %x,%%%s\n",addr,regname[rt]); output_byte(0x0F); output_byte(0xB6); output_modrm(0,5,rt); output_w32(addr); } void emit_movzbl_indexed(int addr, int rs, int rt) { assem_debug("movzbl %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x0F); output_byte(0xB6); output_modrm(2,rs,rt); output_w32(addr); } void emit_movzbl_map(int addr, int map, int rt) { if(map<0) emit_movzbl(addr, rt); else { assem_debug("movzbl (%x,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x0F); output_byte(0xB6); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_movzbl_indexed_map(int addr, int rs, int map, int rt) { if(map<0) emit_movzbl_indexed(addr, rs, rt); else { assem_debug("movzbl %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assert(rs!=ESP); output_byte(0x0F); output_byte(0xB6); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(2,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(2,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(2,map,rs); output_w32(addr); } } } void emit_movzwl(int addr, int rt) { assem_debug("movzwl %x,%%%s\n",addr,regname[rt]); output_byte(0x0F); output_byte(0xB7); output_modrm(0,5,rt); output_w32(addr); } void emit_movzwl_indexed(int addr, int rs, int rt) { assem_debug("movzwl %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x0F); output_byte(0xB7); output_modrm(2,rs,rt); output_w32(addr); } void emit_movzwl_map(int addr, int map, int rt) { if(map<0) emit_movzwl(addr, rt); else { assem_debug("movzwl (%x,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x0F); output_byte(0xB7); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_xchg(int rs, int rt) { assem_debug("xchg %%%s,%%%s\n",regname[rs],regname[rt]); if(rs==EAX) { output_byte(0x90+rt); } else { output_byte(0x87); output_modrm(3,rs,rt); } } void emit_writeword(int rt, int addr) { assem_debug("movl %%%s,%x\n",regname[rt],addr); output_byte(0x89); output_modrm(0,5,rt); output_w32(addr); } void emit_writeword_indexed(int rt, int addr, int rs) { assem_debug("mov %%%s,%x+%%%s\n",regname[rt],addr,regname[rs]); output_byte(0x89); if(addr<128&&addr>=-128) { output_modrm(1,rs,rt); if(rs==ESP) output_sib(0,4,4); output_byte(addr); } else { output_modrm(2,rs,rt); if(rs==ESP) output_sib(0,4,4); output_w32(addr); } } #if 0 void emit_writeword_map(int rt, int addr, int map) { if(map<0) { emit_writeword(rt, addr+(int)rdram-0x80000000); } else { emit_writeword_indexed(rt, addr+(int)rdram-0x80000000, map); } } #endif void emit_writeword_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writeword_indexed(rt, addr, rs); else { assem_debug("mov %%%s,%x(%%%s,%%%s,1)\n",regname[rt],addr,regname[rs],regname[map]); assert(rs!=ESP); output_byte(0x89); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(0,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(0,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(0,map,rs); output_w32(addr); } } } void emit_writehword(int rt, int addr) { assem_debug("movw %%%s,%x\n",regname[rt]+1,addr); output_byte(0x66); output_byte(0x89); output_modrm(0,5,rt); output_w32(addr); } void emit_writehword_indexed(int rt, int addr, int rs) { assem_debug("movw %%%s,%x+%%%s\n",regname[rt]+1,addr,regname[rs]); output_byte(0x66); output_byte(0x89); if(addr<128&&addr>=-128) { output_modrm(1,rs,rt); output_byte(addr); } else { output_modrm(2,rs,rt); output_w32(addr); } } #if 0 void emit_writehword_map(int rt, int addr, int map) { if(map<0) { emit_writehword(rt, addr+(int)rdram-0x80000000); } else { emit_writehword_indexed(rt, addr+(int)rdram-0x80000000, map); } } #endif void emit_writehword_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writeword_indexed(rt, addr, rs); else { assem_debug("movw %%%s,%x(%%%s,%%%s,1)\n",regname[rt]+1,addr,regname[rs],regname[map]); assert(rs!=ESP); output_byte(0x66); output_byte(0x89); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(0,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(0,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(0,map,rs); output_w32(addr); } } } void emit_writebyte(int rt, int addr) { if(rt<4) { assem_debug("movb %%%cl,%x\n",regname[rt][1],addr); output_byte(0x88); output_modrm(0,5,rt); output_w32(addr); } else { emit_xchg(EAX,rt); emit_writebyte(EAX,addr); emit_xchg(EAX,rt); } } void emit_writebyte_indexed(int rt, int addr, int rs) { if(rt<4) { assem_debug("movb %%%cl,%x+%%%s\n",regname[rt][1],addr,regname[rs]); output_byte(0x88); if(addr<128&&addr>=-128) { output_modrm(1,rs,rt); output_byte(addr); } else { output_modrm(2,rs,rt); output_w32(addr); } } else { emit_xchg(EAX,rt); emit_writebyte_indexed(EAX,addr,rs==EAX?rt:rs); emit_xchg(EAX,rt); } } #if 0 void emit_writebyte_map(int rt, int addr, int map) { if(map<0) { emit_writebyte(rt, addr+(int)rdram-0x80000000); } else { emit_writebyte_indexed(rt, addr+(int)rdram-0x80000000, map); } } #endif void emit_writebyte_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writebyte_indexed(rt, addr, rs); else if(rt<4) { assem_debug("movb %%%cl,%x(%%%s,%%%s,1)\n",regname[rt][1],addr,regname[rs],regname[map]); assert(rs!=ESP); output_byte(0x88); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(0,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(0,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(0,map,rs); output_w32(addr); } } else { emit_xchg(EAX,rt); emit_writebyte_indexed_map(EAX,addr,rs==EAX?rt:rs,map==EAX?rt:map,temp); emit_xchg(EAX,rt); } } void emit_writeword_imm(int imm, int addr) { assem_debug("movl $%x,%x\n",imm,addr); output_byte(0xC7); output_modrm(0,5,0); output_w32(addr); output_w32(imm); } void emit_writeword_imm_esp(int imm, int addr) { assem_debug("mov $%x,%x(%%esp)\n",imm,addr); assert(addr>=-128&&addr<128); output_byte(0xC7); output_modrm(1,4,0); output_sib(0,4,4); output_byte(addr); output_w32(imm); } void emit_writebyte_imm(int imm, int addr) { assem_debug("movb $%x,%x\n",imm,addr); assert(imm>=-128&&imm<128); output_byte(0xC6); output_modrm(0,5,0); output_w32(addr); output_byte(imm); } void emit_writebyte_imm_esp(int imm, int addr) { assem_debug("movb $%x,%x(%%esp)\n",imm,addr); assert(addr>=-128&&addr<128); output_byte(0xC6); output_modrm(1,4,0); output_sib(0,4,4); output_byte(addr); output_byte(imm); } void emit_rmw_andimm(int addr, int map, int imm) { if(map<0) { assem_debug("andb $0x%x,(%%%s)\n",imm,regname[addr]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,addr,4); } else { assem_debug("andb $0x%x,(%%%s,%%%s,1)\n",imm,regname[addr],regname[map]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,4,4); if(addr!=EBP) { output_sib(0,map,addr); } else { assert(addr!=map); output_sib(0,addr,map); } } output_byte(imm); } void emit_rmw_xorimm(int addr, int map, int imm) { if(map<0) { assem_debug("xorb $0x%x,(%%%s)\n",imm,regname[addr]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,addr,6); } else { assem_debug("xorb $0x%x,(%%%s,%%%s,1)\n",imm,regname[addr],regname[map]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,4,6); if(addr!=EBP) { output_sib(0,map,addr); } else { assert(addr!=map); output_sib(0,addr,map); } } output_byte(imm); } void emit_rmw_orimm(int addr, int map, int imm) { if(map<0) { assem_debug("orb $0x%x,(%%%s)\n",imm,regname[addr]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,addr,1); } else { assem_debug("orb $0x%x,(%%%s,%%%s,1)\n",imm,regname[addr],regname[map]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,4,1); if(addr!=EBP) { output_sib(0,map,addr); } else { assert(addr!=map); output_sib(0,addr,map); } } output_byte(imm); } void emit_sh2tas(int addr, int map, int sr) { emit_shrimm(sr,1,sr); if(map<0) { assem_debug("cmpb $1,(%%%s)\n",regname[addr]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,addr,7); } else { assem_debug("cmpb $1,(%%%s,%%%s,1)\n",regname[addr],regname[map]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,4,7); if(addr!=EBP) { output_sib(0,map,addr); } else { assert(addr!=map); output_sib(0,addr,map); } } output_byte(1); emit_adc(sr,sr); emit_rmw_orimm(addr,map,0x80); } void emit_mul(int rs) { assem_debug("mul %%%s\n",regname[rs]); output_byte(0xF7); output_modrm(3,rs,4); } void emit_imul(int rs) { assem_debug("imul %%%s\n",regname[rs]); output_byte(0xF7); output_modrm(3,rs,5); } void emit_multiply(int rs1,int rs2,int rt) { if(rs1==rt) { assem_debug("imul %%%s,%%%s\n",regname[rs2],regname[rt]); output_byte(0x0F); output_byte(0xAF); output_modrm(3,rs2,rt); } else { emit_mov(rs1,rt); emit_multiply(rt,rs2,rt); } } void emit_div(int rs) { assem_debug("div %%%s\n",regname[rs]); output_byte(0xF7); output_modrm(3,rs,6); } void emit_idiv(int rs) { assem_debug("idiv %%%s\n",regname[rs]); output_byte(0xF7); output_modrm(3,rs,7); } void emit_cdq() { assem_debug("cdq\n"); output_byte(0x99); } void emit_div0s(int s1, int s2, int sr, int temp) { emit_shlimm(sr,24,sr); emit_mov(s2,temp); assem_debug("bt %%%s,31\n",regname[s2]); output_byte(0x0f); output_byte(0xba); output_modrm(3,s2,4); output_byte(0x1f); assem_debug("rcr %%%s\n",regname[sr]); output_byte(0xD1); output_modrm(3,sr,3); emit_xor(temp,s1,temp); assem_debug("bt %%%s,31\n",regname[s1]); output_byte(0x0f); output_byte(0xba); output_modrm(3,s1,4); output_byte(0x1f); assem_debug("rcr %%%s,24\n",regname[sr]); output_byte(0xc1); output_modrm(3,sr,3); output_byte(24); assem_debug("bt %%%s,31\n",regname[temp]); output_byte(0x0f); output_byte(0xba); output_modrm(3,temp,4); output_byte(0x1f); emit_adc(sr,sr); } // Load return address void emit_load_return_address(unsigned int rt) { // (assumes this instruction will be followed by a 5-byte jmp instruction) emit_movimm((pointer)out+10,rt); } // Load 2 immediates optimizing for small code size void emit_mov2imm_compact(int imm1,unsigned int rt1,int imm2,unsigned int rt2) { emit_movimm(imm1,rt1); if(imm2-imm1<128&&imm2-imm1>=-128) emit_addimm(rt1,imm2-imm1,rt2); else emit_movimm(imm2,rt2); } // compare byte in memory void emit_cmpmem_imm_byte(pointer addr,int imm) { assert(imm<128&&imm>=-127); assem_debug("cmpb $%d,%x\n",imm,addr); output_byte(0x80); output_modrm(0,5,7); output_w32(addr); output_byte(imm); } // special case for checking invalid_code void emit_cmpmem_indexedsr12_imm(int addr,int r,int imm) { assert(imm<128&&imm>=-127); assert(r>=0&&r<8); emit_shrimm(r,12,r); assem_debug("cmp $%d,%x+%%%s\n",imm,addr,regname[r]); output_byte(0x80); output_modrm(2,r,7); output_w32(addr); output_byte(imm); } // special case for checking hash_table void emit_cmpmem_indexed(int addr,int rs,int rt) { assert(rs>=0&&rs<8); assert(rt>=0&&rt<8); assem_debug("cmp %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x39); output_modrm(2,rs,rt); output_w32(addr); } // special case for checking memory_map in verify_mapping void emit_cmpmem(int addr,int rt) { assert(rt>=0&&rt<8); assem_debug("cmp %x,%%%s\n",addr,regname[rt]); output_byte(0x39); output_modrm(0,5,rt); output_w32(addr); } // Used to preload hash table entries void emit_prefetch(void *addr) { assem_debug("prefetch %x\n",(int)addr); output_byte(0x0F); output_byte(0x18); output_modrm(0,5,1); output_w32((int)addr); } /*void emit_submem(int r,int addr) { assert(r>=0&&r<8); assem_debug("sub %x,%%%s\n",addr,regname[r]); output_byte(0x2B); output_modrm(0,5,r); output_w32((int)addr); }*/ void emit_subfrommem(int addr,int r) { assert(r>=0&&r<8); assem_debug("sub %%%s,%x\n",regname[r],addr); output_byte(0x29); output_modrm(0,5,r); output_w32((int)addr); } void emit_flds(int r) { assem_debug("flds (%%%s)\n",regname[r]); output_byte(0xd9); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_fldl(int r) { assem_debug("fldl (%%%s)\n",regname[r]); output_byte(0xdd); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_fucomip(unsigned int r) { assem_debug("fucomip %d\n",r); assert(r<8); output_byte(0xdf); output_byte(0xe8+r); } void emit_fchs() { assem_debug("fchs\n"); output_byte(0xd9); output_byte(0xe0); } void emit_fabs() { assem_debug("fabs\n"); output_byte(0xd9); output_byte(0xe1); } void emit_fsqrt() { assem_debug("fsqrt\n"); output_byte(0xd9); output_byte(0xfa); } void emit_fadds(int r) { assem_debug("fadds (%%%s)\n",regname[r]); output_byte(0xd8); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_faddl(int r) { assem_debug("faddl (%%%s)\n",regname[r]); output_byte(0xdc); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_fadd(int r) { assem_debug("fadd st%d\n",r); output_byte(0xd8); output_byte(0xc0+r); } void emit_fsubs(int r) { assem_debug("fsubs (%%%s)\n",regname[r]); output_byte(0xd8); if(r!=EBP) output_modrm(0,r,4); else {output_modrm(1,EBP,4);output_byte(0);} } void emit_fsubl(int r) { assem_debug("fsubl (%%%s)\n",regname[r]); output_byte(0xdc); if(r!=EBP) output_modrm(0,r,4); else {output_modrm(1,EBP,4);output_byte(0);} } void emit_fsub(int r) { assem_debug("fsub st%d\n",r); output_byte(0xd8); output_byte(0xe0+r); } void emit_fmuls(int r) { assem_debug("fmuls (%%%s)\n",regname[r]); output_byte(0xd8); if(r!=EBP) output_modrm(0,r,1); else {output_modrm(1,EBP,1);output_byte(0);} } void emit_fmull(int r) { assem_debug("fmull (%%%s)\n",regname[r]); output_byte(0xdc); if(r!=EBP) output_modrm(0,r,1); else {output_modrm(1,EBP,1);output_byte(0);} } void emit_fmul(int r) { assem_debug("fmul st%d\n",r); output_byte(0xd8); output_byte(0xc8+r); } void emit_fdivs(int r) { assem_debug("fdivs (%%%s)\n",regname[r]); output_byte(0xd8); if(r!=EBP) output_modrm(0,r,6); else {output_modrm(1,EBP,6);output_byte(0);} } void emit_fdivl(int r) { assem_debug("fdivl (%%%s)\n",regname[r]); output_byte(0xdc); if(r!=EBP) output_modrm(0,r,6); else {output_modrm(1,EBP,6);output_byte(0);} } void emit_fdiv(int r) { assem_debug("fdiv st%d\n",r); output_byte(0xd8); output_byte(0xf0+r); } void emit_fpop() { // fstp st(0) assem_debug("fpop\n"); output_byte(0xdd); output_byte(0xd8); } void emit_fildl(int r) { assem_debug("fildl (%%%s)\n",regname[r]); output_byte(0xdb); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_fildll(int r) { assem_debug("fildll (%%%s)\n",regname[r]); output_byte(0xdf); if(r!=EBP) output_modrm(0,r,5); else {output_modrm(1,EBP,5);output_byte(0);} } void emit_fistpl(int r) { assem_debug("fistpl (%%%s)\n",regname[r]); output_byte(0xdb); if(r!=EBP) output_modrm(0,r,3); else {output_modrm(1,EBP,3);output_byte(0);} } void emit_fistpll(int r) { assem_debug("fistpll (%%%s)\n",regname[r]); output_byte(0xdf); if(r!=EBP) output_modrm(0,r,7); else {output_modrm(1,EBP,7);output_byte(0);} } void emit_fstps(int r) { assem_debug("fstps (%%%s)\n",regname[r]); output_byte(0xd9); if(r!=EBP) output_modrm(0,r,3); else {output_modrm(1,EBP,3);output_byte(0);} } void emit_fstpl(int r) { assem_debug("fstpl (%%%s)\n",regname[r]); output_byte(0xdd); if(r!=EBP) output_modrm(0,r,3); else {output_modrm(1,EBP,3);output_byte(0);} } void emit_fnstcw_stack() { assem_debug("fnstcw (%%esp)\n"); output_byte(0xd9); output_modrm(0,4,7); output_sib(0,4,4); } void emit_fldcw_stack() { assem_debug("fldcw (%%esp)\n"); output_byte(0xd9); output_modrm(0,4,5); output_sib(0,4,4); } void emit_fldcw_indexed(int addr,int r) { assem_debug("fldcw %x(%%%s)\n",addr,regname[r]); output_byte(0xd9); output_modrm(0,4,5); output_sib(1,r,5); output_w32(addr); } void emit_fldcw(int addr) { assem_debug("fldcw %x\n",addr); output_byte(0xd9); output_modrm(0,5,5); output_w32(addr); } void emit_movss_load(unsigned int addr,unsigned int ssereg) { assem_debug("movss (%%%s),xmm%d\n",regname[addr],ssereg); assert(ssereg<8); output_byte(0xf3); output_byte(0x0f); output_byte(0x10); if(addr!=EBP) output_modrm(0,addr,ssereg); else {output_modrm(1,EBP,ssereg);output_byte(0);} } void emit_movsd_load(unsigned int addr,unsigned int ssereg) { assem_debug("movsd (%%%s),xmm%d\n",regname[addr],ssereg); assert(ssereg<8); output_byte(0xf2); output_byte(0x0f); output_byte(0x10); if(addr!=EBP) output_modrm(0,addr,ssereg); else {output_modrm(1,EBP,ssereg);output_byte(0);} } void emit_movd_store(unsigned int ssereg,unsigned int addr) { assem_debug("movd xmm%d,(%%%s)\n",ssereg,regname[addr]); assert(ssereg<8); output_byte(0x66); output_byte(0x0f); output_byte(0x7e); if(addr!=EBP) output_modrm(0,addr,ssereg); else {output_modrm(1,EBP,ssereg);output_byte(0);} } void emit_cvttps2dq(unsigned int ssereg1,unsigned int ssereg2) { assem_debug("cvttps2dq xmm%d,xmm%d\n",ssereg1,ssereg2); assert(ssereg1<8); assert(ssereg2<8); output_byte(0xf3); output_byte(0x0f); output_byte(0x5b); output_modrm(3,ssereg1,ssereg2); } void emit_cvttpd2dq(unsigned int ssereg1,unsigned int ssereg2) { assem_debug("cvttpd2dq xmm%d,xmm%d\n",ssereg1,ssereg2); assert(ssereg1<8); assert(ssereg2<8); output_byte(0x66); output_byte(0x0f); output_byte(0xe6); output_modrm(3,ssereg1,ssereg2); } unsigned int count_bits(u32 reglist) { int count=0; while(reglist) { count+=reglist&1; reglist>>=1; } return count; } // Save registers before function call // This code is executed infrequently so we try to minimize code size // by pushing registers onto the stack instead of writing them to their // usual locations void save_regs(u32 reglist) { reglist&=0x7; // only save the caller-save registers, %eax, %ecx, %edx int hr; int count=count_bits(reglist); if(count) { for(hr=0;hr>hr)&1) { emit_pushreg(hr); } } } } if(slave) emit_addimm(ESP,-(4-count)*4,ESP); // slave has master's return address on stack else emit_addimm(ESP,-(5-count)*4,ESP); } // Restore registers after function call void restore_regs(u32 reglist) { int hr; reglist&=0x7; // only save the caller-save registers, %eax, %ecx, %edx int count=count_bits(reglist); if(slave) emit_addimm(ESP,(4-count)*4,ESP); else emit_addimm(ESP,(5-count)*4,ESP); if(count) { for(hr=HOST_REGS-1;hr>=0;hr--) { if(hr!=EXCLUDE_REG) { if((reglist>>hr)&1) { emit_popreg(hr); } } } } } /* Stubs/epilogue */ emit_extjump(pointer addr, int target) { u8 *ptr=(u8 *)addr; if(*ptr==0x0f) { assert(ptr[1]>=0x80&&ptr[1]<=0x8f); addr+=2; } else { assert(*ptr==0xe8||*ptr==0xe9); addr++; } emit_movimm(target,EAX); //emit_movimm(target|slave,EAX); emit_movimm(addr,EBX); //assert(addr>=0x7000000&&addr<0x7FFFFFF); //DEBUG > #ifdef DEBUG_CYCLE_COUNT emit_readword((int)&last_count,ECX); emit_add(HOST_CCREG,ECX,HOST_CCREG); emit_readword((int)&next_interupt,ECX); emit_writeword(HOST_CCREG,(int)&Count); emit_sub(HOST_CCREG,ECX,HOST_CCREG); emit_writeword(ECX,(int)&last_count); #endif //DEBUG < emit_jmp((pointer)dyna_linker); } do_readstub(int n) { assem_debug("do_readstub %x\n",start+stubs[n][3]*2); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); int rt; rt=get_reg(i_regmap,rt1[i]==TBIT?-1:rt1[i]); assert(rs>=0); if(addr<0) addr=rt; if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); if(rs!=EAX) emit_mov(rs,EAX); if(type==LOADB_STUB) emit_xorimm(EAX,1,EAX); //if(i_regmap[HOST_CCREG]==CCREG) emit_storereg(CCREG,HOST_CCREG);//DEBUG /*if(i_regmap[HOST_CCREG]==CCREG) { emit_addimm(HOST_CCREG,CLOCK_DIVIDER*(stubs[n][6]),HOST_CCREG); output_byte(0x03); output_modrm(1,4,HOST_CCREG); output_sib(0,4,4); output_byte(12+16); //emit_writeword(HOST_CCREG,(int)&MSH2->cycles); emit_writeword(HOST_CCREG,slave?(int)&SSH2->cycles:(int)&MSH2->cycles); output_byte(0x2B); output_modrm(1,4,HOST_CCREG); output_sib(0,4,4); output_byte(12+16); emit_addimm(HOST_CCREG,-CLOCK_DIVIDER*(stubs[n][6]),HOST_CCREG); } if(i_regmap[HOST_CCREG]!=CCREG) { emit_loadreg(CCREG,ECX); emit_addimm(ECX,CLOCK_DIVIDER*(stubs[n][6]),ECX); output_byte(0x03); output_modrm(1,4,ECX); output_sib(0,4,4); output_byte(12+16); //emit_writeword(ECX,(int)&MSH2->cycles); emit_writeword(ECX,slave?(int)&SSH2->cycles:(int)&MSH2->cycles); } /* int temp; int cc=get_reg(i_regmap,CCREG); if(cc<0) { if(addr==HOST_CCREG) { cc=0;temp=1; assert(cc!=HOST_CCREG); assert(temp!=HOST_CCREG); emit_loadreg(CCREG,cc); } else { cc=HOST_CCREG; emit_loadreg(CCREG,cc); temp=!addr; } } else { temp=!addr; }*/ if(type==LOADB_STUB) emit_call((int)MappedMemoryReadByte); if(type==LOADW_STUB) emit_call((int)MappedMemoryReadWord); if(type==LOADL_STUB) emit_call((int)MappedMemoryReadLong); if(type==LOADS_STUB) { // RTE instruction, pop PC and SR from stack int pc=get_reg(i_regmap,RTEMP); assert(pc>=0); if(rs==EAX||rs==ECX||rs==EDX) emit_writeword_indexed(rs,0,ESP); emit_call((int)MappedMemoryReadLong); if(rs==ECX||rs==EDX) emit_readword_indexed(0,ESP,rs); if(pc==EAX) { emit_writeword_indexed(EAX,0,ESP); } else { if(pc==ECX||pc==EDX) emit_writeword_indexed(EAX,0,ESP); else emit_mov(EAX,pc); if(rs==EAX) { emit_readword_indexed(0,ESP,EAX); emit_addimm(EAX,4,EAX); }else emit_addimm(rs,4,EAX); } emit_call((int)MappedMemoryReadLong); assert(rt>=0); if(rt!=EAX) emit_mov(EAX,rt); if(pc==EAX||pc==ECX||pc==EDX) emit_readword_indexed(0,ESP,pc); } else if(type==LOADB_STUB) { if(rt>=0) emit_movsbl_reg(EAX,rt); } else if(type==LOADW_STUB) { if(rt>=0) emit_movswl_reg(EAX,rt); } else { if(rt!=EAX&&rt>=0) emit_mov(EAX,rt); } restore_regs(reglist); if(type==LOADS_STUB) emit_addimm(rs,8,rs); emit_jmp(stubs[n][2]); // return address } inline_readstub(int type, int i, u32 addr, signed char regmap[], int target, int adj, u32 reglist) { assem_debug("inline_readstub\n"); //int rs=get_reg(regmap,target); int rt=get_reg(regmap,target); //if(rs<0) rs=get_reg(regmap,-1); if(rt<0) rt=get_reg(regmap,-1); //rt=get_reg(i_regmap,rt1[i]==TBIT?-1:rt1[i]); assert(rt>=0); //if(addr<0) addr=rt; //if(addr<0) addr=get_reg(i_regmap,-1); //assert(addr>=0); save_regs(reglist); emit_movimm(addr,EAX); if(type==LOADB_STUB) emit_call((int)MappedMemoryReadByte); if(type==LOADW_STUB) emit_call((int)MappedMemoryReadWord); if(type==LOADL_STUB) emit_call((int)MappedMemoryReadLong); assert(type!=LOADS_STUB); if(type==LOADB_STUB) { if(rt>=0) emit_movsbl_reg(EAX,rt); } else if(type==LOADW_STUB) { if(rt>=0) emit_movswl_reg(EAX,rt); } else { if(rt!=EAX&&rt>=0) emit_mov(EAX,rt); } restore_regs(reglist); } do_writestub(int n) { assem_debug("do_writestub %x\n",start+stubs[n][3]*2); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); int rt=get_reg(i_regmap,rs1[i]); assert(rs>=0); assert(rt>=0); if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); // "FASTCALL" api: address in eax, data in edx if(rs!=EAX) { if(rt==EAX) { if(rs==EDX) emit_xchg(EAX,EDX); else { emit_mov(rt,EDX); emit_mov(rs,EAX); } } else { emit_mov(rs,EAX); if(rt!=EDX) emit_mov(rt,EDX); } } else if(rt!=EDX) emit_mov(rt,EDX); //if(type==STOREB_STUB) emit_xorimm(EAX,1,EAX); // WriteInvalidateByteSwapped does this //if(i_regmap[HOST_CCREG]==CCREG) emit_storereg(CCREG,HOST_CCREG);//DEBUG /*if(i_regmap[HOST_CCREG]==CCREG) { emit_addimm(HOST_CCREG,CLOCK_DIVIDER*(stubs[n][6]),HOST_CCREG); output_byte(0x03); output_modrm(1,4,HOST_CCREG); output_sib(0,4,4); output_byte(12+16); //emit_writeword(HOST_CCREG,(int)&MSH2->cycles); emit_writeword(HOST_CCREG,slave?(int)&SSH2->cycles:(int)&MSH2->cycles); output_byte(0x2B); output_modrm(1,4,HOST_CCREG); output_sib(0,4,4); output_byte(12+16); emit_addimm(HOST_CCREG,-CLOCK_DIVIDER*(stubs[n][6]),HOST_CCREG); } if(i_regmap[HOST_CCREG]!=CCREG) { emit_loadreg(CCREG,ECX); emit_addimm(ECX,CLOCK_DIVIDER*(stubs[n][6]),ECX); output_byte(0x03); output_modrm(1,4,ECX); output_sib(0,4,4); output_byte(12+16); //emit_writeword(ECX,(int)&MSH2->cycles); emit_writeword(ECX,slave?(int)&SSH2->cycles:(int)&MSH2->cycles); } //ds=i_regs!=®s[i]; //int real_rs=get_reg(i_regmap,rs2[i]); //if(!ds) load_all_consts(regs[i].regmap_entry,regs[i].was32,regs[i].wasdirty&~(1<regmap_entry,i_regs->was32,i_regs->wasdirty&~(1<=0); assert(rt>=0); save_regs(reglist); // "FASTCALL" api: address in eax, data in edx if(rt!=EDX) emit_mov(rt,EDX); emit_movimm(addr,EAX); // FIXME - should be able to move the existing value if(type==STOREB_STUB) emit_call((int)WriteInvalidateByte); if(type==STOREW_STUB) emit_call((int)WriteInvalidateWord); if(type==STOREL_STUB) emit_call((int)WriteInvalidateLong); restore_regs(reglist); } do_rmwstub(int n) { assem_debug("do_rmwstub %x\n",start+stubs[n][3]*2); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); //int rt=get_reg(i_regmap,rs1[i]); assert(rs>=0); //assert(rt>=0); if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); // "FASTCALL" api: address in eax, data in edx emit_xorimm(rs,1,rs); if(rs!=EAX) emit_mov(rs,EAX); if(rs==EAX||rs==ECX||rs==EDX) emit_writeword_indexed(rs,0,ESP); //if(i_regmap[HOST_CCREG]==CCREG) emit_storereg(CCREG,HOST_CCREG);//DEBUG /*if(i_regmap[HOST_CCREG]==CCREG) { emit_addimm(HOST_CCREG,CLOCK_DIVIDER*(stubs[n][6]),HOST_CCREG); output_byte(0x03); output_modrm(1,4,HOST_CCREG); output_sib(0,4,4); output_byte(12+16); emit_writeword(HOST_CCREG,(int)&MSH2->cycles); output_byte(0x2B); output_modrm(1,4,HOST_CCREG); output_sib(0,4,4); output_byte(12+16); emit_addimm(HOST_CCREG,-CLOCK_DIVIDER*(stubs[n][6]),HOST_CCREG); } if(i_regmap[HOST_CCREG]!=CCREG) { emit_loadreg(CCREG,ECX); emit_addimm(ECX,CLOCK_DIVIDER*(stubs[n][6]),ECX); output_byte(0x03); output_modrm(1,4,ECX); output_sib(0,4,4); output_byte(12+16); emit_writeword(ECX,(int)&MSH2->cycles); }*/ emit_call((int)MappedMemoryReadByte); emit_mov(EAX,EDX); if(rs==EAX||rs==ECX||rs==EDX) emit_readword_indexed(0,ESP,EAX); else emit_mov(rs,EAX); if(type==RMWA_STUB) emit_andimm(EDX,imm[i],EDX); if(type==RMWX_STUB) emit_xorimm(EDX,imm[i],EDX); if(type==RMWO_STUB) emit_orimm(EDX,imm[i],EDX); if(type==RMWT_STUB) { // TAS.B //emit_writeword_indexed(EDX,0,ESP); emit_writeword(EDX,(pointer)&rmw_temp); emit_orimm(EDX,0x80,EDX); } //emit_call((int)MappedMemoryWriteByte); emit_call((int)WriteInvalidateByte); restore_regs(reglist); if(opcode2[i]==11) { // TAS.B signed char sr; sr=get_reg(i_regs->regmap,SR); assert(sr>=0); // Liveness analysis? emit_andimm(sr,~1,sr); //assem_debug("cmp $%d,%d+%%%s\n",1,-16,regname[ESP]); //output_byte(0x80); //output_modrm(1,4,7); //output_sib(0,4,4); //output_byte(-16); //output_byte(1); emit_cmpmem_imm_byte((pointer)&rmw_temp,1); emit_adcimm(0,sr); } emit_jmp(stubs[n][2]); // return address } do_unalignedwritestub(int n) { set_jump_target(stubs[n][1],(int)out); output_byte(0xCC); emit_jmp(stubs[n][2]); // return address } void printregs(int edi,int esi,int ebp,int esp,int b,int d,int c,int a) { printf("regs: %x %x %x %x %x %x %x (%x)\n",a,b,c,d,ebp,esi,edi,(&edi)[-1]); } int do_dirty_stub(int i) { assem_debug("do_dirty_stub %x\n",start+i*2); u32 alignedlen=((((u32)source)+slen*2+2)&~2)-(u32)alignedsource; emit_pushimm(start+i*2+slave); emit_movimm(((u32)source)&~3,EAX); //alignedsource emit_movimm((u32)copy,EBX); emit_movimm((((u32)source+slen*2+2)&~3)-((u32)source&~3),ECX); emit_call((int)&verify_code); emit_addimm(ESP,4,ESP); int entry=(int)out; load_regs_entry(i); if(entry==(int)out) entry=instr_addr[i]; emit_jmp(instr_addr[i]); return entry; } /* Memory Map */ int do_map_r(int s,int ar,int map,int cache,int x,int a,int shift,int c,u32 addr) { if(c) { /*if(can_direct_read(addr)) { emit_readword((int)(memory_map+(addr>>12)),map); } else*/ return -1; // No mapping } else { if(s!=map) emit_mov(s,map); emit_shrimm(map,12,map); // Schedule this while we wait on the load if(x) emit_xorimm(s,x,ar); //if(shift>=0) emit_lea8(s,shift); //if(~a) emit_andimm(s,a,ar); emit_movmem_indexedx4((int)memory_map,map,map); } return map; } int do_map_r_branch(int map, int c, u32 addr, int *jaddr) { if(!c) { emit_test(map,map); *jaddr=(int)out; emit_js(0); } return map; } int gen_tlb_addr_r(int ar, int map) { if(map>=0) { emit_leairrx4(0,ar,map,ar); } } int do_map_w(int s,int ar,int map,int cache,int x,int c,u32 addr) { if(c) { if(can_direct_write(addr)) { emit_readword((int)(memory_map+(addr>>12)),map); } else return -1; // No mapping } else { if(s!=map) emit_mov(s,map); //if(s!=ar) emit_mov(s,ar); emit_shrimm(map,12,map); // Schedule this while we wait on the load if(x) emit_xorimm(s,x,ar); emit_movmem_indexedx4((int)memory_map,map,map); } emit_shlimm(map,2,map); return map; } int do_map_w_branch(int map, int c, u32 addr, int *jaddr) { if(!c||can_direct_write(addr)) { *jaddr=(int)out; emit_jc(0); } } int gen_tlb_addr_w(int ar, int map) { if(map>=0) { emit_leairrx1(0,ar,map,ar); } } // We don't need this for x86 generate_map_const(u32 addr,int reg) { // void *mapaddr=memory_map+(addr>>12); } /* Special assem */ void do_preload_rhash(int r) { emit_movimm(0xf8,r); } void do_preload_rhtbl(int r) { // Don't need this for x86 } void do_rhash(int rs,int rh) { emit_and(rs,rh,rh); } void do_miniht_load(int ht,int rh) { // Don't need this for x86. The load and compare can be combined into // a single instruction (below) } void do_miniht_jump(int rs,int rh,int ht) { emit_cmpmem_indexed(slave?(u32)mini_ht_slave:(u32)mini_ht_master,rh,rs); emit_jne(jump_vaddr_reg[slave][rs]); emit_jmpmem_indexed(slave?(u32)mini_ht_slave+4:(u32)mini_ht_master+4,rh); } void do_miniht_insert(int return_address,int rt,int temp) { emit_movimm(return_address,rt); // PC into link register //emit_writeword_imm(return_address,(int)&mini_ht[(return_address&0xFF)>>8][0]); if(slave) emit_writeword(rt,(int)&mini_ht_slave[(return_address&0xFF)>>3][0]); else emit_writeword(rt,(int)&mini_ht_master[(return_address&0xFF)>>3][0]); add_to_linker((int)out,return_address,1); if(slave) emit_writeword_imm(0,(int)&mini_ht_slave[(return_address&0xFF)>>3][1]); else emit_writeword_imm(0,(int)&mini_ht_master[(return_address&0xFF)>>3][1]); } void wb_valid(signed char pre[],signed char entry[],u32 dirty_pre,u32 dirty,u64 u) { //if(dirty_pre==dirty) return; int hr,reg,new_hr; for(hr=0;hr>(reg&63))&1) { if(reg>=0) { if(((dirty_pre&~dirty)>>hr)&1) { if(reg>=0&® #include #include //include for uint64_t #include #include //include for memset #include #include "../memory.h" #include "../sh2core.h" #include "../yabause.h" #include "sh2_dynarec.h" #ifdef __i386__ #include "assem_x86.h" #endif #ifdef __x86_64__ #include "assem_x64.h" #endif #ifdef __arm__ #include "assem_arm.h" #endif #define MAXBLOCK 4096 #define MAX_OUTPUT_BLOCK_SIZE 262144 #define CLOCK_DIVIDER 1 #define SH2_REGS 23 struct regstat { signed char regmap_entry[HOST_REGS]; signed char regmap[HOST_REGS]; u32 wasdirty; u32 dirty; u64 u; u32 wasdoingcp; u32 isdoingcp; u32 cpmap[HOST_REGS]; u32 isconst; u32 constmap[SH2_REGS]; }; struct ll_entry { u32 vaddr; u32 reg32; void *addr; struct ll_entry *next; }; u32 start; u16 *source; void *alignedsource; u32 pagelimit; char insn[MAXBLOCK][10]; unsigned char itype[MAXBLOCK]; unsigned char opcode[MAXBLOCK]; unsigned char opcode2[MAXBLOCK]; unsigned char opcode3[MAXBLOCK]; unsigned char addrmode[MAXBLOCK]; unsigned char bt[MAXBLOCK]; signed char rs1[MAXBLOCK]; signed char rs2[MAXBLOCK]; signed char rs3[MAXBLOCK]; signed char rt1[MAXBLOCK]; signed char rt2[MAXBLOCK]; unsigned char us1[MAXBLOCK]; unsigned char us2[MAXBLOCK]; unsigned char dep1[MAXBLOCK]; unsigned char dep2[MAXBLOCK]; signed char lt1[MAXBLOCK]; int imm[MAXBLOCK]; u32 ba[MAXBLOCK]; char is_ds[MAXBLOCK]; char ooo[MAXBLOCK]; u64 unneeded_reg[MAXBLOCK]; u64 branch_unneeded_reg[MAXBLOCK]; signed char regmap_pre[MAXBLOCK][HOST_REGS]; u32 cpmap[MAXBLOCK][HOST_REGS]; struct regstat regs[MAXBLOCK]; struct regstat branch_regs[MAXBLOCK]; signed char minimum_free_regs[MAXBLOCK]; u32 needed_reg[MAXBLOCK]; u32 wont_dirty[MAXBLOCK]; u32 will_dirty[MAXBLOCK]; int cycles[MAXBLOCK]; int ccadj[MAXBLOCK]; int slen; pointer instr_addr[MAXBLOCK]; u32 link_addr[MAXBLOCK][3]; int linkcount; u32 stubs[MAXBLOCK*3][8]; int stubcount; pointer ccstub_return[MAXBLOCK]; u32 literals[1024][2]; int literalcount; int is_delayslot; u8 *out; struct ll_entry *jump_in[2048]; struct ll_entry *jump_out[2048]; struct ll_entry *jump_dirty[2048]; ALIGNED(16) u32 hash_table[65536][4]; ALIGNED(16) char shadow[2097152]; char *copy; int expirep; unsigned int stop_after_jal; //char invalid_code[0x100000]; char cached_code[0x20000]; char cached_code_words[2048*128]; u32 recent_writes[8]; u32 recent_write_index=0; unsigned int slave; u32 invalidate_count; extern int master_reg[22]; extern int master_cc; extern int master_pc; // Virtual PC extern void * master_ip; // Translated PC extern int slave_reg[22]; extern int slave_cc; extern int slave_pc; // Virtual PC extern void * slave_ip; // Translated PC extern u8 restore_candidate[512]; /* registers that may be allocated */ /* 0-15 gpr */ #define SR 16 // Status register, including T bit #define GBR 17 // Global base register #define VBR 18 // Vector base register #define MACH 19 // MACH #define MACL 20 // MACL #define PR 21 // Return address #define TBIT 22 // T bit, seperate from SR #define CCREG 23 // Cycle count #define MMREG 24 // Pointer to memory_map #define TEMPREG 25 #define PTEMP 25 // Prefetch temporary register #define MOREG 26 // offset from memory_map #define RHASH 27 // Return address hash #define RHTBL 28 // Return address hash table address #define RTEMP 29 // BRAF/BSRF address register #define MAXREG 29 #define AGEN1 30 // Address generation temporary register #define AGEN2 31 // Address generation temporary register #define MGEN1 32 // Maptable address generation temporary register #define MGEN2 33 // Maptable address generation temporary register /* instruction types */ #define NOP 0 // No operation #define LOAD 1 // Load #define STORE 2 // Store #define RMW 3 // Read-Modify-Write #define PCREL 4 // PC-relative Load #define MOV 5 // Move #define ALU 6 // Arithmetic/logic #define MULTDIV 7 // Multiply/divide #define SHIFTIMM 8// Shift by immediate #define IMM8 9 // 8-bit immediate #define EXT 10 // Sign/Zero Extension #define FLAGS 11 // SETT/CLRT/MOVT #define UJUMP 12 // Unconditional jump #define RJUMP 13 // Unconditional jump to register #define CJUMP 14 // Conditional branch (BT/BF) #define SJUMP 15 // Conditional branch with delay slot #define COMPLEX 16// Complex instructions (function call) #define SYSTEM 17 // Halt/Trap/Exception #define SYSCALL 18// SYSCALL (TRAPA) #define NI 19 // Not implemented #define DATA 20 // Constant pool data not decoded as instructions #define BIOS 21 // Emulate BIOS function /* addressing modes */ #define REGIND 1 // @Rn #define POSTINC 2 // @Rn+ #define PREDEC 3 // @-Rm #define DUALIND 4 // @(R0,Rn) #define GBRIND 5 // @(R0,GBR) #define GBRDISP 6 // @(disp,GBR) #define REGDISP 7 // @(disp,Rn) /* stubs */ #define CC_STUB 1 #define FP_STUB 2 #define LOADB_STUB 3 #define LOADW_STUB 4 #define LOADL_STUB 5 #define LOADS_STUB 6 #define STOREB_STUB 7 #define STOREW_STUB 8 #define STOREL_STUB 9 #define RMWT_STUB 10 #define RMWA_STUB 11 #define RMWX_STUB 12 #define RMWO_STUB 13 /* branch codes */ #define TAKEN 1 #define NOTTAKEN 2 #define NODS 3 // asm linkage int sh2_recompile_block(int addr); void *get_addr_ht(u32 vaddr); void get_bounds(pointer addr,u32 *start,u32 *end); void invalidate_addr(u32 addr); void remove_hash(int vaddr); void dyna_linker(); void verify_code(); void cc_interrupt(); void cc_interrupt_master(); void slave_entry(); void div1(); void macl(); void macw(); void master_handle_bios(); void slave_handle_bios(); // Needed by assembler void wb_register(signed char r,signed char regmap[],u32 dirty); void wb_dirtys(signed char i_regmap[],u32 i_dirty); void wb_needed_dirtys(signed char i_regmap[],u32 i_dirty,int addr); void load_regs(signed char entry[],signed char regmap[],int rs1,int rs2,int rs3); void load_all_regs(signed char i_regmap[]); void load_needed_regs(signed char i_regmap[],signed char next_regmap[]); void load_regs_entry(int t); void load_all_consts(signed char regmap[],u32 dirty,int i); int tracedebug=0; //#define DEBUG_CYCLE_COUNT 1 void nullf(const char *format, ...) {} //#define assem_debug printf //#define inv_debug printf #define assem_debug nullf #define inv_debug nullf // Get address from virtual address // This is called from the recompiled BRAF/BSRF instructions void *get_addr(u32 vaddr) { struct ll_entry *head; u32 page=(vaddr&0xDFFFFFFF)>>12; if(page>1024) page=1024+(page&1023); //printf("TRACE: count=%d next=%d (get_addr %x,page %d)\n",Count,next_interupt,vaddr,page); head=jump_in[page]; while(head!=NULL) { //printf("TRACE: (get_addr check %x: %x)\n",vaddr,(int)head->addr); if(head->vaddr==vaddr) { //printf("TRACE: count=%d next=%d (get_addr match %x: %x)\n",Count,next_interupt,vaddr,(int)head->addr); //printf("TRACE: (get_addr match %x: %x)\n",vaddr,(int)head->addr); int *ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF]; ht_bin[3]=ht_bin[1]; ht_bin[2]=ht_bin[0]; ht_bin[1]=(int)head->addr; ht_bin[0]=vaddr; //printf("TRACE: get_addr clean (%x,%x)\n",vaddr,(int)head->addr); return head->addr; } head=head->next; } head=jump_dirty[page]; while(head!=NULL) { if(head->vaddr==vaddr) { //printf("TRACE: count=%d next=%d (get_addr match dirty %x: %x)\n",Count,next_interupt,vaddr,(int)head->addr); // Don't restore blocks which are about to expire from the cache if((((u32)head->addr-(u32)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2))) if(verify_dirty(head->addr)) { u32 start,end; int *ht_bin; //printf("restore candidate: %x (%d) d=%d\n",vaddr,page,(cached_code[vaddr>>15]>>((vaddr>>12)&7))&1); //invalid_code[vaddr>>12]=0; cached_code[vaddr>>15]|=1<<((vaddr>>12)&7); cached_code[(vaddr^0x20000000)>>15]|=1<<((vaddr>>12)&7); #ifdef POINTERS_64BIT memory_map[vaddr>>12]|=0x4000000000000000LL; memory_map[(vaddr^0x20000000)>>12]|=0x4000000000000000LL; #else memory_map[vaddr>>12]|=0x40000000; memory_map[(vaddr^0x20000000)>>12]|=0x40000000; #endif restore_candidate[page>>3]|=1<<(page&7); get_bounds((pointer)head->addr,&start,&end); if(start-(u32)HighWram<0x100000) { u32 vstart=start-(u32)HighWram+0x6000000; u32 vend=end-(u32)HighWram+0x6000000; int i; //printf("write protect: start=%x, end=%x\n",vstart,vend); for(i=0;i>5]|=1<<(((vstart+i)>>2)&7); } } if(start-(u32)LowWram<0x100000) { u32 vstart=start-(u32)LowWram+0x200000; u32 vend=end-(u32)LowWram+0x200000; int i; //printf("write protect: start=%x, end=%x\n",vstart,vend); for(i=0;i>5]|=1<<(((vstart+i)>>2)&7); } } ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF]; if(ht_bin[0]==vaddr) { ht_bin[1]=(int)head->addr; // Replace existing entry } else { ht_bin[3]=ht_bin[1]; ht_bin[2]=ht_bin[0]; ht_bin[1]=(int)head->addr; ht_bin[0]=vaddr; } //printf("TRACE: get_addr dirty (%x,%x)\n",vaddr,(int)head->addr); return head->addr; } } head=head->next; } sh2_recompile_block(vaddr); return get_addr(vaddr); } // Look up address in hash table first void *get_addr_ht(u32 vaddr) { //printf("TRACE: count=%d next=%d (get_addr_ht %x)\n",Count,next_interupt,vaddr); //if(vaddr>>12==0x60a0) printf("TRACE: (get_addr_ht %x)\n",vaddr); int *ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF]; //if(vaddr>>12==0x60a0) printf("%x %x %x %x\n",ht_bin[0],ht_bin[1],ht_bin[2],ht_bin[3]); if(ht_bin[0]==vaddr) return (void *)ht_bin[1]; if(ht_bin[2]==vaddr) return (void *)ht_bin[3]; return get_addr(vaddr); } void clear_all_regs(signed char regmap[]) { int hr; for (hr=0;hr=0;hr--) if(hr!=EXCLUDE_REG&®map[hr]==r) return hr; return -1; } // Find a register that is available for two consecutive cycles signed char get_reg2(signed char regmap1[],signed char regmap2[],int r) { int hr; for (hr=0;hrregmap[hr]&63)==reg) { cur->dirty|=1<regmap[hr]==reg) { cur->isdoingcp|=1<cpmap[hr]=value; } else if((cur->regmap[hr]^64)==reg) { cur->isdoingcp|=1<cpmap[hr]=value>>32; } } } void clear_const(struct regstat *cur,signed char reg) { int hr; if(reg<0) return; for (hr=0;hrregmap[hr]&63)==reg) { cur->isdoingcp&=~(1<regmap[hr]&63)==reg) { return (cur->isdoingcp>>hr)&1; } } return 0; } u64 get_const(struct regstat *cur,signed char reg) { int hr; if(reg<0) return 0; for (hr=0;hrregmap[hr]==reg) { return cur->cpmap[hr]; } } printf("Unknown constant in r%d\n",reg); exit(1); } void sh2_set_const(u32 *isconst,u32 *constmap,signed char reg,u64 value) { *isconst|=1<=slen) { j=slen-i-1; break; } if(itype[i+j]==UJUMP||itype[i+j]==RJUMP) { // Don't go past an unconditonal jump j++; break; } } for(;j>=0;j--) { if(rs1[i+j]>=0) hsn[rs1[i+j]]=j; if(rs2[i+j]>=0) hsn[rs2[i+j]]=j; if(rs3[i+j]>=0) hsn[rs3[i+j]]=j; if(rt1[i+j]>=0) hsn[rt1[i+j]]=j; if(rt2[i+j]>=0) hsn[rt2[i+j]]=j; if(rs1[i+j]==TBIT) hsn[SR]=j; if(rs2[i+j]==TBIT) hsn[SR]=j; if(rs3[i+j]==TBIT) hsn[SR]=j; if(rt1[i+j]==TBIT) hsn[SR]=j; if(rt2[i+j]==TBIT) hsn[SR]=j; if(i+j>=0&&(itype[i+j]==UJUMP||itype[i+j]==CJUMP||itype[i+j]==SJUMP)) { hsn[CCREG]=j; b=j; } } if(b>=0) { if(ba[i+b]>=start && ba[i+b]<(start+slen*4)) { // Follow first branch int t=(ba[i+b]-start)>>2; j=7-b;if(t+j>=slen) j=slen-t-1; for(;j>=0;j--) { if(rs1[t+j]>=0) if(hsn[rs1[t+j]]>j+b+2) hsn[rs1[t+j]]=j+b+2; if(rs2[t+j]>=0) if(hsn[rs2[t+j]]>j+b+2) hsn[rs2[t+j]]=j+b+2; if(rs3[t+j]>=0) if(hsn[rs2[t+j]]>j+b+2) hsn[rs2[t+j]]=j+b+2; //if(rt1[t+j]) if(hsn[rt1[t+j]]>j+b+2) hsn[rt1[t+j]]=j+b+2; //if(rt2[t+j]) if(hsn[rt2[t+j]]>j+b+2) hsn[rt2[t+j]]=j+b+2; } } // TODO: preferred register based on backward branch } // Delay slot should preferably not overwrite branch conditions or cycle count if(i>0&&(itype[i-1]==RJUMP||itype[i-1]==UJUMP||itype[i-1]==SJUMP)) { if(rs1[i-1]>=0) if(hsn[rs1[i-1]]>1) hsn[rs1[i-1]]=1; if(rs2[i-1]>=0) if(hsn[rs2[i-1]]>1) hsn[rs2[i-1]]=1; if(rs3[i-1]>=0) if(hsn[rs3[i-1]]>1) hsn[rs3[i-1]]=1; if(itype[i-1]==SJUMP) if(hsn[SR]>1) hsn[SR]=1; hsn[CCREG]=1; // ...or hash tables hsn[RHASH]=1; hsn[RHTBL]=1; // .. or branch target hsn[RTEMP]=1; } // If reading/writing T bit, need SR if(rs1[i]==TBIT||rs2[i]==TBIT||rt1[i]==TBIT||rt2[i]==TBIT) { hsn[SR]=0; } // Don't remove the memory_map registers either if(itype[i]==LOAD || itype[i]==STORE || itype[i]==RMW || itype[i]==PCREL) { hsn[MOREG]=0; } if(itype[i]==UJUMP || itype[i]==RJUMP || itype[i]==SJUMP) { if(itype[i+1]==LOAD || itype[i+1]==STORE || itype[i+1]==RMW || itype[i+1]==PCREL) { hsn[MOREG]=0; } } if(itype[i]==SYSTEM && opcode[i]==12) { // TRAPA hsn[MOREG]=0; } // Don't remove the miniht registers if(itype[i]==UJUMP||itype[i]==RJUMP) { hsn[RHASH]=0; hsn[RHTBL]=0; // or branch target hsn[RTEMP]=0; } } // We only want to allocate registers if we're going to use them again soon int needed_again(int r, int i) { int j; int b=-1; int rn=10; if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP)) { if(ba[i-1]start+slen*4-4) return 0; // Don't need any registers if exiting the block } for(j=0;j<9;j++) { if(i+j>=slen) { j=slen-i-1; break; } if(itype[i+j]==UJUMP||itype[i+j]==RJUMP) { // Don't go past an unconditonal jump j++; break; } if(itype[i+j]==SYSCALL||itype[i+j]==SYSTEM) { break; } } for(;j>=1;j--) { if(rs1[i+j]==r) rn=j; if(rs2[i+j]==r) rn=j; if((unneeded_reg[i+j]>>r)&1) rn=10; if(i+j>=0&&(itype[i+j]==UJUMP||itype[i+j]==CJUMP||itype[i+j]==SJUMP)) { b=j; } } /* if(b>=0) { if(ba[i+b]>=start && ba[i+b]<(start+slen*4)) { // Follow first branch int o=rn; int t=(ba[i+b]-start)>>2; j=7-b;if(t+j>=slen) j=slen-t-1; for(;j>=0;j--) { if(!((unneeded_reg[t+j]>>r)&1)) { if(rs1[t+j]==r) if(rn>j+b+2) rn=j+b+2; if(rs2[t+j]==r) if(rn>j+b+2) rn=j+b+2; } else rn=o; } } }*/ if(rn<10) return 1; return 0; } // Try to match register allocations at the end of a loop with those // at the beginning int loop_reg(int i, int r, int hr) { int j,k; for(j=0;j<9;j++) { if(i+j>=slen) { j=slen-i-1; break; } if(itype[i+j]==UJUMP||itype[i+j]==RJUMP) { // Don't go past an unconditonal jump j++; break; } } k=0; if(i>0){ if(itype[i-1]==UJUMP||itype[i-1]==CJUMP||itype[i-1]==SJUMP) k--; } for(;k>r)&1)) return hr; if(i+k>=0&&(itype[i+k]==UJUMP||itype[i+k]==CJUMP||itype[i+k]==SJUMP)) { if(ba[i+k]>=start && ba[i+k]<(start+i*2)) { int t=(ba[i+k]-start)>>1; int reg=get_reg(regs[t].regmap_entry,r); if(reg>=0) return reg; //reg=get_reg(regs[t+1].regmap_entry,r); //if(reg>=0) return reg; } } } return hr; } // Allocate every register, preserving source/target regs void alloc_all(struct regstat *cur,int i) { int hr; for(hr=0;hrregmap[hr]&63)!=rs1[i])&&((cur->regmap[hr]&63)!=rs2[i])&&((cur->regmap[hr]&63)!=rs3[i])&& ((cur->regmap[hr]&63)!=rt1[i])&&((cur->regmap[hr]&63)!=rt2[i])) { cur->regmap[hr]=-1; cur->dirty&=~(1<vaddr=vaddr; new_entry->reg32=0; new_entry->addr=addr; new_entry->next=*head; *head=new_entry; } // Add to linked list only if there is not an existing record void ll_add_nodup(struct ll_entry **head,int vaddr,void *addr) { struct ll_entry *ptr; ptr=*head; while(ptr!=NULL) { if(ptr->vaddr==vaddr) { return; } ptr=ptr->next; } ll_add(head,vaddr,addr); } // Check if an address is already compiled // but don't return addresses which are about to expire from the cache void *check_addr(u32 vaddr) { struct ll_entry *head; u32 page; u32 *ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF]; if(ht_bin[0]==vaddr) { if(((ht_bin[1]-MAX_OUTPUT_BLOCK_SIZE-(u32)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2))) if(isclean(ht_bin[1])) return (void *)ht_bin[1]; } if(ht_bin[2]==vaddr) { if(((ht_bin[3]-MAX_OUTPUT_BLOCK_SIZE-(u32)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2))) if(isclean(ht_bin[3])) return (void *)ht_bin[3]; } page=(vaddr&0xDFFFFFFF)>>12; if(page>1024) page=1024+(page&1023); head=jump_in[page]; while(head!=NULL) { if(head->vaddr==vaddr) { if((((u32)head->addr-(u32)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2))) { // Update existing entry with current address if(ht_bin[0]==vaddr) { ht_bin[1]=(int)head->addr; return head->addr; } if(ht_bin[2]==vaddr) { ht_bin[3]=(int)head->addr; return head->addr; } // Insert into hash table with low priority. // Don't evict existing entries, as they are probably // addresses that are being accessed frequently. if(ht_bin[0]==-1) { ht_bin[1]=(int)head->addr; ht_bin[0]=vaddr; }else if(ht_bin[2]==-1) { ht_bin[3]=(int)head->addr; ht_bin[2]=vaddr; } return head->addr; } } head=head->next; } return 0; } void remove_hash(int vaddr) { //printf("remove hash: %x\n",vaddr); int *ht_bin=hash_table[(((vaddr)>>16)^vaddr)&0xFFFF]; if(ht_bin[2]==vaddr) { ht_bin[2]=ht_bin[3]=-1; } if(ht_bin[0]==vaddr) { ht_bin[0]=ht_bin[2]; ht_bin[1]=ht_bin[3]; ht_bin[2]=ht_bin[3]=-1; } } void ll_remove_matching_addrs(struct ll_entry **head,int addr,int shift) { struct ll_entry *next; while(*head) { if(((u32)((*head)->addr)>>shift)==(addr>>shift) || ((u32)(((char *)(*head)->addr)-MAX_OUTPUT_BLOCK_SIZE)>>shift)==(addr>>shift)) { inv_debug("EXP: Remove pointer to %x (%x)\n",(int)(*head)->addr,(*head)->vaddr); remove_hash((*head)->vaddr); next=(*head)->next; free(*head); *head=next; } else { head=&((*head)->next); } } } // Remove all entries from linked list void ll_clear(struct ll_entry **head) { struct ll_entry *cur; struct ll_entry *next; if(cur=*head) { *head=0; while(cur) { next=cur->next; free(cur); cur=next; } } } // Dereference the pointers and remove if it matches void ll_kill_pointers(struct ll_entry *head,int addr,int shift) { while(head) { int ptr=get_pointer(head->addr); inv_debug("EXP: Lookup pointer to %x at %x (%x)\n",(int)ptr,(int)head->addr,head->vaddr); if(((ptr>>shift)==(addr>>shift)) || (((ptr-MAX_OUTPUT_BLOCK_SIZE)>>shift)==(addr>>shift))) { u32 host_addr; inv_debug("EXP: Kill pointer at %x (%x)\n",(int)head->addr,head->vaddr); host_addr=(u32)kill_pointer(head->addr); #ifdef __arm__ needs_clear_cache[(host_addr-(u32)BASE_ADDR)>>17]|=1<<(((host_addr-(u32)BASE_ADDR)>>12)&31); #endif } head=head->next; } } // This is called when we write to a compiled block void invalidate_page(u32 page) { struct ll_entry *head; struct ll_entry *next; head=jump_in[page]; jump_in[page]=0; while(head!=NULL) { inv_debug("INVALIDATE: %x\n",head->vaddr); remove_hash(head->vaddr); next=head->next; free(head); head=next; } head=jump_out[page]; jump_out[page]=0; while(head!=NULL) { u32 host_addr; inv_debug("INVALIDATE: kill pointer to %x (%x)\n",head->vaddr,(int)head->addr); host_addr=(u32)kill_pointer(head->addr); #ifdef __arm__ needs_clear_cache[(host_addr-(u32)BASE_ADDR)>>17]|=1<<(((host_addr-(u32)BASE_ADDR)>>12)&31); #endif next=head->next; free(head); head=next; } } void invalidate_blocks(u32 firstblock,u32 lastblock) { u32 page; int block; u32 first,last; first=firstblock<1024?firstblock:1024+(firstblock&1023); last=lastblock<1024?lastblock:1024+(lastblock&1023); // Invalidate the adjacent pages if a block crosses a 4K boundary for(block=firstblock;block<=lastblock;block++) { struct ll_entry *head; page=block&0xDFFFF; if(page>1024) page=1024+(page&1023); inv_debug("INVALIDATE: %x..%x (%d)\n",firstblock<<12,lastblock<<12,page); //inv_debug("invalid_code[block]=%d\n",invalid_code[block]); head=jump_dirty[page]; //printf("page=%d vpage=%d\n",page,vpage); while(head!=NULL) { u32 start,end; if((head->vaddr>>12)==block) { // Ignore vaddr hash collision get_bounds((pointer)head->addr,&start,&end); //printf("start: %x end: %x\n",start,end); if(start>=(u32)LowWram&&end<(u32)LowWram+1048576) { if(((start-(u32)LowWram)>>12)<=page&&((end-1-(u32)LowWram)>>12)>=page) { if((((start-(u32)LowWram)>>12)+512)>12)&1023; if((((end-1-(u32)LowWram)>>12)+512)>last) last=((end-1-(u32)LowWram)>>12)&1023; } } // FIXME: Aliasing/mirroring is wrong here if(start>=(u32)HighWram&&end<(u32)HighWram+1048576) { if(((start-(u32)HighWram)>>12)<=page-1024&&((end-1-(u32)HighWram)>>12)>=page-1024) { if((((start-(u32)HighWram)>>12)&255)>12)&255)+1024; if((((end-1-(u32)HighWram)>>12)&255)>last-1024) last=(((end-1-(u32)HighWram)>>12)&255)+1024; } } } head=head->next; } } //printf("first=%d last=%d\n",first,last); while(first<=last) { invalidate_page(first); first++; } #ifdef __arm__ do_clear_cache(); #endif for(block=firstblock;block<=lastblock;block++) { // Don't trap writes cached_code[block>>3]&=~(1<<(block&7)); cached_code[(block^0x20000)>>3]&=~(1<<(block&7)); #ifdef POINTERS_64BIT if((block>=0x0200&&block<0x0300)||(block>=0x20200&&block<0x20300)) { memory_map[block]=((u64)LowWram-((block<<12)&0xFFF00000))>>2; memory_map[block^0x20000]=((u64)LowWram-(((block^0x20000)<<12)&0xFFF00000))>>2; } if((block>=0x6000&&block<0x8000)||(block>=0x26000&&block<0x28000)) { memory_map[block]=((u64)HighWram-((block<<12)&0xFFF00000))>>2; memory_map[block^0x20000]=((u64)HighWram-(((block^0x20000)<<12)&0xFFF00000))>>2; } #else if((block>=0x0200&&block<0x0300)||(block>=0x20200&&block<0x20300)) { memory_map[block]=((u32)LowWram-((block<<12)&0xFFF00000))>>2; memory_map[block^0x20000]=((u32)LowWram-(((block^0x20000)<<12)&0xFFF00000))>>2; } if((block>=0x6000&&block<0x8000)||(block>=0x26000&&block<0x28000)) { memory_map[block]=((u32)HighWram-((block<<12)&0xFFF00000))>>2; memory_map[block^0x20000]=((u32)HighWram-(((block^0x20000)<<12)&0xFFF00000))>>2; } #endif page=block&0xDFFFF; if(page>1024) page=1024+(page&1023); memset(cached_code_words+(page<<7),0,128); } #ifdef USE_MINI_HT memset(mini_ht_master,-1,sizeof(mini_ht_master)); memset(mini_ht_slave,-1,sizeof(mini_ht_slave)); #endif } void invalidate_addr(u32 addr) { u32 index=addr&0xDFFFFFFF; if(index>4194304) index=(addr|0x400000)&0x7fffff; if(!((cached_code_words[index>>5]>>((index>>2)&7))&1)) { // If we get an excessive number of these, // then we probably do want to invalidate the page if(invalidate_count++<500) { if((restore_candidate[index>>15]>>((index>>12)&7))&1) { recent_writes[recent_write_index]=addr; recent_write_index=(recent_write_index+1)&7; } return; } } //printf("invalidate_count: %d\n",invalidate_count); //printf("invalidate_addr(%x)\n",addr); //invalidate_block(addr>>12); invalidate_blocks(addr>>12,addr>>12); assert(!((cached_code_words[index>>5]>>((index>>2)&7))&1)); // Keep track of recent writes that invalidated the cache, so we don't // attempt constant propagation in areas that are frequently written recent_writes[recent_write_index]=addr; recent_write_index=(recent_write_index+1)&7; } // This is called when loading a save state. // Anything could have changed, so invalidate everything. void invalidate_all_pages() { u32 page; for(page=0;page<2048;page++) invalidate_page(page); for(page=0;page<256;page++) { if(cached_code[page]) { restore_candidate[page]|=cached_code[page]; // LowWram/bios } if(cached_code[3072+page]) { restore_candidate[page+256]|=cached_code[3072+page]; // HighWram } } memset(cached_code_words,0,262144); #ifdef __arm__ __clear_cache((void *)BASE_ADDR,(void *)BASE_ADDR+(1<>12; if(page>1024) page=1024+(page&1023); inv_debug("add_link: %x -> %x (%d)\n",(int)src,vaddr,page); ll_add(jump_out+page,vaddr,src); //int ptr=get_pointer(src); //inv_debug("add_link: Pointer is to %x\n",(int)ptr); } // If a code block was found to be unmodified (bit was set in // restore_candidate) and it remains unmodified (bit is set // in cached_code) then move the entries for that 4K page from // the dirty list to the clean list. void clean_blocks(u32 page) { struct ll_entry *head; inv_debug("INV: clean_blocks page=%d\n",page); head=jump_dirty[page]; while(head!=NULL) { if((cached_code[head->vaddr>>15]>>((head->vaddr>>12)&7))&1) {; // Don't restore blocks which are about to expire from the cache if((((u32)head->addr-(u32)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2))) { u32 start,end,vstart=0,vend; if(verify_dirty((int)head->addr)) { //printf("Possibly Restore %x (%x)\n",head->vaddr, (int)head->addr); u32 i; u32 inv=0; get_bounds((pointer)head->addr,&start,&end); if(start-(u32)HighWram<0x100000) { vstart=start-(u32)HighWram+0x6000000; vend=end-(u32)HighWram+0x6000000; for(i=(start-(u32)HighWram+0x6000000)>>12;i<=(end-1-(u32)HighWram+0x6000000)>>12;i++) { // Check that all the pages are write-protected if(!((cached_code[i>>3]>>(i&7))&1)) inv=1; } } if(start-(u32)LowWram<0x100000) { vstart=start-(u32)LowWram+0x200000; vend=end-(u32)LowWram+0x200000; for(i=(start-(u32)LowWram+0x200000)>>12;i<=(end-1-(u32)LowWram+0x200000)>>12;i++) { // Check that all the pages are write-protected if(!((cached_code[i>>3]>>(i&7))&1)) inv=1; } } // Don't restore stuff that recently got hit, it will probably get hit again if(vstart) for(i=0;i<8;i++) { if(recent_writes[i]>=vstart&&recent_writes[i]addr); if((((u32)clean_addr-(u32)out)<<(32-TARGET_SIZE_2))>0x60000000+(MAX_OUTPUT_BLOCK_SIZE<<(32-TARGET_SIZE_2))) { int *ht_bin; inv_debug("INV: Restored %x (%x/%x)\n",head->vaddr, (int)head->addr, (int)clean_addr); //printf("page=%x, addr=%x\n",page,head->vaddr); //assert(head->vaddr>>12==(page|0x80000)); ll_add_nodup(jump_in+page,head->vaddr,clean_addr); ht_bin=hash_table[((head->vaddr>>16)^head->vaddr)&0xFFFF]; if(ht_bin[0]==head->vaddr) { ht_bin[1]=(int)clean_addr; // Replace existing entry } if(ht_bin[2]==head->vaddr) { ht_bin[3]=(int)clean_addr; // Replace existing entry } } if(vstart) { //printf("start=%x, end=%x\n",vstart,vend); for(i=0;i>5]|=1<<(((vstart+i)>>2)&7); } } } } } } head=head->next; } } do_consts(int i,u32 *isconst,u32 *constmap) { switch(itype[i]) { case LOAD: sh2_clear_const(isconst,constmap,rt1[i]); if(addrmode[i]==POSTINC) { int size=(opcode[i]==4)?2:(opcode2[i]&3); constmap[rt2[i]]+=1<>1)>1]; // MOV.W else value=(source[(((start+i*2+4)&~3)+imm[i]-start)>>1]<<16)+source[(((start+i*2+4)&~3)+imm[i]+2-start)>>1]; // MOV.L sh2_set_const(isconst,constmap,rt1[i],value); } else sh2_clear_const(isconst,constmap,rt1[i]); } break; case MOV: if(((*isconst)>>rs1[i])&1) { int v=constmap[rs1[i]]; sh2_set_const(isconst,constmap,rt1[i],v); } else sh2_clear_const(isconst,constmap,rt1[i]); break; case IMM8: if(opcode[i]==0x7) { // ADD if(((*isconst)>>rs1[i])&1) { int v=constmap[rs1[i]]; sh2_set_const(isconst,constmap,rt1[i],v+imm[i]); } else sh2_clear_const(isconst,constmap,rt1[i]); } else if(opcode[i]==0x8) { // CMP/EQ } else if(opcode[i]==12) { if(opcode2[i]==8) { // TST }else // AND/XOR/OR if(((*isconst)>>rs1[i])&1) { int v=constmap[rs1[i]]; if(opcode2[i]==0x09) sh2_set_const(isconst,constmap,rt1[i],v&imm[i]); if(opcode2[i]==0x0a) sh2_set_const(isconst,constmap,rt1[i],v^imm[i]); if(opcode2[i]==0x0b) sh2_set_const(isconst,constmap,rt1[i],v|imm[i]); } else sh2_clear_const(isconst,constmap,rt1[i]); } else { // opcode[i]==0xE assert(opcode[i]==0xE); sh2_set_const(isconst,constmap,rt1[i],imm[i]); // MOV } break; case FLAGS: if(opcode2[i]==9) { // MOVT sh2_clear_const(isconst,constmap,rt1[i]); } break; case ALU: sh2_clear_const(isconst,constmap,rt1[i]); break; case EXT: sh2_clear_const(isconst,constmap,rt1[i]); break; case MULTDIV: if(opcode[i]==0) { if(opcode2[i]==7) // MUL.L { sh2_clear_const(isconst,constmap,MACL); } if(opcode2[i]==8) // CLRMAC { sh2_clear_const(isconst,constmap,MACH); sh2_clear_const(isconst,constmap,MACL); } if(opcode2[i]==9) // DIV0U { } } if(opcode[i]==2) { if(opcode2[i]==7) // DIV0S { } if(opcode2[i]==14||opcode2[i]==15) // MULU.W / MULS.W { sh2_clear_const(isconst,constmap,MACL); } } if(opcode[i]==3) { // DMULU.L / DMULS.L sh2_clear_const(isconst,constmap,MACH); sh2_clear_const(isconst,constmap,MACL); } break; case SHIFTIMM: sh2_clear_const(isconst,constmap,rt1[i]); break; case UJUMP: case RJUMP: case SJUMP: case CJUMP: break; case SYSTEM: *isconst=0; break; case COMPLEX: *isconst=0; break; } } void mov_alloc(struct regstat *current,int i) { // Note: Don't need to actually alloc the source registers // TODO: Constant propagation //alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rt1[i]); clear_const(current,rs1[i]); clear_const(current,rt1[i]); dirty_reg(current,rt1[i]); } void shiftimm_alloc(struct regstat *current,int i) { clear_const(current,rs1[i]); clear_const(current,rt1[i]); alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rt1[i]); dirty_reg(current,rt1[i]); if(opcode[i]==4) { if(opcode2[i]<6) { // SHLL/SHAL/SHLR/SHAR/ROTL/ROTCL/ROTR/ROTCR if(opcode2[i]<4||opcode3[i]<2) { // SHL/SHA/ROT don't need T bit as a source, only a destination if(!(current->u&(1LL<8&&opcode2[i]<=11) { // AND/XOR/OR alloc_reg(current,i,rt1[i]); } else // TST or CMP/STR { alloc_reg(current,i,SR); // Liveness analysis on TBIT? dirty_reg(current,SR); //#ifdef __x86__ ? //#ifdef NEEDS_TEMP if(opcode2[i]==8) { // TST alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } if(opcode2[i]==12) { // CMP/STR alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } } } if(opcode[i]==3) { alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rs2[i]); clear_const(current,rs2[i]); if(opcode2[i]<8) { // CMP intructions alloc_reg(current,i,SR); // Liveness analysis on TBIT? dirty_reg(current,SR); alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; }else{ // ADD/SUB alloc_reg(current,i,rt1[i]); if(opcode2[i]&3) { alloc_reg(current,i,SR); dirty_reg(current,SR); //#ifdef NEEDS_TEMP if((opcode2[i]&3)==3) { // Need a temporary register for ADDV/SUBV on x86 alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } } } } if(opcode[i]==4) { // DT/CMPPZ/CMPPL // Single operand forms alloc_reg(current,i,rs1[i]); if(opcode2[i]==0) dirty_reg(current,rt1[i]); // DT alloc_reg(current,i,SR); // Liveness analysis on TBIT? dirty_reg(current,SR); if(opcode2[i]>0) { alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } } if(opcode[i]==6) { // NOT/NEG/NEGC if(needed_again(rs1[i],i)) alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rt1[i]); if(opcode2[i]==8||opcode2[i]==9) { // SWAP needs temp (?) alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } if(opcode2[i]==10) { // NEGC sets T bit alloc_reg(current,i,SR); // Liveness analysis on TBIT? dirty_reg(current,SR); } } clear_const(current,rs1[i]); clear_const(current,rt1[i]); dirty_reg(current,rt1[i]); } void imm8_alloc(struct regstat *current,int i) { //if(rs1[i]>=0&&needed_again(rs1[i],i)) alloc_reg(current,i,rs1[i]); //else lt1[i]=rs1[i]; alloc_reg(current,i,rs1[i]); if(rt1[i]>=0&&rt1[i]!=TBIT) alloc_reg(current,i,rt1[i]); if(opcode[i]==0x7) { // ADD if(is_const(current,rs1[i])) { int v=get_const(current,rs1[i]); set_const(current,rt1[i],v+imm[i]); } else clear_const(current,rt1[i]); } else if(opcode[i]==0x8) { // CMP/EQ alloc_reg(current,i,SR); // Liveness analysis on TBIT? dirty_reg(current,SR); alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } else if(opcode[i]==12) { if(opcode2[i]==8) { // TST alloc_reg(current,i,SR); // Liveness analysis on TBIT? dirty_reg(current,SR); alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; }else // AND/XOR/OR if(is_const(current,rs1[i])) { int v=get_const(current,rs1[i]); if(opcode2[i]==0x09) set_const(current,rt1[i],v&imm[i]); if(opcode2[i]==0x0a) set_const(current,rt1[i],v^imm[i]); if(opcode2[i]==0x0b) set_const(current,rt1[i],v|imm[i]); } else clear_const(current,rt1[i]); } else { // opcode[i]==0xE assert(opcode[i]==0xE); set_const(current,rt1[i],imm[i]); // MOV } if(rt1[i]>=0&&rt1[i]!=TBIT) dirty_reg(current,rt1[i]); } void ext_alloc(struct regstat *current,int i) { // Note: Don't need to actually alloc the source registers // FIXME: Constant propagation //alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rt1[i]); clear_const(current,rs1[i]); clear_const(current,rt1[i]); dirty_reg(current,rt1[i]); } void flags_alloc(struct regstat *current,int i) { if(opcode2[i]==8) { // CLRT/SETT alloc_reg(current,i,SR); dirty_reg(current,SR); }else if(opcode2[i]==9) { // MOVT alloc_reg(current,i,SR); alloc_reg(current,i,rt1[i]); clear_const(current,rt1[i]); dirty_reg(current,rt1[i]); } } void load_alloc(struct regstat *current,int i) { int hr; clear_const(current,rt1[i]); //if(rs1[i]!=rt1[i]&&needed_again(rs1[i],i)) clear_const(current,rs1[i]); // Does this help or hurt? if(needed_again(rs1[i],i)) alloc_reg(current,i,rs1[i]); // if(rs2[i]>=0) alloc_reg(current,i,rs2[i]); alloc_reg(current,i,rt1[i]==TBIT?SR:rt1[i]); if(addrmode[i]==DUALIND||addrmode[i]==GBRIND) { alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rs2[i]); if(!is_const(current,rs1[i])||!is_const(current,rs2[i])) { // Both must be constants to propagate the sum clear_const(current,rs1[i]); clear_const(current,rs2[i]); } } else if(addrmode[i]==POSTINC) { if(is_const(current,rt2[i])) { int v=get_const(current,rt2[i]); set_const(current,rt2[i],v+(1<<((opcode[i]==4)?2:(opcode2[i]&3)))); // Note: constant is preincremented, address_generation corrects the offset } else { alloc_reg(current,i,rt2[i]); dirty_reg(current,rt2[i]); } } // Need a register to load from memory_map alloc_reg(current,i,MOREG); if(rt1[i]==TBIT||get_reg(current->regmap,rt1[i])<0) { // dummy load, but we still need a register to calculate the address alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } if(rt1[i]==TBIT) dirty_reg(current,SR); else dirty_reg(current,rt1[i]); // Make MOREG a temporary, give pass 5 another register to work with hr=get_reg(current->regmap,MOREG); assert(hr>=0); assert(current->regmap[hr]==MOREG); current->regmap[hr]=-1; minimum_free_regs[i]++; } void store_alloc(struct regstat *current,int i) { int hr; //printf("%x: eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",start+i*2,current->regmap[0],current->regmap[1],current->regmap[2],current->regmap[3],current->regmap[5],current->regmap[6],current->regmap[7]); if(addrmode[i]==DUALIND) { alloc_reg(current,i,rs2[i]); alloc_reg(current,i,0); // rs3[i] if(!is_const(current,rs2[i])||!is_const(current,rs3[i])) { // Both must be constants to propagate the sum clear_const(current,rs2[i]); clear_const(current,rs3[i]); } } if(addrmode[i]==PREDEC) { if(is_const(current,rt1[i])) { int v=get_const(current,rt1[i]); set_const(current,rt1[i],v-(1<<((opcode[i]==4)?2:(opcode2[i]&3)))); } else { alloc_reg(current,i,rt1[i]); dirty_reg(current,rt1[i]); } } if(needed_again(rs2[i],i)) alloc_reg(current,i,rs2[i]); clear_const(current,rs1[i]); alloc_reg(current,i,rs1[i]); // Need a register to load from memory_map alloc_reg(current,i,MOREG); // We need a temporary register for address generation alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; // Make MOREG a temporary, give pass 5 another register to work with hr=get_reg(current->regmap,MOREG); assert(hr>=0); assert(current->regmap[hr]==MOREG); current->regmap[hr]=-1; minimum_free_regs[i]++; } void rmw_alloc(struct regstat *current,int i) { //printf("%x: eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",start+i*2,current->regmap[0],current->regmap[1],current->regmap[2],current->regmap[3],current->regmap[5],current->regmap[6],current->regmap[7]); if(addrmode[i]==GBRIND) { alloc_reg(current,i,GBR); alloc_reg(current,i,0); if(!is_const(current,rs2[i])||!is_const(current,rs3[i])) { // Both must be constants to propagate the sum clear_const(current,rs2[i]); clear_const(current,rs3[i]); } } if(addrmode[i]==REGIND&&needed_again(rs1[i],i)) alloc_reg(current,i,rs1[i]); if(rt1[i]==TBIT) { alloc_reg(current,i,SR); dirty_reg(current,SR); } // Need a register to load from memory_map alloc_reg(current,i,MOREG); // We need a temporary register for address generation alloc_reg_temp(current,i,-1); // And one for the read-modify-write //alloc_reg_temp(current,i,-2); // Can re-use mapping reg for this minimum_free_regs[i]=1; } void pcrel_alloc(struct regstat *current,int i) { u32 addr; alloc_reg(current,i,rt1[i]); addr=((start+i*2+4)&~3)+imm[i]; if(opcode[i]==12) { // MOVA, address generation only set_const(current,rt1[i],addr); }else if((unsigned)((addr-start)>>1)>1]); } else // MOV.L set_const(current,rt1[i],(source[(addr-start)>>1]<<16)+source[(addr+2-start)>>1]); } else { // Do actual load //alloc_reg(current,i,MOREG); clear_const(current,rt1[i]); } dirty_reg(current,rt1[i]); } #ifndef multdiv_alloc void multdiv_alloc(struct regstat *current,int i) { //printf("%x: eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",start+i*2,current->regmap[0],current->regmap[1],current->regmap[2],current->regmap[3],current->regmap[5],current->regmap[6],current->regmap[7]); if(opcode[i]==0) { if(opcode2[i]==7) // MUL.L { clear_const(current,rs1[i]); clear_const(current,rs2[i]); clear_const(current,MACL); alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rs2[i]); alloc_reg(current,i,MACL); dirty_reg(current,MACL); } if(opcode2[i]==8) // CLRMAC { clear_const(current,MACH); clear_const(current,MACL); alloc_reg(current,i,MACH); alloc_reg(current,i,MACL); dirty_reg(current,MACH); dirty_reg(current,MACL); } if(opcode2[i]==9) // DIV0U { alloc_reg(current,i,SR); dirty_reg(current,SR); } } if(opcode[i]==2) { if(opcode2[i]==7) // DIV0S { clear_const(current,rs1[i]); // Is this necessary? clear_const(current,rs2[i]); // Is this necessary? alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rs2[i]); alloc_reg(current,i,SR); dirty_reg(current,SR); #if defined(__i386__) || defined(__x86_64__) //#ifdef NEEDS_TEMP alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; #endif } if(opcode2[i]==14||opcode2[i]==15) // MULU.W / MULS.W { clear_const(current,rs1[i]); clear_const(current,rs2[i]); clear_const(current,MACL); alloc_reg(current,i,rs1[i]); alloc_reg(current,i,rs2[i]); alloc_reg(current,i,MACL); dirty_reg(current,MACL); //#ifdef NEEDS_TEMP alloc_reg_temp(current,i,-1); minimum_free_regs[i]=1; } } if(opcode[i]==3) { // DMULU.L / DMULS.L #if defined(__i386__) || defined(__x86_64__) if(!(current->u&(1LL<u&=~(1LL<u&(1LL<u&=~(1LL<isdoingcp=0; } void delayslot_alloc(struct regstat *current,int i) { switch(itype[i]) { case UJUMP: case CJUMP: case SJUMP: case RJUMP: case SYSCALL: assem_debug("jump in the delay slot. this shouldn't happen.\n");//exit(1); printf("Disabled speculative precompilation\n"); stop_after_jal=1; break; case IMM8: imm8_alloc(current,i); break; case LOAD: load_alloc(current,i); break; case STORE: store_alloc(current,i); break; case RMW: rmw_alloc(current,i); break; case PCREL: pcrel_alloc(current,i); break; case ALU: alu_alloc(current,i); break; case MULTDIV: multdiv_alloc(current,i); break; case SHIFTIMM: shiftimm_alloc(current,i); break; case MOV: mov_alloc(current,i); break; case EXT: ext_alloc(current,i); break; case FLAGS: flags_alloc(current,i); break; case COMPLEX: complex_alloc(current,i); break; } } add_stub(int type,int addr,int retaddr,int a,int b,int c,int d,int e) { stubs[stubcount][0]=type; stubs[stubcount][1]=addr; stubs[stubcount][2]=retaddr; stubs[stubcount][3]=a; stubs[stubcount][4]=b; stubs[stubcount][5]=c; stubs[stubcount][6]=d; stubs[stubcount][7]=e; stubcount++; } // Write out a single register void wb_register(signed char r,signed char regmap[],u32 dirty) { int hr; for(hr=0;hr>hr)&1) { emit_storereg(r,hr); } } } } } /*int mchecksum() { //if(!tracedebug) return 0; int i; int sum=0; for(i=0;i<2097152;i++) { unsigned int temp=sum; sum<<=1; sum|=(~temp)>>31; sum^=((u_int *)rdram)[i]; } return sum; } /*int rchecksum() { int i; int sum=0; for(i=0;i<64;i++) sum^=((u_int *)reg)[i]; return sum; } /*int fchecksum() { int i; int sum=0; for(i=0;i<64;i++) sum^=((u_int *)reg_cop1_fgr_64)[i]; return sum; }*/ /*void rlist() { int i; printf("TRACE: "); for(i=0;i<32;i++) printf("r%d:%8x%8x ",i,((int *)(reg+i))[1],((int *)(reg+i))[0]); printf("\n"); //printf("TRACE: "); //for(i=0;i<32;i++) // printf("f%d:%8x%8x ",i,((int*)reg_cop1_simple[i])[1],*((int*)reg_cop1_simple[i])); //printf("\n"); }*/ void enabletrace() { tracedebug=1; } #if 0 void memdebug(int i) { //printf("TRACE: count=%d next=%d (checksum %x) lo=%8x%8x\n",Count,next_interupt,mchecksum(),(int)(reg[LOREG]>>32),(int)reg[LOREG]); //printf("TRACE: count=%d next=%d (rchecksum %x)\n",Count,next_interupt,rchecksum()); //rlist(); //if(tracedebug) { //if(Count>=-2084597794) { //if((signed int)Count>=-2084597794&&(signed int)Count<0) { //if(0) { printf("TRACE: (checksum %x)\n",mchecksum()); //printf("TRACE: count=%d next=%d (checksum %x)\n",Count,next_interupt,mchecksum()); //printf("TRACE: count=%d next=%d (checksum %x) Status=%x\n",Count,next_interupt,mchecksum(),Status); //printf("TRACE: count=%d next=%d (checksum %x) hi=%8x%8x\n",Count,next_interupt,mchecksum(),(int)(reg[HIREG]>>32),(int)reg[HIREG]); //rlist(); #ifdef __i386__ printf("TRACE: %x\n",(&i)[-1]); #endif #ifdef __arm__ int j; printf("TRACE: %x \n",(&j)[10]); printf("TRACE: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",(&j)[1],(&j)[2],(&j)[3],(&j)[4],(&j)[5],(&j)[6],(&j)[7],(&j)[8],(&j)[9],(&j)[10],(&j)[11],(&j)[12],(&j)[13],(&j)[14],(&j)[15],(&j)[16],(&j)[17],(&j)[18],(&j)[19],(&j)[20]); #endif //fflush(stdout); //} //printf("TRACE: %x\n",(&i)[-1]); } #endif void alu_assemble(int i,struct regstat *i_regs) { if(opcode[i]==2) { if(opcode2[i]>=9&&opcode2[i]<=11) { // AND/XOR/OR signed char s,t; s=get_reg(i_regs->regmap,rs1[i]); t=get_reg(i_regs->regmap,rt1[i]); //assert(s>=0); if(t>=0) { if(opcode2[i]==9) emit_and(s,t,t); if(opcode2[i]==10) emit_xor(rs1[i]>=0?s:t,t,t); if(opcode2[i]==11) emit_or(s,t,t); } } else { signed char s1,s2,sr,temp; s1=get_reg(i_regs->regmap,rs1[i]); s2=get_reg(i_regs->regmap,rs2[i]); sr=get_reg(i_regs->regmap,SR); temp=get_reg(i_regs->regmap,-1); assert(s1>=0); assert(s2>=0); assert(sr>=0); assert(temp>=0); // Not needed for TST on ARM? if(opcode2[i]==8) { // TST emit_sh2tst(s1,s2,sr,temp); } else if(opcode2[i]==12) { // CMP/STR emit_cmpstr(s1,s2,sr,temp); } } } if(opcode[i]==3) { // ADD/SUB if(opcode2[i]<8) { // CMP signed char s1,s2,sr,temp; s1=get_reg(i_regs->regmap,rs1[i]); s2=get_reg(i_regs->regmap,rs2[i]); sr=get_reg(i_regs->regmap,SR); temp=get_reg(i_regs->regmap,-1); assert(s1>=0); assert(s2>=0); assert(temp>=0); if(opcode2[i]==0) emit_cmpeq(s1,s2,sr,temp); if(opcode2[i]==2) emit_cmphs(s1,s2,sr,temp); if(opcode2[i]==3) emit_cmpge(s1,s2,sr,temp); if(opcode2[i]==6) emit_cmphi(s1,s2,sr,temp); if(opcode2[i]==7) emit_cmpgt(s1,s2,sr,temp); } else { signed char s,t,sr,temp; t=get_reg(i_regs->regmap,rt1[i]); if(t>=0) { s=get_reg(i_regs->regmap,rs1[i]); sr=get_reg(i_regs->regmap,SR); temp=get_reg(i_regs->regmap,-1); assert(s>=0); //assert(s2==t); if(opcode2[i]==8) emit_sub(t,s,t); if(opcode2[i]==10) emit_subc(s,t,sr); //if(opcode2[i]==11) emit_subv(s,sr,temp); assert(opcode2[i]!=11); if(opcode2[i]==12) emit_add(s,t,t); if(opcode2[i]==14) emit_addc(s,t,sr); //if(opcode2[i]==15) emit_addv(s,sr,temp); assert(opcode2[i]!=15); } } } if(opcode[i]==4) { // DT/CMPPZ/CMPPL signed char s,t,sr,temp; s=get_reg(i_regs->regmap,rs1[i]); sr=get_reg(i_regs->regmap,SR); assert(s>=0); assert(sr>=0); if(opcode2[i]==0) { t=get_reg(i_regs->regmap,rt1[i]); assert(t>=0); // FIXME - Liveness analysis assert(s==t); emit_dt(s,sr); } else if(opcode2[i]==1) emit_cmppz(s,sr); else if(opcode2[i]==5) { temp=get_reg(i_regs->regmap,-1); emit_cmppl(s,sr,temp); } } if(opcode[i]==6) { // NOT/SWAP/NEG int s=get_reg(i_regs->regmap,rs1[i]); int t=get_reg(i_regs->regmap,rt1[i]); if(s<0) { // FIXME: Preload? emit_loadreg(rs1[i],t); s=t; } if(t>=0) { if(opcode2[i]==7) emit_not(s,t); if(opcode2[i]==8) emit_swapb(s,t); if(opcode2[i]==9) emit_rorimm(s,16,t); if(opcode2[i]==11) emit_neg(s,t); } if(opcode2[i]==10) { // NEGC int sr=get_reg(i_regs->regmap,SR); if(i_regs->u&(1LL<=0); emit_negc(s,t,sr); } } } void imm8_assemble(int i,struct regstat *i_regs) { if(opcode[i]==0x7) { // ADD signed char s,t; t=get_reg(i_regs->regmap,rt1[i]); s=get_reg(i_regs->regmap,rs1[i]); //assert(t>=0); assert(s>=0); if(t>=0) { if(!((i_regs->isdoingcp>>t)&1)) { if(s<0) { if(i_regs->regmap_entry[t]!=rs1[i]) emit_loadreg(rs1[i],t); emit_addimm(t,imm[i],t); }else{ if(!((i_regs->wasdoingcp>>s)&1)) emit_addimm(s,imm[i],t); else emit_movimm(cpmap[i][s]+imm[i],t); } } } } else if(opcode[i]==0x8) { // CMP/EQ signed char s,sr,temp; s=get_reg(i_regs->regmap,rs1[i]); sr=get_reg(i_regs->regmap,SR); temp=get_reg(i_regs->regmap,-1); assert(s>=0); assert(sr>=0); // Liveness analysis? assert(temp>=0); emit_cmpeqimm(s,imm[i],sr,temp); } else if(opcode[i]==12) { if(opcode2[i]==8) { // TST signed char s,sr,temp; s=get_reg(i_regs->regmap,rs1[i]); sr=get_reg(i_regs->regmap,SR); temp=get_reg(i_regs->regmap,-1); assert(s>=0); assert(sr>=0); // Liveness analysis? assert(temp>=0); emit_sh2tstimm(s,imm[i],sr,temp); }else{ signed char s,t; t=get_reg(i_regs->regmap,rt1[i]); s=get_reg(i_regs->regmap,rs1[i]); if(t>=0 && !((i_regs->isdoingcp>>t)&1)) { if(opcode2[i]==9) //AND { if(s<0) { if(i_regs->regmap_entry[t]!=rs1[i]) emit_loadreg(rs1[i],t); emit_andimm(t,imm[i],t); }else{ if(!((i_regs->wasdoingcp>>s)&1)) emit_andimm(s,imm[i],t); else emit_movimm(cpmap[i][s]&imm[i],t); } } else if(opcode2[i]==10) //XOR { if(s<0) { if(i_regs->regmap_entry[t]!=rs1[i]) emit_loadreg(rs1[i],t); emit_xorimm(t,imm[i],t); }else{ if(!((i_regs->wasdoingcp>>s)&1)) emit_xorimm(s,imm[i],t); else emit_movimm(cpmap[i][s]^imm[i],t); } } else if(opcode2[i]==11) //OR { if(s<0) { if(i_regs->regmap_entry[t]!=rs1[i]) emit_loadreg(rs1[i],t); emit_orimm(t,imm[i],t); }else{ if(!((i_regs->wasdoingcp>>s)&1)) emit_orimm(s,imm[i],t); else emit_movimm(cpmap[i][s]|imm[i],t); } } } } } else { // opcode[i]==0xE signed char t; assert(opcode[i]==0xE); t=get_reg(i_regs->regmap,rt1[i]); //assert(t>=0); if(t>=0) { if(!((i_regs->isdoingcp>>t)&1)) emit_movimm(imm[i]<<16,t); } } } void shiftimm_assemble(int i,struct regstat *i_regs) { if(opcode[i]==4) // SHL/SHR { if(opcode2[i]<8) { signed char s,t,sr; s=get_reg(i_regs->regmap,rs1[i]); t=get_reg(i_regs->regmap,rt1[i]); sr=get_reg(i_regs->regmap,SR); assert(s==t); if(opcode2[i]==0) // SHLL/SHAL { if(i_regs->u&(1LL<u&(1LL<u&(1LL<u&(1LL<regmap,rs1[i]); t=get_reg(i_regs->regmap,rt1[i]); //assert(t>=0); if(t>=0){ if(opcode2[i]==8) // SHLL { if(opcode3[i]==0) emit_shlimm(s,2,t); if(opcode3[i]==1) emit_shlimm(s,8,t); if(opcode3[i]==2) emit_shlimm(s,16,t); } if(opcode2[i]==9) // SHLR { if(opcode3[i]==0) emit_shrimm(s,2,t); if(opcode3[i]==1) emit_shrimm(s,8,t); if(opcode3[i]==2) emit_shrimm(s,16,t); } } } } else if(opcode[i]==2) // XTRCT { signed char s,t,sr; s=get_reg(i_regs->regmap,rs1[i]); t=get_reg(i_regs->regmap,rt1[i]); assert(rs2[i]==rt1[i]); emit_shrdimm(t,s,16,t); } } void load_assemble(int i,struct regstat *i_regs) { int dummy; int s,o,t,addr,map=-1,cache=-1; int offset; int jaddr=0; int memtarget,c=0; int dualindex=(addrmode[i]==DUALIND||addrmode[i]==GBRIND); int size=(opcode[i]==4)?2:(opcode2[i]&3); unsigned int hr; u32 reglist=0; pointer constaddr; t=get_reg(i_regs->regmap,rt1[i]==TBIT?-1:rt1[i]); s=get_reg(i_regs->regmap,rs1[i]); o=get_reg(i_regs->regmap,rs2[i]); offset=imm[i]; for(hr=0;hrregmap[hr]>=0) reglist|=1<regmap[HOST_CCREG]==CCREG) reglist&=~(1<=0) { if(dualindex) c=(i_regs->wasdoingcp>>s)&(i_regs->wasdoingcp>>o)&1; else c=(i_regs->wasdoingcp>>s)&1; if(c) { if(dualindex) constaddr=cpmap[i][s]+cpmap[i][o]; else constaddr=cpmap[i][s]+offset; //if(dualindex) { // if((i_regs->isconst>>rs1[i])&(i_regs->isconst>>rs2[i])&1) // assert(constaddr==i_regs->constmap[rs1[i]]+i_regs->constmap[rs2[i]]); //}else // if((i_regs->isconst>>rs1[i])&1) // assert(constaddr==i_regs->constmap[rs1[i]]+offset); if(addrmode[i]==POSTINC) constaddr-=1<regmap,-1); if(!c) { if(dualindex) { c=(i_regs->isconst>>rs1[i])&(i_regs->isconst>>rs2[i])&1; } else { c=(i_regs->isconst>>rs1[i])&1; } if(c) { if(dualindex) constaddr=i_regs->constmap[rs1[i]]+i_regs->constmap[rs2[i]]; else constaddr=i_regs->constmap[rs1[i]]+offset; if(addrmode[i]==POSTINC) constaddr-=1<=0); // Even if the load is a NOP, we must check for I/O reglist&=~(1<regmap,MMREG); map=get_reg(i_regs->regmap,MOREG); if(map<0) map=get_alt_reg(i_regs->regmap,-1); assert(map>=0); assert(map!=s); assert(map!=t); reglist&=~(1<regmap,rt1[i])); // ignore loads to unneeded reg if(opcode[i]==12&&opcode2[i]==12) // TST.B dummy=i_regs->u&(1LL<regmap,rt1[i],ccadj[i],reglist); if(rt1[i]==TBIT&&!dummy) { // TST.B signed char sr; sr=get_reg(i_regs->regmap,SR); assert(sr>=0); // Liveness analysis? emit_sh2tstimm(t,imm[i],sr,t); } } if (size==1) { // MOV.W if(!c||memtarget) { if(!dummy) { #ifdef HOST_IMM_ADDR32 if(c) emit_movswl(constaddr,t); else #endif { int x=0; emit_movswl_indexed_map(0,addr,map,t); } } if(jaddr) add_stub(LOADW_STUB,jaddr,(int)out,i,addr,(int)i_regs,ccadj[i],reglist); } else inline_readstub(LOADW_STUB,i,constaddr,i_regs->regmap,rt1[i],ccadj[i],reglist); } if (size==2) { // MOV.L if(!c||memtarget) { if(!dummy) { #ifdef HOST_IMM_ADDR32 if(c) emit_readword(constaddr,t); else #endif emit_readword_indexed_map(0,addr,map,t); emit_rorimm(t,16,t); } if(jaddr) add_stub(LOADL_STUB,jaddr,(int)out,i,addr,(int)i_regs,ccadj[i],reglist); } else inline_readstub(LOADL_STUB,i,constaddr,i_regs->regmap,rt1[i],ccadj[i],reglist); } if(addrmode[i]==POSTINC) { if(!((i_regs->wasdoingcp>>s)&1)) { if(!(i_regs->u&(1LL<regmap,CCREG)<0) emit_loadreg(CCREG,HOST_CCREG); emit_add(HOST_CCREG,ECX,HOST_CCREG); emit_addimm(HOST_CCREG,2*ccadj[i],HOST_CCREG); emit_writeword(HOST_CCREG,(int)&Count); #endif #ifdef __arm__ if(get_reg(i_regs->regmap,CCREG)<0) emit_loadreg(CCREG,0); else emit_mov(HOST_CCREG,0); emit_add(0,ECX,0); emit_addimm(0,2*ccadj[i],0); emit_writeword(0,(int)&Count); #endif emit_call((int)memdebug); //emit_popa(); restore_regs(0x100f); }/**/ } void store_assemble(int i,struct regstat *i_regs) { int s,t,o,map=-1,cache=-1; int addr,temp; int offset; int jaddr=0,jaddr2,type; int memtarget,c=0,constaddr; int dualindex=(addrmode[i]==DUALIND); int size=(opcode[i]==4)?2:(opcode2[i]&3); int agr=AGEN1+(i&1); unsigned int hr; u32 reglist=0; t=get_reg(i_regs->regmap,rs1[i]); s=get_reg(i_regs->regmap,rs2[i]); o=get_reg(i_regs->regmap,rs3[i]); temp=get_reg(i_regs->regmap,agr); if(temp<0) temp=get_reg(i_regs->regmap,-1); offset=imm[i]; for(hr=0;hrregmap[hr]>=0) reglist|=1<regmap[HOST_CCREG]==CCREG) reglist&=~(1<=0) { if(dualindex) c=(i_regs->wasdoingcp>>s)&(i_regs->wasdoingcp>>o)&1; else c=(i_regs->wasdoingcp>>s)&1; if(c) { if(dualindex) constaddr=cpmap[i][s]+cpmap[i][o]; else constaddr=cpmap[i][s]+offset; } //printf("constaddr=%x offset=%x\n",constaddr,offset); memtarget=can_direct_write(constaddr); } if(!c) { if(dualindex) { c=(i_regs->isconst>>rs2[i])&(i_regs->isconst>>rs3[i])&1; } else { c=(i_regs->isconst>>rs2[i])&1; } if(c) { if(dualindex) constaddr=i_regs->constmap[rs2[i]]+i_regs->constmap[rs3[i]]; else constaddr=i_regs->constmap[rs2[i]]+offset; //printf("constaddr=%x offset=%x\n",constaddr,offset); memtarget=can_direct_write(constaddr); // In this case, the constant is not already loaded into a register if(can_direct_write(constaddr)) { emit_movimm(constaddr^(!size),temp); map=get_reg(i_regs->regmap,MOREG); if(map<0) map=get_alt_reg(i_regs->regmap,-1); generate_map_const(constaddr,map); } } } assert(t>=0); assert(temp>=0); if(offset||dualindex||s<0||c) addr=temp; else addr=s; //printf("store_assemble: c=%d\n",c); if(addrmode[i]==PREDEC&&!c&&rt1[i]==rs1[i]) addr=temp; // Old value is written, so decremented address is in a temporary register if(addrmode[i]==REGIND&&!c&&rs1[i]==rs2[i]) {// Swapped value is written, so unswapped value must be used as the address emit_mov(addr,temp);addr=temp; } if(!c||memtarget) { int x=0; if (!c&&size==0) x=1; // MOV.B cache=get_reg(i_regs->regmap,MMREG); map=get_reg(i_regs->regmap,MOREG); if(map<0) map=get_alt_reg(i_regs->regmap,-1); assert(map>=0); assert(map!=temp); assert(map!=s); reglist&=~(1<u&(1LL<regmap,rs1[i],ccadj[i],reglist); } if(addrmode[i]==PREDEC) { assert(s>=0); if(!((i_regs->wasdoingcp>>s)&1)&&rt1[i]==rs1[i]) emit_addimm(s,-(1<regmap,CCREG)<0) emit_loadreg(CCREG,HOST_CCREG); emit_add(HOST_CCREG,ECX,HOST_CCREG); emit_addimm(HOST_CCREG,2*ccadj[i],HOST_CCREG); emit_writeword(HOST_CCREG,(int)&Count); #endif #ifdef __arm__ if(get_reg(i_regs->regmap,CCREG)<0) emit_loadreg(CCREG,0); else emit_mov(HOST_CCREG,0); emit_add(0,ECX,0); emit_addimm(0,2*ccadj[i],0); emit_writeword(0,(int)&Count); #endif emit_call((int)memdebug); //emit_popa(); restore_regs(0x100f); }/**/ } void rmw_assemble(int i,struct regstat *i_regs) { int s,o,t,addr,map=-1,cache=-1; int jaddr=0; int type; int memtarget,c=0,constaddr; int dualindex=(addrmode[i]==GBRIND); unsigned int hr; u32 reglist=0; t=get_reg(i_regs->regmap,-1); s=get_reg(i_regs->regmap,rs1[i]); o=get_reg(i_regs->regmap,rs2[i]); for(hr=0;hrregmap[hr]>=0) reglist|=1<=0) { if(dualindex) c=(i_regs->wasdoingcp>>s)&(i_regs->wasdoingcp>>o)&1; else c=(i_regs->wasdoingcp>>s)&1; if(c) { if(dualindex) constaddr=cpmap[i][s]+cpmap[i][o]; else constaddr=cpmap[i][s]; } //printf("constaddr=%x offset=%x\n",constaddr,offset); memtarget=1; // FIXME } if(dualindex||s<0||c) addr=t; else addr=s; assert(t>=0); reglist&=~(1<regmap,MOREG); cache=get_reg(i_regs->regmap,MMREG); assert(map>=0); reglist&=~(1<regmap,SR); assert(sr>=0); // Liveness analysis? assert(rt1[i]==TBIT); if(sr>=0&&!(i_regs->u&(1LL<regmap,rt1[i]); offset=imm[i]; for(hr=0;hrregmap[hr]>=0) reglist|=1<regmap[HOST_CCREG]==CCREG) reglist&=~(1<=0) { if(!((i_regs->isdoingcp>>t)&1)) { int jaddr=0; // This is to handle the exceptional case where we can not do constant propagation assert(opcode[i]!=12); // MOVA should always be able to do constant propagation constaddr=((start+i*2+4)&~3)+imm[i]; if(opcode[i]==9) constaddr=(start+i*2+4)+imm[i]; // MOV.W assem_debug("Can't do constant propagation, doing PC-relatve load\n"); //int map=get_reg(i_regs->regmap,MOREG); //int cache=get_reg(i_regs->regmap,MMREG); //assert(map>=0); reglist&=~(1<regmap,rs1[i]); int s2=get_reg(i_regs->regmap,rs2[i]); int t=get_reg(i_regs->regmap,MACL); if(t>=0) emit_multiply(s1,s2,t); } if(opcode2[i]==8) // CLRMAC { int t1=get_reg(i_regs->regmap,rt1[i]); int t2=get_reg(i_regs->regmap,rt2[i]); if(!(i_regs->u&(1LL<u&(1LL<regmap,SR); emit_andimm(sr,0xfe,sr); } } if(opcode[i]==2) { if(opcode2[i]==7) // DIV0S { int s1=get_reg(i_regs->regmap,rs1[i]); int s2=get_reg(i_regs->regmap,rs2[i]); int sr=get_reg(i_regs->regmap,SR); int temp=get_reg(i_regs->regmap,-1); assert(s1>=0); assert(s2>=0); assert(sr>=0); emit_div0s(s1,s2,sr,temp); } if(opcode2[i]==14||opcode2[i]==15) // MULU.W / MULS.W { int s1=get_reg(i_regs->regmap,rs1[i]); int s2=get_reg(i_regs->regmap,rs2[i]); int t=get_reg(i_regs->regmap,MACL); #ifdef HOST_TEMPREG int temp=HOST_TEMPREG; #else int temp=get_reg(i_regs->regmap,-1); #endif if(t>=0) { assert(temp>=0); if(opcode2[i]==14) { // MULU.W emit_movzwl_reg(s1,t); emit_movzwl_reg(s2,temp); }else{ // MULS.W emit_movswl_reg(s1,t); emit_movswl_reg(s2,temp); } emit_multiply(t,temp,t); } /* DEBUG emit_pusha(); emit_pushreg(t); emit_pushreg(t); emit_pushreg(s2); emit_pushreg(s1); emit_call((int)debug_multiplication); emit_addimm(ESP,16,ESP); emit_popa();*/ } } if(opcode[i]==3) { int s1=get_reg(i_regs->regmap,rs1[i]); int s2=get_reg(i_regs->regmap,rs2[i]); int th=get_reg(i_regs->regmap,MACH); int tl=get_reg(i_regs->regmap,MACL); if(th>=0) { // DMULU.L / DMULS.L #if defined(__i386__) || defined(__x86_64__) assert(tl==EAX); assert(th==EDX); assert(s1!=EAX); // This would work only if s1 is clean or dead if(s1!=EAX) emit_mov(s1,EAX); if(opcode2[i]==5) emit_mul(s2); // DMULU.L if(opcode2[i]==13) emit_imul(s2); // DMULS.L #else if(opcode2[i]==5) emit_umull(s1,s2,th,tl); // DMULU.L if(opcode2[i]==13) emit_smull(s1,s2,th,tl); // DMULS.L #endif }else if(tl>=0) { // MACH is unneeded, 32-bit result only emit_multiply(s1,s2,tl); } /* DEBUG emit_pusha(); emit_pushreg(tl); emit_pushreg(th); emit_pushreg(s2); emit_pushreg(s1); emit_call((int)debug_multiplication); emit_addimm(ESP,16,ESP); emit_popa();*/ } } #endif void mov_assemble(int i,struct regstat *i_regs) { signed char s,t; t=get_reg(i_regs->regmap,rt1[i]); //assert(t>=0); if(t>=0) { s=get_reg(i_regs->regmap,rs1[i]); if(s>=0) {if(s!=t) emit_mov(s,t);} else emit_loadreg(rs1[i],t); } } void ext_assemble(int i,struct regstat *i_regs) { signed char s,t; t=get_reg(i_regs->regmap,rt1[i]); //assert(t>=0); if(t>=0) { s=get_reg(i_regs->regmap,rs1[i]); if(s>=0) { if(opcode2[i]==12) emit_movzbl_reg(s,t); if(opcode2[i]==13) emit_movzwl_reg(s,t); if(opcode2[i]==14) emit_movsbl_reg(s,t); if(opcode2[i]==15) emit_movswl_reg(s,t); } else { emit_loadreg(rs1[i],t); // Fix - do byte/halfword loads? if(opcode2[i]==12) emit_movzbl_reg(t,t); if(opcode2[i]==13) emit_movzwl_reg(t,t); if(opcode2[i]==14) emit_movsbl_reg(t,t); if(opcode2[i]==15) emit_movswl_reg(t,t); } } } void flags_assemble(int i,struct regstat *i_regs) { signed char sr,t; sr=get_reg(i_regs->regmap,SR); if(opcode2[i]==8) { // CLRT/SETT if(opcode3[i]==0) emit_andimm(sr,~1,sr); if(opcode3[i]==1) emit_orimm(sr,1,sr); }else if(opcode2[i]==9) { // MOVT t=get_reg(i_regs->regmap,rt1[i]); if(t>=0) emit_andimm(sr,1,t); } } void complex_assemble(int i,struct regstat *i_regs) { if(opcode[i]==3&&opcode2[i]==4) { // DIV1 emit_call((pointer)div1); } if(opcode[i]==0&&opcode2[i]==15) { // MAC.L load_regs(i_regs->regmap_entry,i_regs->regmap,MACL,MACH,MACH); // If both registers are the same, the register is incremented twice. // Pre-increment one of the function arguments. #if defined(__i386__) || defined(__x86_64__) if(rs1[i]==rs2[i]) {emit_mov(EDI,EBP);emit_addimm(EDI,4,EDI);} #else #if defined(__arm__) if(rs1[i]==rs2[i]) {emit_mov(6,5);emit_addimm(6,4,6);} #else // FIXME assert(0); #endif #endif /* DEBUG //if(i_regmap[HOST_CCREG]!=CCREG) { emit_loadreg(CCREG,ECX); emit_addimm(ECX,CLOCK_DIVIDER*(ccadj[i]),ECX); output_byte(0x03); output_modrm(1,4,ECX); output_sib(0,4,4); output_byte(4); emit_writeword(ECX,slave?(int)&SSH2->cycles:(int)&MSH2->cycles); // }*/ emit_call((pointer)macl); } if(opcode[i]==4&&opcode2[i]==15) { // MAC.W load_regs(i_regs->regmap_entry,i_regs->regmap,MACL,MACH,MACH); // If both registers are the same, the register is incremented twice. // Pre-increment one of the function arguments. #if defined(__i386__) || defined(__x86_64__) if(rs1[i]==rs2[i]) {emit_mov(EDI,EBP);emit_addimm(EDI,2,EDI);} #else #if defined(__arm__) if(rs1[i]==rs2[i]) {emit_mov(6,5);emit_addimm(6,2,6);} #else // FIXME assert(0); #endif #endif /* DEBUG //if(i_regmap[HOST_CCREG]!=CCREG) { emit_loadreg(CCREG,ECX); emit_addimm(ECX,CLOCK_DIVIDER*(ccadj[i]),ECX); output_byte(0x03); output_modrm(1,4,ECX); output_sib(0,4,4); output_byte(4); emit_writeword(ECX,slave?(int)&SSH2->cycles:(int)&MSH2->cycles); // }*/ emit_call((pointer)macw); } } void ds_assemble(int i,struct regstat *i_regs) { is_delayslot=1; switch(itype[i]) { case ALU: alu_assemble(i,i_regs);break; case IMM8: imm8_assemble(i,i_regs);break; case SHIFTIMM: shiftimm_assemble(i,i_regs);break; case LOAD: load_assemble(i,i_regs);break; case STORE: store_assemble(i,i_regs);break; case RMW: rmw_assemble(i,i_regs);break; case PCREL: pcrel_assemble(i,i_regs);break; case MULTDIV: multdiv_assemble(i,i_regs);break; case MOV: mov_assemble(i,i_regs);break; case EXT: ext_assemble(i,i_regs);break; case FLAGS: flags_assemble(i,i_regs);break; case COMPLEX: complex_assemble(i,i_regs);break; case SYSTEM: case SYSCALL: case UJUMP: case RJUMP: case CJUMP: case SJUMP: printf("Jump in the delay slot. This is probably a bug.\n"); } is_delayslot=0; } // Is the branch target a valid internal jump? int internal_branch(int addr) { if(addr&1) return 0; // Indirect (register) jump if(addr>=start && addr=0) { if((dirty>>hr)&1) { if(!((u>>pre[hr])&1)) { int nr; if((nr=get_reg(entry,pre[hr]))<0) { emit_storereg(pre[hr],hr); }else{ // Register move would overwrite another register, so write back if(pre[nr]>=0) if(get_reg(entry,pre[nr])>=0) emit_storereg(pre[hr],hr); } } } } } } } // Move from one register to another (no writeback) for(hr=0;hr=0&&(pre[hr]&63)=0) { if(pre[nr]<0||get_reg(entry,pre[nr])<0) { emit_mov(hr,nr); } } } } } } // Reload registers that couldn't be directly moved for(hr=0;hr=0&&(pre[hr]&63)=0) { if(pre[nr]>=0) { if(get_reg(entry,pre[nr])>=0) { emit_loadreg(pre[hr],nr); } } } } } } } } #endif // Load the specified registers // This only loads the registers given as arguments because // we don't want to load things that will be overwritten void load_regs(signed char entry[],signed char regmap[],int rs1,int rs2,int rs3) { int hr; if(rs1==TBIT) rs1=SR; if(rs2==TBIT) rs2=SR; if(rs3==TBIT) rs3=SR; // Load 32-bit regs for(hr=0;hr=0) { if(entry[hr]!=regmap[hr]) { if(regmap[hr]==rs1||regmap[hr]==rs2||regmap[hr]==rs3) { emit_loadreg(regmap[hr],hr); } } } } } // Load registers prior to the start of a loop // so that they are not loaded within the loop static void loop_preload(signed char pre[],signed char entry[]) { int hr; for(hr=0;hr=0) { if(get_reg(pre,entry[hr])<0) { assem_debug("loop preload:\n"); //printf("loop preload: %d\n",hr); if(entry[hr]regmap,rt1[i]); if(ra<0||rt1[i]==TBIT) ra=get_reg(i_regs->regmap,-1); assert(ra>=0); } if(itype[i]==STORE||itype[i]==RMW) { ra=get_reg(i_regs->regmap,agr); if(ra<0) ra=get_reg(i_regs->regmap,-1); assert(ra>=0); } if(itype[i]==STORE) { rs=get_reg(i_regs->regmap,rs2[i]); ri=get_reg(i_regs->regmap,rs3[i]); }else{ rs=get_reg(i_regs->regmap,rs1[i]); ri=get_reg(i_regs->regmap,rs2[i]); } rm=get_reg(i_regs->regmap,MOREG); if(rm<0) rm=get_alt_reg(i_regs->regmap,-1); if(ra>=0) { int offset=imm[i]; int c; u32 constaddr; if(addrmode[i]==DUALIND||addrmode[i]==GBRIND) { c=(i_regs->wasdoingcp>>rs)&(i_regs->wasdoingcp>>ri)&1; constaddr=cpmap[i][rs]+cpmap[i][ri]; }else{ c=(i_regs->wasdoingcp>>rs)&1; constaddr=cpmap[i][rs]+offset; if(addrmode[i]==POSTINC) constaddr-=1<<((opcode[i]==4)?2:(opcode2[i]&3)); } if(addrmode[i]==PREDEC&&!c) { if(rt1[i]!=rs1[i]) emit_addimm(rs,-(1<<((opcode[i]==4)?2:(opcode2[i]&3))),rs); else offset=-(1<<((opcode[i]==4)?2:(opcode2[i]&3))); } if(rs<0) { if(itype[i]==LOAD) { if(!entry||entry[ra]!=rs1[i]) emit_loadreg(rs1[i],ra); } if(itype[i]==STORE) { if(!entry||entry[ra]!=rs2[i]) emit_loadreg(rs2[i],ra); } //if(!entry||entry[ra]!=rs1[i]) // printf("poor load scheduling!\n"); } else if(c) { // Stores to memory go thru the mapper to detect self-modifying // code, loads don't. if(rm>=0) { if(!entry||entry[rm]!=mgr) { if(itype[i]==STORE) { if(can_direct_write(constaddr)) generate_map_const(constaddr,rm); } if(itype[i]==RMW) { generate_map_const(constaddr,rm); } } } if((opcode2[i]&3)==0||itype[i]==RMW) constaddr^=1; // byteswap for little-endian if(rs1[i]!=rt1[i]||itype[i]!=LOAD||addrmode[i]==DUALIND||addrmode[i]==GBRIND) { if(!entry||entry[ra]!=agr) { #ifdef HOST_IMM_ADDR32 if(itype[i]==RMW || (itype[i]==STORE && can_direct_write(constaddr))) #endif { if(itype[i]==LOAD&&can_direct_read(constaddr)) emit_movimm(map_address(constaddr),ra); else emit_movimm(constaddr,ra); } } // else did it in the previous cycle } // else load_consts already did it } if(!c) { if(rs>=0) { if(addrmode[i]==DUALIND||addrmode[i]==GBRIND) emit_add(rs,ri,ra); else if(offset) emit_addimm(rs,offset,ra); }else{ if(addrmode[i]==DUALIND||addrmode[i]==GBRIND) emit_add(ra,ri,ra); else if(offset) emit_addimm(ra,offset,ra); } } } } // Preload constants for next instruction if(itype[i+1]==LOAD||itype[i+1]==STORE||itype[i+1]==RMW) { int agr,ra,rm; #ifndef HOST_IMM_ADDR32 // Mapper entry agr=MGEN1+((i+1)&1); rm=get_reg(i_regs->regmap,agr); if(rm>=0) { int rs,ri; if(itype[i+1]==STORE) { rs=get_reg(regs[i+1].regmap,rs2[i+1]); ri=get_reg(regs[i+1].regmap,rs3[i+1]); }else{ rs=get_reg(regs[i+1].regmap,rs1[i+1]); ri=get_reg(regs[i+1].regmap,rs2[i+1]); } //int rm=get_reg(i_regs->regmap,MOREG); int offset=imm[i+1]; int c; u32 constaddr; if(addrmode[i+1]==DUALIND||addrmode[i+1]==GBRIND) { c=(regs[i+1].wasdoingcp>>rs)&(regs[i+1].wasdoingcp>>ri)&1; constaddr=cpmap[i+1][rs]+cpmap[i+1][ri]; }else{ c=(regs[i+1].wasdoingcp>>rs)&1; constaddr=cpmap[i+1][rs]+offset; if(addrmode[i+1]==POSTINC) constaddr-=1<<((opcode[i+1]==4)?2:(opcode2[i+1]&3)); } if((opcode2[i+1]&3)==0||itype[i+1]==RMW) constaddr^=1; // byteswap for little-endian if(c) { // Stores to memory go thru the mapper to detect self-modifying // code, loads don't. if(itype[i+1]==STORE) { if(can_direct_write(constaddr)) generate_map_const(constaddr,rm); } if(itype[i+1]==RMW) { generate_map_const(constaddr,rm); } } } #endif // Actual address agr=AGEN1+((i+1)&1); ra=get_reg(i_regs->regmap,agr); if(ra>=0) { int c; int offset; int rs,ri; u32 constaddr; if(itype[i+1]==STORE) { rs=get_reg(regs[i+1].regmap,rs2[i+1]); ri=get_reg(regs[i+1].regmap,rs3[i+1]); }else{ rs=get_reg(regs[i+1].regmap,rs1[i+1]); ri=get_reg(regs[i+1].regmap,rs2[i+1]); } offset=imm[i+1]; if(addrmode[i+1]==DUALIND||addrmode[i+1]==GBRIND) { c=(regs[i+1].wasdoingcp>>rs)&(regs[i+1].wasdoingcp>>ri)&1; constaddr=cpmap[i+1][rs]+cpmap[i+1][ri]; }else{ c=(regs[i+1].wasdoingcp>>rs)&1; constaddr=cpmap[i+1][rs]+offset; if(addrmode[i+1]==POSTINC) constaddr-=1<<((opcode[i+1]==4)?2:(opcode2[i+1]&3)); } if((opcode2[i+1]&3)==0||itype[i+1]==RMW) constaddr^=1; // byteswap for little-endian if(c&&(rs1[i+1]!=rt1[i+1]||itype[i+1]!=LOAD||addrmode[i+1]==DUALIND||addrmode[i+1]==GBRIND)) { //if(c&&(rs1[i+1]!=rt1[i+1]||itype[i+1]!=LOAD)) { #ifdef HOST_IMM_ADDR32 if(itype[i+1]==RMW || (itype[i+1]==STORE && can_direct_write(constaddr))) #endif { if(itype[i+1]==LOAD&&can_direct_read(constaddr)) emit_movimm(map_address(constaddr),ra); else emit_movimm(constaddr,ra); } } } } } int get_final_value(int hr, int i, int *value) { int reg=regs[i].regmap[hr]; while(i>hr)&1)) break; if(bt[i+1]) break; i++; } if(i>hr)&1)) { if(addrmode[i+2]==DUALIND||addrmode[i+2]==GBRIND) { *value=cpmap[i][hr]; return 1; } // Don't load address if can_direct_read and HOST_IMM_ADDR32 #ifdef HOST_IMM_ADDR32 if(can_direct_read(cpmap[i][hr]+imm[i+2])) return 0; #endif // Precompute load address *value=cpmap[i][hr]+imm[i+2]; if(can_direct_read(*value)) *value=map_address(*value); if((opcode2[i+2]&3)==0) *value^=1; // byteswap for little-endian return 1; } } if(itype[i+1]==LOAD&&rs1[i+1]==reg&&rt1[i+1]==reg) { if(addrmode[i+1]==DUALIND||addrmode[i+1]==GBRIND) { *value=cpmap[i][hr]; return 1; } // Don't load address if can_direct_read and HOST_IMM_ADDR32 #ifdef HOST_IMM_ADDR32 if(can_direct_read(cpmap[i][hr]+imm[i+1])) return 0; #endif // Precompute load address *value=cpmap[i][hr]+imm[i+1]; if(can_direct_read(*value)) *value=map_address(*value); if((opcode2[i+1]&3)==0) *value^=1; // byteswap for little-endian //printf("c=%x imm=%x\n",(int)cpmap[i][hr],imm[i+1]); return 1; } } } *value=cpmap[i][hr]; //printf("c=%x\n",(int)cpmap[i][hr]); if(i==slen-1) return 1; return !((unneeded_reg[i+1]>>reg)&1); } // Load registers with known constants void load_consts(signed char pre[],signed char regmap[],int i) { int hr; // Load 32-bit regs for(hr=0;hr=0) { if(i==0||!((regs[i-1].isdoingcp>>hr)&1)||pre[hr]!=regmap[hr]||bt[i]) { if(((regs[i].isdoingcp>>hr)&1)&®map[hr]<64&®map[hr]>=0) { int value; if(get_final_value(hr,i,&value)) { emit_movimm(value,hr); } } } } } } void load_all_consts(signed char regmap[],u32 dirty,int i) { int hr; // Load 32-bit regs for(hr=0;hr=0&&((dirty>>hr)&1)) { if(((regs[i].isdoingcp>>hr)&1)&®map[hr]<64&®map[hr]>=0) { int value=cpmap[i][hr]; emit_movimm(value,hr); } } } } // Write out all dirty registers (except cycle count) void wb_dirtys(signed char i_regmap[],u32 i_dirty) { int hr; for(hr=0;hr=0) { if(i_regmap[hr]!=CCREG) { if((i_dirty>>hr)&1) { emit_storereg(i_regmap[hr],hr); } } } } } } // Write out dirty registers that we need to reload (pair with load_needed_regs) // This writes the registers not written by store_regs_bt void wb_needed_dirtys(signed char i_regmap[],u32 i_dirty,int addr) { int hr; int t=(addr-start)>>1; for(hr=0;hr=0) { if(i_regmap[hr]!=CCREG) { if((i_regmap[hr]==regs[t].regmap_entry[hr] && ((regs[t].dirty>>hr)&1)) || i_regmap[hr]==SR || i_regmap[hr]==15) { if((i_dirty>>hr)&1) { emit_storereg(i_regmap[hr],hr); } } } } } } } // Load all registers (except cycle count) void load_all_regs(signed char i_regmap[]) { int hr; for(hr=0;hr=0 && i_regmap[hr]=0) { if(i_regmap[hr]>=0 && i_regmap[hr]=0&®s[t].regmap_entry[hr]>1; int hr; for(hr=0;hr=0 && i_regmap[hr]!=CCREG) { if(i_regmap[hr]!=regs[t].regmap_entry[hr] || !((regs[t].dirty>>hr)&1) ) { if((i_dirty>>hr)&1) { if(!((unneeded_reg[t]>>i_regmap[hr])&1)) { emit_storereg(i_regmap[hr],hr); } } } } } } } else { // Branch out of this block, write out all dirty regs wb_dirtys(i_regmap,i_dirty); } } // Load all needed registers for branch target void load_regs_bt(signed char i_regmap[],u32 i_dirty,int addr) { //if(addr>=start && addr<(start+slen*4)) if(internal_branch(addr)) { int t=(addr-start)>>1; int hr; // Store the cycle count before loading something else if(i_regmap[HOST_CCREG]!=CCREG) { assert(i_regmap[HOST_CCREG]==-1); } if(regs[t].regmap_entry[HOST_CCREG]!=CCREG) { emit_storereg(CCREG,HOST_CCREG); } // Load 32-bit regs for(hr=0;hr=0&®s[t].regmap_entry[hr]=start && addr>1; int hr; if(regs[t].regmap_entry[HOST_CCREG]!=CCREG) return 0; for(hr=0;hr=0&®s[t].regmap_entry[hr]>hr)&1) { if(!((unneeded_reg[t]>>i_regmap[hr])&1)) return 0; } } else // Same register but is it dirty? if(i_regmap[hr]>=0) { if(!((regs[t].dirty>>hr)&1)) { if((i_dirty>>hr)&1) { if(!((unneeded_reg[t]>>i_regmap[hr])&1)) { //printf("%x: dirty no match\n",addr); return 0; } } } } } } // Delay slots require additional processing, so do not match if(is_ds[t]) return 0; } else { int hr; for(hr=0;hr=0) { if(hr!=HOST_CCREG||i_regmap[hr]!=CCREG) { if((i_dirty>>hr)&1) { return 0; } } } } } } return 1; } // Used when a branch jumps into the delay slot of another branch void ds_assemble_entry(int i) { int t=(ba[i]-start)>>1; if(!instr_addr[t]) instr_addr[t]=(pointer)out; assem_debug("Assemble delay slot at %x\n",ba[i]); assem_debug("<->\n"); if(regs[t].regmap_entry[HOST_CCREG]==CCREG&®s[t].regmap[HOST_CCREG]!=CCREG) wb_register(CCREG,regs[t].regmap_entry,regs[t].wasdirty); load_regs(regs[t].regmap_entry,regs[t].regmap,rs1[t],rs2[t],rs3[t]); address_generation(t,®s[t],regs[t].regmap_entry); if(itype[t]==LOAD||itype[t]==STORE) load_regs(regs[t].regmap_entry,regs[t].regmap,MMREG,MMREG,MMREG); is_delayslot=0; switch(itype[t]) { case ALU: alu_assemble(t,®s[t]);break; case IMM8: imm8_assemble(t,®s[t]);break; case SHIFTIMM: shiftimm_assemble(t,®s[t]);break; case LOAD: load_assemble(t,®s[t]);break; case STORE: store_assemble(t,®s[t]);break; case RMW: rmw_assemble(t,®s[t]);break; case PCREL: pcrel_assemble(t,®s[t]);break; case MULTDIV: multdiv_assemble(t,®s[t]);break; case MOV: mov_assemble(t,®s[t]);break; case EXT: ext_assemble(i,®s[t]);break; case FLAGS: flags_assemble(i,®s[t]);break; case COMPLEX: complex_assemble(i,®s[t]);break; case SYSTEM: case SYSCALL: case UJUMP: case RJUMP: case CJUMP: case SJUMP: printf("Jump in the delay slot. This is probably a bug.\n"); } store_regs_bt(regs[t].regmap,regs[t].dirty,ba[i]+2); load_regs_bt(regs[t].regmap,regs[t].dirty,ba[i]+2); if(internal_branch(ba[i]+2)) assem_debug("branch: internal\n"); else assem_debug("branch: external\n"); assert(internal_branch(ba[i]+2)); add_to_linker((int)out,ba[i]+2,internal_branch(ba[i]+2)); emit_jmp(0); } void do_cc(int i,signed char i_regmap[],int *adj,int addr,int taken,int invert) { int count; int jaddr; int idle=0; if(itype[i]==RJUMP) { *adj=0; } //if(ba[i]>=start && ba[i]<(start+slen*4)) if(internal_branch(ba[i])) { int t=(ba[i]-start)>>1; if(is_ds[t]) *adj=ccadj[t+1]-cycles[t]; // Branch into delay slot adds an extra cycle else *adj=ccadj[t]; } else { *adj=0; } if(itype[i]==CJUMP) *adj-=2+cycles[i]; // Two extra cycles for taken BT/BF if(itype[i]==SJUMP) *adj-=1+cycles[i]+cycles[i+1]; // One extra cycle for taken BT/BF with delay slot count=ccadj[i]+((taken==NODS)?0:cycles[i]+cycles[i+1]); if(taken==TAKEN && i==(ba[i]-start)>>1 && source[i+1]==0) { // Idle loop // FIXME //if(count&1) emit_addimm_and_set_flags(2*(count+2),HOST_CCREG); idle=(int)out; //emit_subfrommem(&idlecount,HOST_CCREG); // Count idle cycles emit_andimm(HOST_CCREG,3,HOST_CCREG); jaddr=(int)out; emit_jmp(0); } else if(*adj==0||invert) { emit_addimm_and_set_flags(CLOCK_DIVIDER*count,HOST_CCREG); jaddr=(int)out; emit_jns(0); } else { emit_cmpimm(HOST_CCREG,-CLOCK_DIVIDER*count); jaddr=(int)out; emit_jns(0); } add_stub(CC_STUB,jaddr,idle?idle:(int)out,(*adj==0||invert||idle)?0:count,i,addr,taken,0); } void do_ccstub(int n) { int i; literal_pool(256); assem_debug("do_ccstub %x\n",start+stubs[n][4]*2); set_jump_target(stubs[n][1],(pointer)out); i=stubs[n][4]; if(stubs[n][6]==NODS) { if(itype[i+1]==LOAD&&rs1[i+1]==rt1[i+1]&&addrmode[i+1]!=DUALIND&&addrmode[i+1]!=GBRIND) { int hr=get_reg(regs[i].regmap,rs1[i+1]); if(hr>=0&&((regs[i].wasdoingcp>>hr)&1)) { emit_movimm(cpmap[i][hr],hr); } } wb_dirtys(regs[i].regmap_entry,regs[i].dirty); } else if(stubs[n][6]!=TAKEN) { wb_dirtys(branch_regs[i].regmap,branch_regs[i].dirty); } else { if(internal_branch(ba[i])) wb_needed_dirtys(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); } if(stubs[n][5]!=-1) { // Save PC as return address emit_movimm(stubs[n][5],0); emit_writeword(0,slave?(int)&slave_pc:(int)&master_pc); } else { // Return address is branch target if(itype[i]==RJUMP) { int r=get_reg(branch_regs[i].regmap,rs1[i]); if(rs1[i]==rt1[i+1]||rs1[i]==rt2[i+1]) { r=get_reg(branch_regs[i].regmap,RTEMP); } else if(opcode[i]==0&&opcode2[i]==3) { // BSRF/BRAF r=get_reg(branch_regs[i].regmap,RTEMP); } else if(opcode[i]==0&&opcode2[i]==11&&opcode3[i]==2) { // RTE r=get_reg(branch_regs[i].regmap,RTEMP); } emit_writeword(r,slave?(int)&slave_pc:(int)&master_pc); } else {printf("Unknown branch type in do_ccstub\n");exit(1);} } // Update cycle count if(stubs[n][6]==NODS) assert(regs[i].regmap[HOST_CCREG]==CCREG||regs[i].regmap[HOST_CCREG]==-1); else assert(branch_regs[i].regmap[HOST_CCREG]==CCREG||branch_regs[i].regmap[HOST_CCREG]==-1); if(stubs[n][3]) emit_addimm(HOST_CCREG,CLOCK_DIVIDER*stubs[n][3],HOST_CCREG); if(slave) { emit_load_return_address(SLAVERA_REG); emit_jmp((pointer)cc_interrupt); } else { emit_call((pointer)slave_entry); } if(stubs[n][3]&&stubs[n][6]!=NODS) emit_addimm(HOST_CCREG,-CLOCK_DIVIDER*stubs[n][3],HOST_CCREG); if(stubs[n][6]==TAKEN) { if(internal_branch(ba[i])) load_needed_regs(branch_regs[i].regmap,regs[(ba[i]-start)>>1].regmap_entry); else if(itype[i]==RJUMP) { if(get_reg(branch_regs[i].regmap,RTEMP)>=0) emit_readword(slave?(int)&slave_pc:(int)&master_pc,get_reg(branch_regs[i].regmap,RTEMP)); else emit_loadreg(rs1[i],get_reg(branch_regs[i].regmap,rs1[i])); } }else if(stubs[n][6]==NOTTAKEN) { if(i=0&&((regs[i].wasdoingcp>>hr)&1)) { #ifdef HOST_IMM_ADDR32 if(!can_direct_read(cpmap[i][hr]+imm[i+1])) #endif { int value=cpmap[i][hr]+imm[i+1]; if(can_direct_read(value)) value=map_address(value); if((opcode2[i+1]&3)==0) value^=1; // byteswap for little-endian emit_movimm(value,hr); } } } ccstub_return[i]=0; } } else load_all_regs(branch_regs[i].regmap); } emit_jmp(stubs[n][2]); // return address } add_to_linker(int addr,int target,int ext) { link_addr[linkcount][0]=addr; link_addr[linkcount][1]=target|slave; link_addr[linkcount][2]=ext; linkcount++; } void ujump_assemble(int i,struct regstat *i_regs) { u64 bc_unneeded; int cc,adj; signed char *i_regmap=i_regs->regmap; if(i==(ba[i]-start)>>1) assem_debug("idle loop\n"); address_generation(i+1,i_regs,regs[i].regmap_entry); #ifdef REG_PREFETCH int temp=get_reg(branch_regs[i].regmap,PTEMP); if(rt1[i]==PR&&temp>=0) { int return_address=start+i*2+4; if(get_reg(branch_regs[i].regmap,PR)>0) if(i_regmap[temp]==PTEMP) emit_movimm((int)hash_table[((return_address>>16)^return_address)&0xFFFF],temp); } #endif if(rt1[i]==PR) { if(rt1[i+1]==PR||rt2[i+1]==PR) { // Delay slot abuse, set PR before executing delay slot int rt; unsigned int return_address; rt=get_reg(regs[i].regmap,PR); return_address=start+i*2+4; assert(rt>=0); if(rt>=0) { #ifdef REG_PREFETCH if(temp>=0) { if(i_regmap[temp]!=PTEMP) emit_movimm((int)hash_table[((return_address>>16)^return_address)&0xFFFF],temp); } #endif emit_movimm(return_address,rt); // PC into link register } } } ds_assemble(i+1,i_regs); bc_unneeded=regs[i].u; bc_unneeded|=1LL<=0); return_address=start+i*2+4; if(rt>=0&&rt1[i+1]!=PR&&rt2[i+1]!=PR) { #ifdef USE_MINI_HT if(internal_branch(return_address)) { int temp=rt+1; if(temp==EXCLUDE_REG||temp>=HOST_REGS|| branch_regs[i].regmap[temp]>=0) { temp=get_reg(branch_regs[i].regmap,-1); } #ifdef HOST_TEMPREG if(temp<0) temp=HOST_TEMPREG; #endif if(temp>=0) do_miniht_insert(return_address,rt,temp); else emit_movimm(return_address,rt); } else #endif { #ifdef REG_PREFETCH if(temp>=0) { if(i_regmap[temp]!=PTEMP) emit_movimm((int)hash_table[((return_address>>16)^return_address)&0xFFFF],temp); } #endif emit_movimm(return_address,rt); // PC into link register #ifdef IMM_PREFETCH emit_prefetch(hash_table[((return_address>>16)^return_address)&0xFFFF]); #endif } } } cc=get_reg(branch_regs[i].regmap,CCREG); assert(cc==HOST_CCREG); store_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); #ifdef REG_PREFETCH if(rt1[i]==PR&&temp>=0) emit_prefetchreg(temp); #endif do_cc(i,branch_regs[i].regmap,&adj,ba[i],TAKEN,0); if(adj) emit_addimm(cc,CLOCK_DIVIDER*(ccadj[i]+cycles[i]+cycles[i+1]-adj),cc); load_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); if(internal_branch(ba[i])) assem_debug("branch: internal\n"); else assem_debug("branch: external\n"); if(internal_branch(ba[i])&&is_ds[(ba[i]-start)>>1]) { ds_assemble_entry(i); } else { add_to_linker((int)out,ba[i],internal_branch(ba[i])); emit_jmp(0); } } void rjump_assemble(int i,struct regstat *i_regs) { signed char *i_regmap=i_regs->regmap; int temp; int rs,cc,adj,rh,ht; u64 bc_unneeded; rs=get_reg(branch_regs[i].regmap,rs1[i]); assert(rs>=0); if(!((i_regs->wasdoingcp>>rs)&1)) { if(opcode[i]==0&&opcode2[i]==3) { // PC-relative branch, put PC in a temporary register temp=get_reg(branch_regs[i].regmap,RTEMP); assert(temp>=0); if(regs[i].regmap[temp]==RTEMP) emit_movimm(start+i*2+4,temp); } if(rs1[i]==rt1[i+1]||rs1[i]==rt2[i+1]) { // Delay slot abuse, make a copy of the branch address register temp=get_reg(branch_regs[i].regmap,RTEMP); assert(temp>=0); assert(regs[i].regmap[temp]==RTEMP); if(opcode[i]==0&&opcode2[i]==3) emit_add(rs,temp,temp); else emit_mov(rs,temp); rs=temp; } } address_generation(i+1,i_regs,regs[i].regmap_entry); #ifdef REG_PREFETCH if(rt1[i]==PR) { if((temp=get_reg(branch_regs[i].regmap,PTEMP))>=0) { int return_address=start+i*2+4; if(i_regmap[temp]==PTEMP) emit_movimm((int)hash_table[((return_address>>16)^return_address)&0xFFFF],temp); } } #endif #ifdef USE_MINI_HT if(rs1[i]==PR) { int rh=get_reg(regs[i].regmap,RHASH); if(rh>=0) do_preload_rhash(rh); } #endif if(rt1[i]==PR) { if(rt1[i+1]==PR||rt2[i+1]==PR) { // Delay slot abuse, set PR before executing delay slot int rt,return_address; rt=get_reg(regs[i].regmap,rt1[i]); assert(rt>=0); if(rt>=0) { return_address=start+i*2+4; #ifdef REG_PREFETCH if(temp>=0) { if(i_regmap[temp]!=PTEMP) emit_movimm((int)hash_table[((return_address>>16)^return_address)&0xFFFF],temp); } #endif emit_movimm(return_address,rt); // PC into link register } } } ds_assemble(i+1,i_regs); bc_unneeded=regs[i].u; bc_unneeded|=1LL<=0&&rt1[i+1]!=PR&&rt2[i+1]!=PR) { return_address=start+i*2+4; #ifdef REG_PREFETCH if(temp>=0) { if(i_regmap[temp]!=PTEMP) emit_movimm((int)hash_table[((return_address>>16)^return_address)&0xFFFF],temp); } #endif emit_movimm(return_address,rt); // PC into link register #ifdef IMM_PREFETCH emit_prefetch(hash_table[((return_address>>16)^return_address)&0xFFFF]); #endif } } cc=get_reg(branch_regs[i].regmap,CCREG); assert(cc==HOST_CCREG); #ifdef USE_MINI_HT rh=get_reg(branch_regs[i].regmap,RHASH); ht=get_reg(branch_regs[i].regmap,RHTBL); if(rs1[i]==PR) { if(regs[i].regmap[rh]!=RHASH) do_preload_rhash(rh); do_preload_rhtbl(ht); do_rhash(rs,rh); } #endif if(opcode[i]==0&&opcode2[i]==11&&opcode3[i]==2) { // Return From Exception (RTE) - pop PC and SR from stack //printf("RTE\n"); int map=get_reg(branch_regs[i].regmap,MOREG); int cache=get_reg(branch_regs[i].regmap,MMREG); int sp=get_reg(branch_regs[i].regmap,15); int sr=get_reg(branch_regs[i].regmap,SR); int jaddr=0; unsigned int hr; u32 reglist=0; temp=get_reg(branch_regs[i].regmap,RTEMP); for(hr=0;hrregmap[hr]>=0) reglist|=1<=0); assert(sr>=0); assert(temp>=0); assert(map>=0); reglist&=~(1<wasdoingcp>>rs)&1)&®s[i].regmap[rs]==branch_regs[i].regmap[rs]) ||((i_regs->isconst>>rs1[i])&1)) { // Do constant propagation, branch to fixed address u32 constaddr; if(((i_regs->wasdoingcp>>rs)&1)&®s[i].regmap[rs]==branch_regs[i].regmap[rs]) constaddr=cpmap[i][rs]; else constaddr=i_regs->constmap[rs1[i]]; if(opcode[i]==0&&opcode2[i]==3) { // PC-relative branch, add PC+4 constaddr+=start+i*2+4; } assert(ba[i]==constaddr); store_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); //emit_addimm_and_set_flags(CLOCK_DIVIDER*(ccadj[i]+cycles[i]+cycles[i+1]),HOST_CCREG); //add_stub(CC_STUB,(int)out,jump_vaddr_reg[rs],0,i,-1,TAKEN,0); //emit_jns(0); do_cc(i,branch_regs[i].regmap,&adj,constaddr,TAKEN,0); if(adj) emit_addimm(cc,CLOCK_DIVIDER*(ccadj[i]+cycles[i]+cycles[i+1]-adj),cc); load_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); if(internal_branch(constaddr)) assert(bt[(constaddr-start)>>1]); if(internal_branch(constaddr)&&bt[(constaddr-start)>>1]) { assem_debug("branch: internal (constant address)\n"); if(is_ds[(constaddr-start)>>1]) { ds_assemble_entry(i); } else { add_to_linker((int)out,constaddr,1/*internal_branch*/); emit_jmp(0); } } else { assem_debug("branch: external (constant address)\n"); add_to_linker((int)out,constaddr,0/*internal_branch*/); emit_jmp(0); } } else { ba[i]=-1; store_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,-1); #ifdef REG_PREFETCH if(rt1[i]==PR&&temp>=0) emit_prefetchreg(temp); #endif #ifdef USE_MINI_HT if(rs1[i]==PR) { do_miniht_load(ht,rh); } #endif //#ifdef HOST_IMM_ADDR32 alternative using lea? if(rs1[i]!=rt1[i+1]&&rs1[i]!=rt2[i+1]) { if(opcode[i]==0&&opcode2[i]==3) { // PC-relative branch, add offset to PC temp=get_reg(branch_regs[i].regmap,RTEMP); if(regs[i].regmap[temp]!=RTEMP) { // Load PC if necessary emit_movimm(start+i*2+4,temp); } emit_add(rs,temp,temp); rs=temp; } } //do_cc(i,branch_regs[i].regmap,&adj,-1,TAKEN); //if(adj) emit_addimm(cc,2*(ccadj[i]+2-adj),cc); // ??? - Shouldn't happen //assert(adj==0); emit_addimm_and_set_flags(CLOCK_DIVIDER*(ccadj[i]+cycles[i]+cycles[i+1]),HOST_CCREG); add_stub(CC_STUB,(int)out,jump_vaddr_reg[slave][rs],0,i,-1,TAKEN,0); emit_jns(0); //load_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,-1); #ifdef USE_MINI_HT if(rs1[i]==PR) { do_miniht_jump(rs,rh,ht); } else #endif { emit_jmp(jump_vaddr_reg[slave][rs]); } } } #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(rt1[i]!=PR&&iregmap; int cc; int match; int sr; int unconditional=0,nop=0; int adj; int invert=0; int internal; match=match_bt(regs[i].regmap,regs[i].dirty,ba[i]); assem_debug("match=%d\n",match); internal=internal_branch(ba[i]); if(i==(ba[i]-start)>>1) assem_debug("idle loop\n"); if(!match) invert=1; #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(i>(ba[i]-start)>>1) invert=1; #endif sr=get_reg(i_regmap,SR); assert(sr>=0); cc=get_reg(i_regmap,CCREG); assert(cc==HOST_CCREG); do_cc(i,regs[i].regmap,&adj,start+i*2,NODS,invert); if(unconditional) store_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); if(unconditional) { do_cc(i,branch_regs[i].regmap,&adj,ba[i],TAKEN,0); if(i!=(ba[i]-start)>>1 || source[i+1]!=0) { if(adj) emit_addimm(cc,CLOCK_DIVIDER*(ccadj[i]+2-adj),cc); load_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); if(internal) assem_debug("branch: internal\n"); else assem_debug("branch: external\n"); if(internal&&is_ds[(ba[i]-start)>>1]) { ds_assemble_entry(i); } else { add_to_linker((int)out,ba[i],internal); emit_jmp(0); } #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(((u32)out)&7) emit_addnop(0); #endif } } else if(nop) { int jaddr; emit_addimm_and_set_flags(CLOCK_DIVIDER*(ccadj[i]+2),cc); jaddr=(int)out; emit_jns(0); add_stub(CC_STUB,jaddr,(int)out,0,i,start+i*2+4,NOTTAKEN,0); } else { pointer taken=0,nottaken=0,nottaken1=0; //do_cc(i,regs[i].regmap,&adj,-1,0,invert); if(adj&&!invert) emit_addimm(cc,CLOCK_DIVIDER*(ccadj[i]-adj),cc); //printf("branch(%d): eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",i,branch_regs[i].regmap[0],branch_regs[i].regmap[1],branch_regs[i].regmap[2],branch_regs[i].regmap[3],branch_regs[i].regmap[5],branch_regs[i].regmap[6],branch_regs[i].regmap[7]); emit_testimm(sr,1); if(opcode2[i]==9) // BT { if(invert){ nottaken=(pointer)out; emit_jeq(1); }else{ add_to_linker((int)out,ba[i],internal); emit_jne(0); } } if(opcode2[i]==11) // BF { if(invert){ nottaken=(pointer)out; emit_jne(1); }else{ add_to_linker((int)out,ba[i],internal); emit_jeq(0); } } if(invert) { if(taken) set_jump_target(taken,(pointer)out); #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(match&&(!internal||!is_ds[(ba[i]-start)>>1])) { if(adj) { emit_addimm(cc,-CLOCK_DIVIDER*adj,cc); add_to_linker((int)out,ba[i],internal); }else{ emit_addnop(13); add_to_linker((int)out,ba[i],internal*2); } emit_jmp(0); }else #endif { if(adj) emit_addimm(cc,-CLOCK_DIVIDER*adj,cc); store_regs_bt(regs[i].regmap,regs[i].dirty,ba[i]); load_regs_bt(regs[i].regmap,regs[i].dirty,ba[i]); if(internal) assem_debug("branch: internal\n"); else assem_debug("branch: external\n"); if(internal&&is_ds[(ba[i]-start)>>1]) { ds_assemble_entry(i); } else { add_to_linker((int)out,ba[i],internal); emit_jmp(0); } } set_jump_target(nottaken,(pointer)out); } //if(nottaken1) set_jump_target(nottaken1,(int)out); if(adj&&!invert) emit_addimm(cc,CLOCK_DIVIDER*adj,cc); } // (!unconditional) } void sjump_assemble(int i,struct regstat *i_regs) { signed char *i_regmap=i_regs->regmap; int cc; int adj; int match; int sr; int unconditional=0,nop=0; int invert=0; int internal=internal_branch(ba[i]); match=match_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); assem_debug("match=%d\n",match); internal=internal_branch(ba[i]); if(i==(ba[i]-start)>>1) assem_debug("idle loop\n"); if(!match) invert=1; #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(i>(ba[i]-start)>>1) invert=1; #endif if(ooo[i]) { sr=get_reg(branch_regs[i].regmap,SR); } else { sr=get_reg(i_regmap,SR); } cc=get_reg(i_regmap,CCREG); assert(cc==HOST_CCREG); if(ooo[i]) { u64 bc_unneeded; // Out of order execution (delay slot first) //printf("OOOE\n"); do_cc(i,regs[i].regmap,&adj,start+i*2,NODS,invert); address_generation(i+1,i_regs,regs[i].regmap_entry); ds_assemble(i+1,i_regs); bc_unneeded=regs[i].u; bc_unneeded&=~((1LL<>1 || source[i+1]!=0) { if(adj) emit_addimm(cc,CLOCK_DIVIDER*(ccadj[i]+2-adj),cc); load_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); if(internal) assem_debug("branch: internal\n"); else assem_debug("branch: external\n"); if(internal&&is_ds[(ba[i]-start)>>1]) { ds_assemble_entry(i); } else { add_to_linker((int)out,ba[i],internal); emit_jmp(0); } #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(((u32)out)&7) emit_addnop(0); #endif } } else if(nop) { int jaddr; emit_addimm_and_set_flags(CLOCK_DIVIDER*(ccadj[i]+2),cc); jaddr=(int)out; emit_jns(0); add_stub(CC_STUB,jaddr,(int)out,0,i,start+i*2+4,NOTTAKEN,0); } else { pointer taken=0,nottaken=0,nottaken1=0; //do_cc(i,branch_regs[i].regmap,&adj,-1,0,invert); if(adj&&!invert) emit_addimm(cc,CLOCK_DIVIDER*(ccadj[i]-adj),cc); //printf("branch(%d): eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",i,branch_regs[i].regmap[0],branch_regs[i].regmap[1],branch_regs[i].regmap[2],branch_regs[i].regmap[3],branch_regs[i].regmap[5],branch_regs[i].regmap[6],branch_regs[i].regmap[7]); assert(sr>=0); emit_testimm(sr,1); if(opcode2[i]==13) // BT/S { if(invert){ nottaken=(pointer)out; emit_jeq(1); }else{ add_to_linker((int)out,ba[i],internal); emit_jne(0); } } if(opcode2[i]==15) // BF/S { if(invert){ nottaken=(pointer)out; emit_jne(1); }else{ add_to_linker((int)out,ba[i],internal); emit_jeq(0); } } if(invert) { if(taken) set_jump_target(taken,(pointer)out); #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK if(match&&(!internal||!is_ds[(ba[i]-start)>>1])) { if(adj) { emit_addimm(cc,-CLOCK_DIVIDER*adj,cc); add_to_linker((int)out,ba[i],internal); }else{ emit_addnop(13); add_to_linker((int)out,ba[i],internal*2); } emit_jmp(0); }else #endif { if(adj) emit_addimm(cc,-CLOCK_DIVIDER*adj,cc); store_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); load_regs_bt(branch_regs[i].regmap,branch_regs[i].dirty,ba[i]); if(internal) assem_debug("branch: internal\n"); else assem_debug("branch: external\n"); if(internal&&is_ds[(ba[i]-start)>>1]) { ds_assemble_entry(i); } else { add_to_linker((int)out,ba[i],internal); emit_jmp(0); } } set_jump_target(nottaken,(pointer)out); } if(nottaken1) set_jump_target(nottaken1,(pointer)out); if(adj&&!invert) emit_addimm(cc,CLOCK_DIVIDER*adj,cc); } // (!unconditional) } // if(ooo) else { // In-order execution (branch first) //printf("IOE\n"); u64 ds_unneeded; pointer taken=0,nottaken=0,nottaken1=0; do_cc(i,regs[i].regmap,&adj,start+i*2,NODS,1); if(!unconditional&&!nop) { //printf("branch(%d): eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",i,branch_regs[i].regmap[0],branch_regs[i].regmap[1],branch_regs[i].regmap[2],branch_regs[i].regmap[3],branch_regs[i].regmap[5],branch_regs[i].regmap[6],branch_regs[i].regmap[7]); assert(sr>=0); emit_testimm(sr,1); if(opcode2[i]==13) // BT/S { nottaken=(pointer)out; emit_jeq(2); } if(opcode2[i]==15) // BF/S { nottaken=(pointer)out; emit_jne(2); } } // if(!unconditional) ds_unneeded=regs[i].u; ds_unneeded&=~((1LL<>1]) { ds_assemble_entry(i); } else { add_to_linker((int)out,ba[i],internal); emit_jmp(0); } } // branch not taken if(!unconditional) { if(nottaken1) set_jump_target(nottaken1,(int)out); set_jump_target(nottaken,(int)out); assem_debug("2:\n"); wb_invalidate(regs[i].regmap,branch_regs[i].regmap,regs[i].dirty, ds_unneeded); load_regs(regs[i].regmap,branch_regs[i].regmap,rs1[i+1],rs2[i+1],rs3[i+1]); address_generation(i+1,&branch_regs[i],0); if(itype[i+1]==COMPLEX) { if((opcode[i+1]|4)==4&&opcode2[i+1]==15) { // MAC.W/MAC.L load_regs(regs[i].regmap,branch_regs[i].regmap,MACL,MACH,MACH); } } load_regs(regs[i].regmap,branch_regs[i].regmap,CCREG,CCREG,CCREG); ds_assemble(i+1,&branch_regs[i]); } } } void system_assemble(int i,struct regstat *i_regs) { signed char ccreg=get_reg(i_regs->regmap,CCREG); assert(ccreg==HOST_CCREG); assert(!is_delayslot); if(opcode[i]==0&&opcode2[i]==11&&opcode3[i]==1) { // SLEEP pointer jaddr, return_address; emit_addimm(HOST_CCREG,CLOCK_DIVIDER*ccadj[i],HOST_CCREG); jaddr=(pointer)out; emit_jns(0); return_address=(pointer)out; emit_zeroreg(HOST_CCREG); set_jump_target(jaddr,(pointer)out); add_stub(CC_STUB,(int)out,return_address,0,i,start+i*2,TAKEN,0); emit_jmp(0); // DEBUG: Count in multiples of three to match interpreter //emit_addimm_and_set_flags(CLOCK_DIVIDER*3,HOST_CCREG); //add_stub(CC_STUB,(int)out,return_address,0,i,start+i*2,TAKEN,0); //emit_jns(0); emit_jmp(return_address); } else { int b,t,sr,st,map=-1,cache=-1; int jaddr=0; unsigned int hr; u32 reglist=0; assert(opcode[i]==12); // TRAPA t=get_reg(i_regs->regmap,-1); b=get_reg(i_regs->regmap,VBR); sr=get_reg(i_regs->regmap,SR); st=get_reg(i_regs->regmap,15); // STACK for(hr=0;hrregmap[hr]>=0) reglist|=1<=0); assert(b>=0); assert(sr>=0); assert(st>=0); emit_addimm(st,-4,st); map=get_reg(i_regs->regmap,MOREG); cache=get_reg(i_regs->regmap,MMREG); assert(map>=0); reglist&=~(1<regmap,i_regs->dirty,-1); emit_movimm(start+i*2+2,sr); emit_addimm(b,imm[i]<<2,b); map=do_map_w(st,st,map,cache,0,0,0); do_map_w_branch(map,0,0,&jaddr); // Save PC emit_rorimm(sr,16,sr); emit_writeword_indexed_map(sr,0,st,map,map); if(jaddr) { add_stub(STOREL_STUB,jaddr,(int)out,i,st,(int)i_regs,ccadj[i],reglist); } // Load PC map=do_map_r(b,b,map,cache,0,-1,-1,0,0); do_map_r_branch(map,0,0,&jaddr); emit_readword_indexed_map(0,b,map,t); emit_rorimm(t,16,t); if(jaddr) add_stub(LOADL_STUB,jaddr,(int)out,i,t,(int)i_regs,ccadj[i],reglist); if(i_regs->regmap[HOST_CCREG]!=CCREG) { emit_loadreg(CCREG,HOST_CCREG); } emit_addimm_and_set_flags(CLOCK_DIVIDER*(ccadj[i]+cycles[i]),HOST_CCREG); //add_stub(CC_STUB,(int)out,jump_vaddr_reg[slave][t],0,i,-1,TAKEN,0); // FIXME //emit_jns(0); emit_jmp(jump_vaddr_reg[slave][t]); } } void bios_assemble(int i,struct regstat *i_regs) { signed char ccreg=get_reg(i_regs->regmap,CCREG); assert(ccreg==HOST_CCREG); assert(!is_delayslot); emit_movimm(start+i*2,0); //emit_writeword(0,slave?(int)&slave_pc:(int)&master_pc); emit_addimm(HOST_CCREG,CLOCK_DIVIDER*ccadj[i],HOST_CCREG); if(slave) emit_call((pointer)slave_handle_bios); // Probably doesn't work else emit_call((pointer)master_handle_bios); } // Basic liveness analysis for SH2 registers void unneeded_registers(int istart,int iend,int r) { int i; u64 u,uu,b,bu; u64 temp_u,temp_uu; u64 tdep; if(iend==slen-1) { u=0; }else{ u=unneeded_reg[iend+1]; u=0; } for (i=iend;i>=istart;i--) { //printf("unneeded registers i=%d (%d,%d) r=%d\n",i,istart,iend,r); if(itype[i]==RJUMP||itype[i]==UJUMP||itype[i]==CJUMP||itype[i]==SJUMP) { if(ba[i]=(start+slen*2)) { // Branch out of this block, flush all regs u=0; branch_unneeded_reg[i]=u; if(itype[i]!=CJUMP) { // Merge in delay slot if(rt1[i+1]>=0) u|=1LL<=0) u|=1LL<=0) u&=~(1LL<=0) u&=~(1LL<=0) u&=~(1LL<=0) temp_u|=1LL<=0) temp_u|=1LL<=0) temp_u&=~(1LL<=0) temp_u&=~(1LL<=0) temp_u&=~(1LL<=0) temp_u|=1LL<=0) temp_u|=1LL<=0) temp_u&=~(1LL<=0) temp_u&=~(1LL<=0) temp_u&=~(1LL<>1,i-1,r+1); }else{ unneeded_reg[(ba[i]-start)>>1]=0; } } /*else*/ if(1) { if(itype[i]==UJUMP||itype[i]==RJUMP) { // Unconditional branch u=unneeded_reg[(ba[i]-start)>>1]; // Always need stack and status in case of interrupt u&=~((1LL<<15)|(1LL<=0) u|=1LL<=0) u|=1LL<=0) u&=~(1LL<=0) u&=~(1LL<=0) u&=~(1LL<>1]; branch_unneeded_reg[i]=b; //b=0; // for debugging //branch_unneeded_reg[i]=b; // for debugging // Branch delay slot if(itype[i]!=CJUMP) { if(rt1[i+1]>=0) b|=1LL<=0) b|=1LL<=0) b&=~(1LL<=0) b&=~(1LL<=0) b&=~(1LL<>rt1[i])&1; // Written registers are unneeded if(rt1[i]>=0) u|=1LL<=0) u|=1LL<=0) u&=~(1LL<=0) u&=~(1LL<=0) u&=~(1LL<=istart;i--) { if(itype[i]==RJUMP||itype[i]==UJUMP||itype[i]==CJUMP||itype[i]==SJUMP) { if(ba[i]=(start+slen*2)) { // Branch out of this block, flush all regs if(itype[i]==RJUMP||itype[i]==UJUMP) { // Unconditional branch will_dirty_i=0; wont_dirty_i=0; // Merge in delay slot (will dirty) for(r=0;rTBIT) will_dirty_i&=~(1<TBIT) will_dirty_i&=~(1<TBIT) will_dirty_i&=~(1<TBIT) will_dirty_i&=~(1<TBIT) temp_will_dirty&=~(1<TBIT) temp_will_dirty&=~(1<TBIT) temp_will_dirty&=~(1<TBIT) temp_will_dirty&=~(1<=0 && (regmap_pre[i][r]&63)>(regmap_pre[i][r]&63))&1)<>(regmap_pre[i][r]&63))&1)<>1,i-1,0); }else{ // Limit recursion. It can take an excessive amount // of time if there are a lot of nested loops. will_dirty[(ba[i]-start)>>1]=0; wont_dirty[(ba[i]-start)>>1]=-1; } } /*else*/ if(1) { if(itype[i]==RJUMP||itype[i]==UJUMP) { // Unconditional branch will_dirty_i=0; wont_dirty_i=0; //if(ba[i]>start+i*4) { // Disable recursion (for debugging) for(r=0;r>1].regmap_entry[r]) { will_dirty_i|=will_dirty[(ba[i]-start)>>1]&(1<>1]&(1<=0) { will_dirty_i|=((unneeded_reg[(ba[i]-start)>>1]>>branch_regs[i].regmap[r])&1)<>1]>>branch_regs[i].regmap[r])&1)<TBIT) will_dirty_i&=~(1<TBIT) will_dirty_i&=~(1<start+i*4) { // Disable recursion (for debugging) for(r=0;r>1].regmap_entry[r]) { will_dirty_i&=will_dirty[(ba[i]-start)>>1]&(1<>1]&(1<=0) { will_dirty_i&=((unneeded_reg[(ba[i]-start)>>1]>>target_reg)&1)<>1]>>target_reg)&1)<TBIT) will_dirty_i&=~(1<TBIT) will_dirty_i&=~(1<TBIT) will_dirty_i&=~(1<istart) { if(itype[i]!=RJUMP&&itype[i]!=UJUMP&&itype[i]!=CJUMP&&itype[i]!=SJUMP) { // Don't store a register immediately after writing it, // may prevent dual-issue. if((regs[i].regmap[r]&63)==rt1[i-1]) wont_dirty_i|=1<>r)&1) { printf(" r%d",r); } } printf("\n");*/ regs[i].dirty|=will_dirty_i; //#ifndef DESTRUCTIVE_WRITEBACK regs[i].dirty&=wont_dirty_i; if(itype[i]==RJUMP||itype[i]==UJUMP||itype[i]==SJUMP) { if(i>r)&1));*/} } } } } else { if(i>r)&1));*/} } } } } //#endif } // Deal with changed mappings temp_will_dirty=will_dirty_i; temp_wont_dirty=wont_dirty_i; for(r=0;r=0&&(nr=get_reg(regs[i].regmap,regmap_pre[i][r]))>=0) { // Register moved to a different register will_dirty_i&=~(1<>nr)&1)<>nr)&1)<=0 && (regmap_pre[i][r]&63)>(regmap_pre[i][r]&63))&1)<>(regmap_pre[i][r]&63))&1)<>r)&1));*/ } } } } } } /* disassembly */ void disassemble_inst(int i) { if (bt[i]) printf("*"); else printf(" "); switch(itype[i]) { case UJUMP: case CJUMP: case SJUMP: printf (" %x: %s %8x\n",start+i*2,insn[i],ba[i]);break; case RJUMP: printf (" %x: %s r%d\n",start+i*2,insn[i],rs1[i]);break; case IMM8: printf (" %x: %s #%d,r%d\n",start+i*2,insn[i],imm[i],opcode[i]==14?rt1[i]:rs1[i]); break; case LOAD: switch(addrmode[i]) { case REGIND: printf (" %x: %s @r%d,r%d\n",start+i*2,insn[i],rs1[i],rt1[i]); break; case POSTINC: printf (" %x: %s @r%d+,r%d\n",start+i*2,insn[i],rs1[i],rt1[i]); break; case PREDEC: printf (" %x: %s @-r%d,r%d\n",start+i*2,insn[i],rs1[i],rt1[i]); break; case DUALIND: printf (" %x: %s @(R0,r%d),r%d\n",start+i*2,insn[i],rs1[i],rt1[i]); break; case GBRIND: printf (" %x: %s #%d,@(R0,GBR)\n",start+i*2,insn[i],imm[i]); break; case GBRDISP: printf (" %x: %s @(%d,GBR),r%d\n",start+i*2,insn[i],imm[i],rt1[i]); break; case REGDISP: printf (" %x: %s @(%d,r%d),r%d\n",start+i*2,insn[i],imm[i],rs1[i],rt1[i]); break; } break; case STORE: switch(addrmode[i]) { case REGIND: printf (" %x: %s r%d,@r%d\n",start+i*2,insn[i],rs1[i],rs2[i]); break; case POSTINC: printf (" %x: %s r%d,@r%d+\n",start+i*2,insn[i],rs1[i],rs2[i]); break; case PREDEC: printf (" %x: %s r%d,@-r%d\n",start+i*2,insn[i],rs1[i],rs2[i]); break; case DUALIND: printf (" %x: %s r%d,@(R0,r%d)\n",start+i*2,insn[i],rs1[i],rs2[i]); break; case GBRDISP: printf (" %x: %s r%d,@(%d,GBR)\n",start+i*2,insn[i],rs1[i],imm[i]); break; case REGDISP: printf (" %x: %s r%d,@(%d,r%d)\n",start+i*2,insn[i],rs1[i],imm[i],rs2[i]); break; } break; case RMW: switch(addrmode[i]) { case REGIND: printf (" %x: %s @r%d\n",start+i*2,insn[i],rs1[i]); break; case GBRIND: printf (" %x: %s #%d,@(R0,GBR)\n",start+i*2,insn[i],imm[i]); break; } break; case PCREL: printf (" %x: %s @(%x,PC),r%d (PC+%d=%x)",start+i*2,insn[i],imm[i],rt1[i],imm[i],((start+i*2+4)&(opcode[i]==9?~1:~3))+imm[i]); if (opcode[i]==9 && (unsigned)(i+(imm[i]>>1))>1]); // MOV.W else if (opcode[i]==13 && (unsigned)(i+(imm[i]>>1))>1]<<16)+source[(((start+i*2+4)&~3)+imm[i]+2-start)>>1]); // MOV.L else printf("\n"); if (opcode[i]==13 && (unsigned)(i+(imm[i]>>1))>1]<<16)+source[(((start+i*2+4)&~3)+imm[i]+2-start)>>1]-(start+i*2)<(unsigned)1024) printf("Within 1024\n"); break; case ALU: if(rs1[i]<0&&rs2[i]<0) // XOR reg,reg case printf (" %x: %s r%d,r%d\n",start+i*2,insn[i],rt1[i],rt1[i]); else if(rs2[i]>=0&&rs2[i]!=TBIT) printf (" %x: %s r%d,r%d\n",start+i*2,insn[i],rs1[i],rs2[i]); else if(rt1[i]!=rs1[i]) printf (" %x: %s r%d,r%d\n",start+i*2,insn[i],rs1[i],rt1[i]); else printf (" %x: %s r%d\n",start+i*2,insn[i],rs1[i]); break; case MULTDIV: //printf (" %x: %s rt1=%d rt2=%d\n",start+i*2,insn[i],rt1[i],rt2[i]); printf (" %x: %s r%d,r%d\n",start+i*2,insn[i],rs1[i],rs2[i]); break; case SHIFTIMM: if(rs2[i]>=0) printf (" %x: %s r%d,r%d\n",start+i*2,insn[i],rs1[i],rs2[i],imm[i]); else printf (" %x: %s r%d\n",start+i*2,insn[i],rt1[i],imm[i]); break; case MOV: printf (" %x: %s r%d,r%d\n",start+i*2,insn[i],rs1[i],rt1[i]); break; case EXT: printf (" %x: %s r%d,r%d\n",start+i*2,insn[i],rs1[i],rt1[i]); break; case FLAGS: if(opcode2[i]==9) printf (" %x: %s r%d\n",start+i*2,insn[i],rt1[i]); else printf (" %x: %s\n",start+i*2,insn[i]); break; case COMPLEX: printf (" %x: %s r%d,r%d\n",start+i*2,insn[i],rs1[i],rs2[i]); break; case DATA: printf (" %x: WORD %4x\n",start+i*2,source[i]&0xFFFF); // Constant data break; default: //printf (" %s %8x\n",insn[i],source[i]); printf (" %x: %s\n",start+i*2,insn[i]); } } void sh2_dynarec_init() { int n; //printf("Init new dynarec\n"); out=(u8 *)BASE_ADDR; if (mmap (out, 1<>2)|0x4000000000000000LL; #else memory_map[n]=(((u32)BiosRom-((n<<12)&0x80000))>>2)|0x40000000; #endif }else if(n>=0x0200&&n<0x0300) { #ifdef POINTERS_64BIT memory_map[n]=((u64)LowWram-((n<<12)&0xFFF00000))>>2; #else memory_map[n]=((u32)LowWram-((n<<12)&0xFFF00000))>>2; #endif }else if(n>=0x6000&&n<0x8000) { #ifdef POINTERS_64BIT memory_map[n]=((u64)HighWram-((n<<12)&0xFFF00000))>>2; #else memory_map[n]=((u32)HighWram-((n<<12)&0xFFF00000))>>2; #endif }else if(n>=0x20200&&n<0x20300) { #ifdef POINTERS_64BIT memory_map[n]=((u64)LowWram-((n<<12)&0xFFF00000))>>2; #else memory_map[n]=((u32)LowWram-((n<<12)&0xFFF00000))>>2; #endif }else if(n>=0x26000&&n<0x28000) { #ifdef POINTERS_64BIT memory_map[n]=((u64)HighWram-((n<<12)&0xFFF00000))>>2; #else memory_map[n]=((u32)HighWram-((n<<12)&0xFFF00000))>>2; #endif }else memory_map[n]=-1LL; } master_cc=slave_cc=0; slave_ip=(void *)0; // Slave not running, go directly to interrupt handler arch_init(); } void SH2DynarecReset(SH2_struct *context) { //printf("SH2DynarecReset\n"); if(context==MSH2) master_cc=0; if(context==SSH2) { slave_ip=(void*)0; slave_cc=0; } } void sh2_dynarec_cleanup() { int n; if (munmap ((void *)BASE_ADDR, 1< %x\n", (int)addr, (int)out); //printf("NOTCOMPILED: addr = %x -> %x\n", (int)addr, (int)out); //printf("TRACE: count=%d next=%d (compile %x)\n",Count,next_interupt,addr); //if(debug) //printf("TRACE: count=%d next=%d (checksum %x)\n",Count,next_interupt,mchecksum()); //printf("fpu mapping=%x enabled=%x\n",(Status & 0x04000000)>>26,(Status & 0x20000000)>>29); /*if(Count>=312978186) { rlist(); }*/ //rlist(); start = (u32)addr&~1; slave = (u32)addr&1; cached_addr = start&~0x20000000; //assert(((u32)addr&1)==0); if (cached_addr >= 0x00000000 && cached_addr < 0x00100000) { source = (u16 *)((char *)BiosRom+(start & 0x7FFFF)); pagelimit = (addr|0x7FFFF) + 1; } else if (cached_addr >= 0x00200000 && cached_addr < 0x00300000) { source = (u16 *)((char *)LowWram+(start & 0xFFFFF)); pagelimit = (addr|0xFFFFF) + 1; } else if (cached_addr >= 0x06000000 && cached_addr < 0x08000000) { source = (u16 *)((char *)HighWram+(start & 0xFFFFF)); pagelimit = (addr|0xFFFFF) + 1; } else { printf("Compile at bogus memory address: %x \n", (int)addr); exit(1); } //printf("source= %x\n",(int)source); alignedsource=(void *)(((pointer)source)&~3); /* Pass 1: disassemble */ /* Pass 2: register dependencies, branch targets */ /* Pass 3: register allocation */ /* Pass 4: branch dependencies */ /* Pass 5: pre-alloc */ /* Pass 6: optimize clean/dirty state */ /* Pass 7: identify interrupt return locations */ /* Pass 8: assembly */ /* Pass 9: linker */ /* Pass 10: garbage collection / free memory */ slen=MAXBLOCK; //printf("addr = %x source = %x %x\n", addr,source,source[0]); /* Pass 1 disassembly */ for(i=0;i<8;i++) { //printf("recent write: %x\n",recent_writes[i]); if(recent_writes[i]start) writelimit=recent_writes[i]; } } for(i=0;!done;i++) { bt[i]=0;ooo[i]=0;op2=0;op3=0;mode=0; minimum_free_regs[i]=0; opcode[i]=op=source[i]>>12; strcpy(insn[i],"???"); type=NI; switch(op) { case 0x00: op2=source[i]&0xf; op3=(source[i]>>4)&0xf; switch(op2) { case 0x02: strcpy(insn[i],"STC"); type=MOV; break; case 0x03: switch(op3) { case 0x00: strcpy(insn[i],"BSRF"); type=RJUMP; break; case 0x02: strcpy(insn[i],"BRAF"); type=RJUMP; break; } break; case 0x04: strcpy(insn[i],"MOV.B"); type=STORE;mode=DUALIND; break; case 0x05: strcpy(insn[i],"MOV.W"); type=STORE;mode=DUALIND; break; case 0x06: strcpy(insn[i],"MOV.L"); type=STORE;mode=DUALIND; break; case 0x07: strcpy(insn[i],"MUL.L"); type=MULTDIV; break; case 0x08: switch(op3) { case 0x00: strcpy(insn[i],"CLRT"); type=FLAGS; break; case 0x01: strcpy(insn[i],"SETT"); type=FLAGS; break; case 0x02: strcpy(insn[i],"CLRMAC"); type=MULTDIV; break; } break; case 0x09: switch(op3) { case 0x00: strcpy(insn[i],"NOP"); type=NOP; break; case 0x01: strcpy(insn[i],"DIV0U"); type=MULTDIV; break; case 0x02: strcpy(insn[i],"MOVT"); type=FLAGS; break; } break; case 0x0A: strcpy(insn[i],"STS"); type=MOV; break; case 0x0B: switch(op3) { case 0x00: strcpy(insn[i],"RTS"); type=RJUMP; break; case 0x01: strcpy(insn[i],"SLEEP"); type=SYSTEM; break; case 0x02: strcpy(insn[i],"RTE"); type=RJUMP; break; } break; case 0x0C: strcpy(insn[i],"MOV.B"); type=LOAD;mode=DUALIND; break; case 0x0D: strcpy(insn[i],"MOV.W"); type=LOAD;mode=DUALIND; break; case 0x0E: strcpy(insn[i],"MOV.L"); type=LOAD;mode=DUALIND; break; case 0x0F: strcpy(insn[i],"MAC.L"); type=COMPLEX; break; } break; case 0x01: strcpy(insn[i],"MOV.L"); type=STORE;mode=REGDISP;op2=2; break; case 0x02: op2=source[i]&0xf; switch(op2) { case 0x00: strcpy(insn[i],"MOV.B"); type=STORE;mode=REGIND; break; case 0x01: strcpy(insn[i],"MOV.W"); type=STORE;mode=REGIND; break; case 0x02: strcpy(insn[i],"MOV.L"); type=STORE;mode=REGIND; break; case 0x04: strcpy(insn[i],"MOV.B"); type=STORE;mode=PREDEC; break; case 0x05: strcpy(insn[i],"MOV.W"); type=STORE;mode=PREDEC; break; case 0x06: strcpy(insn[i],"MOV.L"); type=STORE;mode=PREDEC; break; case 0x07: strcpy(insn[i],"DIV0S"); type=MULTDIV; break; case 0x08: strcpy(insn[i],"TST"); type=ALU; break; case 0x09: strcpy(insn[i],"AND"); type=ALU; break; case 0x0A: strcpy(insn[i],"XOR"); type=ALU; break; case 0x0B: strcpy(insn[i],"OR"); type=ALU; break; case 0x0C: strcpy(insn[i],"CMP/ST"); type=ALU; break; case 0x0D: strcpy(insn[i],"XTRCT"); type=SHIFTIMM; break; case 0x0E: strcpy(insn[i],"MULU.W"); type=MULTDIV; break; case 0x0F: strcpy(insn[i],"MULS.W"); type=MULTDIV; break; } break; case 0x03: op2=source[i]&0xf; switch(op2) { case 0x00: strcpy(insn[i],"CMP/EQ"); type=ALU; break; case 0x02: strcpy(insn[i],"CMP/HS"); type=ALU; break; case 0x03: strcpy(insn[i],"CMP/GE"); type=ALU; break; case 0x04: strcpy(insn[i],"DIV1"); type=COMPLEX; break; case 0x05: strcpy(insn[i],"DMULU.L"); type=MULTDIV; break; case 0x06: strcpy(insn[i],"CMP/HI"); type=ALU; break; case 0x07: strcpy(insn[i],"CMP/GT"); type=ALU; break; case 0x08: strcpy(insn[i],"SUB"); type=ALU; break; case 0x0A: strcpy(insn[i],"SUBC"); type=ALU; break; case 0x0B: strcpy(insn[i],"SUBV"); type=ALU; break; case 0x0C: strcpy(insn[i],"ADD"); type=ALU; break; case 0x0D: strcpy(insn[i],"DMULS.L"); type=MULTDIV; break; case 0x0E: strcpy(insn[i],"ADDC"); type=ALU; break; case 0x0F: strcpy(insn[i],"ADDV"); type=ALU; break; } break; case 0x04: op2=source[i]&0xf; op3=(source[i]>>4)&0xf; switch(op2) { case 0x00: switch(op3) { case 0x00: strcpy(insn[i],"SHLL"); type=SHIFTIMM; break; case 0x01: strcpy(insn[i],"DT"); type=ALU; break; case 0x02: strcpy(insn[i],"SHAL"); type=SHIFTIMM; break; } break; case 0x01: switch(op3) { case 0x00: strcpy(insn[i],"SHLR"); type=SHIFTIMM; break; case 0x01: strcpy(insn[i],"CMP/PZ"); type=ALU; break; case 0x02: strcpy(insn[i],"SHAR"); type=SHIFTIMM; break; } break; case 0x02: strcpy(insn[i],"STS.L"); type=STORE;mode=PREDEC; break; case 0x03: strcpy(insn[i],"STC.L"); type=STORE;mode=PREDEC; break; case 0x04: switch(op3) { case 0x00: strcpy(insn[i],"ROTL"); type=SHIFTIMM; break; case 0x02: strcpy(insn[i],"ROTCL"); type=SHIFTIMM; break; } break; case 0x05: switch(op3) { case 0x00: strcpy(insn[i],"ROTR"); type=SHIFTIMM; break; case 0x01: strcpy(insn[i],"CMP/PL"); type=ALU; break; case 0x02: strcpy(insn[i],"ROTCR"); type=SHIFTIMM; break; } break; case 0x06: strcpy(insn[i],"LDS.L"); type=LOAD;mode=POSTINC; break; case 0x07: strcpy(insn[i],"LDC.L"); type=LOAD;mode=POSTINC; break; case 0x08: switch(op3) { case 0x00: strcpy(insn[i],"SHLL2"); type=SHIFTIMM; break; case 0x01: strcpy(insn[i],"SHLL8"); type=SHIFTIMM; break; case 0x02: strcpy(insn[i],"SHLL16"); type=SHIFTIMM; break; } break; case 0x09: switch(op3) { case 0x00: strcpy(insn[i],"SHLR2"); type=SHIFTIMM; break; case 0x01: strcpy(insn[i],"SHLR8"); type=SHIFTIMM; break; case 0x02: strcpy(insn[i],"SHLR16"); type=SHIFTIMM; break; } break; case 0x0A: strcpy(insn[i],"LDS"); type=MOV; break; case 0x0B: switch(op3) { case 0x00: strcpy(insn[i],"JSR"); type=RJUMP; break; case 0x01: strcpy(insn[i],"TAS.B"); type=RMW;mode=REGIND; break; case 0x02: strcpy(insn[i],"JMP"); type=RJUMP; break; } break; case 0x0E: strcpy(insn[i],"LDC"); type=MOV; break; case 0x0F: strcpy(insn[i],"MAC.W"); type=COMPLEX; break; } break; case 0x05: strcpy(insn[i],"MOV.L"); type=LOAD;mode=REGDISP;op2=2; break; case 0x06: op2=source[i]&0xf; switch(op2) { case 0x00: strcpy(insn[i],"MOV.B"); type=LOAD;mode=REGIND; break; case 0x01: strcpy(insn[i],"MOV.W"); type=LOAD;mode=REGIND; break; case 0x02: strcpy(insn[i],"MOV.L"); type=LOAD;mode=REGIND; break; case 0x03: strcpy(insn[i],"MOV"); type=MOV; break; case 0x04: strcpy(insn[i],"MOV.B"); type=LOAD;mode=POSTINC; break; case 0x05: strcpy(insn[i],"MOV.W"); type=LOAD;mode=POSTINC; break; case 0x06: strcpy(insn[i],"MOV.L"); type=LOAD;mode=POSTINC; break; case 0x07: strcpy(insn[i],"NOT"); type=ALU; break; case 0x08: strcpy(insn[i],"SWAP.B"); type=ALU; break; case 0x09: strcpy(insn[i],"SWAP.W"); type=ALU; break; case 0x0A: strcpy(insn[i],"NEGC"); type=ALU; break; case 0x0B: strcpy(insn[i],"NEG"); type=ALU; break; case 0x0C: strcpy(insn[i],"EXTU.B"); type=EXT; break; case 0x0D: strcpy(insn[i],"EXTU.W"); type=EXT; break; case 0x0E: strcpy(insn[i],"EXTS.B"); type=EXT; break; case 0x0F: strcpy(insn[i],"EXTS.W"); type=EXT; break; } break; case 0x07: strcpy(insn[i],"ADD"); type=IMM8; break; case 0x08: op2=(source[i]>>8)&0xf; switch(op2) { case 0x00: strcpy(insn[i],"MOV.B"); type=STORE;mode=REGDISP; break; case 0x01: strcpy(insn[i],"MOV.W"); type=STORE;mode=REGDISP; break; case 0x04: strcpy(insn[i],"MOV.B"); type=LOAD;mode=REGDISP; break; case 0x05: strcpy(insn[i],"MOV.W"); type=LOAD;mode=REGDISP; break; case 0x08: strcpy(insn[i],"CMP/EQ"); type=IMM8; break; case 0x09: strcpy(insn[i],"BT"); type=CJUMP; break; case 0x0B: strcpy(insn[i],"BF"); type=CJUMP; break; case 0x0D: strcpy(insn[i],"BT/S"); type=SJUMP; break; case 0x0F: strcpy(insn[i],"BF/S"); type=SJUMP; break; } break; case 0x09: strcpy(insn[i],"MOV.W"); type=PCREL; break; case 0x0A: strcpy(insn[i],"BRA"); type=UJUMP; break; case 0x0B: strcpy(insn[i],"BSR"); type=UJUMP; break; case 0x0C: op2=(source[i]>>8)&0xf; switch(op2) { case 0x00: strcpy(insn[i],"MOV.B"); type=STORE;mode=GBRDISP; break; case 0x01: strcpy(insn[i],"MOV.W"); type=STORE;mode=GBRDISP; break; case 0x02: strcpy(insn[i],"MOV.L"); type=STORE;mode=GBRDISP; break; case 0x03: strcpy(insn[i],"TRAPA"); type=SYSTEM; break; case 0x04: strcpy(insn[i],"MOV.B"); type=LOAD;mode=GBRDISP; break; case 0x05: strcpy(insn[i],"MOV.W"); type=LOAD;mode=GBRDISP; break; case 0x06: strcpy(insn[i],"MOV.L"); type=LOAD;mode=GBRDISP; break; case 0x07: strcpy(insn[i],"MOVA"); type=PCREL; break; case 0x08: strcpy(insn[i],"TST"); type=IMM8; break; case 0x09: strcpy(insn[i],"AND"); type=IMM8; break; case 0x0A: strcpy(insn[i],"XOR"); type=IMM8; break; case 0x0B: strcpy(insn[i],"OR"); type=IMM8; break; case 0x0C: strcpy(insn[i],"TST.B"); type=LOAD;mode=GBRIND; break; case 0x0D: strcpy(insn[i],"AND.B"); type=RMW;mode=GBRIND; break; case 0x0E: strcpy(insn[i],"XOR.B"); type=RMW;mode=GBRIND; break; case 0x0F: strcpy(insn[i],"OR.B"); type=RMW;mode=GBRIND; break; } break; case 0x0D: strcpy(insn[i],"MOV.L"); type=PCREL; break; case 0x0E: strcpy(insn[i],"MOV"); type=IMM8; break; default: strcpy(insn[i],"???"); type=NI; break; } itype[i]=type; addrmode[i]=mode; opcode2[i]=op2; opcode3[i]=op3; /* Get registers/immediates */ rs1[i]=-1; rs2[i]=-1; rs3[i]=-1; rt1[i]=-1; rt2[i]=-1; lt1[i]=-1; cycles[i]=1; switch(type) { case LOAD: if(mode==GBRDISP||mode==GBRIND) rs1[i]=GBR; else rs1[i]=(source[i]>>4)&0xf; if(mode==DUALIND||mode==GBRIND) rs2[i]=0; if(op==4) { // LDS/LDC rs1[i]=(source[i]>>8)&0xf; if(op2==6) rt1[i]=((source[i]>>4)&0xf)+MACH; if(op2==7) {rt1[i]=((source[i]>>4)&0xf)+SR;cycles[i]=3;} if(rt1[i]==SR) rt2[i]=TBIT; } else if(op==8) rt1[i]=0; // (@disp,rm),r0 else if(op==12) { if(op2!=12) rt1[i]=0; // (@disp,GBR),r0 else { imm[i]=(unsigned int)((unsigned char)source[i]); rt1[i]=TBIT; // TST.B cycles[i]=3; } } else { rt1[i]=(source[i]>>8)&0xf; } if(mode==REGDISP) { imm[i]=(unsigned int)source[i]&0xF; if(op==5) imm[i]<<=2; // MOV.L if(op==8&&op2==5) imm[i]<<=1; // MOV.W } else if(mode==GBRDISP) { imm[i]=(unsigned int)((unsigned char)source[i])<<(op2&3); } else if(mode!=GBRIND) imm[i]=0; if(mode==POSTINC) rt2[i]=rs1[i]; break; case STORE: if(op==4) { // STS/STC if(op2==2) rs1[i]=((source[i]>>4)&0xf)+MACH; if(op2==3) {rs1[i]=((source[i]>>4)&0xf)+SR;cycles[i]=2;} if(rs1[i]==SR) rs3[i]=TBIT; } else if(op==8) rs1[i]=0; // r0,(@disp,rn) else if(op==12) rs1[i]=0; // r0,(@disp,GBR) else rs1[i]=(source[i]>>4)&0xf; if(mode==GBRDISP) rs2[i]=GBR; else if(op==8) rs2[i]=(source[i]>>4)&0xf; // r0,(@disp,rn) else rs2[i]=(source[i]>>8)&0xf; if(mode==DUALIND) rs3[i]=0; if(mode==REGDISP) { imm[i]=(unsigned int)source[i]&0xF; if(op==1) imm[i]<<=2; // MOV.L if(op==8&&op2==1) imm[i]<<=1; // MOV.W } else if(mode==GBRDISP) { imm[i]=(unsigned int)((unsigned char)source[i])<<(op2&3); } else imm[i]=0; if(mode==PREDEC) rt1[i]=rs2[i]; if( (mode==DUALIND&&((p_isconst>>rs2[i])&(p_isconst>>rs3[i])&1)) || (mode!=DUALIND&&((p_isconst>>rs2[i])&1)) ) { u32 addr; if(mode==DUALIND) addr=p_constmap[rs2[i]]+p_constmap[rs3[i]]; if(mode==REGDISP||mode==GBRDISP) addr=p_constmap[rs2[i]]+imm[i]; if(mode==PREDEC) addr=(p_constmap[rs2[i]]-=4); if(mode==REGIND) addr=p_constmap[rs2[i]]; if(addr>start+i*2&&addr>8)&0xf; rt1[i]=TBIT; imm[i]=0; cycles[i]=4; } if(op==12) // AND.B/XOR.B/OR.B { rs1[i]=GBR; rs2[i]=0; imm[i]=(unsigned int)((unsigned char)source[i]); cycles[i]=3; } break; case PCREL: imm[i]=(signed int)((unsigned char)source[i]); if(op==12) rt1[i]=0; // MOVA else rt1[i]=(source[i]>>8)&0xf; if(op==9) imm[i]<<=1; // MOV.W else imm[i]<<=2; // Extend block to include consts // FIXME: Don't go past limit if (op==9 && lastconst < (start+i*2+4)+imm[i]) // MOV.W lastconst = (start+i*2+4)+imm[i]; if (op==13 && lastconst < ((start+i*2+4)&~3)+imm[i]+2) // MOV.L lastconst = ((start+i*2+4)&~3)+imm[i]+2; //printf("lastconst=%x\n",lastconst); break; case MOV: if(op==6) { rs1[i]=(source[i]>>4)&0xf; rt1[i]=(source[i]>>8)&0xf; } if(op==0) { // STC/STS if(op2==2) rs1[i]=((source[i]>>4)&0xf)+SR; //STC if(op2==10) rs1[i]=((source[i]>>4)&0xf)+MACH; //STS rt1[i]=(source[i]>>8)&0xf; if(rs1[i]==SR) rs2[i]=TBIT; // For liveness analysis } if(op==4) { // LDC/LDS if(op2==14) rt1[i]=((source[i]>>4)&0xf)+SR; //LDC if(op2==10) rt1[i]=((source[i]>>4)&0xf)+MACH; //LDS rs1[i]=(source[i]>>8)&0xf; if(rt1[i]==SR) rt2[i]=TBIT; // For liveness analysis } break; case IMM8: if(op==8) { // CMP/EQ r0 rs1[i]=0; rt1[i]=TBIT; imm[i]=(signed int)((signed char)source[i]); }else if(op==12) { rs1[i]=0; if(op2==8) rt1[i]=TBIT; // TST else rt1[i]=0; // AND/XOR/OR imm[i]=(unsigned int)((unsigned char)source[i]); }else{ // ADD/MOV if(op==7) rs1[i]=(source[i]>>8)&0xf; // ADD rt1[i]=(source[i]>>8)&0xf; imm[i]=(signed int)((signed char)source[i]); } break; case FLAGS: if(op2==8) rt1[i]=TBIT; // CLRT/SETT if(op2==9) {rs1[i]=TBIT;rt1[i]=(source[i]>>8)&0xf;} // MOVT break; case ALU: if(op==2) { if(op2==8||op2==12) { // TST or CMP/STR rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=TBIT; } else { // AND/OR/XOR rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=(source[i]>>8)&0xf; if(op2==10&&rs1[i]==rs2[i]) { rs1[i]=-1;rs2[i]=-1; // Optimize XOR reg,reg } } } if(op==3) { if(op2<8) { // CMP rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=TBIT; } else { // ADD/SUB rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=(source[i]>>8)&0xf; if(op2==10||op2==14) rs3[i]=TBIT; // ADDC/SUBC read T bit if(op2!=8&&op2!=12) // ADDC/ADDV/SUBC/SUBV set T bit rt2[i]=TBIT; } } if(op==4) { // DT and compare with zero rs1[i]=(source[i]>>8)&0xf; if(op2==0) rt1[i]=(source[i]>>8)&0xf; // DT rt2[i]=TBIT; } if(op==6) { // NOT/NEG/NEGC/SWAP rs1[i]=(source[i]>>4)&0xf; rt1[i]=(source[i]>>8)&0xf; if(op2==10) rs2[i]=rt2[i]=TBIT; // NEGC sets T bit } break; case EXT: rs1[i]=(source[i]>>4)&0xf; rt1[i]=(source[i]>>8)&0xf; break; case MULTDIV: if(op==0) { if(op2==7) // MUL.L { rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=MACL; cycles[i]=2; } if(op2==8) // CLRMAC { rt1[i]=MACH; rt2[i]=MACL; } if(op2==9) // DIV0U { rs1[i]=SR; rt1[i]=SR; rt2[i]=TBIT; } } if(op==2) { if(op2==7) // DIV0S { rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rs3[i]=SR; rt1[i]=SR; rt2[i]=TBIT; } if(op2==14) // MULU.W { rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=MACL; } if(op2==15) // MULS.W { rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=MACL; } } if(op==3) { if(op2==5) // DMULU.L { rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=MACH; rt2[i]=MACL; cycles[i]=2; } if(op2==13) // DMULS.L { rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rt1[i]=MACH; rt2[i]=MACL; cycles[i]=2; } } break; case SHIFTIMM: rs1[i]=(source[i]>>8)&0xf; rt1[i]=(source[i]>>8)&0xf; if(op==4) { if(op2<6) rt2[i]=TBIT; if(op2==4||op2==5) {if(op3==2) rs2[i]=TBIT;} // ROTCL/ROTCR } if(op==2&op2==13) { // XTRCT rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; } break; case UJUMP: rs2[i]=CCREG; if(op==11) rt1[i]=PR; // BSR cycles[i]=2; break; case RJUMP: rs1[i]=(source[i]>>8)&0xf; if (op==0&&op2==11&&op3==0) rs1[i]=PR; // RTS if ((op==0&&op2==3)||(op==4&&op2==11)) { // BSRF/JSR if(op3==0) rt1[i]=PR; } rs2[i]=CCREG; cycles[i]=2; if(op==0&&op2==11&&op3==2) { // RTE rs1[i]=15; // Stack pointer rs2[i]=CCREG; rt1[i]=SR; rt2[i]=15; cycles[i]=4; } break; case CJUMP: rs1[i]=TBIT; rs2[i]=CCREG; //cycles[i]=3; // Will be adjusted if branch is taken break; case SJUMP: rs1[i]=TBIT; rs2[i]=CCREG; //cycles[i]=2; // Will be adjusted if branch is taken break; case SYSTEM: if(op2==11&&op3==2) { // RTE rs1[i]=15; // Stack pointer rs2[i]=CCREG; rt1[i]=SR; rt2[i]=TBIT; cycles[i]=4; } else if(op==12) { // TRAPA rs1[i]=SR; // Status/flags //rs2[i]=CCREG; rs2[i]=VBR; rs3[i]=15; // Stack pointer imm[i]=(unsigned int)((unsigned char)source[i]); cycles[i]=8; } else { // SLEEP rs2[i]=CCREG; cycles[i]=8; } break; case COMPLEX: if(op==3&&op2==4) { // DIV1 rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rs3[i]=SR; rt1[i]=(source[i]>>8)&0xf; rt2[i]=SR; } if(op==0&&op2==15) { // MAC.L rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rs3[i]=SR; rt1[i]=(source[i]>>4)&0xf; rt2[i]=(source[i]>>8)&0xf; cycles[i]=3; } if(op==4&&op2==15) { // MAC.W rs1[i]=(source[i]>>4)&0xf; rs2[i]=(source[i]>>8)&0xf; rs3[i]=SR; rt1[i]=(source[i]>>4)&0xf; rt2[i]=(source[i]>>8)&0xf; cycles[i]=3; } break; } // Do preliminary constant propagation do_consts(i,&p_isconst,p_constmap); /* Calculate branch target addresses */ if(type==UJUMP) ba[i]=start+i*2+4+((((signed int)source[i])<<20)>>19); else if(type==CJUMP||type==SJUMP) ba[i]=start+i*2+4+((((signed int)source[i])<<24)>>23); else { ba[i]=-1; if(type==RJUMP) { if(op!=0||op2!=11||op3!=2) { // !RTE if((p_isconst>>rs1[i])&1) { u32 constaddr=p_constmap[rs1[i]]; if(op==0&&op2==3) { // PC-relative branch, add PC+4 constaddr+=start+i*2+4; } ba[i]=constaddr; } } } } // If the branch target was previously identified as data, back up if(ba[i]>start&&ba[i]>1]!=DATA); if(itype[(ba[i]-start)>>1]==DATA||itype[(ba[i]+2-start)>>1]==DATA) { //printf("back up and redecode %x\n",ba[i]); i=(ba[i]-2-start)>>1; continue; } } /* Is this the end of the block? */ if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP)) { if(rt1[i-1]!=PR) { // Continue past subroutine call (BSR/JSR) unsigned int firstbt=0xFFFFFFFF; done=1; // Find next branch target (if any) for(j=i-1;j>=0;j--) { if(ba[j]>start+i*2-2&&ba[j]>1;j>23); if(branch_addr>start+i*2&&branch_addr>19); if(branch_addr>start+i*2&&branch_addrMAXBLOCK/2) done=1; } if(yabsys.emulatebios) { if(start+i*2>=0x200&&start+i*2<0x600) { strcpy(insn[i],"(BIOS)"); itype[i]=BIOS; done=1; } } //if(i>0&&itype[i-1]==SYSCALL&&stop_after_jal) done=1; //if(i>0&&itype[i-1]==SYSTEM&&source[i-1]==0x002B) done=1; // RTE //assert(i0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP)) isconst[i+1]=0; if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP)) p_isconst=0; } } slen=i; assert(slen>0); /* Pass 2 - Register dependencies and branch targets */ // Flag branch targets for(i=0;i=start && ba[i]<(start+slen*2) ) { // Possibly internal branch, flag target bt[(ba[i]-start)>>1]=1; } } } // Do constant propagation p_isconst=0; for(i=0;i1&&(itype[i-2]==UJUMP||itype[i-2]==RJUMP)) p_isconst=0; if(i>0&&(itype[i-1]==UJUMP||itype[i-1]==RJUMP)) p_isconst=0; if(i>0&&(itype[i-1]==CJUMP||itype[i-1]==SJUMP)) p_isconst=0; do_consts(i,&p_isconst,p_constmap); if(itype[i]==RJUMP) { if(opcode[i]!=0||opcode2[i]!=11||opcode3[i]!=2) { // Not RTE if((p_isconst>>rs1[i])&1) { // Do constant propagation, branch to fixed address u32 constaddr=p_constmap[rs1[i]]; if(opcode[i]==0&&opcode2[i]==3) { // PC-relative branch, add PC+4 constaddr+=start+i*2+4; } ba[i]=constaddr; //if(internal_branch(constaddr)) // if(!bt[(constaddr-start)>>1]) printf("oops: %x\n",constaddr); //assert(bt[(constaddr-start)>>1]); } } } // No stack-based addressing modes in the delay slot, // to avoid incorrect constants due to pre-incrementing. // TODO: This really should only drop the address register if(itype[i]==UJUMP||itype[i]==RJUMP||itype[i]==SJUMP) { if((source[i+1]&0xF00A)==0x4002) p_isconst=0; if((source[i+1]&0xB00E)==0x2004) p_isconst=0; if((source[i+1]&0xB00F)==0x2006) p_isconst=0; } memcpy(regs[i].constmap,p_constmap,sizeof(u32)*SH2_REGS); regs[i].isconst=p_isconst; } unneeded_registers(0,slen-1,0); /* Pass 3 - Register allocation */ { struct regstat current; // Current register allocations/status int cc=0; current.dirty=0; current.u=unneeded_reg[0]; clear_all_regs(current.regmap); alloc_reg(¤t,0,CCREG); dirty_reg(¤t,CCREG); current.isdoingcp=0; current.wasdoingcp=0; for(i=0;i=0) current.u|=1LL<=0) current.u|=1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) current.u&=~(1LL<=0) { if(r!=regmap_pre[i][hr]) { regs[i].regmap_entry[hr]=-1; } else { if((current.u>>r)&1) { regs[i].regmap_entry[hr]=-1; regs[i].regmap[hr]=-1; //Don't clear regs in the delay slot as the branch might need them //current.regmap[hr]=-1; }else regs[i].regmap_entry[hr]=r; } } else { // First instruction expects CCREG to be allocated if(i==0&&hr==HOST_CCREG) regs[i].regmap_entry[hr]=CCREG; else regs[i].regmap_entry[hr]=-1; } } } else { // Not delay slot switch(itype[i]) { case UJUMP: //current.isdoingcp=0; // DEBUG //current.wasdoingcp=0; // DEBUG //regs[i].wasdoingcp=0; // DEBUG clear_const(¤t,rt1[i]); alloc_cc(¤t,i); dirty_reg(¤t,CCREG); if (rt1[i]==PR) { alloc_reg(¤t,i,PR); dirty_reg(¤t,PR); assert(rs1[i+1]!=PR&&rs2[i+1]!=PR); #ifdef REG_PREFETCH alloc_reg(¤t,i,PTEMP); #endif } ooo[i]=1; delayslot_alloc(¤t,i+1); //current.isdoingcp=0; // DEBUG ds=1; //printf("i=%d, isdoingcp=%x\n",i,current.isdoingcp); break; case RJUMP: //current.isdoingcp=0; //current.wasdoingcp=0; //regs[i].wasdoingcp=0; clear_const(¤t,rs1[i]); clear_const(¤t,rt1[i]); alloc_cc(¤t,i); dirty_reg(¤t,CCREG); if(opcode[i]==0&&opcode2[i]==11&&opcode3[i]==2) { // RTE alloc_reg(¤t,i,15); // Stack reg dirty_reg(¤t,15); alloc_reg(¤t,i,SR); // SR will be loaded from stack dirty_reg(¤t,SR); assert(rt1[i+1]!=15&&rt2[i+1]!=15); assert(rt1[i+1]!=SR&&rt2[i+1]!=SR); assert(rt1[i+1]!=TBIT&&rt2[i+1]!=TBIT); delayslot_alloc(¤t,i+1); } else if(rs1[i]!=rt1[i+1]&&rs1[i]!=rt2[i+1]) { alloc_reg(¤t,i,rs1[i]); if (rt1[i]==PR) { alloc_reg(¤t,i,rt1[i]); dirty_reg(¤t,rt1[i]); assert(rs1[i+1]!=PR&&rs2[i+1]!=PR); if(rs1[i+1]==PR||rs2[i+1]==PR) {printf("OOPS\n");} #ifdef REG_PREFETCH alloc_reg(¤t,i,PTEMP); #endif } #ifdef USE_MINI_HT if(rs1[i]==PR) { // BSRF/JSR alloc_reg(¤t,i,RHASH); #ifndef HOST_IMM_ADDR32 alloc_reg(¤t,i,RHTBL); #endif } #endif // PC-relative branch needs a temporary register to add PC if(opcode[i]==0&&opcode2[i]==3) alloc_reg(¤t,i,RTEMP); delayslot_alloc(¤t,i+1); } else { // The delay slot overwrites our source register, // allocate a temporary register to hold the old value. current.isdoingcp=0; current.wasdoingcp=0; regs[i].wasdoingcp=0; delayslot_alloc(¤t,i+1); current.isdoingcp=0; alloc_reg(¤t,i,RTEMP); } //current.isdoingcp=0; // DEBUG ooo[i]=1; ds=1; break; case CJUMP: //current.isdoingcp=0; //current.wasdoingcp=0; //regs[i].wasdoingcp=0; clear_const(¤t,rs1[i]); clear_const(¤t,rs2[i]); alloc_cc(¤t,i); dirty_reg(¤t,CCREG); alloc_reg(¤t,i,SR); // No delay slot, don't do constant propagation current.isdoingcp=0; current.wasdoingcp=0; regs[i].wasdoingcp=0; //ds=1; // BT/BF don't have delay slots break; case SJUMP: //current.isdoingcp=0; //current.wasdoingcp=0; //regs[i].wasdoingcp=0; clear_const(¤t,rs1[i]); clear_const(¤t,rt1[i]); alloc_cc(¤t,i); dirty_reg(¤t,CCREG); alloc_reg(¤t,i,SR); if(rt1[i+1]==TBIT||rt2[i+1]==TBIT||rt1[i+1]==SR||rt2[i+1]==SR) { // The delay slot overwrites the branch condition. // Allocate the branch condition registers instead. current.isdoingcp=0; current.wasdoingcp=0; regs[i].wasdoingcp=0; } else if(itype[i+1]==COMPLEX) { // The MAC and DIV instructions make function calls which // do not save registers. Do the branch and update the // cycle count first. current.isdoingcp=0; current.wasdoingcp=0; regs[i].wasdoingcp=0; } else { ooo[i]=1; delayslot_alloc(¤t,i+1); } ds=1; //current.isdoingcp=0; break; case IMM8: imm8_alloc(¤t,i); break; case LOAD: load_alloc(¤t,i); break; case STORE: store_alloc(¤t,i); break; case RMW: rmw_alloc(¤t,i); break; case PCREL: pcrel_alloc(¤t,i); break; case ALU: alu_alloc(¤t,i); break; case MULTDIV: multdiv_alloc(¤t,i); break; case SHIFTIMM: shiftimm_alloc(¤t,i); break; case MOV: mov_alloc(¤t,i); break; case EXT: ext_alloc(¤t,i); break; case FLAGS: flags_alloc(¤t,i); break; case COMPLEX: complex_alloc(¤t,i); break; case SYSTEM: system_alloc(¤t,i); break; } //printf("xxx: eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",current.regmap[0],current.regmap[1],current.regmap[2],current.regmap[3],current.regmap[5],current.regmap[6],current.regmap[7]); // Create entry (branch target) regmap for(hr=0;hr=0) { if(r!=regmap_pre[i][hr]) { // TODO: delay slot (?) or=get_reg(regmap_pre[i],r); // Get old mapping for this register if(or<0||(r&63)>=TEMPREG){ regs[i].regmap_entry[hr]=-1; } else { // Just move it to a different register regs[i].regmap_entry[hr]=r; // If it was dirty before, it's still dirty if((regs[i].wasdirty>>or)&1) dirty_reg(¤t,r&63); } } else { if(r<64){ if((current.u>>r)&1) { regs[i].regmap_entry[hr]=-1; //regs[i].regmap[hr]=-1; current.regmap[hr]=-1; }else regs[i].regmap_entry[hr]=r; } } } else { // Branches expect CCREG to be allocated at the target if(regmap_pre[i][hr]==CCREG) regs[i].regmap_entry[hr]=CCREG; else regs[i].regmap_entry[hr]=-1; } } memcpy(regs[i].regmap,current.regmap,sizeof(current.regmap)); } /* Branch post-alloc */ if(i>0) { current.wasdirty=current.dirty; switch(itype[i-1]) { case UJUMP: memcpy(&branch_regs[i-1],¤t,sizeof(current)); branch_regs[i-1].isdoingcp=0; branch_regs[i-1].wasdoingcp=0; branch_regs[i-1].isconst=0; branch_regs[i-1].u=branch_unneeded_reg[i-1]&~((1LL<=0;j--) { if(ba[j]==start+i*2+2) { if(itype[j]==CJUMP) { memcpy(current.regmap,regs[j].regmap,sizeof(current.regmap)); current.dirty=regs[j].dirty; }else{ memcpy(current.regmap,branch_regs[j].regmap,sizeof(current.regmap)); current.dirty=branch_regs[j].dirty; } break; } } while(j>=0) { if(ba[j]==start+i*2+2) { for(hr=0;hr0&&(itype[i-1]==RJUMP||itype[i-1]==UJUMP)) { cc=0; } else if(itype[i]==CJUMP||itype[i]==SJUMP) { cc=1; } else { cc+=cycles[i]; } if(!is_ds[i]) { regs[i].dirty=current.dirty; regs[i].isdoingcp=current.isdoingcp; memcpy(cpmap[i],current.cpmap,sizeof(current.cpmap)); } for(hr=0;hr=0) { if(regmap_pre[i][hr]!=regs[i].regmap[hr]) { regs[i].wasdoingcp&=~(1<=0;i--) { int hr; if(itype[i]==RJUMP||itype[i]==UJUMP||itype[i]==CJUMP||itype[i]==SJUMP) { if(ba[i]=(start+slen*2)) { // Branch out of this block, don't need anything nr=0; } else { // Internal branch // Need whatever matches the target int t=(ba[i]-start)>>1; nr=0; for(hr=0;hr=0) { if(regs[i].regmap_entry[hr]==regs[t].regmap_entry[hr]) nr|=1<=0&&get_reg(regs[i+2].regmap_entry,regmap_pre[i+2][hr])<0) nr&=~(1<=0) if(!((nr>>hr)&1)) printf("%x-bogus(%d=%d)\n",start+i*2,hr,regmap_entry[i+2][hr]); } } } else if(itype[i]==CJUMP) { if(i=0&&get_reg(regs[i+1].regmap_entry,regmap_pre[i+1][hr])<0) nr&=~(1<=0) if(!((nr>>hr)&1)) printf("%x-bogus(%d=%d)\n",start+i*2,hr,regmap_entry[i+2][hr]); } } } // Don't need stuff which is overwritten for(hr=0;hr=0&&rt1[i+1]==(regs[i].regmap[hr]&63)) nr&=~(1<=0&&rt2[i+1]==(regs[i].regmap[hr]&63)) nr&=~(1<=0&&rs1[i+1]==regmap_pre[i][hr]) nr|=1<=0&&rs2[i+1]==regmap_pre[i][hr]) nr|=1<=0&&rs3[i+1]==regmap_pre[i][hr]) nr|=1<=0&&rs1[i+1]==regs[i].regmap_entry[hr]) nr|=1<=0&&rs2[i+1]==regs[i].regmap_entry[hr]) nr|=1<=0&&rs3[i+1]==regs[i].regmap_entry[hr]) nr|=1<>dep1[i+1])&1)) { // if(dep1[i+1]==(regmap_pre[i][hr]&63)) nr|=1<>dep2[i+1])&1)) { // if(dep1[i+1]==(regs[i].regmap_entry[hr]&63)) nr|=1<=0&&get_reg(regs[i+1].regmap_entry,regmap_pre[i+1][hr])<0) nr&=~(1<=0&&rt1[i]==(regs[i].regmap[hr]&63)) nr&=~(1<=0&&rt2[i]==(regs[i].regmap[hr]&63)) nr&=~(1<=0&&rs1[i]==regmap_pre[i][hr]) nr|=1<=0&&rs2[i]==regmap_pre[i][hr]) nr|=1<=0&&rs3[i]==regmap_pre[i][hr]) nr|=1<=0&&rs1[i]==regs[i].regmap_entry[hr]) nr|=1<=0&&rs2[i]==regs[i].regmap_entry[hr]) nr|=1<=0&&rs3[i]==regs[i].regmap_entry[hr]) nr|=1<>dep1[i])&1)) { // if(dep1[i]==(regmap_pre[i][hr]&63)) nr|=1<>dep2[i])&1)) { // if(dep2[i]==(regmap_pre[i][hr]&63)) nr|=1<0&&!bt[i]&&((regs[i].wasdirty>>hr)&1)) { if(regmap_pre[i][hr]>=0&&!((unneeded_reg[i]>>regmap_pre[i][hr])&1)) { if(rt1[i-1]==(regmap_pre[i][hr]&63)) nr|=1<=0&&!((unneeded_reg[i]>>regs[i].regmap_entry[hr])&1)) { if(rt1[i-1]==(regs[i].regmap_entry[hr]&63)) nr|=1<>hr)&1)) { if(regs[i].regmap_entry[hr]!=CCREG) regs[i].regmap_entry[hr]=-1; if((regs[i].regmap[hr]&63)!=rs1[i] && (regs[i].regmap[hr]&63)!=rs2[i] && (regs[i].regmap[hr]&63)!=rt1[i] && (regs[i].regmap[hr]&63)!=rt2[i] && (regs[i].regmap[hr]&63)!=PTEMP && (regs[i].regmap[hr]&63)!=CCREG) { if(itype[i]==CJUMP) { regs[i].regmap[hr]=-1; regs[i].isdoingcp&=~(1<=0||get_reg(branch_regs[i].regmap,rt1[i+1]|64)>=0) //{ // d1=dep1[i+1]; // d2=dep2[i+1]; //} if(itype[i+1]==LOAD || itype[i+1]==STORE || itype[i+1]==RMW || itype[i+1]==PCREL || itype[i+1]==SYSTEM || source[i]==0x002B /* RTE */ ) temp1=MOREG; if(itype[i+1]==COMPLEX) { temp1=MACH; temp2=MACL; } if(regs[i].regmap[hr]!=rs1[i] && regs[i].regmap[hr]!=rs2[i] && regs[i].regmap[hr]!=rs3[i] && regs[i].regmap[hr]!=rt1[i] && regs[i].regmap[hr]!=rt2[i] && regs[i].regmap[hr]!=rs1[i+1] && regs[i].regmap[hr]!=rs2[i+1] && regs[i].regmap[hr]!=rs3[i+1] && regs[i].regmap[hr]!=rt1[i+1] && regs[i].regmap[hr]!=rt2[i+1] && regs[i].regmap[hr]!=RHASH && regs[i].regmap[hr]!=RHTBL && regs[i].regmap[hr]!=RTEMP && regs[i].regmap[hr]!=PTEMP && regs[i].regmap[hr]!=CCREG && regs[i].regmap[hr]!=temp1 && regs[i].regmap[hr]!=temp2 ) { regs[i].regmap[hr]=-1; regs[i].isdoingcp&=~(1<0) { int temp1=-1,temp2=-1; //if(get_reg(regs[i].regmap,rt1[i]|64)>=0) //{ // d1=dep1[i]; // d2=dep2[i]; //} if(itype[i]==LOAD || itype[i]==STORE || itype[i]==RMW || itype[i]==PCREL || itype[i]==SYSTEM ) temp1=MOREG; if(itype[i]==COMPLEX) { temp1=MACH; temp2=MACL; } else if(itype[i]==SYSTEM) { temp2=CCREG; } if(regs[i].regmap[hr]!=rt1[i] && regs[i].regmap[hr]!=rt2[i] && regs[i].regmap[hr]!=rs1[i] && regs[i].regmap[hr]!=rs2[i] && regs[i].regmap[hr]!=rs3[i] && regs[i].regmap[hr]!=temp1 && regs[i].regmap[hr]!=temp2 && regs[i].regmap[hr]!=CCREG) { if(i=start && ba[i]<(start+i*2)) if(itype[i]==CJUMP||itype[i+1]==NOP||itype[i+1]==MOV||itype[i+1]==ALU ||itype[i+1]==SHIFTIMM||itype[i+1]==IMM8||itype[i+1]==LOAD ||itype[i+1]==STORE||itype[i+1]==RMW||itype[i+1]==PCREL||itype[i+1]==EXT||itype[i+1]==FLAGS) { // Track register allocation int t=(ba[i]-start)>>1; if(t>0&&(itype[t-1]!=UJUMP&&itype[t-1]!=RJUMP&&itype[t-1]!=SJUMP)) // loop_preload can't handle jumps into delay slots if(t<2||(itype[t-2]!=UJUMP&&itype[t-2]!=RJUMP)||rt1[t-2]!=PR) // call/ret assumes no registers allocated for(hr=0;hr=0) { if(f_regmap[hr]!=regs[i].regmap[hr]) { // dealloc old register int n; for(n=0;n=0) { if(f_regmap[hr]!=branch_regs[i].regmap[hr]) { // dealloc old register int n; for(n=0;nclean transition //if(t>0) if(get_reg(regmap_pre[t],f_regmap[hr])>=0) if((regs[t].wasdirty>>get_reg(regmap_pre[t],f_regmap[hr]))&1) f_regmap[hr]=-1; // This check isn't required, but it's a good idea. We can't hoist // the load if the register was already allocated, so there's no // point wasting time analyzing most of these cases. It only // "succeeds" when the mapping was different and the load can be // replaced with a mov, which is of negligible benefit. So such // cases are skipped below. if(f_regmap[hr]>=0) { if(regs[t].regmap[hr]==f_regmap[hr]||(regs[t].regmap_entry[hr]<0&&get_reg(regmap_pre[t],f_regmap[hr])<0)) { int r=f_regmap[hr]; for(j=t;j<=i;j++) { //printf("Test %x -> %x, %x %d/%d\n",start+i*2,ba[i],start+j*2,hr,r); if(r>r)&1)) break; if(regs[j].regmap[hr]==f_regmap[hr]&&(f_regmap[hr]&63) %x, %x %d/%d\n",start+i*2,ba[i],start+j*2,hr,r); int k; if(regs[i].regmap[hr]==-1&&branch_regs[i].regmap[hr]==-1) { if(get_reg(regs[i+2].regmap,f_regmap[hr])>=0) break; k=i; while(k>1&®s[k-1].regmap[hr]==-1) { if(count_free_regs(regs[k-1].regmap)<=minimum_free_regs[k-1]) { //printf("no free regs for store %x\n",start+(k-1)*4); break; } if(get_reg(regs[k-1].regmap,f_regmap[hr])>=0) { //printf("no-match due to different register\n"); break; } if(itype[k-2]==UJUMP||itype[k-2]==RJUMP||itype[k-2]==CJUMP||itype[k-2]==SJUMP) { //printf("no-match due to branch\n"); break; } // call/ret fast path assumes no registers allocated if(k>2&&(itype[k-3]==UJUMP||itype[k-3]==RJUMP)&&rt1[k-3]==PR) { break; } k--; } if(regs[k-1].regmap[hr]==f_regmap[hr]&®map_pre[k][hr]==f_regmap[hr]) { //printf("Extend r%d, %x ->\n",hr,start+k*4); while(k\n",hr,start+k*4); break; } assert(regs[i-1].regmap[hr]==f_regmap[hr]); if(regs[i-1].regmap[hr]==f_regmap[hr]&®map_pre[i][hr]==f_regmap[hr]) { //printf("OK fill %x (r%d)\n",start+i*4,hr); regs[i].regmap_entry[hr]=f_regmap[hr]; regs[i].regmap[hr]=f_regmap[hr]; regs[i].wasdirty&=~(1<=0) break; if(get_reg(regs[j].regmap,f_regmap[hr])>=0) { //printf("no-match due to different register\n"); break; } if(itype[j]==UJUMP||itype[j]==RJUMP) { // Stop on unconditional branch break; } if(itype[j]==SJUMP) { if(ooo[j]) { if(count_free_regs(regs[j].regmap)<=minimum_free_regs[j+1]) break; }else{ if(count_free_regs(branch_regs[j].regmap)<=minimum_free_regs[j+1]) break; } if(get_reg(branch_regs[j].regmap,f_regmap[hr])>=0) { //printf("no-match due to different register (branch)\n"); break; } } if(count_free_regs(regs[j].regmap)<=minimum_free_regs[j]) { //printf("No free regs for store %x\n",start+j*4); break; } } } } } } }else{ // Non branch or undetermined branch target for(hr=0;hr=0) { if(f_regmap[hr]!=regs[i].regmap[hr]) { // dealloc old register int n; for(n=0;n %x\n",start+k*4,start+j*4); while(ki&&f_regmap[HOST_CCREG]==CCREG) { //printf("Extend backwards\n"); int k; k=i; while(regs[k-1].regmap[HOST_CCREG]==-1) { if(count_free_regs(regs[k-1].regmap)<=minimum_free_regs[k-1]) { //printf("no free regs for store %x\n",start+(k-1)*4); break; } k--; } if(regs[k-1].regmap[HOST_CCREG]==CCREG) { //printf("Extend CC, %x ->\n",start+k*4); while(k<=i) { regs[k].regmap_entry[HOST_CCREG]=CCREG; regs[k].regmap[HOST_CCREG]=CCREG; regmap_pre[k+1][HOST_CCREG]=CCREG; regs[k+1].wasdirty|=1<\n",start+k*4); } } } // Don't try to add registers to complex instructions like MAC, division, etc. if(itype[i]!=STORE&&itype[i]!=RMW&&itype[i]!=PCREL&& itype[i]!=NOP&&itype[i]!=MOV&&itype[i]!=ALU&&itype[i]!=SHIFTIMM&& itype[i]!=IMM8&&itype[i]!=LOAD&&itype[i]!=EXT&&itype[i]!=FLAGS) { memcpy(f_regmap,regs[i].regmap,sizeof(f_regmap)); } } } // Cache memory_map pointer if a register is available #ifndef HOST_IMM_ADDR32 { int earliest_available[HOST_REGS]; int loop_start[HOST_REGS]; int score[HOST_REGS]; int end[HOST_REGS]; int reg=MMREG; // Init for(hr=0;hr=0) { score[hr]=0;earliest_available[hr]=i+1; loop_start[hr]=MAXBLOCK; } if(itype[i]==UJUMP||itype[i]==RJUMP||itype[i]==SJUMP) { if(branch_regs[i].regmap[hr]>=0) { score[hr]=0;earliest_available[hr]=i+2; loop_start[hr]=MAXBLOCK; } } } // No register allocations after unconditional jumps if(itype[i]==UJUMP||itype[i]==RJUMP) { for(hr=0;hr=0) break; if(itype[j]==UJUMP||itype[j]==RJUMP||itype[j]==SJUMP) { if(branch_regs[j].regmap[hr]>=0) break; if(ooo[j]) { if(count_free_regs(regs[j].regmap)<=minimum_free_regs[j+1]) break; }else{ if(count_free_regs(branch_regs[j].regmap)<=minimum_free_regs[j+1]) break; } } else if(count_free_regs(regs[j].regmap)<=minimum_free_regs[j]) break; if(itype[j]==UJUMP||itype[j]==RJUMP||itype[j]==CJUMP||itype[j]==SJUMP) { int t=(ba[j]-start)>>1; if(t=earliest_available[hr]) { if(t==1||(t>1&&itype[t-2]!=UJUMP&&itype[t-2]!=RJUMP)||(t>1&&rt1[t-2]!=PR)) { // call/ret assumes no registers allocated // Score a point for hoisting loop invariant if(tscore[maxscore]) { maxscore=hr; //printf("highest score: %d %d (%x->%x)\n",score[hr],hr,start+i*2,start+end[hr]*2); } } } if(score[maxscore]>1) { if(i=0) {printf("oops: %x %x was %d=%d\n",loop_start[maxscore]*2+start,j*2+start,maxscore,regs[j].regmap[maxscore]);} assert(regs[j].regmap[maxscore]<0); if(j>loop_start[maxscore]) regs[j].regmap_entry[maxscore]=reg; regs[j].regmap[maxscore]=reg; regs[j].dirty&=~(1<>1; if(t==loop_start[maxscore]) { if(t==1||(t>1&&itype[t-2]!=UJUMP&&itype[t-2]!=RJUMP)||(t>1&&rt1[t-2]!=PR)) // call/ret assumes no registers allocated regs[t].regmap_entry[maxscore]=reg; } } else { if(j<1||(itype[j-1]!=RJUMP&&itype[j-1]!=UJUMP&&itype[j-1]!=SJUMP)) { regmap_pre[j+1][maxscore]=reg; regs[j+1].wasdirty&=~(1<=0) { if((hr=get_reg(regs[i+1].regmap,rs1[i+1]==TBIT?SR:rs1[i+1]))>=0) { if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0 &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { regs[i].regmap[hr]=regs[i+1].regmap[hr]; regmap_pre[i+1][hr]=regs[i+1].regmap[hr]; regs[i+1].regmap_entry[hr]=regs[i+1].regmap[hr]; regs[i].isdoingcp&=~(1<=0) { if((hr=get_reg(regs[i+1].regmap,rs2[i+1]==TBIT?SR:rs2[i+1]))>=0) { if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0 &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { regs[i].regmap[hr]=regs[i+1].regmap[hr]; regmap_pre[i+1][hr]=regs[i+1].regmap[hr]; regs[i+1].regmap_entry[hr]=regs[i+1].regmap[hr]; regs[i].isdoingcp&=~(1<=0) { if((hr=get_reg(regs[i+1].regmap,rs3[i+1]==TBIT?SR:rs3[i+1]))>=0) { if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0 &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { regs[i].regmap[hr]=regs[i+1].regmap[hr]; regmap_pre[i+1][hr]=regs[i+1].regmap[hr]; regs[i+1].regmap_entry[hr]=regs[i+1].regmap[hr]; regs[i].isdoingcp&=~(1<=0) { if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0 &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { regs[i].regmap[hr]=regs[i+1].regmap[hr]; regmap_pre[i+1][hr]=regs[i+1].regmap[hr]; regs[i+1].regmap_entry[hr]=regs[i+1].regmap[hr]; regs[i].isdoingcp&=~(1<=0&&get_reg(regs[i+1].regmap,rs1[i+1])<0) { if((hr=get_reg(regs[i+1].regmap,rt1[i+1]))>=0) { if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0 &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { regs[i].regmap[hr]=rs1[i+1]; regmap_pre[i+1][hr]=rs1[i+1]; regs[i+1].regmap_entry[hr]=rs1[i+1]; regs[i].isdoingcp&=~(1<=0&&get_reg(regs[i+1].regmap,rs1[i+1])<0) { if((hr=get_reg(regs[i+1].regmap,rt1[i+1]))>=0) { if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0 &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { regs[i].regmap[hr]=rs1[i+1]; regmap_pre[i+1][hr]=rs1[i+1]; regs[i+1].regmap_entry[hr]=rs1[i+1]; regs[i].isdoingcp&=~(1<=0) { int sr=get_reg(regs[i+1].regmap,rs1[i+1]); if(sr>=0&&((regs[i+1].wasdoingcp>>sr)&1) &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { int nr; if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0) { regs[i].regmap[hr]=MGEN1+((i+1)&1); regmap_pre[i+1][hr]=MGEN1+((i+1)&1); regs[i+1].regmap_entry[hr]=MGEN1+((i+1)&1); regs[i].isdoingcp&=~(1<=0) { // move it to another register regs[i+1].regmap[hr]=-1; regmap_pre[i+2][hr]=-1; regs[i+1].regmap[nr]=MOREG; regmap_pre[i+2][nr]=MOREG; regs[i].regmap[nr]=MGEN1+((i+1)&1); regmap_pre[i+1][nr]=MGEN1+((i+1)&1); regs[i+1].regmap_entry[nr]=MGEN1+((i+1)&1); regs[i].isdoingcp&=~(1<=0); if(regs[i].regmap[hr]<0&®s[i+1].regmap_entry[hr]<0 &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { regs[i].regmap[hr]=rs1[i+1]; regmap_pre[i+1][hr]=rs1[i+1]; regs[i+1].regmap_entry[hr]=rs1[i+1]; regs[i].isdoingcp&=~(1<=0&®s[i].regmap[hr]<0 &&count_free_regs(regs[i].regmap)>minimum_free_regs[i]) { int rs=get_reg(regs[i+1].regmap,rs1[i+1]); if(rs>=0&&((regs[i+1].wasdoingcp>>rs)&1)) { regs[i].regmap[hr]=AGEN1+((i+1)&1); regmap_pre[i+1][hr]=AGEN1+((i+1)&1); regs[i+1].regmap_entry[hr]=AGEN1+((i+1)&1); regs[i].isdoingcp&=~(1<=0;i--) { if(itype[i]==CJUMP||itype[i]==SJUMP) { // Avoid unnecessary constant propagation int hr; u32 sregs; for(hr=0;hr=0) { if(itype[i]==SJUMP) { if(regs[i].regmap_entry[hr]==rs1[i+1]) continue; if(regs[i].regmap_entry[hr]==rs2[i+1]) continue; if(regs[i].regmap_entry[hr]==rs3[i+1]) continue; if(regs[i].regmap_entry[hr]==rt1[i+1]) continue; if(regs[i].regmap_entry[hr]==rt2[i+1]) continue; } if(i>0) { if(regs[i].regmap_entry[hr]==rs1[i-1]) continue; if(regs[i].regmap_entry[hr]==rs2[i-1]) continue; if(regs[i].regmap_entry[hr]==rs3[i-1]) continue; if(regs[i].regmap_entry[hr]==rt1[i-1]) continue; if(regs[i].regmap_entry[hr]==rt2[i-1]) continue; } //if(regs[i].wasdoingcp&(1<=0) sregs|=1<=0) sregs|=1<=0) sregs|=1<=0) sregs|=1<>r)&1) { if(r==SR) printf(" SR(16)"); else if(r==GBR) printf(" GBR(17)"); else if(r==VBR) printf(" VBR(18)"); else if(r==MACH) printf(" MACH(19)"); else if(r==MACL) printf(" MACL(20)"); else if(r==PR) printf(" PR(21)"); else if(r==TBIT) printf(" T(22)"); else printf(" r%d",r); } } printf("\n"); #if defined(__i386__) || defined(__x86_64__) printf("pre: eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",regmap_pre[i][0],regmap_pre[i][1],regmap_pre[i][2],regmap_pre[i][3],regmap_pre[i][5],regmap_pre[i][6],regmap_pre[i][7]); #endif #ifdef __arm__ printf("pre: r0=%d r1=%d r2=%d r3=%d r4=%d r5=%d r6=%d r7=%d r8=%d r9=%d r10=%d r12=%d\n",regmap_pre[i][0],regmap_pre[i][1],regmap_pre[i][2],regmap_pre[i][3],regmap_pre[i][4],regmap_pre[i][5],regmap_pre[i][6],regmap_pre[i][7],regmap_pre[i][8],regmap_pre[i][9],regmap_pre[i][10],regmap_pre[i][12]); #endif printf("needs: "); if(needed_reg[i]&1) printf("eax "); if((needed_reg[i]>>1)&1) printf("ecx "); if((needed_reg[i]>>2)&1) printf("edx "); if((needed_reg[i]>>3)&1) printf("ebx "); if((needed_reg[i]>>5)&1) printf("ebp "); if((needed_reg[i]>>6)&1) printf("esi "); if((needed_reg[i]>>7)&1) printf("edi "); printf("\n"); #if defined(__i386__) || defined(__x86_64__) printf("entry: eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d\n",regs[i].regmap_entry[0],regs[i].regmap_entry[1],regs[i].regmap_entry[2],regs[i].regmap_entry[3],regs[i].regmap_entry[5],regs[i].regmap_entry[6],regs[i].regmap_entry[7]); printf("dirty: "); if(regs[i].wasdirty&1) printf("eax "); if((regs[i].wasdirty>>1)&1) printf("ecx "); if((regs[i].wasdirty>>2)&1) printf("edx "); if((regs[i].wasdirty>>3)&1) printf("ebx "); if((regs[i].wasdirty>>5)&1) printf("ebp "); if((regs[i].wasdirty>>6)&1) printf("esi "); if((regs[i].wasdirty>>7)&1) printf("edi "); #endif #ifdef __arm__ printf("entry: r0=%d r1=%d r2=%d r3=%d r4=%d r5=%d r6=%d r7=%d r8=%d r9=%d r10=%d r12=%d\n",regs[i].regmap_entry[0],regs[i].regmap_entry[1],regs[i].regmap_entry[2],regs[i].regmap_entry[3],regs[i].regmap_entry[4],regs[i].regmap_entry[5],regs[i].regmap_entry[6],regs[i].regmap_entry[7],regs[i].regmap_entry[8],regs[i].regmap_entry[9],regs[i].regmap_entry[10],regs[i].regmap_entry[12]); printf("dirty: "); if(regs[i].wasdirty&1) printf("r0 "); if((regs[i].wasdirty>>1)&1) printf("r1 "); if((regs[i].wasdirty>>2)&1) printf("r2 "); if((regs[i].wasdirty>>3)&1) printf("r3 "); if((regs[i].wasdirty>>4)&1) printf("r4 "); if((regs[i].wasdirty>>5)&1) printf("r5 "); if((regs[i].wasdirty>>6)&1) printf("r6 "); if((regs[i].wasdirty>>7)&1) printf("r7 "); if((regs[i].wasdirty>>8)&1) printf("r8 "); if((regs[i].wasdirty>>9)&1) printf("r9 "); if((regs[i].wasdirty>>10)&1) printf("r10 "); if((regs[i].wasdirty>>12)&1) printf("r12 "); #endif printf("ccadj=%d",ccadj[i]); printf("\n"); disassemble_inst(i); //printf ("ccadj[%d] = %d\n",i,ccadj[i]); #if defined(__i386__) || defined(__x86_64__) printf("eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d dirty: ",regs[i].regmap[0],regs[i].regmap[1],regs[i].regmap[2],regs[i].regmap[3],regs[i].regmap[5],regs[i].regmap[6],regs[i].regmap[7]); if(regs[i].dirty&1) printf("eax "); if((regs[i].dirty>>1)&1) printf("ecx "); if((regs[i].dirty>>2)&1) printf("edx "); if((regs[i].dirty>>3)&1) printf("ebx "); if((regs[i].dirty>>5)&1) printf("ebp "); if((regs[i].dirty>>6)&1) printf("esi "); if((regs[i].dirty>>7)&1) printf("edi "); #endif #ifdef __arm__ printf("r0=%d r1=%d r2=%d r3=%d r4=%d r5=%d r6=%d r7=%d r8=%d r9=%d r10=%d r12=%d dirty: ",regs[i].regmap[0],regs[i].regmap[1],regs[i].regmap[2],regs[i].regmap[3],regs[i].regmap[4],regs[i].regmap[5],regs[i].regmap[6],regs[i].regmap[7],regs[i].regmap[8],regs[i].regmap[9],regs[i].regmap[10],regs[i].regmap[12]); if(regs[i].dirty&1) printf("r0 "); if((regs[i].dirty>>1)&1) printf("r1 "); if((regs[i].dirty>>2)&1) printf("r2 "); if((regs[i].dirty>>3)&1) printf("r3 "); if((regs[i].dirty>>4)&1) printf("r4 "); if((regs[i].dirty>>5)&1) printf("r5 "); if((regs[i].dirty>>6)&1) printf("r6 "); if((regs[i].dirty>>7)&1) printf("r7 "); if((regs[i].dirty>>8)&1) printf("r8 "); if((regs[i].dirty>>9)&1) printf("r9 "); if((regs[i].dirty>>10)&1) printf("r10 "); if((regs[i].dirty>>12)&1) printf("r12 "); #endif printf("\n"); if(regs[i].isdoingcp) { printf("constants: "); #if defined(__i386__) || defined(__x86_64__) if(regs[i].isdoingcp&1) printf("eax=%x ",(int)cpmap[i][0]); if((regs[i].isdoingcp>>1)&1) printf("ecx=%x ",(int)cpmap[i][1]); if((regs[i].isdoingcp>>2)&1) printf("edx=%x ",(int)cpmap[i][2]); if((regs[i].isdoingcp>>3)&1) printf("ebx=%x ",(int)cpmap[i][3]); if((regs[i].isdoingcp>>5)&1) printf("ebp=%x ",(int)cpmap[i][5]); if((regs[i].isdoingcp>>6)&1) printf("esi=%x ",(int)cpmap[i][6]); if((regs[i].isdoingcp>>7)&1) printf("edi=%x ",(int)cpmap[i][7]); #endif #ifdef __arm__ if(regs[i].isdoingcp&1) printf("r0=%x ",(int)cpmap[i][0]); if((regs[i].isdoingcp>>1)&1) printf("r1=%x ",(int)cpmap[i][1]); if((regs[i].isdoingcp>>2)&1) printf("r2=%x ",(int)cpmap[i][2]); if((regs[i].isdoingcp>>3)&1) printf("r3=%x ",(int)cpmap[i][3]); if((regs[i].isdoingcp>>4)&1) printf("r4=%x ",(int)cpmap[i][4]); if((regs[i].isdoingcp>>5)&1) printf("r5=%x ",(int)cpmap[i][5]); if((regs[i].isdoingcp>>6)&1) printf("r6=%x ",(int)cpmap[i][6]); if((regs[i].isdoingcp>>7)&1) printf("r7=%x ",(int)cpmap[i][7]); if((regs[i].isdoingcp>>8)&1) printf("r8=%x ",(int)cpmap[i][8]); if((regs[i].isdoingcp>>9)&1) printf("r9=%x ",(int)cpmap[i][9]); if((regs[i].isdoingcp>>10)&1) printf("r10=%x ",(int)cpmap[i][10]); if((regs[i].isdoingcp>>12)&1) printf("r12=%x ",(int)cpmap[i][12]); #endif printf("\n"); } if(itype[i]==RJUMP||itype[i]==UJUMP||itype[i]==CJUMP||itype[i]==SJUMP) { #if defined(__i386__) || defined(__x86_64__) printf("branch(%d): eax=%d ecx=%d edx=%d ebx=%d ebp=%d esi=%d edi=%d dirty: ",i,branch_regs[i].regmap[0],branch_regs[i].regmap[1],branch_regs[i].regmap[2],branch_regs[i].regmap[3],branch_regs[i].regmap[5],branch_regs[i].regmap[6],branch_regs[i].regmap[7]); if(branch_regs[i].dirty&1) printf("eax "); if((branch_regs[i].dirty>>1)&1) printf("ecx "); if((branch_regs[i].dirty>>2)&1) printf("edx "); if((branch_regs[i].dirty>>3)&1) printf("ebx "); if((branch_regs[i].dirty>>5)&1) printf("ebp "); if((branch_regs[i].dirty>>6)&1) printf("esi "); if((branch_regs[i].dirty>>7)&1) printf("edi "); #endif #ifdef __arm__ printf("branch(%d): r0=%d r1=%d r2=%d r3=%d r4=%d r5=%d r6=%d r7=%d r8=%d r9=%d r10=%d r12=%d dirty: ",i,branch_regs[i].regmap[0],branch_regs[i].regmap[1],branch_regs[i].regmap[2],branch_regs[i].regmap[3],branch_regs[i].regmap[4],branch_regs[i].regmap[5],branch_regs[i].regmap[6],branch_regs[i].regmap[7],branch_regs[i].regmap[8],branch_regs[i].regmap[9],branch_regs[i].regmap[10],branch_regs[i].regmap[12]); if(branch_regs[i].dirty&1) printf("r0 "); if((branch_regs[i].dirty>>1)&1) printf("r1 "); if((branch_regs[i].dirty>>2)&1) printf("r2 "); if((branch_regs[i].dirty>>3)&1) printf("r3 "); if((branch_regs[i].dirty>>4)&1) printf("r4 "); if((branch_regs[i].dirty>>5)&1) printf("r5 "); if((branch_regs[i].dirty>>6)&1) printf("r6 "); if((branch_regs[i].dirty>>7)&1) printf("r7 "); if((branch_regs[i].dirty>>8)&1) printf("r8 "); if((branch_regs[i].dirty>>9)&1) printf("r9 "); if((branch_regs[i].dirty>>10)&1) printf("r10 "); if((branch_regs[i].dirty>>12)&1) printf("r12 "); #endif printf("\n"); } } /* Pass 8 - Assembly */ { u32 dirty_pre=0; linkcount=0;stubcount=0; ds=0;is_delayslot=0; beginning=(pointer)out; for(i=0;i\n"); // load regs if(regs[i].regmap_entry[HOST_CCREG]==CCREG&®s[i].regmap[HOST_CCREG]!=CCREG) wb_register(CCREG,regs[i].regmap_entry,regs[i].wasdirty); load_regs(regs[i].regmap_entry,regs[i].regmap,rs1[i],rs2[i],rs3[i]); srloaded=(rs1[i]==TBIT||rs2[i]==TBIT||rs3[i]==TBIT||rs1[i]==SR||rs2[i]==SR||rs3[i]==SR); if(rt1[i]==TBIT||rt2[i]==TBIT) if(!srloaded&&rt1[i]!=SR&&rt2[i]!=SR) {srloaded=1;load_regs(regs[i].regmap_entry,regs[i].regmap,SR,SR,SR);} address_generation(i,®s[i],regs[i].regmap_entry); load_consts(regmap_pre[i],regs[i].regmap,i); if(itype[i]==RJUMP||itype[i]==UJUMP||itype[i]==SJUMP) { // Load the delay slot registers if necessary if(!srloaded&&rt1[i+1]!=SR&&rt2[i+1]!=SR&&(rt1[i+1]==TBIT||rt2[i+1]==TBIT)) {srloaded=1;load_regs(regs[i].regmap_entry,regs[i].regmap,SR,SR,SR);} if(rs1[i+1]!=rs1[i]&&rs1[i+1]!=rs2[i]&&rs1[i+1]!=rs3[i]) if(!srloaded||(rs1[i+1]!=TBIT&&rs1[i+1]!=SR)) load_regs(regs[i].regmap_entry,regs[i].regmap,rs1[i+1],rs1[i+1],rs1[i+1]); if(rs2[i+1]!=rs1[i+1]&&rs2[i+1]!=rs1[i]&&rs2[i+1]!=rs2[i]&&rs2[i+1]!=rs3[i]) if(!srloaded||(rs2[i+1]!=TBIT&&rs2[i+1]!=SR)) load_regs(regs[i].regmap_entry,regs[i].regmap,rs2[i+1],rs2[i+1],rs2[i+1]); if(rs3[i+1]!=rs1[i+1]&&rs3[i+1]!=rs2[i+1]&&rs3[i+1]!=rs1[i]&&rs3[i+1]!=rs2[i]&&rs3[i+1]!=rs3[i]) if(!srloaded||(rs3[i+1]!=TBIT&&rs3[i+1]!=SR)) load_regs(regs[i].regmap_entry,regs[i].regmap,rs3[i+1],rs3[i+1],rs3[i+1]); } else if(i+1>16)==0x1000) literal_pool(1024); else literal_pool_jumpover(256); } } // If the block did not end with an unconditional branch, // add a jump to the next instruction. if(i>1) { if(itype[i-2]!=UJUMP&&itype[i-2]!=RJUMP&&itype[i-1]!=DATA) { assert(i==slen); if(itype[i-2]!=SJUMP) { store_regs_bt(regs[i-1].regmap,regs[i-1].dirty,start+i*2); if(regs[i-1].regmap[HOST_CCREG]!=CCREG) emit_loadreg(CCREG,HOST_CCREG); emit_addimm(HOST_CCREG,CLOCK_DIVIDER*(ccadj[i-1]+1),HOST_CCREG); } else { store_regs_bt(regs[i-2].regmap,regs[i-2].dirty,start+i*2); assert(regs[i-2].regmap[HOST_CCREG]==CCREG); } add_to_linker((int)out,start+i*2,0); emit_jmp(0); } } else { assert(i>0); store_regs_bt(regs[i-1].regmap,regs[i-1].dirty,start+i*2); if(regs[i-1].regmap[HOST_CCREG]!=CCREG) emit_loadreg(CCREG,HOST_CCREG); emit_addimm(HOST_CCREG,CLOCK_DIVIDER*(ccadj[i-1]+1),HOST_CCREG); add_to_linker((int)out,start+i*2,0); emit_jmp(0); } // Stubs for(i=0;i %8x\n",link_addr[i][0],link_addr[i][1]); literal_pool(64); if(!link_addr[i][2]) { void *stub=out; void *addr=check_addr(link_addr[i][1]); emit_extjump(link_addr[i][0],link_addr[i][1]); if(addr) { set_jump_target(link_addr[i][0],(int)addr); add_link(link_addr[i][1],stub); } else set_jump_target(link_addr[i][0],(int)stub); } else { // Internal branch int target=(link_addr[i][1]-start)>>1; assert(target>=0&&target>1); //#else set_jump_target(link_addr[i][0],instr_addr[target]); //#endif } } // External Branch Targets (jump_in) if(copy+slen*2+4>shadow+sizeof(shadow)) copy=shadow; for(i=0;i>12; if(page>1024) page=1024+(page&1023); literal_pool(256); assem_debug("%8x (%d) <- %8x\n",instr_addr[i],i,start+i*2); assem_debug("jump_in: %x\n",start+i*2); ll_add(jump_dirty+page,vaddr,(void *)out); entry_point=do_dirty_stub(i); ll_add_nodup(jump_in+page,vaddr,(void *)entry_point); if((itype[i]==CJUMP||itype[i]==SJUMP)&&ccstub_return[i]) set_jump_target(ccstub_return[i],entry_point); // If there was an existing entry in the hash table, // replace it with the new address. // Don't add new entries. We'll insert the // ones that actually get used in check_addr(). ht_bin=hash_table[((vaddr>>16)^vaddr)&0xFFFF]; if(ht_bin[0]==vaddr) { ht_bin[1]=entry_point; } if(ht_bin[2]==vaddr) { ht_bin[3]=entry_point; } } } } // Write out the literal pool if necessary literal_pool(0); #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK // Align code if(((u32)out)&7) emit_addnop(13); #endif assert((pointer)out-beginningBASE_ADDR+(1<>12;i<=(start+slen*2)>>12;i++) { //invalid_code[i]=0; cached_code[i>>3]|=1<<(i&7); cached_code[(i^0x20000)>>3]|=1<<(i&7); #ifdef POINTERS_64BIT memory_map[i]|=0x4000000000000000LL; memory_map[i^0x20000]|=0x4000000000000000LL; #else memory_map[i]|=0x40000000; memory_map[i^0x20000]|=0x40000000; #endif } alignedstart=start&~3; index=alignedstart&0xDFFFFFFF; if(index>4194304) index=(addr|0x400000)&0x7fffff; for(i=0;i>5]|=1<<(((index+i)>>2)&7); } } /* Pass 10 - Free memory by expiring oldest blocks */ { int end=((((int)out-BASE_ADDR)>>(TARGET_SIZE_2-16))+16384)&65535; while(expirep!=end) { int shift=TARGET_SIZE_2-3; // Divide into 8 blocks int base=BASE_ADDR+((expirep>>13)<>11)&3) { case 0: // Clear jump_in and jump_dirty ll_remove_matching_addrs(jump_in+(expirep&2047),base,shift); ll_remove_matching_addrs(jump_dirty+(expirep&2047),base,shift); break; case 1: // Clear pointers ll_kill_pointers(jump_out[expirep&2047],base,shift); break; case 2: // Clear hash table for(i=0;i<32;i++) { int *ht_bin=hash_table[((expirep&2047)<<5)+i]; if((ht_bin[3]>>shift)==(base>>shift) || ((ht_bin[3]-MAX_OUTPUT_BLOCK_SIZE)>>shift)==(base>>shift)) { inv_debug("EXP: Remove hash %x -> %x\n",ht_bin[2],ht_bin[3]); ht_bin[2]=ht_bin[3]=-1; } if((ht_bin[1]>>shift)==(base>>shift) || ((ht_bin[1]-MAX_OUTPUT_BLOCK_SIZE)>>shift)==(base>>shift)) { inv_debug("EXP: Remove hash %x -> %x\n",ht_bin[0],ht_bin[1]); ht_bin[0]=ht_bin[2]; ht_bin[1]=ht_bin[3]; ht_bin[2]=ht_bin[3]=-1; } } break; case 3: // Clear jump_out if((expirep&2047)==0) { #ifdef __arm__ do_clear_cache(); #endif #ifdef USE_MINI_HT memset(mini_ht_master,-1,sizeof(mini_ht_master)); memset(mini_ht_slave,-1,sizeof(mini_ht_slave)); #endif } ll_remove_matching_addrs(jump_out+(expirep&2047),base,shift); break; } expirep=(expirep+1)&65535; } } return 0; } #include "../sh2core.h" extern int framecounter; void DynarecMasterHandleInterrupts() { if (MSH2->interrupts[MSH2->NumberOfInterrupts-1].level > ((master_reg[SR]>>4)&0xF)) { master_reg[15] -= 4; MappedMemoryWriteLong(master_reg[15], master_reg[SR]); master_reg[15] -= 4; MappedMemoryWriteLong(master_reg[15], master_pc); master_reg[SR] &= 0xFFFFFF0F; master_reg[SR] |= (MSH2->interrupts[MSH2->NumberOfInterrupts-1].level)<<4; master_pc = MappedMemoryReadLong(master_reg[VBR] + (MSH2->interrupts[MSH2->NumberOfInterrupts-1].vector << 2)); master_ip = get_addr_ht(master_pc); MSH2->NumberOfInterrupts--; MSH2->isIdle = 0; MSH2->isSleeping = 0; } //printf("DynarecMasterHandleInterrupts pc=%x ip=%x\n",master_pc,(int)master_ip); //printf("master_cc=%d slave_cc=%d\n",master_cc,slave_cc); //printf("frame=%d\n",framecounter); } void DynarecSlaveHandleInterrupts() { if (SSH2->interrupts[SSH2->NumberOfInterrupts-1].level > ((slave_reg[SR]>>4)&0xF)) { slave_reg[15] -= 4; MappedMemoryWriteLong(slave_reg[15], slave_reg[SR]); slave_reg[15] -= 4; MappedMemoryWriteLong(slave_reg[15], slave_pc); slave_reg[SR] &= 0xFFFFFF0F; slave_reg[SR] |= (SSH2->interrupts[SSH2->NumberOfInterrupts-1].level)<<4; slave_pc = MappedMemoryReadLong(slave_reg[VBR] + (SSH2->interrupts[SSH2->NumberOfInterrupts-1].vector << 2)); slave_ip = get_addr_ht(slave_pc|1); SSH2->NumberOfInterrupts--; SSH2->isIdle = 0; SSH2->isSleeping = 0; } //printf("DynarecSlaveHandleInterrupts pc=%x ip=%x\n",slave_pc,(int)slave_ip); //printf("master_cc=%d slave_cc=%d\n",master_cc,slave_cc); } #define SH2CORE_DYNAREC 2 void SH2InterpreterSendInterrupt(SH2_struct *context, u8 level, u8 vector); int SH2InterpreterGetInterrupts(SH2_struct *context, interrupt_struct interrupts[MAX_INTERRUPTS]); void SH2InterpreterSetInterrupts(SH2_struct *context, int num_interrupts, const interrupt_struct interrupts[MAX_INTERRUPTS]); int SH2DynarecInit(void) {return 0;} void SH2DynarecDeInit() { sh2_dynarec_cleanup(); } void FASTCALL SH2DynarecExec(SH2_struct *context, u32 cycles) { printf("SH2DynarecExec called! oops\n"); printf("master_ip=%x\n",(int)master_ip); exit(1); } u32 SH2DynarecGetSR(SH2_struct *context) { if(context==MSH2) return master_reg[SR]; else return slave_reg[SR]; } u32 SH2DynarecGetGBR(SH2_struct *context) { if(context==MSH2) return master_reg[GBR]; else return slave_reg[GBR]; } u32 SH2DynarecGetVBR(SH2_struct *context) { if(context==MSH2) return master_reg[VBR]; else return slave_reg[VBR]; } u32 SH2DynarecGetMACH(SH2_struct *context) { if(context==MSH2) return master_reg[MACH]; else return slave_reg[MACH]; } u32 SH2DynarecGetMACL(SH2_struct *context) { if(context==MSH2) return master_reg[MACL]; else return slave_reg[MACL]; } u32 SH2DynarecGetPR(SH2_struct *context) { if(context==MSH2) return master_reg[PR]; else return slave_reg[PR]; } u32 SH2DynarecGetGPR(SH2_struct *context, int num) { if(context==MSH2) return master_reg[num]; else return slave_reg[num]; } u32 SH2DynarecGetPC(SH2_struct *context) { if(context==MSH2) return master_pc; else return slave_pc; } void SH2DynarecSetSR(SH2_struct *context, u32 value) { if(context==MSH2) master_reg[SR]=value; else slave_reg[SR]=value; } void SH2DynarecSetGBR(SH2_struct *context, u32 value) { if(context==MSH2) master_reg[GBR]=value; else slave_reg[GBR]=value; } void SH2DynarecSetVBR(SH2_struct *context, u32 value) { if(context==MSH2) master_reg[VBR]=value; else slave_reg[VBR]=value; } void SH2DynarecSetMACH(SH2_struct *context, u32 value) { if(context==MSH2) master_reg[MACH]=value; else slave_reg[MACH]=value; } void SH2DynarecSetMACL(SH2_struct *context, u32 value) { if(context==MSH2) master_reg[MACL]=value; else slave_reg[MACL]=value; } void SH2DynarecSetPR(SH2_struct *context, u32 value) { if(context==MSH2) master_reg[PR]=value; else slave_reg[PR]=value; } void SH2DynarecSetGPR(SH2_struct *context, int num, u32 value) { if(context==MSH2) master_reg[num]=value; else slave_reg[num]=value; } void SH2DynarecSetPC(SH2_struct *context, u32 value) { //printf("SH2DynarecSetPC(%s,%x)\n",(context==MSH2)?"master":"slave",value); if(context==MSH2) { master_pc=value; master_ip=get_addr_ht(value); } else { slave_pc=value; slave_ip=get_addr_ht(value+1); } } #undef SR #undef GBR #undef VBR #undef MACH #undef MACL #undef PR void SH2DynarecGetRegisters(SH2_struct *context, sh2regs_struct *regs) { if(context==MSH2) memcpy(&(regs->R), master_reg, 16*sizeof(int)); else memcpy(&(regs->R), slave_reg, 16*sizeof(int)); regs->SR.all=SH2DynarecGetSR(context); regs->GBR=SH2DynarecGetGBR(context); regs->VBR=SH2DynarecGetVBR(context); regs->MACH=SH2DynarecGetMACH(context); regs->MACL=SH2DynarecGetMACL(context); regs->PR=SH2DynarecGetPR(context); regs->PC=SH2DynarecGetPC(context); } void SH2DynarecSetRegisters(SH2_struct *context, const sh2regs_struct *regs) { if(context==MSH2) memcpy(master_reg, &(regs->R), 16*sizeof(int)); else memcpy(slave_reg, &(regs->R), 16*sizeof(int)); SH2DynarecSetSR(context, regs->SR.all); SH2DynarecSetGBR(context, regs->GBR); SH2DynarecSetVBR(context, regs->VBR); SH2DynarecSetMACH(context, regs->MACH); SH2DynarecSetMACL(context, regs->MACL); SH2DynarecSetPR(context, regs->PR); SH2DynarecSetPC(context, regs->PC); } void SH2DynarecWriteNotify(u32 start, u32 length) { int block,wp=0; // Ignore non-RAM regions if((start&0xDFF00000)!=0x200000&&(start&0xDE000000)!=0x6000000) return; // Check if any pages contain compiled code for(block=start>>12;block<=(start+length-1)>>12;block++) wp|=((cached_code[block>>3]>>(block&7))&1); if(!wp) return; //printf("SH2DynarecWriteNotify(%x,%x)\n",start,length); invalidate_blocks(start>>12,(start+length-1)>>12); } SH2Interface_struct SH2Dynarec = { SH2CORE_DYNAREC, "SH2 Dynamic Recompiler", SH2DynarecInit, SH2DynarecDeInit, SH2DynarecReset, SH2DynarecExec, SH2DynarecGetRegisters, SH2DynarecGetGPR, SH2DynarecGetSR, SH2DynarecGetGBR, SH2DynarecGetVBR, SH2DynarecGetMACH, SH2DynarecGetMACL, SH2DynarecGetPR, SH2DynarecGetPC, SH2DynarecSetRegisters, SH2DynarecSetGPR, SH2DynarecSetSR, SH2DynarecSetGBR, SH2DynarecSetVBR, SH2DynarecSetMACH, SH2DynarecSetMACL, SH2DynarecSetPR, SH2DynarecSetPC, SH2InterpreterSendInterrupt, SH2InterpreterGetInterrupts, SH2InterpreterSetInterrupts, SH2DynarecWriteNotify }; u32 * decilinestop_p = &yabsys.DecilineStop; u32 * decilineusec_p = &yabsys.DecilineUsec; u32 * SH2CycleFrac_p = &yabsys.SH2CycleFrac; u32 * UsecFrac_p = &yabsys.UsecFrac; //u32 decilinecycles = yabsys.DecilineStop >> YABSYS_TIMING_BITS; u32 yabsys_timing_bits = YABSYS_TIMING_BITS; u32 yabsys_timing_mask = YABSYS_TIMING_MASK; int * linecount_p = &yabsys.LineCount; int * vblanklinecount_p = &yabsys.VBlankLineCount; int * maxlinecount_p = &yabsys.MaxLineCount; void * NumberOfInterruptsOffset = &((SH2_struct *)0)->NumberOfInterrupts; yabause-0.9.13.1/src/sh2_dynarec/linkage_x86.s000644 001750 001750 00000043210 12256006124 022677 0ustar00guillaumeguillaume000000 000000 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Yabause - linkage_x86.s * * Copyright (C) 2009-2011 Ari64 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ .file "linkage_x86.s" .bss .align 4 .section .rodata .text .globl YabauseDynarecOneFrameExec .type YabauseDynarecOneFrameExec, @function YabauseDynarecOneFrameExec: push %ebp mov %esp,%ebp mov master_ip, %eax xor %ecx, %ecx push %edi push %esi push %ebx push %ecx /* zero */ push %ecx push %ecx push %ecx push %ecx /* put m68k here (?) */ push %ecx call .+5 /* 40+4=44 */ mov %eax,-40(%ebp) /* overwrite return address */ /* Stack frame: arg2 - m68kcenticycles (+8/+12) arg1 - m68kcycles (+4/+8) return address (0) ebp (4/0) save edi (8/4) save esi (12/8) save ebx (16/12) decilinecount (20/16) decilinecycles (24/20) sh2cycles (28/24) scucycles (32/28) ... (36/32) ... (40/36) ret address/master_ip (44/40) (alternate esp at call) save %eax (48/44) save %ecx (52/48) save %edx (56/52) ... (esp at call) next return address (64/60) total = 64 */ /* usecinc? cyclesinc?*/ newline: /* const u32 decilinecycles = yabsys.DecilineStop >> YABSYS_TIMING_BITS; */ /* const u32 cyclesinc = yabsys.DecilineStop * 10; */ mov decilinestop_p, %eax mov yabsys_timing_bits, %ecx mov (%eax), %eax lea (%eax,%eax,4), %ebx /* decilinestop*5 */ shr %cl, %eax /* decilinecycles */ shl %ebx /* cyclesinc=decilinestop*10 */ lea (%eax,%eax,8), %edx /* decilinecycles*9 */ /* yabsys.SH2CycleFrac += cyclesinc;*/ /* sh2cycles = (yabsys.SH2CycleFrac >> (YABSYS_TIMING_BITS + 1)) << 1;*/ /* yabsys.SH2CycleFrac &= ((YABSYS_TIMING_MASK << 1) | 1);*/ mov SH2CycleFrac_p, %esi mov yabsys_timing_mask, %edi inc %ecx /* yabsys_timing_bits+1 */ add (%esi), %ebx /* SH2CycleFrac */ stc adc %edi, %edi /* ((YABSYS_TIMING_MASK << 1) | 1) */ mov %eax, -20(%ebp) /* decilinecycles */ and %ebx, %edi mov %edi, (%esi) /* SH2CycleFrac */ shr %cl, %ebx mov %ebx, -28(%ebp) /* scucycles */ add %ebx, %ebx /* sh2cycles */ mov MSH2, %eax mov NumberOfInterruptsOffset, %ecx sub %edx, %ebx /* sh2cycles(full line) - decilinecycles*9 */ mov %eax, CurrentSH2 mov %ebx, -24(%ebp) /* sh2cycles */ cmp $0, (%eax, %ecx) jne master_handle_interrupts mov master_cc, %esi sub %ebx, %esi ret /* jmp master_ip */ .size YabauseDynarecOneFrameExec, .-YabauseDynarecOneFrameExec .globl master_handle_interrupts .type master_handle_interrupts, @function master_handle_interrupts: mov -40(%ebp), %eax /* get return address */ mov %eax, master_ip call DynarecMasterHandleInterrupts mov master_ip, %eax mov master_cc, %esi mov %eax,-40(%ebp) /* overwrite return address */ sub %ebx, %esi ret /* jmp master_ip */ .size master_handle_interrupts, .-master_handle_interrupts .globl slave_entry .type slave_entry, @function slave_entry: mov 16(%esp), %ebx /* sh2cycles */ mov %esi, master_cc sub $12, %esp push %ebx call FRTExec mov %ebx, (%esp) call WDTExec mov slave_ip, %edx add $16, %esp test %edx, %edx je cc_interrupt_master /* slave not running */ mov SSH2, %eax mov NumberOfInterruptsOffset, %ecx mov %eax, CurrentSH2 cmp $0, (%eax, %ecx) jne slave_handle_interrupts mov slave_cc, %esi sub %ebx, %esi jmp *%edx /* jmp *slave_ip */ .size slave_entry, .-slave_entry .globl slave_handle_interrupts .type slave_handle_interrupts, @function slave_handle_interrupts: call DynarecSlaveHandleInterrupts mov slave_ip, %edx mov slave_cc, %esi sub %ebx, %esi jmp *%edx /* jmp *slave_ip */ .size slave_handle_interrupts, .-slave_handle_interrupts .globl cc_interrupt .type cc_interrupt, @function cc_interrupt: /* slave */ mov 16(%esp), %ebx /* sh2cycles */ mov %ebp, slave_ip mov %esi, slave_cc add $-12, %esp push %ebx call FRTExec mov %ebx, (%esp) call WDTExec add $16, %esp .size cc_interrupt, .-cc_interrupt .globl cc_interrupt_master .type cc_interrupt_master, @function cc_interrupt_master: lea 40(%esp), %ebp mov -16(%ebp), %eax /* decilinecount */ mov -20(%ebp), %ebx /* decilinecycles */ inc %eax cmp $9, %eax ja .A3 mov %eax, -16(%ebp) /* decilinecount++ */ je .A2 mov %ebx, -24(%ebp) /* sh2cycles */ .A1: mov master_cc, %esi mov MSH2, %eax mov NumberOfInterruptsOffset, %ecx mov %eax, CurrentSH2 cmp $0, (%eax, %ecx) jne master_handle_interrupts sub %ebx, %esi ret /* jmp master_ip */ .A2: call Vdp2HBlankIN jmp .A1 .A3: mov -28(%ebp), %ebx /* scucycles */ add $-12, %esp push %ebx call ScuExec call M68KSync call Vdp2HBlankOUT call ScspExec mov linecount_p, %ebx mov maxlinecount_p, %eax mov vblanklinecount_p, %ecx mov (%ebx), %edx mov (%eax), %eax mov (%ecx), %ecx inc %edx andl $0, -16(%ebp) /* decilinecount=0 */ cmp %eax, %edx /* max ? */ je nextframe mov %edx, (%ebx) /* linecount++ */ cmp %ecx, %edx /* vblank ? */ je vblankin nextline: add $16, %esp call finishline jmp newline finishline: /* CHECK - Stack align? */ /*const u32 usecinc = yabsys.DecilineUsec * 10;*/ mov decilineusec_p, %eax mov UsecFrac_p, %ebx mov yabsys_timing_bits, %ecx mov (%eax), %eax mov (%ebx), %edx lea (%eax,%eax,4), %esi mov yabsys_timing_mask, %edi add %esi, %esi /*yabsys.UsecFrac += usecinc;*/ add %edx, %esi add $-8, %esp /* Align stack */ /*SmpcExec(yabsys.UsecFrac >> YABSYS_TIMING_BITS); /*Cs2Exec(yabsys.UsecFrac >> YABSYS_TIMING_BITS); /*yabsys.UsecFrac &= YABSYS_TIMING_MASK;*/ mov %esi, (%ebx) /* UsecFrac */ shr %cl, %esi push %esi call SmpcExec /* SmpcExec may modify UsecFrac; must reload it */ mov (%ebx), %esi /* UsecFrac */ mov yabsys_timing_bits, %ecx and %esi, %edi shr %cl, %esi mov %esi, (%esp) call Cs2Exec mov %edi, (%ebx) /* UsecFrac */ mov saved_centicycles, %ecx mov 12(%ebp), %ebx /* m68kcenticycles */ mov 8(%ebp), %eax /* m68kcycles */ add %ebx, %ecx mov %ecx, %ebx add $-100, %ecx cmovnc %ebx, %ecx adc $0, %eax mov %ecx, saved_centicycles mov %eax, (%esp) /* cycles */ call M68KExec add $12, %esp ret vblankin: call SmpcINTBACKEnd call Vdp2VBlankIN call CheatDoPatches jmp nextline nextframe: call Vdp2VBlankOUT andl $0, (%ebx) /* linecount = 0 */ call finishline call M68KSync mov rccount, %esi inc %esi andl $0, invalidate_count and $0x3f, %esi cmpl $0, restore_candidate(,%esi,4) mov %esi, rccount jne .A5 .A4: mov 16(%esp), %eax add $44, %esp mov %eax, master_ip pop %ebx pop %esi pop %edi pop %ebp ret .A5: /* Move 'dirty' blocks to the 'clean' list */ mov restore_candidate(,%esi,4), %ebx mov %esi, %ebp andl $0, restore_candidate(,%esi,4) shl $5, %ebp .A6: shr $1, %ebx jnc .A7 mov %ebp, (%esp) call clean_blocks .A7: inc %ebp test $31, %ebp jne .A6 jmp .A4 .size cc_interrupt_master, .-cc_interrupt_master .globl dyna_linker .type dyna_linker, @function dyna_linker: /* eax = virtual target address */ /* ebx = instruction to patch */ mov %eax, %ecx mov $1023, %edx shr $12, %ecx and %ecx, %edx and $0xDFFFF, %ecx or $1024, %edx cmp %edx, %ecx cmova %edx, %ecx /* jump_in lookup */ mov jump_in(,%ecx,4), %edx .B1: test %edx, %edx je .B3 mov (%edx), %edi xor %eax, %edi je .B2 movl 12(%edx), %edx jmp .B1 .B2: mov (%ebx), %edi mov %esi, %ebp lea 4(%ebx,%edi,1), %esi mov %eax, %edi pusha call add_link popa mov 8(%edx), %edi mov %ebp, %esi lea -4(%edi), %edx subl %ebx, %edx movl %edx, (%ebx) jmp *%edi .B3: /* hash_table lookup */ mov %eax, %edi shr $16, %edi xor %eax, %edi movzwl %di, %edi shl $4, %edi cmp hash_table(%edi), %eax jne .B5 .B4: mov hash_table+4(%edi), %edx jmp *%edx .B5: cmp hash_table+8(%edi), %eax lea 8(%edi), %edi je .B4 /* jump_dirty lookup */ mov jump_dirty(,%ecx,4), %edx .B6: testl %edx, %edx je .B8 mov (%edx), %ecx xor %eax, %ecx je .B7 movl 12(%edx), %edx jmp .B6 .B7: mov 8(%edx), %edx /* hash_table insert */ mov hash_table-8(%edi), %ebx mov hash_table-4(%edi), %ecx mov %eax, hash_table-8(%edi) mov %edx, hash_table-4(%edi) mov %ebx, hash_table(%edi) mov %ecx, hash_table+4(%edi) jmp *%edx .B8: mov %eax, %edi pusha call sh2_recompile_block test %eax, %eax popa je dyna_linker /* shouldn't happen */ int3 .size dyna_linker, .-dyna_linker .globl jump_vaddr_eax_master .type jump_vaddr_eax_master, @function jump_vaddr_eax_master: mov %eax, %edi jmp jump_vaddr_edi_master .size jump_vaddr_eax_master, .-jump_vaddr_eax_master .globl jump_vaddr_ecx_master .type jump_vaddr_ecx_master, @function jump_vaddr_ecx_master: mov %ecx, %edi jmp jump_vaddr_edi_master .size jump_vaddr_ecx_master, .-jump_vaddr_ecx_master .globl jump_vaddr_edx_master .type jump_vaddr_edx_master, @function jump_vaddr_edx_master: mov %edx, %edi jmp jump_vaddr_edi_master .size jump_vaddr_edx_master, .-jump_vaddr_edx_master .globl jump_vaddr_ebx_master .type jump_vaddr_ebx_master, @function jump_vaddr_ebx_master: mov %ebx, %edi jmp jump_vaddr_edi_master .size jump_vaddr_ebx_master, .-jump_vaddr_ebx_master .globl jump_vaddr_ebp_master .type jump_vaddr_ebp_master, @function jump_vaddr_ebp_master: mov %ebp, %edi jmp jump_vaddr_edi_master .size jump_vaddr_ebp_master, .-jump_vaddr_ebp_master .globl jump_vaddr_eax_slave .type jump_vaddr_eax_slave, @function jump_vaddr_eax_slave: mov %eax, %edi jmp jump_vaddr_edi_slave .size jump_vaddr_eax_slave, .-jump_vaddr_eax_slave .globl jump_vaddr_ecx_slave .type jump_vaddr_ecx_slave, @function jump_vaddr_ecx_slave: mov %ecx, %edi jmp jump_vaddr_edi_slave .size jump_vaddr_ecx_slave, .-jump_vaddr_ecx_slave .globl jump_vaddr_edx_slave .type jump_vaddr_edx_slave, @function jump_vaddr_edx_slave: mov %edx, %edi jmp jump_vaddr_edi_slave .size jump_vaddr_edx_slave, .-jump_vaddr_edx_slave .globl jump_vaddr_ebx_slave .type jump_vaddr_ebx_slave, @function jump_vaddr_ebx_slave: mov %ebx, %edi jmp jump_vaddr_edi_slave .size jump_vaddr_ebx_slave, .-jump_vaddr_ebx_slave .globl jump_vaddr_ebp_slave .type jump_vaddr_ebp_slave, @function jump_vaddr_ebp_slave: mov %ebp, %edi .size jump_vaddr_ebp_slave, .-jump_vaddr_ebp_slave .globl jump_vaddr_edi_slave .type jump_vaddr_edi_slave, @function jump_vaddr_edi_slave: or $1, %edi .size jump_vaddr_edi_slave, .-jump_vaddr_edi_slave .globl jump_vaddr_edi_master .type jump_vaddr_edi_master, @function jump_vaddr_edi_master: mov %edi, %eax .size jump_vaddr_edi_master, .-jump_vaddr_edi_master .globl jump_vaddr .type jump_vaddr, @function jump_vaddr: /* Check hash table */ shr $16, %eax xor %edi, %eax movzwl %ax, %eax shl $4, %eax cmp hash_table(%eax), %edi jne .C2 .C1: mov hash_table+4(%eax), %edi jmp *%edi .C2: cmp hash_table+8(%eax), %edi lea 8(%eax), %eax je .C1 /* No hit on hash table, call compiler */ push %edi call get_addr add $4, %esp jmp *%eax .size jump_vaddr, .-jump_vaddr .globl verify_code .type verify_code, @function verify_code: /* eax = source */ /* ebx = target */ /* ecx = length */ mov -4(%eax,%ecx,1), %edi xor -4(%ebx,%ecx,1), %edi jne .D5 mov %ecx, %edx add $-4, %ecx je .D3 test $4, %edx cmove %edx, %ecx push %esi .D2: mov -4(%eax,%ecx,1), %edx mov -4(%ebx,%ecx,1), %ebp mov -8(%eax,%ecx,1), %esi xor %edx, %ebp mov -8(%ebx,%ecx,1), %edi jne .D4 xor %esi, %edi jne .D4 add $-8, %ecx jne .D2 pop %esi .D3: ret .D4: pop %esi .D5: add $4, %esp /* pop return address, we're not returning */ call get_addr add $4, %esp /* pop virtual address */ jmp *%eax .size verify_code, .-verify_code .globl WriteInvalidateLong .type WriteInvalidateLong, @function WriteInvalidateLong: mov %eax, %ecx shr $12, %ecx bt %ecx, cached_code jnc MappedMemoryWriteLong push %eax push %edx push %eax call invalidate_addr pop %eax pop %edx pop %eax jmp MappedMemoryWriteLong .size WriteInvalidateLong, .-WriteInvalidateLong .globl WriteInvalidateWord .type WriteInvalidateWord, @function WriteInvalidateWord: mov %eax, %ecx shr $12, %ecx bt %ecx, cached_code jnc MappedMemoryWriteWord push %eax push %edx push %eax call invalidate_addr pop %eax pop %edx pop %eax jmp MappedMemoryWriteWord .size WriteInvalidateWord, .-WriteInvalidateWord .globl WriteInvalidateByteSwapped .type WriteInvalidateByteSwapped, @function WriteInvalidateByteSwapped: xor $1, %eax .size WriteInvalidateByteSwapped, .-WriteInvalidateByteSwapped .globl WriteInvalidateByte .type WriteInvalidateByte, @function WriteInvalidateByte: mov %eax, %ecx shr $12, %ecx bt %ecx, cached_code jnc MappedMemoryWriteByte push %eax push %edx push %eax call invalidate_addr pop %eax pop %edx pop %eax jmp MappedMemoryWriteByte .size WriteInvalidateByte, .-WriteInvalidateByte .globl div1 .type div1, @function div1: /* eax = dividend */ /* ecx = divisor */ /* edx = sr */ bt $9, %edx /* M bit */ jc div1_negative_divisor bts $0, %edx /* Get T bit and set */ adc %eax, %eax /* rn=(rn<<1)+T */ adc %ebx, %ebx /* New Q in ebx */ mov %ecx, %ebp btr $8, %edx /* Get Q bit and clear it */ cmc sbb %edi, %edi /* 0xFFFFFFFF if old_Q clear, 0 otherwise */ sbb $0, %ebp xor %edi, %ebp add %ebp, %eax /* rn+rm if old_Q, rn-rm if !old_Q */ /* carry set if rn < old_rn */ adc %edi, %ebx /* low bit = (rn=old_rn)^new_Q */ not %edi /* if old_Q clear, edi=0 */ or %ebp, %edi /* zero if old_Q==0 && rn==old_rn */ neg %edi /* clear carry if edi==0 */ adc $-1, %ebx /* invert result for old_Q==0 && rn==old_rn */ and $1, %ebx xor %ebx, %edx /* New T = (Q==M) */ shl $8, %ebx or %ebx, %edx /* save new Q */ /* push %edx push %eax push %ecx call debug_division pop %ecx pop %eax pop %edx */ ret div1_negative_divisor: btr $0, %edx /* Get T bit and clear */ adc %eax, %eax /* rn=(rn<<1)+T */ adc %ebx, %ebx /* New Q in ebx */ mov %ecx, %ebp btr $8, %edx /* Get Q bit and clear it */ sbb %edi, %edi /* 0xFFFFFFFF if old_Q set, 0 otherwise */ sbb $0, %ebp xor %edi, %ebp not %edi /* if old_Q clear, edi=-1 */ add %ebp, %eax /* rn+rm if !old_Q, rn-rm if old_Q */ /* carry set if rn < old_rn */ adc %edi, %ebx /* low bit = (rn=old_rn)^new_Q */ or %ebp, %edi /* zero if old_Q==1 && rn==old_rn */ neg %edi /* clear carry if edi==0 */ adc $-1, %ebx /* invert result for old_Q==1 && rn==old_rn */ and $1, %ebx xor %ebx, %edx /* New T = (Q==M) */ shl $8, %ebx or %ebx, %edx /* save new Q */ ret .size div1, .-div1 .globl macl .type macl, @function macl: /* ebx = sr */ /* ebp = multiplicand address */ /* edi = multiplicand address */ /* eax = return MACL */ /* edx = return MACH */ push %edx /* MACH */ push %eax /* MACL */ mov %edi, %eax call MappedMemoryReadLong mov %eax, %esi mov %ebp, %eax call MappedMemoryReadLong add $4, %ebp add $4, %edi imul %esi add (%esp), %eax /* MACL */ adc 4(%esp), %edx /* MACH */ add $8, %esp test $0x2, %bl jne macl_saturation ret macl_saturation: mov $0xFFFF8000, %esi xor %ecx, %ecx cmp %esi, %edx cmovl %esi, %edx cmovl %ecx, %eax not %esi not %ecx cmp %esi, %edx cmovg %esi, %edx cmovg %ecx, %eax ret .size macl, .-macl .globl macw .type macw, @function macw: /* ebx = sr */ /* ebp = multiplicand address */ /* edi = multiplicand address */ /* eax = return MACL */ /* edx = return MACH */ push %edx /* MACH */ push %eax /* MACL */ mov %edi, %eax call MappedMemoryReadWord movswl %ax, %esi mov %ebp, %eax call MappedMemoryReadWord movswl %ax, %eax add $2, %ebp add $2, %edi imul %esi test $0x2, %bl jne macw_saturation add (%esp), %eax /* MACL */ adc 4(%esp), %edx /* MACH */ add $8, %esp ret macw_saturation: mov (%esp), %esi sar $31, %esi add (%esp), %eax /* MACL */ adc %esi, %edx mov $0x80000000, %esi mov $0x7FFFFFFF, %ecx add %eax, %esi adc $0, %edx cmovne %ecx, %eax not %ecx cmovl %ecx, %eax pop %edx pop %edx ret .size macw, .-macw .globl master_handle_bios .type master_handle_bios, @function master_handle_bios: mov (%esp), %edx /* get return address */ mov %eax, master_pc mov %esi, master_cc mov %edx, master_ip mov MSH2, %eax call BiosHandleFunc mov master_ip, %edx mov master_cc, %esi mov %edx, (%esp) ret /* jmp *master_ip */ .size master_handle_bios, .-master_handle_bios .globl slave_handle_bios .type slave_handle_bios, @function slave_handle_bios: pop %edx /* get return address */ mov %eax, slave_pc mov %esi, slave_cc mov %edx, slave_ip mov SSH2, %eax call BiosHandleFunc mov slave_ip, %edx mov slave_cc, %esi jmp *%edx /* jmp *slave_ip */ .size slave_handle_bios, .-slave_handle_bios .globl breakpoint .type breakpoint, @function breakpoint: ret /* Set breakpoint here for debugging */ .size breakpoint, .-breakpoint yabause-0.9.13.1/src/sh2_dynarec/linkage_arm.s000644 001750 001750 00000061736 12256006124 023046 0ustar00guillaumeguillaume000000 000000 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Yabause - linkage_arm.s * * Copyright (C) 2009-2011 Ari64 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /*.cpu arm9tdmi*/ /* .fpu softvfp .eabi_attribute 20, 1 .eabi_attribute 21, 1 .eabi_attribute 23, 3 .eabi_attribute 24, 1 .eabi_attribute 25, 1 .eabi_attribute 26, 2 .eabi_attribute 30, 6 .eabi_attribute 18, 4*/ .file "linkage_arm.s" .global sh2_dynarec_target .global dynarec_local .global master_reg .global master_cc .global master_pc .global master_ip .global slave_reg .global slave_cc .global slave_pc .global slave_ip .global mini_ht_master .global mini_ht_slave .global restore_candidate .global memory_map .global rccount .bss .align 12 .type sh2_dynarec_target, %object .size sh2_dynarec_target, 16777216 sh2_dynarec_target: .space 16777216 .align 4 .type dynarec_local, %object .size dynarec_local, 64 dynarec_local: .space 64+88+12+88+12+28+256+256+512+4194304 master_reg = dynarec_local + 64 .type master_reg, %object .size master_reg, 88 master_cc = master_reg + 88 .type master_cc, %object .size master_cc, 4 master_pc = master_cc + 4 .type master_pc, %object .size master_pc, 4 master_ip = master_pc + 4 .type master_ip, %object .size master_ip, 4 slave_reg = master_ip + 4 .type slave_reg, %object .size slave_reg, 88 slave_cc = slave_reg + 88 .type slave_cc, %object .size slave_cc, 4 slave_pc = slave_cc + 4 .type slave_pc, %object .size slave_pc, 4 slave_ip = slave_pc + 4 .type slave_ip, %object .size slave_ip, 4 m68kcenticycles = slave_ip + 4 .type m68kcenticycles, %object .size m68kcenticycles, 4 m68kcycles = m68kcenticycles + 4 .type m68kcycles, %object .size m68kcycles, 4 decilinecount = m68kcycles + 4 .type decilinecount, %object .size decilinecount, 4 decilinecycles = decilinecount + 4 .type decilinecycles, %object .size decilinecycles, 4 sh2cycles = decilinecycles + 4 .type sh2cycles, %object .size sh2cycles, 4 scucycles = sh2cycles + 4 .type scucycles, %object .size scucycles, 4 rccount = scucycles + 4 .type rccount, %object .size rccount, 4 mini_ht_master = rccount + 4 .type mini_ht_master, %object .size mini_ht_master, 256 mini_ht_slave = mini_ht_master + 256 .type mini_ht_slave, %object .size mini_ht_slave, 256 restore_candidate = mini_ht_slave + 256 .type restore_candidate, %object .size restore_candidate, 512 memory_map = restore_candidate + 512 .type memory_map, %object .size memory_map, 4194304 .text .align 2 .global YabauseDynarecOneFrameExec .type YabauseDynarecOneFrameExec, %function YabauseDynarecOneFrameExec: ldr r12, .dlptr str r0, [r12, #m68kcycles-dynarec_local-28] str r1, [r12, #m68kcenticycles-dynarec_local-28] mov r2, #0 stmia r12, {r4, r5, r6, r7, r8, r9, sl, fp, lr} sub fp, r12, #28 str r2, [r12, #decilinecount-dynarec_local-28] ldr r14, [r12, #master_ip-dynarec_local-28] newline: /*movw r0, #:lower16:decilinestop_p*/ /*movt r0, #:upper16:decilinestop_p*/ ldr r0, .dspptr /*movw r1, #:lower16:yabsys_timing_bits*/ /*movt r1, #:upper16:yabsys_timing_bits*/ ldr r1, .ytbptr /*movw r2, #:lower16:SH2CycleFrac_p*/ ldr r2, .scfptr ldr r0, [r0] /* pointer to decilinestop */ /*movt r2, #:upper16:SH2CycleFrac_p*/ /*movw r3, #:lower16:yabsys_timing_mask*/ ldr r3, .ytmptr ldr r1, [r1] /* yabsys_timing_bits */ /*movt r3, #:upper16:yabsys_timing_mask*/ ldr r2, [r2] /* pointer to SH2CycleFrac */ ldr r0, [r0] /* decilinestop */ ldr r3, [r3] /* yabsys_timing_mask */ ldr r4, [r2] /* SH2CycleFrac */ add r5, r0, r0 /* decilinestop*2 */ lsr r6, r0, r1 /* decilinecycles = decilinestop>>yabsys_timing_bits*/ add r5, r5, r0, lsl #3 /* cyclesinc=decilinestop*10 */ str r6, [fp, #decilinecycles-dynarec_local] add r1, r1, #1 /* yabsys_timing_bits+1 */ add r6, r6, r6, lsl #3 /* decilinecycles*9 */ add r3, r3, r3 add r5, r5, r4 /* cyclesinc+=SH2CycleFrac */ /*movw r7, #:lower16:MSH2*/ /*movt r7, #:upper16:MSH2*/ ldr r7, .msh2ptr orr r3, r3, #1 /* ((YABSYS_TIMING_MASK << 1) | 1) */ /*movw r8, #:lower16:NumberOfInterruptsOffset*/ /*movt r8, #:upper16:NumberOfInterruptsOffset*/ ldr r8, .nioptr and r3, r5, r3 /* SH2CycleFrac &= ... */ lsr r5, r5, r1 /* scucycles */ /*movw r9, #:lower16:CurrentSH2*/ /*movt r9, #:upper16:CurrentSH2*/ ldr r9, .csh2ptr ldr r7, [r7] /* MSH2 */ ldr r8, [r8] /* NumberOfInterruptsOffset */ str r5, [fp, #scucycles-dynarec_local] add r5, r5, r5 /* sh2cycles=scucycles*2 */ str r3, [r2] /* SH2CycleFrac */ sub r6, r5, r6 /* sh2cycles(full line) -= decilinecycles*9 */ ldr r12, [r7, r8] str r6, [fp, #sh2cycles-dynarec_local] str r7, [r9] /* CurrentSH2 */ tst r12, r12 bne master_handle_interrupts ldr r10, [fp, #master_cc-dynarec_local] sub r10, r10, r6 mov pc, r14 master_handle_interrupts: str r14, [fp, #master_ip-dynarec_local] bl DynarecMasterHandleInterrupts ldr r10, [fp, #master_cc-dynarec_local] ldr r14, [fp, #master_ip-dynarec_local] sub r10, r10, r6 mov pc, r14 .dlptr: .word dynarec_local+28 .dspptr: .word decilinestop_p .ytbptr: .word yabsys_timing_bits .scfptr: .word SH2CycleFrac_p .ytmptr: .word yabsys_timing_mask .msh2ptr: .word MSH2 .ssh2ptr: .word SSH2 .nioptr: .word NumberOfInterruptsOffset .csh2ptr: .word CurrentSH2 .lcpptr: .word linecount_p .vlcpptr: .word vblanklinecount_p .mlcpptr: .word maxlinecount_p .dupptr: .word decilineusec_p .ufpptr: .word UsecFrac_p .scptr: .word saved_centicycles .icptr: .word invalidate_count .ccptr: .word cached_code .size YabauseDynarecOneFrameExec, .-YabauseDynarecOneFrameExec .global slave_entry .type slave_entry, %function slave_entry: ldr r0, [fp, #sh2cycles-dynarec_local] str r10, [fp, #master_cc-dynarec_local] str r14, [fp, #master_ip-dynarec_local] bl FRTExec ldr r0, [fp, #sh2cycles-dynarec_local] bl WDTExec ldr r4, [fp, #slave_ip-dynarec_local] /*movw r7, #:lower16:SSH2*/ /*movt r7, #:upper16:SSH2*/ ldr r7, .ssh2ptr tst r4, r4 beq cc_interrupt_master /*movw r8, #:lower16:NumberOfInterruptsOffset*/ ldr r6, [fp, #sh2cycles-dynarec_local] /*movt r8, #:upper16:NumberOfInterruptsOffset*/ ldr r8, .nioptr /*movw r9, #:lower16:CurrentSH2*/ ldr r9, .csh2ptr ldr r7, [r7] /*movt r9, #:upper16:CurrentSH2*/ ldr r8, [r8] str r7, [r9] /* CurrentSH2 */ ldr r12, [r7, r8] tst r12, r12 bne slave_handle_interrupts ldr r10, [fp, #slave_cc-dynarec_local] sub r10, r10, r6 mov pc, r4 slave_handle_interrupts: bl DynarecSlaveHandleInterrupts ldr r10, [fp, #slave_cc-dynarec_local] sub r10, r10, r6 ldr pc, [fp, #slave_ip-dynarec_local] .size slave_entry, .-slave_entry .global cc_interrupt .type cc_interrupt, %function cc_interrupt: ldr r0, [fp, #sh2cycles-dynarec_local] str r10, [fp, #slave_cc-dynarec_local] str r8, [fp, #slave_ip-dynarec_local] bl FRTExec ldr r0, [fp, #sh2cycles-dynarec_local] bl WDTExec .size cc_interrupt, .-cc_interrupt .global cc_interrupt_master .type cc_interrupt_master, %function cc_interrupt_master: ldr r0, [fp, #decilinecount-dynarec_local] ldr r6, [fp, #decilinecycles-dynarec_local] cmp r0, #8 add r0, r0, #1 bhi .A3 str r0, [fp, #decilinecount-dynarec_local] beq .A2 str r6, [fp, #sh2cycles-dynarec_local] ldr r14, [fp, #master_ip-dynarec_local] .A1: /*movw r7, #:lower16:MSH2*/ /*movt r7, #:upper16:MSH2*/ /*movw r8, #:lower16:NumberOfInterruptsOffset*/ /*movt r8, #:upper16:NumberOfInterruptsOffset*/ /*movw r9, #:lower16:CurrentSH2*/ /*movt r9, #:upper16:CurrentSH2*/ ldr r7, .msh2ptr ldr r8, .nioptr ldr r9, .csh2ptr ldr r7, [r7] /* MSH2 */ ldr r8, [r8] /* NumberOfInterruptsOffset */ ldr r12, [r7, r8] str r7, [r9] /* CurrentSH2 */ tst r12, r12 bne master_handle_interrupts ldr r10, [fp, #master_cc-dynarec_local] sub r10, r10, r6 mov pc, r14 .A2: bl Vdp2HBlankIN ldr r14, [fp, #master_ip-dynarec_local] b .A1 .A3: ldr r0, [fp, #scucycles-dynarec_local] bl ScuExec /*movw r4, #:lower16:linecount_p*/ /*movt r4, #:upper16:linecount_p*/ ldr r4, .lcpptr bl M68KSync /*movw r5, #:lower16:vblanklinecount_p*/ /*movt r5, #:upper16:vblanklinecount_p*/ ldr r5, .vlcpptr bl Vdp2HBlankOUT /*movw r6, #:lower16:maxlinecount_p*/ /*movt r6, #:upper16:maxlinecount_p*/ ldr r6, .mlcpptr bl ScspExec ldr r4, [r4] /* pointer to linecount */ ldr r5, [r5] /* pointer to vblanklinecount */ ldr r6, [r6] /* pointer to maxlinecount */ mov r0, #0 ldr r7, [r4] /* linecount */ ldr r5, [r5] /* vblanklinecount */ ldr r6, [r6] /* maxlinecount */ add r7, r7, #1 str r0, [fp, #decilinecount-dynarec_local] cmp r5, r7 /* linecount==vblanklinecount ? */ beq vblankin cmp r6, r7 /* linecount==maxlinecount ? */ strne r7, [r4] /* linecount++ */ bleq Vdp2VBlankOUT nextline: /* finishline */ /*const u32 usecinc = yabsys.DecilineUsec * 10;*/ /*movw r3, #:lower16:decilineusec_p*/ /*movt r3, #:upper16:decilineusec_p*/ ldr r3, .dupptr /*movw r5, #:lower16:UsecFrac_p*/ /*movt r5, #:upper16:UsecFrac_p*/ ldr r5, .ufpptr /*movw r8, #:lower16:yabsys_timing_bits*/ /*movt r8, #:upper16:yabsys_timing_bits*/ ldr r8, .ytbptr /*movw r9, #:lower16:yabsys_timing_mask*/ /*movt r9, #:upper16:yabsys_timing_mask*/ ldr r9, .ytmptr ldr r3, [r3] /* pointer to decilineusec */ ldr r5, [r5] /* pointer to usecfrac */ ldr r8, [r8] /* yabsys_timing_bits */ ldr r3, [r3] /* decilineusec */ ldr r0, [r5] /* usecfrac */ ldr r9, [r9] /* yabsys_timing_mask */ add r0, r0, r3, lsl #3 /* UsecFrac += yabsys.DecilineUsec * 8 */ add r0, r0, r3, lsl #1 /* UsecFrac += yabsys.DecilineUsec * 2 */ str r0, [r5] lsr r0, r0, r8 bl SmpcExec /* SmpcExec may modify UsecFrac; must reload it */ ldr r10, [r5] /* usecfrac */ lsr r0, r10, r8 and r10, r10, r9 bl Cs2Exec /*movw r8, #:lower16:saved_centicycles*/ str r10, [r5] /* usecfrac */ /*movt r8, #:upper16:saved_centicycles*/ ldr r8, .scptr ldr r1, [fp, #m68kcenticycles-dynarec_local] ldr r2, [r8] ldr r0, [fp, #m68kcycles-dynarec_local] add r2, r2, r1 cmp r2, #100 subcs r2, r2, #100 addcs r0, r0, #1 str r2, [r8] /* saved_centicycles */ bl M68KExec ldr r14, [fp, #master_ip-dynarec_local] eors r1, r6, r7 /* linecount==maxlinecount ? */ bne newline nextframe: str r1, [r4] /* linecount=0 */ bl M68KSync ldr r2, [fp, #rccount-dynarec_local] /*movw r0, #:lower16:invalidate_count /* FIX: Put into dynarec_local? */ add r3, fp, #restore_candidate-dynarec_local /*movt r0, #:upper16:invalidate_count*/ ldr r0, .icptr add r2, r2, #1 and r2, r2, #0x3f str r2, [fp, #rccount-dynarec_local] ldr r4, [r3, r2, lsl #2] str r1, [r0] /* invalidate_count=0 */ tst r4, r4 bne .A5 .A4: add r12, fp, #28 ldmia r12, {r4, r5, r6, r7, r8, r9, sl, fp, pc} .A5: /* Move 'dirty' blocks to the 'clean' list */ lsl r5, r2, #5 str r1, [r3, r2, lsl #2] .A6: lsrs r4, r4, #1 mov r0, r5 add r5, r5, #1 blcs clean_blocks tst r5, #31 bne .A6 b .A4 vblankin: str r7, [r4] /* linecount++ */ bl SmpcINTBACKEnd add r0, r0, #0 /* NOP for Cortex-A8 branch predictor */ bl Vdp2VBlankIN add r0, r0, #0 /* NOP for Cortex-A8 branch predictor */ bl CheatDoPatches add r0, r0, #0 /* NOP for Cortex-A8 branch predictor */ b nextline .size cc_interrupt_master, .-cc_interrupt_master .align 2 .global dyna_linker .type dyna_linker, %function dyna_linker: /* r0 = virtual target address */ /* r1 = instruction to patch */ mov r6, #2048 ldr r3, .jiptr mvn r2, #0x20000 ldr r7, [r1] sub r6, r6, #1 and r2, r2, r0, lsr #12 and r6, r6, r0, lsr #12 cmp r2, #1024 add r12, r7, #2 orrcs r2, r6, #1024 ldr r5, [r3, r2, lsl #2] lsl r12, r12, #8 /* jump_in lookup */ .B1: movs r4, r5 beq .B3 ldr r3, [r5] ldr r5, [r4, #12] teq r3, r0 bne .B1 ldr r4, [r4, #8] .B2: mov r5, r1 add r1, r1, r12, asr #6 teq r1, r4 moveq pc, r4 /* Stale i-cache */ bl add_link sub r2, r4, r5 and r1, r7, #0xff000000 lsl r2, r2, #6 sub r1, r1, #2 add r1, r1, r2, lsr #8 str r1, [r5] mov pc, r4 .B3: /* hash_table lookup */ ldr r3, .jdptr eor r4, r0, r0, lsl #16 ldr r6, .htptr lsr r4, r4, #12 bic r4, r4, #15 ldr r5, [r3, r2, lsl #2] ldr r7, [r6, r4]! teq r7, r0 ldreq pc, [r6, #4] ldr r7, [r6, #8] teq r7, r0 ldreq pc, [r6, #12] /* jump_dirty lookup */ .B6: movs r4, r5 beq .B8 ldr r3, [r5] ldr r5, [r4, #12] teq r3, r0 bne .B6 .B7: ldr r1, [r4, #8] /* hash_table insert */ ldr r2, [r6] ldr r3, [r6, #4] str r0, [r6] str r1, [r6, #4] str r2, [r6, #8] str r3, [r6, #12] mov pc, r1 .B8: mov r4, r0 mov r5, r1 bl sh2_recompile_block tst r0, r0 mov r0, r4 mov r1, r5 beq dyna_linker /* shouldn't happen */ b .-8 .jiptr: .word jump_in .jdptr: .word jump_dirty .htptr: .word hash_table .align 2 .global jump_vaddr_r0_master .type jump_vaddr_r0_master, %function jump_vaddr_r0_master: eor r2, r0, r0, lsl #16 b jump_vaddr .size jump_vaddr_r0_master, .-jump_vaddr_r0_master .global jump_vaddr_r1_master .type jump_vaddr_r1_master, %function jump_vaddr_r1_master: eor r2, r1, r1, lsl #16 mov r0, r1 b jump_vaddr .size jump_vaddr_r1_master, .-jump_vaddr_r1_master .global jump_vaddr_r2_master .type jump_vaddr_r2_master, %function jump_vaddr_r2_master: mov r0, r2 eor r2, r2, r2, lsl #16 b jump_vaddr .size jump_vaddr_r2_master, .-jump_vaddr_r2_master .global jump_vaddr_r3_master .type jump_vaddr_r3_master, %function jump_vaddr_r3_master: eor r2, r3, r3, lsl #16 mov r0, r3 b jump_vaddr .size jump_vaddr_r3_master, .-jump_vaddr_r3_master .global jump_vaddr_r4_master .type jump_vaddr_r4_master, %function jump_vaddr_r4_master: eor r2, r4, r4, lsl #16 mov r0, r4 b jump_vaddr .size jump_vaddr_r4_master, .-jump_vaddr_r4_master .global jump_vaddr_r5_master .type jump_vaddr_r5_master, %function jump_vaddr_r5_master: eor r2, r5, r5, lsl #16 mov r0, r5 b jump_vaddr .size jump_vaddr_r5_master, .-jump_vaddr_r5_master .global jump_vaddr_r6_master .type jump_vaddr_r6_master, %function jump_vaddr_r6_master: eor r2, r6, r6, lsl #16 mov r0, r6 b jump_vaddr .size jump_vaddr_r6_master, .-jump_vaddr_r6_master .global jump_vaddr_r7_master .type jump_vaddr_r7_master, %function jump_vaddr_r7_master: eor r2, r7, r7, lsl #16 mov r0, r7 b jump_vaddr .size jump_vaddr_r7_master, .-jump_vaddr_r7_master .global jump_vaddr_r8_master .type jump_vaddr_r8_master, %function jump_vaddr_r8_master: eor r2, r8, r8, lsl #16 mov r0, r8 b jump_vaddr .size jump_vaddr_r8_master, .-jump_vaddr_r8_master .global jump_vaddr_r9_master .type jump_vaddr_r9_master, %function jump_vaddr_r9_master: eor r2, r9, r9, lsl #16 mov r0, r9 b jump_vaddr .size jump_vaddr_r9_master, .-jump_vaddr_r9_master .global jump_vaddr_r12_master .type jump_vaddr_r12_master, %function jump_vaddr_r12_master: eor r2, r12, r12, lsl #16 mov r0, r12 b jump_vaddr .size jump_vaddr_r12_master, .-jump_vaddr_r12_master .global jump_vaddr_r0_slave .type jump_vaddr_r0_slave, %function jump_vaddr_r0_slave: eor r2, r0, r0, lsl #16 b jump_vaddr_slave .size jump_vaddr_r0_slave, .-jump_vaddr_r0_slave .global jump_vaddr_r1_slave .type jump_vaddr_r1_slave, %function jump_vaddr_r1_slave: eor r2, r1, r1, lsl #16 mov r0, r1 b jump_vaddr_slave .size jump_vaddr_r1_slave, .-jump_vaddr_r1_slave .global jump_vaddr_r2_slave .type jump_vaddr_r2_slave, %function jump_vaddr_r2_slave: mov r0, r2 eor r2, r2, r2, lsl #16 b jump_vaddr_slave .size jump_vaddr_r2_slave, .-jump_vaddr_r2_slave .global jump_vaddr_r3_slave .type jump_vaddr_r3_slave, %function jump_vaddr_r3_slave: eor r2, r3, r3, lsl #16 mov r0, r3 b jump_vaddr_slave .size jump_vaddr_r3_slave, .-jump_vaddr_r3_slave .global jump_vaddr_r4_slave .type jump_vaddr_r4_slave, %function jump_vaddr_r4_slave: eor r2, r4, r4, lsl #16 mov r0, r4 b jump_vaddr_slave .size jump_vaddr_r4_slave, .-jump_vaddr_r4_slave .global jump_vaddr_r5_slave .type jump_vaddr_r5_slave, %function jump_vaddr_r5_slave: eor r2, r5, r5, lsl #16 mov r0, r5 b jump_vaddr_slave .size jump_vaddr_r5_slave, .-jump_vaddr_r5_slave .global jump_vaddr_r6_slave .type jump_vaddr_r6_slave, %function jump_vaddr_r6_slave: eor r2, r6, r6, lsl #16 mov r0, r6 b jump_vaddr_slave .size jump_vaddr_r6_slave, .-jump_vaddr_r6_slave .global jump_vaddr_r7_slave .type jump_vaddr_r7_slave, %function jump_vaddr_r7_slave: eor r2, r7, r7, lsl #16 mov r0, r7 b jump_vaddr_slave .size jump_vaddr_r7_slave, .-jump_vaddr_r7_slave .global jump_vaddr_r8_slave .type jump_vaddr_r8_slave, %function jump_vaddr_r8_slave: eor r2, r8, r8, lsl #16 mov r0, r8 b jump_vaddr_slave .size jump_vaddr_r8_slave, .-jump_vaddr_r8_slave .global jump_vaddr_r9_slave .type jump_vaddr_r9_slave, %function jump_vaddr_r9_slave: eor r2, r9, r9, lsl #16 mov r0, r9 b jump_vaddr_slave .size jump_vaddr_r9_slave, .-jump_vaddr_r9_slave .global jump_vaddr_r12_slave .type jump_vaddr_r12_slave, %function jump_vaddr_r12_slave: eor r2, r12, r12, lsl #16 mov r0, r12 b jump_vaddr_slave .size jump_vaddr_r12_slave, .-jump_vaddr_r12_slave .global jump_vaddr_slave .type jump_vaddr_slave, %function jump_vaddr_slave: add r2, r2, #65536 add r0, r0, #1 .size jump_vaddr_slave, .-jump_vaddr_slave .global jump_vaddr .type jump_vaddr, %function jump_vaddr: ldr r1, .htptr mvn r3, #15 and r2, r3, r2, lsr #12 ldr r2, [r1, r2]! teq r2, r0 ldreq pc, [r1, #4] ldr r2, [r1, #8] teq r2, r0 ldreq pc, [r1, #12] bl get_addr mov pc, r0 .size jump_vaddr, .-jump_vaddr .align 2 .global verify_code .type verify_code, %function verify_code: /* r1 = source */ /* r2 = target */ /* r3 = length */ tst r3, #4 mov r4, #0 add r3, r1, r3 mov r5, #0 ldrne r4, [r1], #4 mov r12, #0 ldrne r5, [r2], #4 teq r1, r3 beq .D3 .D2: ldr r7, [r1], #4 eor r9, r4, r5 ldr r8, [r2], #4 orrs r9, r9, r12 bne .D4 ldr r4, [r1], #4 eor r12, r7, r8 ldr r5, [r2], #4 cmp r1, r3 bcc .D2 teq r7, r8 .D3: teqeq r4, r5 .D4: moveq pc, lr .D5: bl get_addr mov pc, r0 .size verify_code, .-verify_code .align 2 .global WriteInvalidateLong .type WriteInvalidateLong, %function WriteInvalidateLong: /*movw r12, #:lower16:cached_code*/ lsr r2, r0, #17 /*movt r12, #:upper16:cached_code*/ ldr r12, .ccptr lsr r3, r0, #12 ldr r2, [r12, r2, lsl #2] mov r12, #1 tst r12, r2, ror r3 beq MappedMemoryWriteLong push {r0, r1, r2, lr} bl invalidate_addr pop {r0, r1, r2, lr} b MappedMemoryWriteLong .size WriteInvalidateLong, .-WriteInvalidateLong .align 2 .global WriteInvalidateWord .type WriteInvalidateWord, %function WriteInvalidateWord: /*movw r12, #:lower16:cached_code*/ lsr r2, r0, #17 /*movt r12, #:upper16:cached_code*/ ldr r12, .ccptr lsr r3, r0, #12 bic r1, r1, #0xFF000000 ldr r2, [r12, r2, lsl #2] mov r12, #1 /*movt r1, #0*/ /*uxth r1, r1*/ bic r1, r1, #0xFF0000 tst r12, r2, ror r3 beq MappedMemoryWriteWord push {r0, r1, r2, lr} bl invalidate_addr pop {r0, r1, r2, lr} b MappedMemoryWriteWord .size WriteInvalidateWord, .-WriteInvalidateWord .align 2 .global WriteInvalidateByteSwapped .type WriteInvalidateByteSwapped, %function WriteInvalidateByteSwapped: eor r0, r0, #1 .size WriteInvalidateByteSwapped, .-WriteInvalidateByteSwapped .global WriteInvalidateByte .type WriteInvalidateByte, %function WriteInvalidateByte: /*movw r12, #:lower16:cached_code*/ lsr r2, r0, #17 /*movt r12, #:upper16:cached_code*/ ldr r12, .ccptr lsr r3, r0, #12 ldr r2, [r12, r2, lsl #2] mov r12, #1 and r1, r1, #0xff tst r12, r2, ror r3 beq MappedMemoryWriteByte push {r0, r1, r2, lr} bl invalidate_addr pop {r0, r1, r2, lr} b MappedMemoryWriteByte .size WriteInvalidateByte, .-WriteInvalidateByte .align 2 .global div1 .type div1, %function div1: /* r0 = dividend */ /* r1 = divisor */ /* r2 = sr */ tst r2, #0x200 bne div1_negative_divisor lsrs r2, r2, #1 /* Get T bit and shift out */ adcs r0, r0, r0 /* rn=(rn<<1)+T */ adc r3, r3, r3 /* New Q in r3 */ mov r4, r1 /* divisor (rm) */ tst r2, #0x80 /* Get (shifted) Q bit */ mov r5, #1 rsbeq r4, r1, #0 /* inverted if old_Q clear */ moveq r5, #0 /* 0 if old_Q clear, 1 otherwise */ adds r0, r4, r0 /* rn+rm if old_Q, rn-rm if !old_Q */ /* carry set if rn < old_rn */ adc r3, r5, r3 /* low bit = (rn=old_rn)^new_Q */ orr r2, r2, #0x80 /* set (shifted) Q bit */ and r3, r3, #1 orrs r5, r4, r5 /* zero if old_Q==0 && rn==old_rn */ eoreq r3, r3, #1 /* invert result for old_Q==0 && rn==old_rn */ orr r2, r3, r2, lsl #1 /* New T = (Q==M) */ eor r2, r2, r3, lsl #8 /* save new Q (=!T) */ /* mov r4, r0 mov r5, r1 mov r6, r2 mov r7, r14 bl debug_division mov r0, r4 mov r1, r5 mov r2, r6 mov r14, r7 */ mov pc, lr div1_negative_divisor: lsrs r2, r2, #1 /* Get T bit and shift out */ adcs r0, r0, r0 /* rn=(rn<<1)+T */ adc r3, r3, r3 /* New Q in r3 */ mov r4, r1 /* divisor (rm) */ tst r2, #0x80 /* Get (shifted) Q bit */ mov r5, #1 rsbne r4, r1, #0 /* inverted if old_Q clear */ movne r5, #0 /* 0 if old_Q set, 1 otherwise */ adds r0, r4, r0 /* rn+rm if !old_Q, rn-rm if old_Q */ /* carry set if rn < old_rn */ adc r3, r5, r3 /* low bit = (rn=old_rn)^new_Q */ bic r2, r2, #0x80 /* clear (shifted) Q bit */ and r3, r3, #1 orrs r5, r4, r5 /* zero if old_Q==1 && rn==old_rn */ /*eoreq r3, r3, #1 /* invert result for old_Q==1 && rn==old_rn */ eorne r3, r3, #1 /* don't invert result for old_Q==1 && rn==old_rn */ orr r2, r3, r2, lsl #1 /* New T = (Q==M) */ orr r2, r2, r3, lsl #8 /* save new Q (=T) */ mov pc, lr .size div1, .-div1 .align 2 .global macl .type macl, %function macl: /* r4 = sr */ /* r5 = multiplicand address */ /* r6 = multiplicand address */ /* r0 = return MACL */ /* r1 = return MACH */ mov r7, r14 mov r8, r0 /* MACL */ mov r9, r1 /* MACH */ mov r0, r6 bl MappedMemoryReadLong mov r10, r0 mov r0, r5 bl MappedMemoryReadLong add r5, r5, #4 add r6, r6, #4 mov r12, r0 mov r0, r8 mov r1, r9 mov r14, r7 smlal r0, r1, r10, r12 tst r4, #2 moveq pc, lr macl_saturation: cmn r1, #0x8000 mov r7, #0x8000 movlt r0, #0 rsblt r1, r7, #0 cmp r1, #0x8000 mvnge r0, #0 subge r1, r7, #1 mov pc, lr .size macl, .-macl .align 2 .global macw .type macw, %function macw: /* r4 = sr */ /* r5 = multiplicand address */ /* r6 = multiplicand address */ /* r0 = return MACL */ /* r1 = return MACH */ mov r7, r14 mov r8, r0 /* MACL */ mov r9, r1 /* MACH */ mov r0, r6 bl MappedMemoryReadWord /*sxth r10, r0*/ lsl r10, r0, #16 mov r0, r5 bl MappedMemoryReadWord add r5, r5, #2 add r6, r6, #2 lsl r12, r0, #16 /*sxth r12, r0*/ mov r0, r8 mov r1, r9 asr r10, r10, #16 asr r12, r12, #16 mov r14, r7 smlal r0, r1, r10, r12 tst r4, #2 moveq pc, lr macw_saturation: cmn r0, #0x80000000 adcs r7, r1, #0 mvnne r0, #0x80000000 /* 0x7FFFFFFF */ movlt r0, #0x80000000 /* 0x80000000 */ mov r1, r9 mov pc, lr .size macw, .-macw .align 2 .global master_handle_bios .type master_handle_bios, %function master_handle_bios: /*movw r1, #:lower16:MSH2*/ str r0, [fp, #master_pc-dynarec_local] /*movt r1, #:upper16:MSH2*/ ldr r1, .msh2ptr str r10, [fp, #master_cc-dynarec_local] str r14, [fp, #master_ip-dynarec_local] ldr r0, [r1] /* MSH2 */ bl BiosHandleFunc ldr r14, [fp, #master_ip-dynarec_local] ldr r10, [fp, #master_cc-dynarec_local] mov pc, lr .size master_handle_bios, .-master_handle_bios .align 2 .global slave_handle_bios .type slave_handle_bios, %function slave_handle_bios: /*movw r1, #:lower16:SSH2*/ str r0, [fp, #slave_pc-dynarec_local] /*movt r1, #:upper16:SSH2*/ ldr r1, .ssh2ptr str r10, [fp, #slave_cc-dynarec_local] str r14, [fp, #slave_ip-dynarec_local] ldr r0, [r1] /* SSH2 */ bl BiosHandleFunc ldr r14, [fp, #slave_ip-dynarec_local] ldr r10, [fp, #slave_cc-dynarec_local] mov pc, lr .size slave_handle_bios, .-slave_handle_bios /* __clear_cache syscall for Linux OS with broken libraries */ .align 2 .global clear_cache .type clear_cache, %function clear_cache: push {r7, lr} mov r7, #2 mov r2, #0 orr r7, #0xf0000 svc 0x00000000 pop {r7, pc} .size clear_cache, .-clear_cache .align 2 .global breakpoint .type breakpoint, %function breakpoint: /* Set breakpoint here for debugging */ mov pc, lr .size breakpoint, .-breakpoint .section .note.GNU-stack,"",%progbits yabause-0.9.13.1/src/sh2_dynarec/assem_arm.c000644 001750 001750 00000262775 12256006124 022532 0ustar00guillaumeguillaume000000 000000 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Yabause - assem_arm.c * * Copyright (C) 2009-2011 Ari64 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ extern void *dynarec_local; extern u32 memory_map[1048576]; ALIGNED(8) extern u32 mini_ht_master[32][2]; ALIGNED(8) extern u32 mini_ht_slave[32][2]; ALIGNED(4) extern u8 restore_candidate[512]; void FASTCALL WriteInvalidateLong(u32 addr, u32 val); void FASTCALL WriteInvalidateWord(u32 addr, u32 val); void FASTCALL WriteInvalidateByte(u32 addr, u32 val); void FASTCALL WriteInvalidateByteSwapped(u32 addr, u32 val); void jump_vaddr_r0_master(); void jump_vaddr_r1_master(); void jump_vaddr_r2_master(); void jump_vaddr_r3_master(); void jump_vaddr_r4_master(); void jump_vaddr_r5_master(); void jump_vaddr_r6_master(); void jump_vaddr_r7_master(); void jump_vaddr_r8_master(); void jump_vaddr_r9_master(); void jump_vaddr_r12_master(); void jump_vaddr_r0_slave(); void jump_vaddr_r1_slave(); void jump_vaddr_r2_slave(); void jump_vaddr_r3_slave(); void jump_vaddr_r4_slave(); void jump_vaddr_r5_slave(); void jump_vaddr_r6_slave(); void jump_vaddr_r7_slave(); void jump_vaddr_r8_slave(); void jump_vaddr_r9_slave(); void jump_vaddr_r12_slave(); const pointer jump_vaddr_reg[2][16] = { { (pointer)jump_vaddr_r0_master, (pointer)jump_vaddr_r1_master, (pointer)jump_vaddr_r2_master, (pointer)jump_vaddr_r3_master, (pointer)jump_vaddr_r4_master, (pointer)jump_vaddr_r5_master, (pointer)jump_vaddr_r6_master, (pointer)jump_vaddr_r7_master, (pointer)jump_vaddr_r8_master, (pointer)jump_vaddr_r9_master, 0, 0, (pointer)jump_vaddr_r12_master, 0, 0, 0 },{ (pointer)jump_vaddr_r0_slave, (pointer)jump_vaddr_r1_slave, (pointer)jump_vaddr_r2_slave, (pointer)jump_vaddr_r3_slave, (pointer)jump_vaddr_r4_slave, (pointer)jump_vaddr_r5_slave, (pointer)jump_vaddr_r6_slave, (pointer)jump_vaddr_r7_slave, (pointer)jump_vaddr_r8_slave, (pointer)jump_vaddr_r9_slave, 0, 0, (pointer)jump_vaddr_r12_slave, 0, 0, 0 } }; u32 needs_clear_cache[1<<(TARGET_SIZE_2-17)]; //#define JUMP_TABLE_SIZE (sizeof(jump_table_symbols)*2) #define JUMP_TABLE_SIZE 0 /* Linker */ void set_jump_target(pointer addr,pointer target) { u8 *ptr=(u8 *)addr; u32 *ptr2=(u32 *)ptr; if(ptr[3]==0xe2) { assert((target-(u32)ptr2-8)<1024); assert((addr&3)==0); assert((target&3)==0); *ptr2=(*ptr2&0xFFFFF000)|((target-(u32)ptr2-8)>>2)|0xF00; //printf("target=%x addr=%x insn=%x\n",target,addr,*ptr2); } else if(ptr[3]==0x72) { // generated by emit_jno_unlikely if((target-(u32)ptr2-8)<1024) { assert((addr&3)==0); assert((target&3)==0); *ptr2=(*ptr2&0xFFFFF000)|((target-(u32)ptr2-8)>>2)|0xF00; } else if((target-(u32)ptr2-8)<4096&&!((target-(u32)ptr2-8)&15)) { assert((addr&3)==0); assert((target&3)==0); *ptr2=(*ptr2&0xFFFFF000)|((target-(u32)ptr2-8)>>4)|0xE00; } else *ptr2=(0x7A000000)|(((target-(u32)ptr2-8)<<6)>>8); } else { assert((ptr[3]&0x0e)==0xa); *ptr2=(*ptr2&0xFF000000)|(((target-(u32)ptr2-8)<<6)>>8); } } // This optionally copies the instruction from the target of the branch into // the space before the branch. Works, but the difference in speed is // usually insignificant. void set_jump_target_fillslot(int addr,u32 target,int copy) { u8 *ptr=(u8 *)addr; u32 *ptr2=(u32 *)ptr; assert(!copy||ptr2[-1]==0xe28dd000); if(ptr[3]==0xe2) { assert(!copy); assert((target-(u32)ptr2-8)<4096); *ptr2=(*ptr2&0xFFFFF000)|(target-(u32)ptr2-8); } else { assert((ptr[3]&0x0e)==0xa); u32 target_insn=*(u32 *)target; if((target_insn&0x0e100000)==0) { // ALU, no immediate, no flags copy=0; } if((target_insn&0x0c100000)==0x04100000) { // Load copy=0; } if(target_insn&0x08000000) { copy=0; } if(copy) { ptr2[-1]=target_insn; target+=4; } *ptr2=(*ptr2&0xFF000000)|(((target-(u32)ptr2-8)<<6)>>8); } } /* Literal pool */ add_literal(int addr,int val) { literals[literalcount][0]=addr; literals[literalcount][1]=val; literalcount++; } void *kill_pointer(void *stub) { int *ptr=(int *)(stub+4); assert((*ptr&0x0ff00000)==0x05900000); u32 offset=*ptr&0xfff; int **l_ptr=(void *)ptr+offset+8; int *i_ptr=*l_ptr; set_jump_target((int)i_ptr,(int)stub); return i_ptr; } pointer get_pointer(void *stub) { //printf("get_pointer(%x)\n",(int)stub); int *ptr=(int *)(stub+4); assert((*ptr&0x0ff00000)==0x05900000); u32 offset=*ptr&0xfff; int **l_ptr=(void *)ptr+offset+8; int *i_ptr=*l_ptr; assert((*i_ptr&0x0f000000)==0x0a000000); return (pointer)i_ptr+((*i_ptr<<8)>>6)+8; } // Find the "clean" entry point from a "dirty" entry point // by skipping past the call to verify_code pointer get_clean_addr(pointer addr) { int *ptr=(int *)addr; #ifndef HAVE_ARMv7 ptr+=4; #else ptr+=6; #endif if((*ptr&0xFF000000)!=0xeb000000) ptr++; assert((*ptr&0xFF000000)==0xeb000000); // bl instruction ptr++; if((*ptr&0xFF000000)==0xea000000) { return (int)ptr+((*ptr<<8)>>6)+8; // follow jump } return (pointer)ptr; } int verify_dirty(pointer addr) { u32 *ptr=(u32 *)addr; #ifndef HAVE_ARMv7 // get from literal pool assert((*ptr&0xFFF00000)==0xe5900000); u32 offset=*ptr&0xfff; u32 *l_ptr=(void *)ptr+offset+8; u32 source=l_ptr[0]; u32 copy=l_ptr[1]; u32 len=l_ptr[2]; ptr+=4; #else // ARMv7 movw/movt assert((*ptr&0xFFF00000)==0xe3000000); u32 source=(ptr[0]&0xFFF)+((ptr[0]>>4)&0xF000)+((ptr[2]<<16)&0xFFF0000)+((ptr[2]<<12)&0xF0000000); u32 copy=(ptr[1]&0xFFF)+((ptr[1]>>4)&0xF000)+((ptr[3]<<16)&0xFFF0000)+((ptr[3]<<12)&0xF0000000); u32 len=(ptr[4]&0xFFF)+((ptr[4]>>4)&0xF000); ptr+=6; #endif if((*ptr&0xFF000000)!=0xeb000000) ptr++; assert((*ptr&0xFF000000)==0xeb000000); // bl instruction //printf("verify_dirty: %x %x %x\n",source,copy,len); return !memcmp((void *)source,(void *)copy,len); } // This doesn't necessarily find all clean entry points, just // guarantees that it's not dirty int isclean(pointer addr) { #ifndef HAVE_ARMv7 int *ptr=((u32 *)addr)+4; #else int *ptr=((u32 *)addr)+6; #endif if((*ptr&0xFF000000)!=0xeb000000) ptr++; if((*ptr&0xFF000000)!=0xeb000000) return 1; // bl instruction if((int)ptr+((*ptr<<8)>>6)+8==(int)verify_code) return 0; return 1; } void get_bounds(pointer addr,u32 *start,u32 *end) { u32 *ptr=(u32 *)addr; #ifndef HAVE_ARMv7 // get from literal pool assert((*ptr&0xFFF00000)==0xe5900000); u32 offset=*ptr&0xfff; u32 *l_ptr=(void *)ptr+offset+8; u32 source=l_ptr[0]; //u32 copy=l_ptr[1]; u32 len=l_ptr[2]; ptr+=4; #else // ARMv7 movw/movt assert((*ptr&0xFFF00000)==0xe3000000); u32 source=(ptr[0]&0xFFF)+((ptr[0]>>4)&0xF000)+((ptr[2]<<16)&0xFFF0000)+((ptr[2]<<12)&0xF0000000); //u32 copy=(ptr[1]&0xFFF)+((ptr[1]>>4)&0xF000)+((ptr[3]<<16)&0xFFF0000)+((ptr[3]<<12)&0xF0000000); u32 len=(ptr[4]&0xFFF)+((ptr[4]>>4)&0xF000); ptr+=6; #endif if((*ptr&0xFF000000)!=0xeb000000) ptr++; assert((*ptr&0xFF000000)==0xeb000000); // bl instruction *start=source; *end=source+len; } /* Register allocation */ // Note: registers are allocated clean (unmodified state) // if you intend to modify the register, you must call dirty_reg(). void alloc_reg(struct regstat *cur,int i,signed char reg) { int r,hr; int preferred_reg = (reg&7); if(reg==CCREG) preferred_reg=HOST_CCREG; if(reg==PTEMP) preferred_reg=12; // Don't allocate unused registers if((cur->u>>reg)&1) return; // see if it's already allocated for(hr=0;hrregmap[hr]==reg) return; } // Keep the same mapping if the register was already allocated in a loop preferred_reg = loop_reg(i,reg,preferred_reg); // Try to allocate the preferred register if(cur->regmap[preferred_reg]==-1) { cur->regmap[preferred_reg]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[preferred_reg]; if(r<64&&((cur->u>>r)&1)) { cur->regmap[preferred_reg]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]; if(r>=0) { if((cur->u>>r)&1) if(i==0||(unneeded_reg[i-1]>>r)&1) {cur->regmap[hr]=-1;break;} } } // Try to allocate any available register, but prefer // registers that have not been used recently. if(i>0) { for(hr=0;hrregmap[hr]==-1) { if(regs[i-1].regmap[hr]!=rs1[i-1]&®s[i-1].regmap[hr]!=rs2[i-1]&®s[i-1].regmap[hr]!=rt1[i-1]&®s[i-1].regmap[hr]!=rt2[i-1]) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==-1) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[0],cur->regmap[1],cur->regmap[2],cur->regmap[3],cur->regmap[5],cur->regmap[6],cur->regmap[7]); //printf("hsn(%x): %d %d %d %d %d %d %d\n",start+i*4,hsn[cur->regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); if(i>0) { // Don't evict the cycle count at entry points, otherwise the entry // stub will have to write it. if(bt[i]&&hsn[CCREG]>2) hsn[CCREG]=2; if(i>1&&hsn[CCREG]>2&&(itype[i-2]==RJUMP||itype[i-2]==UJUMP||itype[i-2]==CJUMP||itype[i-2]==SJUMP)) hsn[CCREG]=2; for(j=10;j>=3;j--) { // Alloc preferred register if available if(hsn[r=cur->regmap[preferred_reg]&63]==j) { for(hr=0;hrregmap[hr]&63)==r) { cur->regmap[hr]=-1; cur->dirty&=~(1<isdoingcp&=~(1<regmap[preferred_reg]=reg; return; } for(r=0;r<=MAXREG;r++) { if(hsn[r]==j&&r!=rs1[i-1]&&r!=rs2[i-1]&&r!=rt1[i-1]&&r!=rt2[i-1]) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==reg) return; } // Try to allocate any available register for(hr=HOST_REGS-1;hr>=0;hr--) { if(hr!=EXCLUDE_REG&&cur->regmap[hr]==-1) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;hr--) { r=cur->regmap[hr]; if(r>=0) { if((cur->u>>r)&1) { if(i==0||((unneeded_reg[i-1]>>r)&1)) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); if(i>0) { // Don't evict the cycle count at entry points, otherwise the entry // stub will have to write it. if(bt[i]&&hsn[CCREG]>2) hsn[CCREG]=2; if(i>1&&hsn[CCREG]>2&&(itype[i-2]==RJUMP||itype[i-2]==UJUMP||itype[i-2]==CJUMP||itype[i-2]==SJUMP)) hsn[CCREG]=2; for(j=10;j>=3;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j&&r!=rs1[i-1]&&r!=rs2[i-1]&&r!=rt1[i-1]&&r!=rt2[i-1]) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[n]==reg) { dirty=(cur->dirty>>n)&1; cur->regmap[n]=-1; } } cur->regmap[hr]=reg; cur->dirty&=~(1<dirty|=dirty<isdoingcp&=~(1<0) { if(imm<256) { *encoded=((i&30)<<7)|imm; return 1; } imm=(imm>>2)|(imm<<30);i-=2; } return 0; } u32 genjmp(u32 addr) { if(addr<4) return 0; int offset=addr-(int)out-8; #if 0 if(offset<-33554432||offset>=33554432) { int n; for (n=0;n=-33554432&&offset<33554432); return ((u32)offset>>2)&0xffffff; } void emit_mov(int rs,int rt) { assem_debug("mov %s,%s\n",regname[rt],regname[rs]); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)); } void emit_movs(int rs,int rt) { assem_debug("movs %s,%s\n",regname[rt],regname[rs]); output_w32(0xe1b00000|rd_rn_rm(rt,0,rs)); } void emit_add(int rs1,int rs2,int rt) { assem_debug("add %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0800000|rd_rn_rm(rt,rs1,rs2)); } void emit_adds(int rs1,int rs2,int rt) { assem_debug("adds %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0900000|rd_rn_rm(rt,rs1,rs2)); } void emit_adc(int rs1,int rs2,int rt) { assem_debug("adc %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0a00000|rd_rn_rm(rt,rs1,rs2)); } void emit_adcs(int rs1,int rs2,int rt) { assem_debug("adcs %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0b00000|rd_rn_rm(rt,rs1,rs2)); } void emit_sbc(int rs1,int rs2,int rt) { assem_debug("sbc %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0c00000|rd_rn_rm(rt,rs1,rs2)); } void emit_sbcs(int rs1,int rs2,int rt) { assem_debug("sbcs %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0d00000|rd_rn_rm(rt,rs1,rs2)); } void emit_neg(int rs, int rt) { assem_debug("rsb %s,%s,#0\n",regname[rt],regname[rs]); output_w32(0xe2600000|rd_rn_rm(rt,rs,0)); } void emit_negs(int rs, int rt) { assem_debug("rsbs %s,%s,#0\n",regname[rt],regname[rs]); output_w32(0xe2700000|rd_rn_rm(rt,rs,0)); } void emit_sub(int rs1,int rs2,int rt) { assem_debug("sub %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0400000|rd_rn_rm(rt,rs1,rs2)); } void emit_subs(int rs1,int rs2,int rt) { assem_debug("subs %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0500000|rd_rn_rm(rt,rs1,rs2)); } void emit_zeroreg(int rt) { assem_debug("mov %s,#0\n",regname[rt]); output_w32(0xe3a00000|rd_rn_rm(rt,0,0)); } void emit_loadlp(u32 imm,unsigned int rt) { add_literal((int)out,imm); assem_debug("ldr %s,pc+? [=%x]\n",regname[rt],imm); output_w32(0xe5900000|rd_rn_rm(rt,15,0)); } void emit_movw(u32 imm,unsigned int rt) { assert(imm<65536); assem_debug("movw %s,#%d (0x%x)\n",regname[rt],imm,imm); output_w32(0xe3000000|rd_rn_rm(rt,0,0)|(imm&0xfff)|((imm<<4)&0xf0000)); } void emit_movt(u32 imm,unsigned int rt) { assem_debug("movt %s,#%d (0x%x)\n",regname[rt],imm&0xffff0000,imm&0xffff0000); output_w32(0xe3400000|rd_rn_rm(rt,0,0)|((imm>>16)&0xfff)|((imm>>12)&0xf0000)); } void emit_movimm(u32 imm,unsigned int rt) { u32 armval; if(genimm(imm,&armval)) { assem_debug("mov %s,#%d\n",regname[rt],imm); output_w32(0xe3a00000|rd_rn_rm(rt,0,0)|armval); }else if(genimm(~imm,&armval)) { assem_debug("mvn %s,#%d\n",regname[rt],imm); output_w32(0xe3e00000|rd_rn_rm(rt,0,0)|armval); }else if(imm<65536) { #ifndef HAVE_ARMv7 assem_debug("mov %s,#%d\n",regname[rt],imm&0xFF00); output_w32(0xe3a00000|rd_rn_imm_shift(rt,0,imm>>8,8)); assem_debug("add %s,%s,#%d\n",regname[rt],regname[rt],imm&0xFF); output_w32(0xe2800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); #else emit_movw(imm,rt); #endif }else{ #ifndef HAVE_ARMv7 emit_loadlp(imm,rt); #else emit_movw(imm&0x0000FFFF,rt); emit_movt(imm&0xFFFF0000,rt); #endif } } void emit_pcreladdr(unsigned int rt) { assem_debug("add %s,pc,#?\n",regname[rt]); output_w32(0xe2800000|rd_rn_rm(rt,15,0)); } void emit_loadreg(int r, int hr) { if(r==MMREG) emit_movimm(((int)memory_map-(int)&dynarec_local)>>2,hr); else { int addr=(slave?(int)slave_reg:(int)master_reg)+(r<<2); if(r==CCREG) addr=slave?(int)&slave_cc:(int)&master_cc; u32 offset = addr-(u32)&dynarec_local; assert(offset<4096); assem_debug("ldr %s,fp+%d\n",regname[hr],offset); output_w32(0xe5900000|rd_rn_rm(hr,FP,0)|offset); } } void emit_storereg(int r, int hr) { int addr=(slave?(int)slave_reg:(int)master_reg)+(r<<2); if(r==CCREG) addr=slave?(int)&slave_cc:(int)&master_cc; u32 offset = addr-(u32)&dynarec_local; assert(offset<4096); assem_debug("str %s,fp+%d\n",regname[hr],offset); output_w32(0xe5800000|rd_rn_rm(hr,FP,0)|offset); } void emit_test(int rs, int rt) { assem_debug("tst %s,%s\n",regname[rs],regname[rt]); output_w32(0xe1100000|rd_rn_rm(0,rs,rt)); } void emit_testimm(int rs,int imm) { u32 armval; assem_debug("tst %s,#%d\n",regname[rs],imm); genimm(imm,&armval); output_w32(0xe3100000|rd_rn_rm(0,rs,0)|armval); } void emit_not(int rs,int rt) { assem_debug("mvn %s,%s\n",regname[rt],regname[rs]); output_w32(0xe1e00000|rd_rn_rm(rt,0,rs)); } void emit_and(unsigned int rs1,unsigned int rs2,unsigned int rt) { assem_debug("and %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0000000|rd_rn_rm(rt,rs1,rs2)); } void emit_or(unsigned int rs1,unsigned int rs2,unsigned int rt) { assem_debug("orr %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe1800000|rd_rn_rm(rt,rs1,rs2)); } void emit_or_and_set_flags(int rs1,int rs2,int rt) { assem_debug("orrs %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe1900000|rd_rn_rm(rt,rs1,rs2)); } void emit_xor(unsigned int rs1,unsigned int rs2,unsigned int rt) { assem_debug("eor %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0200000|rd_rn_rm(rt,rs1,rs2)); } void emit_addimm(unsigned int rs,int imm,unsigned int rt) { assert(rs<16); assert(rt<16); if(imm!=0) { assert(imm>-65536&&imm<65536); u32 armval; if(genimm(imm,&armval)) { assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe2800000|rd_rn_rm(rt,rs,0)|armval); }else if(genimm(-imm,&armval)) { assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe2400000|rd_rn_rm(rt,rs,0)|armval); }else if(imm<0) { assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rs],(-imm)&0xFF00); assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rt],(-imm)&0xFF); output_w32(0xe2400000|rd_rn_imm_shift(rt,rs,(-imm)>>8,8)); output_w32(0xe2400000|rd_rn_imm_shift(rt,rt,(-imm)&0xff,0)); }else{ assem_debug("add %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("add %s,%s,#%d\n",regname[rt],regname[rt],imm&0xFF); output_w32(0xe2800000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe2800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } else if(rs!=rt) emit_mov(rs,rt); } void emit_addimm_and_set_flags(int imm,int rt) { assert(imm>-65536&&imm<65536); u32 armval; if(genimm(imm,&armval)) { assem_debug("adds %s,%s,#%d\n",regname[rt],regname[rt],imm); output_w32(0xe2900000|rd_rn_rm(rt,rt,0)|armval); }else if(genimm(-imm,&armval)) { assem_debug("subs %s,%s,#%d\n",regname[rt],regname[rt],imm); output_w32(0xe2500000|rd_rn_rm(rt,rt,0)|armval); }else if(imm<0) { assem_debug("sub %s,%s,#%d\n",regname[rt],regname[rt],(-imm)&0xFF00); assem_debug("subs %s,%s,#%d\n",regname[rt],regname[rt],(-imm)&0xFF); output_w32(0xe2400000|rd_rn_imm_shift(rt,rt,(-imm)>>8,8)); output_w32(0xe2500000|rd_rn_imm_shift(rt,rt,(-imm)&0xff,0)); }else{ assem_debug("add %s,%s,#%d\n",regname[rt],regname[rt],imm&0xFF00); assem_debug("adds %s,%s,#%d\n",regname[rt],regname[rt],imm&0xFF); output_w32(0xe2800000|rd_rn_imm_shift(rt,rt,imm>>8,8)); output_w32(0xe2900000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_addimm_no_flags(u32 imm,unsigned int rt) { emit_addimm(rt,imm,rt); } void emit_addnop(unsigned int r) { assert(r<16); assem_debug("add %s,%s,#0 (nop)\n",regname[r],regname[r]); output_w32(0xe2800000|rd_rn_rm(r,r,0)); } void emit_adcimm(unsigned int rs,int imm,unsigned int rt) { u32 armval; genimm(imm,&armval); assem_debug("adc %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe2a00000|rd_rn_rm(rt,rs,0)|armval); } void emit_sbcimm(unsigned int rs,int imm,unsigned int rt) { u32 armval; genimm(imm,&armval); assem_debug("sbc %s,%s,#%d\n",regname[rt],regname[rt],imm); output_w32(0xe2c00000|rd_rn_rm(rt,rt,0)|armval); } void emit_sbbimm(int imm,unsigned int rt) { assem_debug("sbb $%d,%%%s\n",imm,regname[rt]); assert(rt<8); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,3); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,3); output_w32(imm); } } void emit_rscimm(int rs,int imm,unsigned int rt) { assert(0); u32 armval; genimm(imm,&armval); assem_debug("rsc %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe2e00000|rd_rn_rm(rt,rs,0)|armval); } void emit_addimm64_32(int rsh,int rsl,int imm,int rth,int rtl) { // TODO: if(genimm(imm,&armval)) ... // else emit_movimm(imm,HOST_TEMPREG); emit_adds(HOST_TEMPREG,rsl,rtl); emit_adcimm(rsh,0,rth); } void emit_sbb(int rs1,int rs2) { assem_debug("sbb %%%s,%%%s\n",regname[rs2],regname[rs1]); output_byte(0x19); output_modrm(3,rs1,rs2); } void emit_andimm(int rs,int imm,int rt) { u32 armval; if(imm==0) { emit_zeroreg(rt); }else if(genimm(imm,&armval)) { assem_debug("and %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe2000000|rd_rn_rm(rt,rs,0)|armval); }else if(genimm(~imm,&armval)) { assem_debug("bic %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe3c00000|rd_rn_rm(rt,rs,0)|armval); }else if(imm==65535) { #ifndef HAVE_ARMv6 assem_debug("bic %s,%s,#FF000000\n",regname[rt],regname[rs]); output_w32(0xe3c00000|rd_rn_rm(rt,rs,0)|0x4FF); assem_debug("bic %s,%s,#00FF0000\n",regname[rt],regname[rt]); output_w32(0xe3c00000|rd_rn_rm(rt,rt,0)|0x8FF); #else assem_debug("uxth %s,%s\n",regname[rt],regname[rs]); output_w32(0xe6ff0070|rd_rn_rm(rt,0,rs)); #endif }else{ assert(imm>0&&imm<65535); #ifndef HAVE_ARMv7 assem_debug("mov r14,#%d\n",imm&0xFF00); output_w32(0xe3a00000|rd_rn_imm_shift(HOST_TEMPREG,0,imm>>8,8)); assem_debug("add r14,r14,#%d\n",imm&0xFF); output_w32(0xe2800000|rd_rn_imm_shift(HOST_TEMPREG,HOST_TEMPREG,imm&0xff,0)); #else emit_movw(imm,HOST_TEMPREG); #endif assem_debug("and %s,%s,r14\n",regname[rt],regname[rs]); output_w32(0xe0000000|rd_rn_rm(rt,rs,HOST_TEMPREG)); } } void emit_orimm(int rs,int imm,int rt) { u32 armval; if(imm==0) { if(rs!=rt) emit_mov(rs,rt); }else if(genimm(imm,&armval)) { assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe3800000|rd_rn_rm(rt,rs,0)|armval); }else{ assert(imm>0&&imm<65536); assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF); output_w32(0xe3800000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe3800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_xorimm(int rs,int imm,int rt) { u32 armval; if(imm==0) { if(rs!=rt) emit_mov(rs,rt); }else if(genimm(imm,&armval)) { assem_debug("eor %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe2200000|rd_rn_rm(rt,rs,0)|armval); }else{ assert(imm>0&&imm<65536); assem_debug("eor %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("eor %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF); output_w32(0xe2200000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe2200000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_shlimm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); if(imm==1) emit_add(rs,rs,rt); else { assem_debug("lsl %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|(imm<<7)); } } void emit_lsls_imm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); if(imm==1) emit_adds(rs,rs,rt); else { assem_debug("lsls %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1b00000|rd_rn_rm(rt,0,rs)|(imm<<7)); } } void emit_shrimm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); assem_debug("lsr %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x20|(imm<<7)); } void emit_lsrs_imm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); assem_debug("lsrs %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1b00000|rd_rn_rm(rt,0,rs)|0x20|(imm<<7)); } void emit_sarimm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); assem_debug("asr %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x40|(imm<<7)); } void emit_asrs_imm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); assem_debug("asrs %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1b00000|rd_rn_rm(rt,0,rs)|0x40|(imm<<7)); } void emit_rorimm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); assem_debug("ror %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x60|(imm<<7)); } void emit_rors_imm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); assem_debug("rors %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1b00000|rd_rn_rm(rt,0,rs)|0x60|(imm<<7)); } void emit_rrxs(int rs, int rt) { assem_debug("rrxs %s,%s\n",regname[rt],regname[rs]); output_w32(0xe1b00060|rd_rn_rm(rt,0,rs)); } void emit_swapb(int rs,int rt) { #ifdef HAVE_ARMv6 assem_debug("rev16 %s,%s\n",regname[14],regname[rs]); output_w32(0xe6bf0fb0|rd_rn_rm(14,0,rs)); assem_debug("pkhbt %s,%s,%s\n",regname[rt],regname[14],regname[rs]); output_w32(0xe6800010|rd_rn_rm(rt,14,rs)); #else assem_debug("eor %s,%s,%s,lsr #%d\n",regname[14],regname[rs],regname[rs],8); output_w32(0xe0200020|rd_rn_rm(14,rs,rs)|(8<<7)); emit_andimm(14,0xff,14); emit_xor(14,rt,rt); assem_debug("eor %s,%s,%s,lsl #%d\n",regname[rt],regname[rt],regname[14],8); output_w32(0xe0200000|rd_rn_rm(rt,rt,14)|(8<<7)); #endif } void emit_shldimm(int rs,int rs2,unsigned int imm,int rt) { assem_debug("shld %%%s,%%%s,%d\n",regname[rt],regname[rs2],imm); assert(imm>0); assert(imm<32); //if(imm==1) ... assem_debug("lsl %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|(imm<<7)); assem_debug("orr %s,%s,%s,lsr #%d\n",regname[rt],regname[rt],regname[rs2],32-imm); output_w32(0xe1800020|rd_rn_rm(rt,rt,rs2)|((32-imm)<<7)); } void emit_shrdimm(int rs,int rs2,unsigned int imm,int rt) { assem_debug("shrd %%%s,%%%s,%d\n",regname[rt],regname[rs2],imm); assert(imm>0); assert(imm<32); //if(imm==1) ... assem_debug("lsr %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe1a00020|rd_rn_rm(rt,0,rs)|(imm<<7)); assem_debug("orr %s,%s,%s,lsl #%d\n",regname[rt],regname[rt],regname[rs2],32-imm); output_w32(0xe1800000|rd_rn_rm(rt,rt,rs2)|((32-imm)<<7)); } void emit_shl(unsigned int rs,unsigned int shift,unsigned int rt) { assert(rs<16); assert(rt<16); assert(shift<16); //if(imm==1) ... assem_debug("lsl %s,%s,%s\n",regname[rt],regname[rs],regname[shift]); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x10|(shift<<8)); } void emit_shr(unsigned int rs,unsigned int shift,unsigned int rt) { assert(rs<16); assert(rt<16); assert(shift<16); assem_debug("lsr %s,%s,%s\n",regname[rt],regname[rs],regname[shift]); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x30|(shift<<8)); } void emit_sar(unsigned int rs,unsigned int shift,unsigned int rt) { assert(rs<16); assert(rt<16); assert(shift<16); assem_debug("asr %s,%s,%s\n",regname[rt],regname[rs],regname[shift]); output_w32(0xe1a00000|rd_rn_rm(rt,0,rs)|0x50|(shift<<8)); } void emit_shlcl(int r) { assem_debug("shl %%%s,%%cl\n",regname[r]); assert(0); } void emit_shrcl(int r) { assem_debug("shr %%%s,%%cl\n",regname[r]); assert(0); } void emit_sarcl(int r) { assem_debug("sar %%%s,%%cl\n",regname[r]); assert(0); } void emit_shldcl(int r1,int r2) { assem_debug("shld %%%s,%%%s,%%cl\n",regname[r1],regname[r2]); assert(0); } void emit_shrdcl(int r1,int r2) { assem_debug("shrd %%%s,%%%s,%%cl\n",regname[r1],regname[r2]); assert(0); } void emit_orrshl(unsigned int rs,unsigned int shift,unsigned int rt) { assert(rs<16); assert(rt<16); assert(shift<16); assem_debug("orr %s,%s,%s,lsl %s\n",regname[rt],regname[rt],regname[rs],regname[shift]); output_w32(0xe1800000|rd_rn_rm(rt,rt,rs)|0x10|(shift<<8)); } void emit_orrshr(unsigned int rs,unsigned int shift,unsigned int rt) { assert(rs<16); assert(rt<16); assert(shift<16); assem_debug("orr %s,%s,%s,lsr %s\n",regname[rt],regname[rt],regname[rs],regname[shift]); output_w32(0xe1800000|rd_rn_rm(rt,rt,rs)|0x30|(shift<<8)); } void emit_cmpimm(int rs,int imm) { u32 armval; if(genimm(imm,&armval)) { assem_debug("cmp %s,#%d\n",regname[rs],imm); output_w32(0xe3500000|rd_rn_rm(0,rs,0)|armval); }else if(genimm(-imm,&armval)) { assem_debug("cmn %s,#%d\n",regname[rs],imm); output_w32(0xe3700000|rd_rn_rm(0,rs,0)|armval); }else if(imm>0) { assert(imm<65536); #ifndef HAVE_ARMv7 emit_movimm(imm,HOST_TEMPREG); #else emit_movw(imm,HOST_TEMPREG); #endif assem_debug("cmp %s,r14\n",regname[rs]); output_w32(0xe1500000|rd_rn_rm(0,rs,HOST_TEMPREG)); }else{ assert(imm>-65536); #ifndef HAVE_ARMv7 emit_movimm(-imm,HOST_TEMPREG); #else emit_movw(-imm,HOST_TEMPREG); #endif assem_debug("cmn %s,r14\n",regname[rs]); output_w32(0xe1700000|rd_rn_rm(0,rs,HOST_TEMPREG)); } } void emit_cmovne(u32 *addr,int rt) { assem_debug("cmovne %x,%%%s",(int)addr,regname[rt]); assert(0); } void emit_cmovl(u32 *addr,int rt) { assem_debug("cmovl %x,%%%s",(int)addr,regname[rt]); assert(0); } void emit_cmovs(u32 *addr,int rt) { assem_debug("cmovs %x,%%%s",(int)addr,regname[rt]); assert(0); } void emit_cmovne_imm(int imm,int rt) { assem_debug("movne %s,#%d\n",regname[rt],imm); u32 armval; genimm(imm,&armval); output_w32(0x13a00000|rd_rn_rm(rt,0,0)|armval); } void emit_cmovl_imm(int imm,int rt) { assem_debug("movlt %s,#%d\n",regname[rt],imm); u32 armval; genimm(imm,&armval); output_w32(0xb3a00000|rd_rn_rm(rt,0,0)|armval); } void emit_cmovb_imm(int imm,int rt) { assem_debug("movcc %s,#%d\n",regname[rt],imm); u32 armval; genimm(imm,&armval); output_w32(0x33a00000|rd_rn_rm(rt,0,0)|armval); } void emit_cmovs_imm(int imm,int rt) { assem_debug("movmi %s,#%d\n",regname[rt],imm); u32 armval; genimm(imm,&armval); output_w32(0x43a00000|rd_rn_rm(rt,0,0)|armval); } void emit_cmove_reg(int rs,int rt) { assem_debug("moveq %s,%s\n",regname[rt],regname[rs]); output_w32(0x01a00000|rd_rn_rm(rt,0,rs)); } void emit_cmovne_reg(int rs,int rt) { assem_debug("movne %s,%s\n",regname[rt],regname[rs]); output_w32(0x11a00000|rd_rn_rm(rt,0,rs)); } void emit_cmovl_reg(int rs,int rt) { assem_debug("movlt %s,%s\n",regname[rt],regname[rs]); output_w32(0xb1a00000|rd_rn_rm(rt,0,rs)); } void emit_cmovs_reg(int rs,int rt) { assem_debug("movmi %s,%s\n",regname[rt],regname[rs]); output_w32(0x41a00000|rd_rn_rm(rt,0,rs)); } void emit_movzbl_reg(int rs, int rt) { emit_andimm(rs,0xff,rt); } void emit_movzwl_reg(int rs, int rt) { emit_andimm(rs,0xffff,rt); } void emit_movsbl_reg(int rs, int rt) { #ifdef HAVE_ARMv6 assem_debug("sxtb %s,%s\n",regname[rt],regname[rs]); output_w32(0xe6af0070|rd_rn_rm(rt,0,rs)); #else emit_shlimm(rs,24,rt); emit_sarimm(rt,24,rt); #endif } void emit_movswl_reg(int rs, int rt) { #ifdef HAVE_ARMv6 assem_debug("sxth %s,%s\n",regname[rt],regname[rs]); output_w32(0xe6bf0070|rd_rn_rm(rt,0,rs)); #else emit_shlimm(rs,16,rt); emit_sarimm(rt,16,rt); #endif } void emit_slti32(int rs,int imm,int rt) { if(rs!=rt) emit_zeroreg(rt); emit_cmpimm(rs,imm); if(rs==rt) emit_movimm(0,rt); emit_cmovl_imm(1,rt); } void emit_sltiu32(int rs,int imm,int rt) { if(rs!=rt) emit_zeroreg(rt); emit_cmpimm(rs,imm); if(rs==rt) emit_movimm(0,rt); emit_cmovb_imm(1,rt); } void emit_slti64_32(int rsh,int rsl,int imm,int rt) { assert(rsh!=rt); emit_slti32(rsl,imm,rt); if(imm>=0) { emit_test(rsh,rsh); emit_cmovne_imm(0,rt); emit_cmovs_imm(1,rt); } else { emit_cmpimm(rsh,-1); emit_cmovne_imm(0,rt); emit_cmovl_imm(1,rt); } } void emit_sltiu64_32(int rsh,int rsl,int imm,int rt) { assert(rsh!=rt); emit_sltiu32(rsl,imm,rt); if(imm>=0) { emit_test(rsh,rsh); emit_cmovne_imm(0,rt); } else { emit_cmpimm(rsh,-1); emit_cmovne_imm(1,rt); } } void emit_cmp(int rs,int rt) { assem_debug("cmp %s,%s\n",regname[rs],regname[rt]); output_w32(0xe1500000|rd_rn_rm(0,rs,rt)); } void emit_set_gz32(int rs, int rt) { //assem_debug("set_gz32\n"); emit_cmpimm(rs,1); emit_movimm(1,rt); emit_cmovl_imm(0,rt); } void emit_set_nz32(int rs, int rt) { //assem_debug("set_nz32\n"); if(rs!=rt) emit_movs(rs,rt); else emit_test(rs,rs); emit_cmovne_imm(1,rt); } void emit_set_gz64_32(int rsh, int rsl, int rt) { //assem_debug("set_gz64\n"); emit_set_gz32(rsl,rt); emit_test(rsh,rsh); emit_cmovne_imm(1,rt); emit_cmovs_imm(0,rt); } void emit_set_nz64_32(int rsh, int rsl, int rt) { //assem_debug("set_nz64\n"); emit_or_and_set_flags(rsh,rsl,rt); emit_cmovne_imm(1,rt); } void emit_set_if_less32(int rs1, int rs2, int rt) { //assem_debug("set if less (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]); if(rs1!=rt&&rs2!=rt) emit_zeroreg(rt); emit_cmp(rs1,rs2); if(rs1==rt||rs2==rt) emit_movimm(0,rt); emit_cmovl_imm(1,rt); } void emit_set_if_carry32(int rs1, int rs2, int rt) { //assem_debug("set if carry (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]); if(rs1!=rt&&rs2!=rt) emit_zeroreg(rt); emit_cmp(rs1,rs2); if(rs1==rt||rs2==rt) emit_movimm(0,rt); emit_cmovb_imm(1,rt); } void emit_set_if_less64_32(int u1, int l1, int u2, int l2, int rt) { //assem_debug("set if less64 (%%%s,%%%s,%%%s,%%%s),%%%s\n",regname[u1],regname[l1],regname[u2],regname[l2],regname[rt]); assert(u1!=rt); assert(u2!=rt); emit_cmp(l1,l2); emit_movimm(0,rt); emit_sbcs(u1,u2,HOST_TEMPREG); emit_cmovl_imm(1,rt); } void emit_set_if_carry64_32(int u1, int l1, int u2, int l2, int rt) { //assem_debug("set if carry64 (%%%s,%%%s,%%%s,%%%s),%%%s\n",regname[u1],regname[l1],regname[u2],regname[l2],regname[rt]); assert(u1!=rt); assert(u2!=rt); emit_cmp(l1,l2); emit_movimm(0,rt); emit_sbcs(u1,u2,HOST_TEMPREG); emit_cmovb_imm(1,rt); } void emit_orreq_imm(int rs,int imm,int rt) { u32 armval; if(genimm(imm,&armval)) { assem_debug("orreq %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x03800000|rd_rn_rm(rt,rs,0)|armval); }else{ assert(0); // FIXME assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF); output_w32(0xe3800000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe3800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_orrne_imm(int rs,int imm,int rt) { u32 armval; if(genimm(imm,&armval)) { assem_debug("orrne %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x13800000|rd_rn_rm(rt,rs,0)|armval); }else{ assert(0); // FIXME assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF); output_w32(0xe3800000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe3800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_orrhi_imm(int rs,int imm,int rt) { u32 armval; if(genimm(imm,&armval)) { assem_debug("orrhi %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x83800000|rd_rn_rm(rt,rs,0)|armval); }else{ assert(0); // FIXME assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF); output_w32(0xe3800000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe3800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_orrge_imm(int rs,int imm,int rt) { u32 armval; if(genimm(imm,&armval)) { assem_debug("orrge %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xa3800000|rd_rn_rm(rt,rs,0)|armval); }else{ assert(0); // FIXME assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF); output_w32(0xe3800000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe3800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_orrgt_imm(int rs,int imm,int rt) { u32 armval; if(genimm(imm,&armval)) { assem_debug("orrgt %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xc3800000|rd_rn_rm(rt,rs,0)|armval); }else{ assert(0); // FIXME assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF); output_w32(0xe3800000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe3800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_orrmi_imm(int rs,int imm,int rt) { u32 armval; if(genimm(imm,&armval)) { assem_debug("orrmi %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x43800000|rd_rn_rm(rt,rs,0)|armval); }else{ assert(0); // FIXME assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF00); assem_debug("orr %s,%s,#%d\n",regname[rt],regname[rs],imm&0xFF); output_w32(0xe3800000|rd_rn_imm_shift(rt,rs,imm>>8,8)); output_w32(0xe3800000|rd_rn_imm_shift(rt,rt,imm&0xff,0)); } } void emit_sh2tst(int s1, int s2, int sr, int temp) { emit_andimm(sr,~1,sr); emit_test(s1,s2); emit_orreq_imm(sr,1,sr); } void emit_sh2tstimm(int s, int imm, int sr, int temp) { emit_andimm(sr,~1,sr); emit_testimm(s,imm); emit_orreq_imm(sr,1,sr); } void emit_cmpeq(int s1, int s2, int sr, int temp) { emit_andimm(sr,~1,sr); emit_cmp(s1,s2); emit_orreq_imm(sr,1,sr); } void emit_cmpeqimm(int s, int imm, int sr, int temp) { emit_andimm(sr,~1,sr); emit_cmpimm(s,imm); emit_orreq_imm(sr,1,sr); } void emit_cmpge(int s1, int s2, int sr, int temp) { emit_andimm(sr,~1,sr); emit_cmp(s2,s1); emit_orrge_imm(sr,1,sr); } void emit_cmpgt(int s1, int s2, int sr, int temp) { emit_andimm(sr,~1,sr); emit_cmp(s2,s1); emit_orrgt_imm(sr,1,sr); } void emit_cmphi(int s1, int s2, int sr, int temp) { emit_andimm(sr,~1,sr); emit_cmp(s2,s1); emit_orrhi_imm(sr,1,sr); } void emit_cmphs(int s1, int s2, int sr, int temp) { emit_andimm(sr,~1,sr); emit_cmp(s2,s1); emit_adcimm(sr,0,sr); } void emit_dt(int t, int sr) { emit_andimm(sr,~1,sr); emit_addimm_and_set_flags(-1,t); emit_orreq_imm(sr,1,sr); } void emit_cmppz(int s, int sr) { emit_andimm(sr,~1,sr); emit_testimm(s,0x80000000); emit_orreq_imm(sr,1,sr); } void emit_cmppl(int s, int sr, int temp) { assert(temp>=0); emit_andimm(sr,~1,sr); emit_cmpimm(s,0); emit_orrgt_imm(sr,1,sr); } void emit_addc(int s, int t, int sr) { emit_lsrs_imm(sr,1,sr); emit_adcs(s,t,t); emit_adc(sr,sr,sr); } void emit_subc(int s, int t, int sr) { emit_xorimm(sr,1,sr); emit_lsrs_imm(sr,1,sr); emit_sbcs(t,s,t); emit_adc(sr,sr,sr); emit_xorimm(sr,1,sr); } void emit_shrsr(int t, int sr) { emit_andimm(sr,~1,sr); emit_lsrs_imm(t,1,t); emit_adcimm(sr,0,sr); } void emit_sarsr(int t, int sr) { emit_andimm(sr,~1,sr); emit_asrs_imm(t,1,t); emit_adcimm(sr,0,sr); } void emit_shlsr(int t, int sr) { emit_andimm(sr,~1,sr); emit_lsls_imm(t,1,t); emit_adcimm(sr,0,sr); } void emit_rotl(int t) { emit_rorimm(t,31,t); } void emit_rotlsr(int t, int sr) { emit_andimm(sr,~1,sr); emit_test(t,t); emit_rotl(t); emit_orrmi_imm(sr,1,sr); } void emit_rotr(int t) { emit_rorimm(t,1,t); } void emit_rotrsr(int t, int sr) { emit_andimm(sr,~1,sr); emit_rors_imm(t,1,t); emit_adcimm(sr,0,sr); } void emit_rotclsr(int t, int sr) { emit_lsrs_imm(sr,1,sr); emit_adcs(t,t,t); emit_adc(sr,sr,sr); } void emit_rotcrsr(int t, int sr) { emit_lsrs_imm(sr,1,sr); emit_rrxs(t,t); emit_adc(sr,sr,sr); } void emit_call(int a) { assem_debug("bl %x (%x+%x)\n",a,(int)out,a-(int)out-8); u32 offset=genjmp(a); output_w32(0xeb000000|offset); } void emit_jmp(int a) { assem_debug("b %x (%x+%x)\n",a,(int)out,a-(int)out-8); u32 offset=genjmp(a); output_w32(0xea000000|offset); } void emit_jne(int a) { assem_debug("bne %x\n",a); u32 offset=genjmp(a); output_w32(0x1a000000|offset); } void emit_jeq(int a) { assem_debug("beq %x\n",a); u32 offset=genjmp(a); output_w32(0x0a000000|offset); } void emit_js(int a) { assem_debug("bmi %x\n",a); u32 offset=genjmp(a); output_w32(0x4a000000|offset); } void emit_jns(int a) { assem_debug("bpl %x\n",a); u32 offset=genjmp(a); output_w32(0x5a000000|offset); } void emit_jl(int a) { assem_debug("blt %x\n",a); u32 offset=genjmp(a); output_w32(0xba000000|offset); } void emit_jge(int a) { assem_debug("bge %x\n",a); u32 offset=genjmp(a); output_w32(0xaa000000|offset); } void emit_jno(int a) { assem_debug("bvc %x\n",a); u32 offset=genjmp(a); output_w32(0x7a000000|offset); } void emit_jc(int a) { assem_debug("bcs %x\n",a); u32 offset=genjmp(a); output_w32(0x2a000000|offset); } void emit_jcc(int a) { assem_debug("bcc %x\n",a); u32 offset=genjmp(a); output_w32(0x3a000000|offset); } void emit_pushimm(int imm) { assem_debug("push $%x\n",imm); assert(0); } void emit_pusha() { assem_debug("pusha\n"); assert(0); } void emit_popa() { assem_debug("popa\n"); assert(0); } void emit_pushreg(unsigned int r) { assem_debug("push %%%s\n",regname[r]); assert(0); } void emit_popreg(unsigned int r) { assem_debug("pop %%%s\n",regname[r]); assert(0); } void emit_callreg(unsigned int r) { assem_debug("call *%%%s\n",regname[r]); assert(0); } void emit_jmpreg(unsigned int r) { assem_debug("mov pc,%s\n",regname[r]); output_w32(0xe1a00000|rd_rn_rm(15,0,r)); } void emit_cmpstr(int s1, int s2, int sr, int temp) { // Compare s1 and s2. If any byte is equal, set T. // Calculates the xor of the strings, then checks if any byte is zero emit_xor(s1,s2,14); emit_andimm(sr,~1,sr); emit_testimm(14,0xFF); emit_orreq_imm(sr,1,sr); emit_testimm(14,0xFF00); emit_orreq_imm(sr,1,sr); emit_testimm(14,0xFF0000); emit_orreq_imm(sr,1,sr); emit_testimm(14,0xFF000000); emit_orreq_imm(sr,1,sr); // Compare s1 and s2. If any byte is equal, set T. // Calculates the xor of the strings, then checks if any byte is // zero by subtracting 1 from each byte. If there is a carry/borrow // then a byte was zero. //assert(temp>=0); //emit_xor(s1,s2,s2); //emit_movimm(0x01010101,14); //emit_andimm(sr,~1,sr); //emit_subs(s2,14,temp); //emit_sbcimm(temp,0,temp); //emit_xor(temp,s2,temp); //emit_not(temp,temp); //emit_testimm(temp,14); //emit_xor(s1,s2,s2); //emit_orrne_imm(sr,1,sr); } void emit_negc(int rs, int rt, int sr) { assert(0); /* assert(rs>=0&&rs<8); if(rt<0) { emit_shrimm(sr,1,sr); // Get C flag emit_jc((int)out+10); // 6 emit_neg(rs,rs); // 2 emit_neg(rs,rs); // 2 emit_adc(sr,sr); // Save C flag }else{ if(rs!=rt) emit_mov(rs,rt); emit_shrimm(sr,1,sr); // Get C flag emit_jc((int)out+9); // 6 emit_addimm(rt,-1,rt); // 3 emit_adc(sr,sr); // Save C flag emit_not(rt,rt); } */ } void emit_readword_indexed(int offset, int rs, int rt) { assert(offset>-4096&&offset<4096); assem_debug("ldr %s,%s+%d\n",regname[rt],regname[rs],offset); if(offset>=0) { output_w32(0xe5900000|rd_rn_rm(rt,rs,0)|offset); }else{ output_w32(0xe5100000|rd_rn_rm(rt,rs,0)|(-offset)); } } void emit_readword_dualindexedx4(int rs1, int rs2, int rt) { assem_debug("ldr %s,%s,%s lsl #2\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe7900000|rd_rn_rm(rt,rs1,rs2)|0x100); } void emit_readword_indexed_map(int addr, int rs, int map, int rt) { if(map<0) emit_readword_indexed(addr, rs, rt); else { assert(addr==0); emit_readword_dualindexedx4(rs, map, rt); } } void emit_movsbl_indexed(int offset, int rs, int rt) { assert(offset>-256&&offset<256); assem_debug("ldrsb %s,%s+%d\n",regname[rt],regname[rs],offset); if(offset>=0) { output_w32(0xe1d000d0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf)); }else{ output_w32(0xe15000d0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf)); } } void emit_movsbl_indexed_map(int addr, int rs, int map, int rt) { if(map<0) emit_movsbl_indexed(addr, rs, rt); else { if(addr==0) { emit_shlimm(map,2,HOST_TEMPREG); assem_debug("ldrsb %s,%s+%s\n",regname[rt],regname[rs],regname[HOST_TEMPREG]); output_w32(0xe19000d0|rd_rn_rm(rt,rs,HOST_TEMPREG)); }else{ assert(addr>-256&&addr<256); assem_debug("add %s,%s,%s,lsl #2\n",regname[rt],regname[rs],regname[map]); output_w32(0xe0800000|rd_rn_rm(rt,rs,map)|(2<<7)); emit_movsbl_indexed(addr, rt, rt); } } } void emit_movswl_indexed(int offset, int rs, int rt) { assert(offset>-256&&offset<256); assem_debug("ldrsh %s,%s+%d\n",regname[rt],regname[rs],offset); if(offset>=0) { output_w32(0xe1d000f0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf)); }else{ output_w32(0xe15000f0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf)); } } void emit_movswl_indexed_map(int addr, int rs, int map, int rt) { if(map<0) emit_movswl_indexed(addr, rs, rt); else { if(addr==0) { emit_shlimm(map,2,HOST_TEMPREG); assem_debug("ldrsh %s,%s+%s\n",regname[rt],regname[rs],regname[HOST_TEMPREG]); output_w32(0xe19000f0|rd_rn_rm(rt,rs,HOST_TEMPREG)); }else{ assert(addr>-256&&addr<256); assem_debug("add %s,%s,%s,lsl #2\n",regname[rt],regname[rs],regname[map]); output_w32(0xe0800000|rd_rn_rm(rt,rs,map)|(2<<7)); emit_movswl_indexed(addr, rt, rt); } } } void emit_movzbl_indexed(int offset, int rs, int rt) { assert(offset>-4096&&offset<4096); assem_debug("ldrb %s,%s+%d\n",regname[rt],regname[rs],offset); if(offset>=0) { output_w32(0xe5d00000|rd_rn_rm(rt,rs,0)|offset); }else{ output_w32(0xe5500000|rd_rn_rm(rt,rs,0)|(-offset)); } } void emit_movzbl_dualindexedx4(int rs1, int rs2, int rt) { assem_debug("ldrb %s,%s,%s lsl #2\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe7d00000|rd_rn_rm(rt,rs1,rs2)|0x100); } void emit_movzbl_indexed_map(int addr, int rs, int map, int rt) { if(map<0) emit_movzbl_indexed(addr, rs, rt); else { if(addr==0) { emit_movzbl_dualindexedx4(rs, map, rt); }else{ emit_addimm(rs,addr,rt); emit_movzbl_dualindexedx4(rt, map, rt); } } } void emit_movzwl_indexed(int offset, int rs, int rt) { assert(offset>-256&&offset<256); assem_debug("ldrh %s,%s+%d\n",regname[rt],regname[rs],offset); if(offset>=0) { output_w32(0xe1d000b0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf)); }else{ output_w32(0xe15000b0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf)); } } void emit_readword(int addr, int rt) { u32 offset = addr-(u32)&dynarec_local; assert(offset<4096); assem_debug("ldr %s,fp+%d\n",regname[rt],offset); output_w32(0xe5900000|rd_rn_rm(rt,FP,0)|offset); } void emit_movsbl(int addr, int rt) { u32 offset = addr-(u32)&dynarec_local; assert(offset<256); assem_debug("ldrsb %s,fp+%d\n",regname[rt],offset); output_w32(0xe1d000d0|rd_rn_rm(rt,FP,0)|((offset<<4)&0xf00)|(offset&0xf)); } void emit_movswl(int addr, int rt) { u32 offset = addr-(u32)&dynarec_local; assert(offset<256); assem_debug("ldrsh %s,fp+%d\n",regname[rt],offset); output_w32(0xe1d000f0|rd_rn_rm(rt,FP,0)|((offset<<4)&0xf00)|(offset&0xf)); } void emit_movzbl(int addr, int rt) { u32 offset = addr-(u32)&dynarec_local; assert(offset<4096); assem_debug("ldrb %s,fp+%d\n",regname[rt],offset); output_w32(0xe5d00000|rd_rn_rm(rt,FP,0)|offset); } void emit_movzwl(int addr, int rt) { u32 offset = addr-(u32)&dynarec_local; assert(offset<256); assem_debug("ldrh %s,fp+%d\n",regname[rt],offset); output_w32(0xe1d000b0|rd_rn_rm(rt,FP,0)|((offset<<4)&0xf00)|(offset&0xf)); } void emit_xchg(int rs, int rt) { assem_debug("xchg %%%s,%%%s\n",regname[rs],regname[rt]); assert(0); } void emit_writeword_indexed(int rt, int offset, int rs) { assert(offset>-4096&&offset<4096); assem_debug("str %s,%s+%d\n",regname[rt],regname[rs],offset); if(offset>=0) { output_w32(0xe5800000|rd_rn_rm(rt,rs,0)|offset); }else{ output_w32(0xe5000000|rd_rn_rm(rt,rs,0)|(-offset)); } } void emit_writeword_dualindexedx4(int rt, int rs1, int rs2) { assem_debug("str %s,%s,%s lsl #2\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe7800000|rd_rn_rm(rt,rs1,rs2)|0x100); } void emit_writeword_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writeword_indexed(rt, addr, rs); else { assert(addr==0); emit_writeword_dualindexedx4(rt, rs, map); } } void emit_writehword_indexed(int rt, int offset, int rs) { assert(offset>-256&&offset<256); assem_debug("strh %s,%s+%d\n",regname[rt],regname[rs],offset); if(offset>=0) { output_w32(0xe1c000b0|rd_rn_rm(rt,rs,0)|((offset<<4)&0xf00)|(offset&0xf)); }else{ output_w32(0xe14000b0|rd_rn_rm(rt,rs,0)|(((-offset)<<4)&0xf00)|((-offset)&0xf)); } } void emit_writebyte_indexed(int rt, int offset, int rs) { assert(offset>-4096&&offset<4096); assem_debug("strb %s,%s+%d\n",regname[rt],regname[rs],offset); if(offset>=0) { output_w32(0xe5c00000|rd_rn_rm(rt,rs,0)|offset); }else{ output_w32(0xe5400000|rd_rn_rm(rt,rs,0)|(-offset)); } } void emit_writebyte_dualindexedx4(int rt, int rs1, int rs2) { assem_debug("strb %s,%s,%s lsl #2\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe7c00000|rd_rn_rm(rt,rs1,rs2)|0x100); } void emit_writebyte_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writebyte_indexed(rt, addr, rs); else { if(addr==0) { emit_writebyte_dualindexedx4(rt, rs, map); }else{ emit_addimm(rs,addr,temp); emit_writebyte_dualindexedx4(rt, temp, map); } } } void emit_writehword_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writehword_indexed(rt, addr, rs); else { if(addr==0) { emit_shlimm(map,2,HOST_TEMPREG); assem_debug("strh %s,%s+%s\n",regname[rt],regname[rs],regname[HOST_TEMPREG]); output_w32(0xe18000b0|rd_rn_rm(rt,rs,HOST_TEMPREG)); }else{ assert(addr==0); } } } void emit_writeword(int rt, int addr) { u32 offset = addr-(u32)&dynarec_local; assert(offset<4096); assem_debug("str %s,fp+%d\n",regname[rt],offset); output_w32(0xe5800000|rd_rn_rm(rt,FP,0)|offset); } void emit_writehword(int rt, int addr) { u32 offset = addr-(u32)&dynarec_local; assert(offset<256); assem_debug("strh %s,fp+%d\n",regname[rt],offset); output_w32(0xe1c000b0|rd_rn_rm(rt,FP,0)|((offset<<4)&0xf00)|(offset&0xf)); } void emit_writebyte(int rt, int addr) { u32 offset = addr-(u32)&dynarec_local; assert(offset<4096); assem_debug("strb %s,fp+%d\n",regname[rt],offset); output_w32(0xe5c00000|rd_rn_rm(rt,FP,0)|offset); } void emit_writeword_imm(int imm, int addr) { assem_debug("movl $%x,%x\n",imm,addr); assert(0); } void emit_writebyte_imm(int imm, int addr) { assem_debug("movb $%x,%x\n",imm,addr); assert(0); } void emit_rmw_andimm(int addr, int map, int imm) { if(map<0) { assert(map>=0); } else { emit_movzbl_dualindexedx4(addr, map, 14); emit_andimm(14,imm,14); emit_writebyte_dualindexedx4(14, addr, map); } } void emit_rmw_xorimm(int addr, int map, int imm) { if(map<0) { assert(map>=0); } else { emit_movzbl_dualindexedx4(addr, map, 14); emit_xorimm(14,imm,14); emit_writebyte_dualindexedx4(14, addr, map); } } void emit_rmw_orimm(int addr, int map, int imm) { if(map<0) { assert(map>=0); } else { emit_movzbl_dualindexedx4(addr, map, 14); emit_orimm(14,imm,14); emit_writebyte_dualindexedx4(14, addr, map); } } void emit_sh2tas(int addr, int map, int sr) { if(map<0) { assert(map>=0); } else { emit_movzbl_dualindexedx4(addr, map, 14); emit_andimm(sr,~1,sr); emit_testimm(14,0xff); emit_orimm(14,0x80,14); emit_orreq_imm(sr,1,sr); emit_writebyte_dualindexedx4(14, addr, map); } } void emit_mul(int rs) { assem_debug("mul %%%s\n",regname[rs]); assert(0); } void emit_imul(int rs) { assem_debug("imul %%%s\n",regname[rs]); assert(0); } void emit_multiply(unsigned int rs1,unsigned int rs2,unsigned int rt) { if(rs1==rt&&rs2==rt) {emit_mov(rs1,14);emit_multiply(14,14,rt);} else if(rs1==rt) {emit_multiply(rs2,rs1,rt);} else { assem_debug("mul %s, %s, %s\n",regname[rt],regname[rs1],regname[rs2]); assert(rs1<16); assert(rs2<16); assert(rt<16); output_w32(0xe0000090|(rt<<16)|(rs2<<8)|rs1); } } void emit_umull(unsigned int rs1, unsigned int rs2, unsigned int hi, unsigned int lo) { assem_debug("umull %s, %s, %s, %s\n",regname[lo],regname[hi],regname[rs1],regname[rs2]); assert(rs1<16); assert(rs2<16); assert(hi<16); assert(lo<16); assert(hi!=rs1&&hi!=rs2); assert(lo!=rs1&&lo!=rs2); output_w32(0xe0800090|(hi<<16)|(lo<<12)|(rs2<<8)|rs1); } void emit_smull(unsigned int rs1, unsigned int rs2, unsigned int hi, unsigned int lo) { assem_debug("smull %s, %s, %s, %s\n",regname[lo],regname[hi],regname[rs1],regname[rs2]); assert(rs1<16); assert(rs2<16); assert(hi<16); assert(lo<16); assert(hi!=rs1&&hi!=rs2); assert(lo!=rs1&&lo!=rs2); output_w32(0xe0c00090|(hi<<16)|(lo<<12)|(rs2<<8)|rs1); } void emit_div(int rs) { assem_debug("div %%%s\n",regname[rs]); assert(0); } void emit_idiv(int rs) { assem_debug("idiv %%%s\n",regname[rs]); assert(0); } void emit_cdq() { assem_debug("cdq\n"); assert(0); } void emit_teq(int rs, int rt) { assem_debug("teq %s,%s\n",regname[rs],regname[rt]); output_w32(0xe1300000|rd_rn_rm(0,rs,rt)); } void emit_div0s(int s1, int s2, int sr, int temp) { emit_andimm(sr,0xfe,sr); emit_testimm(s2,0x80000000); emit_orrne_imm(sr,0x100,sr); emit_testimm(s1,0x80000000); emit_orrne_imm(sr,0x200,sr); emit_teq(s1,s2); emit_orrmi_imm(sr,1,sr); } // Load return address void emit_load_return_address(unsigned int rt) { // (assumes this instruction will be followed by a branch instruction) emit_mov(15,rt); //assem_debug("add %s,pc,#0\n",regname[rt]); //output_w32(0xe2800000|rd_rn_rm(rt,15,0)); } void emit_clz(int rs,int rt) { assem_debug("clz %s,%s\n",regname[rt],regname[rs]); output_w32(0xe16f0f10|rd_rn_rm(rt,0,rs)); } void emit_subcs(int rs1,int rs2,int rt) { assem_debug("subcs %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0x20400000|rd_rn_rm(rt,rs1,rs2)); } void emit_shrcc_imm(int rs,unsigned int imm,int rt) { assert(imm>0); assert(imm<32); assem_debug("lsrcc %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x31a00000|rd_rn_rm(rt,0,rs)|0x20|(imm<<7)); } void emit_negmi(int rs, int rt) { assem_debug("rsbmi %s,%s,#0\n",regname[rt],regname[rs]); output_w32(0x42600000|rd_rn_rm(rt,rs,0)); } void emit_negsmi(int rs, int rt) { assem_debug("rsbsmi %s,%s,#0\n",regname[rt],regname[rs]); output_w32(0x42700000|rd_rn_rm(rt,rs,0)); } void emit_orreq(unsigned int rs1,unsigned int rs2,unsigned int rt) { assem_debug("orreq %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0x01800000|rd_rn_rm(rt,rs1,rs2)); } void emit_orrne(unsigned int rs1,unsigned int rs2,unsigned int rt) { assem_debug("orrne %s,%s,%s\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0x11800000|rd_rn_rm(rt,rs1,rs2)); } void emit_bic_lsl(unsigned int rs1,unsigned int rs2,unsigned int shift,unsigned int rt) { assem_debug("bic %s,%s,%s lsl %s\n",regname[rt],regname[rs1],regname[rs2],regname[shift]); output_w32(0xe1C00000|rd_rn_rm(rt,rs1,rs2)|0x10|(shift<<8)); } void emit_biceq_lsl(unsigned int rs1,unsigned int rs2,unsigned int shift,unsigned int rt) { assem_debug("biceq %s,%s,%s lsl %s\n",regname[rt],regname[rs1],regname[rs2],regname[shift]); output_w32(0x01C00000|rd_rn_rm(rt,rs1,rs2)|0x10|(shift<<8)); } void emit_bicne_lsl(unsigned int rs1,unsigned int rs2,unsigned int shift,unsigned int rt) { assem_debug("bicne %s,%s,%s lsl %s\n",regname[rt],regname[rs1],regname[rs2],regname[shift]); output_w32(0x11C00000|rd_rn_rm(rt,rs1,rs2)|0x10|(shift<<8)); } void emit_bic_lsr(unsigned int rs1,unsigned int rs2,unsigned int shift,unsigned int rt) { assem_debug("bic %s,%s,%s lsr %s\n",regname[rt],regname[rs1],regname[rs2],regname[shift]); output_w32(0xe1C00000|rd_rn_rm(rt,rs1,rs2)|0x30|(shift<<8)); } void emit_biceq_lsr(unsigned int rs1,unsigned int rs2,unsigned int shift,unsigned int rt) { assem_debug("biceq %s,%s,%s lsr %s\n",regname[rt],regname[rs1],regname[rs2],regname[shift]); output_w32(0x01C00000|rd_rn_rm(rt,rs1,rs2)|0x30|(shift<<8)); } void emit_bicne_lsr(unsigned int rs1,unsigned int rs2,unsigned int shift,unsigned int rt) { assem_debug("bicne %s,%s,%s lsr %s\n",regname[rt],regname[rs1],regname[rs2],regname[shift]); output_w32(0x11C00000|rd_rn_rm(rt,rs1,rs2)|0x30|(shift<<8)); } void emit_rsbimm(int rs, int imm, int rt) { u32 armval; genimm(imm,&armval); assem_debug("rsb %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0xe2600000|rd_rn_rm(rt,rs,0)|armval); } // Load 2 immediates optimizing for small code size void emit_mov2imm_compact(int imm1,unsigned int rt1,int imm2,unsigned int rt2) { emit_movimm(imm1,rt1); u32 armval; if(genimm(imm2-imm1,&armval)) { assem_debug("add %s,%s,#%d\n",regname[rt2],regname[rt1],imm2-imm1); output_w32(0xe2800000|rd_rn_rm(rt2,rt1,0)|armval); }else if(genimm(imm1-imm2,&armval)) { assem_debug("sub %s,%s,#%d\n",regname[rt2],regname[rt1],imm1-imm2); output_w32(0xe2400000|rd_rn_rm(rt2,rt1,0)|armval); } else emit_movimm(imm2,rt2); } // Conditionally select one of two immediates, optimizing for small code size // This will only be called if HAVE_CMOV_IMM is defined void emit_cmov2imm_e_ne_compact(int imm1,int imm2,unsigned int rt) { u32 armval; if(genimm(imm2-imm1,&armval)) { emit_movimm(imm1,rt); assem_debug("addne %s,%s,#%d\n",regname[rt],regname[rt],imm2-imm1); output_w32(0x12800000|rd_rn_rm(rt,rt,0)|armval); }else if(genimm(imm1-imm2,&armval)) { emit_movimm(imm1,rt); assem_debug("subne %s,%s,#%d\n",regname[rt],regname[rt],imm1-imm2); output_w32(0x12400000|rd_rn_rm(rt,rt,0)|armval); } else { #ifndef HAVE_ARMv7 emit_movimm(imm1,rt); add_literal((int)out,imm2); assem_debug("ldrne %s,pc+? [=%x]\n",regname[rt],imm2); output_w32(0x15900000|rd_rn_rm(rt,15,0)); #else emit_movw(imm1&0x0000FFFF,rt); if((imm1&0xFFFF)!=(imm2&0xFFFF)) { assem_debug("movwne %s,#%d (0x%x)\n",regname[rt],imm2&0xFFFF,imm2&0xFFFF); output_w32(0x13000000|rd_rn_rm(rt,0,0)|(imm2&0xfff)|((imm2<<4)&0xf0000)); } emit_movt(imm1&0xFFFF0000,rt); if((imm1&0xFFFF0000)!=(imm2&0xFFFF0000)) { assem_debug("movtne %s,#%d (0x%x)\n",regname[rt],imm2&0xffff0000,imm2&0xffff0000); output_w32(0x13400000|rd_rn_rm(rt,0,0)|((imm2>>16)&0xfff)|((imm2>>12)&0xf0000)); } #endif } } // special case for checking invalid_code void emit_cmpmem_indexedsr12_imm(int addr,int r,int imm) { assert(0); } // special case for checking invalid_code void emit_cmpmem_indexedsr12_reg(int base,int r,int imm) { assert(imm<128&&imm>=0); assert(r>=0&&r<16); assem_debug("ldrb lr,%s,%s lsr #12\n",regname[base],regname[r]); output_w32(0xe7d00000|rd_rn_rm(HOST_TEMPREG,base,r)|0x620); emit_cmpimm(HOST_TEMPREG,imm); } // special case for memory map void emit_addsr12(int rs1,int rs2,int rt) { assem_debug("add %s,%s,%s lsr #12\n",regname[rt],regname[rs1],regname[rs2]); output_w32(0xe0800620|rd_rn_rm(rt,rs1,rs2)); } void emit_callne(int a) { assem_debug("blne %x\n",a); u32 offset=genjmp(a); output_w32(0x1b000000|offset); } // Used to preload hash table entries void emit_prefetch(void *addr) { assem_debug("prefetch %x\n",(int)addr); output_byte(0x0F); output_byte(0x18); output_modrm(0,5,1); output_w32((int)addr); } void emit_prefetchreg(int r) { assem_debug("pld %s\n",regname[r]); output_w32(0xf5d0f000|rd_rn_rm(0,r,0)); } // Special case for mini_ht void emit_ldreq_indexed(int rs, u32 offset, int rt) { assert(offset<4096); assem_debug("ldreq %s,[%s, #%d]\n",regname[rt],regname[rs],offset); output_w32(0x05900000|rd_rn_rm(rt,rs,0)|offset); } void emit_flds(int r,int sr) { assem_debug("flds s%d,[%s]\n",sr,regname[r]); output_w32(0xed900a00|((sr&14)<<11)|((sr&1)<<22)|(r<<16)); } void emit_vldr(int r,int vr) { assem_debug("vldr d%d,[%s]\n",vr,regname[r]); output_w32(0xed900b00|(vr<<12)|(r<<16)); } void emit_fsts(int sr,int r) { assem_debug("fsts s%d,[%s]\n",sr,regname[r]); output_w32(0xed800a00|((sr&14)<<11)|((sr&1)<<22)|(r<<16)); } void emit_vstr(int vr,int r) { assem_debug("vstr d%d,[%s]\n",vr,regname[r]); output_w32(0xed800b00|(vr<<12)|(r<<16)); } void emit_ftosizs(int s,int d) { assem_debug("ftosizs s%d,s%d\n",d,s); output_w32(0xeebd0ac0|((d&14)<<11)|((d&1)<<22)|((s&14)>>1)|((s&1)<<5)); } void emit_ftosizd(int s,int d) { assem_debug("ftosizd s%d,d%d\n",d,s); output_w32(0xeebd0bc0|((d&14)<<11)|((d&1)<<22)|(s&7)); } void emit_fsitos(int s,int d) { assem_debug("fsitos s%d,s%d\n",d,s); output_w32(0xeeb80ac0|((d&14)<<11)|((d&1)<<22)|((s&14)>>1)|((s&1)<<5)); } void emit_fsitod(int s,int d) { assem_debug("fsitod d%d,s%d\n",d,s); output_w32(0xeeb80bc0|((d&7)<<12)|((s&14)>>1)|((s&1)<<5)); } void emit_fcvtds(int s,int d) { assem_debug("fcvtds d%d,s%d\n",d,s); output_w32(0xeeb70ac0|((d&7)<<12)|((s&14)>>1)|((s&1)<<5)); } void emit_fcvtsd(int s,int d) { assem_debug("fcvtsd s%d,d%d\n",d,s); output_w32(0xeeb70bc0|((d&14)<<11)|((d&1)<<22)|(s&7)); } void emit_fsqrts(int s,int d) { assem_debug("fsqrts d%d,s%d\n",d,s); output_w32(0xeeb10ac0|((d&14)<<11)|((d&1)<<22)|((s&14)>>1)|((s&1)<<5)); } void emit_fsqrtd(int s,int d) { assem_debug("fsqrtd s%d,d%d\n",d,s); output_w32(0xeeb10bc0|((d&7)<<12)|(s&7)); } void emit_fabss(int s,int d) { assem_debug("fabss d%d,s%d\n",d,s); output_w32(0xeeb00ac0|((d&14)<<11)|((d&1)<<22)|((s&14)>>1)|((s&1)<<5)); } void emit_fabsd(int s,int d) { assem_debug("fabsd s%d,d%d\n",d,s); output_w32(0xeeb00bc0|((d&7)<<12)|(s&7)); } void emit_fnegs(int s,int d) { assem_debug("fnegs d%d,s%d\n",d,s); output_w32(0xeeb10a40|((d&14)<<11)|((d&1)<<22)|((s&14)>>1)|((s&1)<<5)); } void emit_fnegd(int s,int d) { assem_debug("fnegd s%d,d%d\n",d,s); output_w32(0xeeb10b40|((d&7)<<12)|(s&7)); } void emit_fadds(int s1,int s2,int d) { assem_debug("fadds s%d,s%d,s%d\n",d,s1,s2); output_w32(0xee300a00|((d&14)<<11)|((d&1)<<22)|((s1&14)<<15)|((s1&1)<<7)|((s2&14)>>1)|((s2&1)<<5)); } void emit_faddd(int s1,int s2,int d) { assem_debug("faddd d%d,d%d,d%d\n",d,s1,s2); output_w32(0xee300b00|((d&7)<<12)|((s1&7)<<16)|(s2&7)); } void emit_fsubs(int s1,int s2,int d) { assem_debug("fsubs s%d,s%d,s%d\n",d,s1,s2); output_w32(0xee300a40|((d&14)<<11)|((d&1)<<22)|((s1&14)<<15)|((s1&1)<<7)|((s2&14)>>1)|((s2&1)<<5)); } void emit_fsubd(int s1,int s2,int d) { assem_debug("fsubd d%d,d%d,d%d\n",d,s1,s2); output_w32(0xee300b40|((d&7)<<12)|((s1&7)<<16)|(s2&7)); } void emit_fmuls(int s1,int s2,int d) { assem_debug("fmuls s%d,s%d,s%d\n",d,s1,s2); output_w32(0xee200a00|((d&14)<<11)|((d&1)<<22)|((s1&14)<<15)|((s1&1)<<7)|((s2&14)>>1)|((s2&1)<<5)); } void emit_fmuld(int s1,int s2,int d) { assem_debug("fmuld d%d,d%d,d%d\n",d,s1,s2); output_w32(0xee200b00|((d&7)<<12)|((s1&7)<<16)|(s2&7)); } void emit_fdivs(int s1,int s2,int d) { assem_debug("fdivs s%d,s%d,s%d\n",d,s1,s2); output_w32(0xee800a00|((d&14)<<11)|((d&1)<<22)|((s1&14)<<15)|((s1&1)<<7)|((s2&14)>>1)|((s2&1)<<5)); } void emit_fdivd(int s1,int s2,int d) { assem_debug("fdivd d%d,d%d,d%d\n",d,s1,s2); output_w32(0xee800b00|((d&7)<<12)|((s1&7)<<16)|(s2&7)); } void emit_fcmps(int x,int y) { assem_debug("fcmps s14, s15\n"); output_w32(0xeeb47a67); } void emit_fcmpd(int x,int y) { assem_debug("fcmpd d6, d7\n"); output_w32(0xeeb46b47); } void emit_fmstat() { assem_debug("fmstat\n"); output_w32(0xeef1fa10); } void emit_bicne_imm(int rs,int imm,int rt) { u32 armval; genimm(imm,&armval); assem_debug("bicne %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x13c00000|rd_rn_rm(rt,rs,0)|armval); } void emit_biccs_imm(int rs,int imm,int rt) { u32 armval; genimm(imm,&armval); assem_debug("biccs %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x23c00000|rd_rn_rm(rt,rs,0)|armval); } void emit_bicvc_imm(int rs,int imm,int rt) { u32 armval; genimm(imm,&armval); assem_debug("bicvc %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x73c00000|rd_rn_rm(rt,rs,0)|armval); } void emit_bichi_imm(int rs,int imm,int rt) { u32 armval; genimm(imm,&armval); assem_debug("bichi %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x83c00000|rd_rn_rm(rt,rs,0)|armval); } void emit_orrvs_imm(int rs,int imm,int rt) { u32 armval; genimm(imm,&armval); assem_debug("orrvs %s,%s,#%d\n",regname[rt],regname[rs],imm); output_w32(0x63800000|rd_rn_rm(rt,rs,0)|armval); } void emit_jno_unlikely(int a) { //emit_jno(a); assem_debug("addvc pc,pc,#? (%x)\n",/*a-(int)out-8,*/a); output_w32(0x72800000|rd_rn_rm(15,15,0)); } // Save registers before function call void save_regs(u32 reglist) { reglist&=0x100f; // only save the caller-save registers, r0-r3, r12 if(!reglist) return; assem_debug("stmia fp,{"); if(reglist&1) assem_debug("r0, "); if(reglist&2) assem_debug("r1, "); if(reglist&4) assem_debug("r2, "); if(reglist&8) assem_debug("r3, "); if(reglist&0x1000) assem_debug("r12"); assem_debug("}\n"); output_w32(0xe88b0000|reglist); } // Restore registers after function call void restore_regs(u32 reglist) { reglist&=0x100f; // only restore the caller-save registers, r0-r3, r12 if(!reglist) return; assem_debug("ldmia fp,{"); if(reglist&1) assem_debug("r0, "); if(reglist&2) assem_debug("r1, "); if(reglist&4) assem_debug("r2, "); if(reglist&8) assem_debug("r3, "); if(reglist&0x1000) assem_debug("r12"); assem_debug("}\n"); output_w32(0xe89b0000|reglist); } /* Write back consts using r14 so we don't disturb the other registers void wb_consts(signed char i_regmap[],uint64_t i_is32,u_int i_dirty,int i) { int hr; for(hr=0;hr=0&&((i_dirty>>hr)&1)) { if(((regs[i].isdoingcp>>hr)&1)&&i_regmap[hr]>0) { if(i_regmap[hr]<64 || !((i_is32>>(i_regmap[hr]&63))&1) ) { int value=cpmap[i][hr]; if(value==0) { emit_zeroreg(HOST_TEMPREG); } else { emit_movimm(value,HOST_TEMPREG); } emit_storereg(i_regmap[hr],HOST_TEMPREG); if((i_is32>>i_regmap[hr])&1) { if(value!=-1&&value!=0) emit_sarimm(HOST_TEMPREG,31,HOST_TEMPREG); emit_storereg(i_regmap[hr]|64,HOST_TEMPREG); } } } } } }*/ /* Stubs/epilogue */ void literal_pool(int n) { if(!literalcount) return; if(n) { if((int)out-literals[0][0]<4096-n) return; } u32 *ptr; int i; for(i=0;i=0x7000000&&addr<0x7FFFFFF); //DEBUG > #ifdef DEBUG_CYCLE_COUNT emit_readword((int)&last_count,ECX); emit_add(HOST_CCREG,ECX,HOST_CCREG); emit_readword((int)&next_interupt,ECX); emit_writeword(HOST_CCREG,(int)&Count); emit_sub(HOST_CCREG,ECX,HOST_CCREG); emit_writeword(ECX,(int)&last_count); #endif //DEBUG < emit_jmp((pointer)dyna_linker); } do_readstub(int n) { assem_debug("do_readstub %x\n",start+stubs[n][3]*2); literal_pool(256); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); int rt; int ds; rt=get_reg(i_regmap,rt1[i]==TBIT?-1:rt1[i]); assert(rs>=0); if(addr<0) addr=rt; if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); if(type==LOADB_STUB) emit_xorimm(rs,1,0); else {if(rs!=0) emit_mov(rs,0);} //ds=i_regs!=®s[i]; //int real_rs=(itype[i]==LOADLR)?-1:get_reg(i_regmap,rs1[i]); //u32 cmask=ds?-1:(0x100f|~i_regs->wasdoingcp); //if(!ds) load_all_consts(regs[i].regmap_entry,regs[i].was32,regs[i].wasdirty&~(1<regmap_entry,i_regs->was32,i_regs->wasdirty&cmask&~(1<=0); if(rs<4||rs==12) emit_writeword(rs,(int)&dynarec_local+24); emit_call((int)MappedMemoryReadLong); if(rs==1||rs==2||rs==3||rs==12) emit_readword((int)&dynarec_local+24,rs); if(pc==0) { emit_writeword(0,(int)&dynarec_local+24); } else { if(pc==1||pc==2||pc==3||pc==12) emit_writeword(0,(int)&dynarec_local+24); else emit_mov(0,pc); if(rs==0) { emit_readword((int)&dynarec_local+24,rs); emit_addimm(rs,4,rs); }else emit_addimm(rs,4,0); } emit_call((int)MappedMemoryReadLong); assert(rt>=0); if(rt!=0) emit_mov(0,rt); if(pc<4||pc==12) emit_readword((int)&dynarec_local+24,pc); } else if(type==LOADB_STUB) { if(rt>=0) emit_movsbl_reg(0,rt); } else if(type==LOADW_STUB) { if(rt>=0) emit_movswl_reg(0,rt); } else { if(rt>0) emit_mov(0,rt); } restore_regs(reglist); if(type==LOADS_STUB) emit_addimm(rs,8,rs); emit_jmp(stubs[n][2]); // return address } inline_readstub(int type, int i, u32 addr, signed char regmap[], int target, int adj, u32 reglist) { assem_debug("inline_readstub\n"); //int rs=get_reg(regmap,target); int rt=get_reg(regmap,target); //if(rs<0) rs=get_reg(regmap,-1); if(rt<0) rt=get_reg(regmap,-1); //rt=get_reg(i_regmap,rt1[i]==TBIT?-1:rt1[i]); assert(rt>=0); //if(addr<0) addr=rt; //if(addr<0) addr=get_reg(i_regmap,-1); //assert(addr>=0); save_regs(reglist); emit_movimm(addr,0); if(type==LOADB_STUB) emit_call((int)MappedMemoryReadByte); if(type==LOADW_STUB) emit_call((int)MappedMemoryReadWord); if(type==LOADL_STUB) emit_call((int)MappedMemoryReadLong); assert(type!=LOADS_STUB); if(type==LOADB_STUB) { if(rt>=0) emit_movsbl_reg(0,rt); } else if(type==LOADW_STUB) { if(rt>=0) emit_movswl_reg(0,rt); } else { if(rt>0) emit_mov(0,rt); } restore_regs(reglist); } do_writestub(int n) { assem_debug("do_writestub %x\n",start+stubs[n][3]*2); literal_pool(256); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); int rt=get_reg(i_regmap,rs1[i]); assert(rs>=0); assert(rt>=0); if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); // "FASTCALL" api: address in r0, data in r1 if(rs!=0) { if(rt==0) { if(rs==1) { emit_mov(0,2); emit_mov(1,0); emit_mov(2,1); } else { emit_mov(rt,1); emit_mov(rs,0); } } else { emit_mov(rs,0); if(rt!=1) emit_mov(rt,1); } } else if(rt!=1) emit_mov(rt,1); //if(type==STOREB_STUB) emit_xorimm(EAX,1,EAX); // WriteInvalidateByteSwapped does this //if(i_regmap[HOST_CCREG]==CCREG) emit_storereg(CCREG,HOST_CCREG);//DEBUG //ds=i_regs!=®s[i]; //int real_rs=get_reg(i_regmap,rs2[i]); //if(!ds) load_all_consts(regs[i].regmap_entry,regs[i].was32,regs[i].wasdirty&~(1<regmap_entry,i_regs->was32,i_regs->wasdirty&~(1<=0); assert(rt>=0); save_regs(reglist); // "FASTCALL" api: address in r0, data in r1 if(rt!=1) emit_mov(rt,1); emit_movimm(addr,0); // FIXME - should be able to move the existing value if(type==STOREB_STUB) emit_call((int)WriteInvalidateByte); if(type==STOREW_STUB) emit_call((int)WriteInvalidateWord); if(type==STOREL_STUB) emit_call((int)WriteInvalidateLong); restore_regs(reglist); } do_rmwstub(int n) { assem_debug("do_rmwstub %x\n",start+stubs[n][3]*2); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); //int rt=get_reg(i_regmap,rs1[i]); assert(rs>=0); //assert(rt>=0); if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); // "FASTCALL" api: address in r0, data in r1 emit_xorimm(rs,1,rs); if(rs!=0) emit_mov(rs,0); if(rs<4||rs==12) emit_writeword(0,(int)&dynarec_local+24); //if(i_regmap[HOST_CCREG]==CCREG) emit_storereg(CCREG,HOST_CCREG);//DEBUG emit_call((int)MappedMemoryReadByte); //emit_mov(0,1); if(type==RMWA_STUB) emit_andimm(0,imm[i],1); if(type==RMWX_STUB) emit_xorimm(0,imm[i],1); if(type==RMWO_STUB) emit_orimm(0,imm[i],1); if(type==RMWT_STUB) { // TAS.B emit_writeword(0,(int)&dynarec_local+20); emit_orimm(0,0x80,1); } if(rs<4||rs==12) emit_readword((int)&dynarec_local+24,0); else emit_mov(rs,0); //emit_call((int)MappedMemoryWriteByte); emit_call((int)WriteInvalidateByte); restore_regs(reglist); if(opcode2[i]==11) { // TAS.B emit_readword((int)&dynarec_local+20,14); signed char sr; sr=get_reg(i_regs->regmap,SR); assert(sr>=0); // Liveness analysis? emit_andimm(sr,~1,sr); emit_testimm(14,0xff); emit_orreq_imm(sr,1,sr); } emit_jmp(stubs[n][2]); // return address } do_unalignedwritestub(int n) { set_jump_target(stubs[n][1],(int)out); output_w32(0xef000000); emit_jmp(stubs[n][2]); // return address } void printregs(int edi,int esi,int ebp,int esp,int b,int d,int c,int a) { printf("regs: %x %x %x %x %x %x %x (%x)\n",a,b,c,d,ebp,esi,edi,(&edi)[-1]); } int do_dirty_stub(int i) { assem_debug("do_dirty_stub %x\n",start+i*2); u32 alignedlen=((((u32)source)+slen*2+2)&~2)-(u32)alignedsource; // Careful about the code output here, verify_dirty needs to parse it. #ifndef HAVE_ARMv7 emit_loadlp(((int)source&~3),1); emit_loadlp((int)copy,2); emit_loadlp((((u32)source+slen*2+2)&~3)-((u32)source&~3),3); #else emit_movw(((u32)source&~3)&0x0000FFFF,1); emit_movw(((u32)copy)&0x0000FFFF,2); emit_movt(((u32)source&~3)&0xFFFF0000,1); emit_movt(((u32)copy)&0xFFFF0000,2); emit_movw((((u32)source+slen*2+2)&~3)-((u32)source&~3),3); #endif emit_movimm(start+i*2+slave,0); emit_call((int)&verify_code); int entry=(int)out; load_regs_entry(i); if(entry==(int)out) entry=instr_addr[i]; emit_jmp(instr_addr[i]); return entry; } /* Memory Map */ int do_map_r(int s,int ar,int map,int cache,int x,int a,int shift,int c,u32 addr) { if(c) { return -1; // No mapping } else { assert(s!=map); if(cache>=0) { // Use cached offset to memory map emit_addsr12(cache,s,map); }else{ emit_movimm(((int)memory_map-(int)&dynarec_local)>>2,map); emit_addsr12(map,s,map); } // Schedule this while we wait on the load if(x) emit_xorimm(s,x,ar); //if(shift>=0) emit_shlimm(s,3,shift); //if(~a) emit_andimm(s,a,ar); emit_readword_dualindexedx4(FP,map,map); } return map; } int do_map_r_branch(int map, int c, u32 addr, int *jaddr) { if(!c) { emit_test(map,map); *jaddr=(int)out; emit_js(0); } return map; } int gen_tlb_addr_r(int ar, int map) { if(map>=0) { assem_debug("add %s,%s,%s lsl #2\n",regname[ar],regname[ar],regname[map]); output_w32(0xe0800100|rd_rn_rm(ar,ar,map)); } } int do_map_w(int s,int ar,int map,int cache,int x,int c,u32 addr) { if(c) { if(can_direct_write(addr)) { // address_generation already loaded the const emit_readword_dualindexedx4(FP,map,map); } else return -1; // No mapping } else { assert(s!=map); if(cache>=0) { // Use cached offset to memory map emit_addsr12(cache,s,map); }else{ emit_movimm(((int)memory_map-(int)&dynarec_local)>>2,map); emit_addsr12(map,s,map); } // Schedule this while we wait on the load if(x) emit_xorimm(s,x,ar); emit_readword_dualindexedx4(FP,map,map); } return map; } int do_map_w_branch(int map, int c, u32 addr, int *jaddr) { if(!c||can_direct_write(addr)) { emit_testimm(map,0x40000000); *jaddr=(int)out; emit_jne(0); } } int gen_tlb_addr_w(int ar, int map) { if(map>=0) { assem_debug("add %s,%s,%s lsl #2\n",regname[ar],regname[ar],regname[map]); output_w32(0xe0800100|rd_rn_rm(ar,ar,map)); } } // This reverses the above operation int gen_orig_addr_w(int ar, int map) { if(map>=0) { assem_debug("sub %s,%s,%s lsl #2\n",regname[ar],regname[ar],regname[map]); output_w32(0xe0400100|rd_rn_rm(ar,ar,map)); } } // Generate the address of the memory_map entry, relative to dynarec_local generate_map_const(u32 addr,int reg) { //printf("generate_map_const(%x,%s)\n",addr,regname[reg]); emit_movimm((addr>>12)+(((u32)memory_map-(u32)&dynarec_local)>>2),reg); } /* Special assem */ void do_preload_rhash(int r) { // Don't need this for ARM. On x86, this puts the value 0xf8 into the // register. On ARM the hash can be done with a single instruction (below) } void do_preload_rhtbl(int ht) { if(slave) emit_addimm(FP,(int)&mini_ht_slave-(int)&dynarec_local,ht); else emit_addimm(FP,(int)&mini_ht_master-(int)&dynarec_local,ht); } void do_rhash(int rs,int rh) { emit_andimm(rs,0xf8,rh); } void do_miniht_load(int ht,int rh) { assem_debug("ldr %s,[%s,%s]!\n",regname[rh],regname[ht],regname[rh]); output_w32(0xe7b00000|rd_rn_rm(rh,ht,rh)); } void do_miniht_jump(int rs,int rh,int ht) { emit_cmp(rh,rs); emit_ldreq_indexed(ht,4,15); #ifdef CORTEX_A8_BRANCH_PREDICTION_HACK emit_mov(rs,7); emit_jmp(jump_vaddr_reg[slave][7]); #else emit_jmp(jump_vaddr_reg[slave][rs]); #endif } void do_miniht_insert(u32 return_address,int rt,int temp) { #ifndef HAVE_ARMv7 emit_movimm(return_address,rt); // PC into link register add_to_linker((int)out,return_address,1); emit_pcreladdr(temp); if(slave) emit_writeword(rt,(int)&mini_ht_slave[(return_address&0xFF)>>3][0]); else emit_writeword(rt,(int)&mini_ht_master[(return_address&0xFF)>>3][0]); if(slave) emit_writeword(temp,(int)&mini_ht_slave[(return_address&0xFF)>>3][1]); else emit_writeword(temp,(int)&mini_ht_master[(return_address&0xFF)>>3][1]); #else emit_movw(return_address&0x0000FFFF,rt); add_to_linker((int)out,return_address,1); emit_pcreladdr(temp); if(slave) emit_writeword(temp,(int)&mini_ht_slave[(return_address&0xFF)>>3][1]); else emit_writeword(temp,(int)&mini_ht_master[(return_address&0xFF)>>3][1]); emit_movt(return_address&0xFFFF0000,rt); if(slave) emit_writeword(rt,(int)&mini_ht_slave[(return_address&0xFF)>>3][0]); else emit_writeword(rt,(int)&mini_ht_master[(return_address&0xFF)>>3][0]); #endif } void wb_valid(signed char pre[],signed char entry[],u32 dirty_pre,u32 dirty,u64 u) { //if(dirty_pre==dirty) return; int hr,reg,new_hr; for(hr=0;hr>(reg&63))&1) { if(reg>=0) { if(((dirty_pre&~dirty)>>hr)&1) { if(reg>=0&®=0;hr--) { if(hr!=EXCLUDE_REG) { if(pre[hr]!=entry[hr]) { if(pre[hr]>=0) { if((dirty>>hr)&1) { if(get_reg(entry,pre[hr])<0) { if(pre[hr]<64) { if(!((u>>pre[hr])&1)) { if(hr<10&&(~hr&1)&&(pre[hr+1]<0||wrote==hr+1)) { if( ((is32>>pre[hr])&1) && !((uu>>pre[hr])&1) ) { emit_sarimm(hr,31,hr+1); emit_strdreg(pre[hr],hr); } else emit_storereg(pre[hr],hr); }else{ emit_storereg(pre[hr],hr); if( ((is32>>pre[hr])&1) && !((uu>>pre[hr])&1) ) { emit_sarimm(hr,31,hr); emit_storereg(pre[hr]|64,hr); } } } }else{ if(!((uu>>(pre[hr]&63))&1) && !((is32>>(pre[hr]&63))&1)) { emit_storereg(pre[hr],hr); } } wrote=hr; } } } } } } for(hr=0;hr=0) { int nr; if((nr=get_reg(entry,pre[hr]))>=0) { emit_mov(hr,nr); } } } } } } #define wb_invalidate wb_invalidate_arm */ // Clearing the cache is rather slow on ARM Linux, so mark the areas // that need to be cleared, and then only clear these areas once. void do_clear_cache() { int i,j; for (i=0;i<(1<<(TARGET_SIZE_2-17));i++) { u32 bitmap=needs_clear_cache[i]; if(bitmap) { u32 start,end; for(j=0;j<32;j++) { if(bitmap&(1<32M int *ptr,*ptr2; ptr=(int *)jump_table_symbols; ptr2=(int *)((void *)BASE_ADDR+(1<=-33554432&&offset<33554432) { *ptr2=0xea000000|((offset>>2)&0xffffff); // direct branch }else{ *ptr2=0xe51ff004; // ldr pc,[pc,#-4] } ptr2++; *ptr2=*ptr; ptr++; ptr2++; } // Jumping thru the trampolines created above slows things down by about 1%. // If part of the cache is beyond the 32M limit, avoid using this area // initially. It will be used later if the cache gets full. if((u32)dyna_linker-33554432>(u32)BASE_ADDR) { if((u32)dyna_linker-33554432<(u32)BASE_ADDR+(1<<(TARGET_SIZE_2-1))) { out=(u8 *)(((u32)dyna_linker-33554432)&~4095); expirep=((((int)out-BASE_ADDR)>>(TARGET_SIZE_2-16))+16384)&65535; } } #endif } yabause-0.9.13.1/src/sh2_dynarec/assem_x64.c000644 001750 001750 00000243723 12256006124 022364 0ustar00guillaumeguillaume000000 000000 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Yabause - assem_x64.c * * Copyright (C) 2009-2011 Ari64 * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ u64 memory_map[1048576]; ALIGNED(8) u32 mini_ht_master[32][2]; ALIGNED(8) u32 mini_ht_slave[32][2]; ALIGNED(4) u8 restore_candidate[512]; int rccount; int master_reg[22]; int master_cc; // Cycle count int master_pc; // Virtual PC void * master_ip; // Translated PC int slave_reg[22]; int slave_cc; // Cycle count int slave_pc; // Virtual PC void * slave_ip; // Translated PC void FASTCALL WriteInvalidateLong(u32 addr, u32 val); void FASTCALL WriteInvalidateWord(u32 addr, u32 val); void FASTCALL WriteInvalidateByte(u32 addr, u32 val); void FASTCALL WriteInvalidateByteSwapped(u32 addr, u32 val); void jump_vaddr_eax_master(); void jump_vaddr_ecx_master(); void jump_vaddr_edx_master(); void jump_vaddr_ebx_master(); void jump_vaddr_ebp_master(); void jump_vaddr_edi_master(); void jump_vaddr_eax_slave(); void jump_vaddr_ecx_slave(); void jump_vaddr_edx_slave(); void jump_vaddr_ebx_slave(); void jump_vaddr_ebp_slave(); void jump_vaddr_edi_slave(); const pointer jump_vaddr_reg[2][8] = { { (pointer)jump_vaddr_eax_master, (pointer)jump_vaddr_ecx_master, (pointer)jump_vaddr_edx_master, (pointer)jump_vaddr_ebx_master, 0, (pointer)jump_vaddr_ebp_master, 0, (pointer)jump_vaddr_edi_master },{ (pointer)jump_vaddr_eax_slave, (pointer)jump_vaddr_ecx_slave, (pointer)jump_vaddr_edx_slave, (pointer)jump_vaddr_ebx_slave, 0, (pointer)jump_vaddr_ebp_slave, 0, (pointer)jump_vaddr_edi_slave } }; // We need these for cmovcc instructions on x86 u32 const_zero=0; u32 const_one=1; /* Linker */ void set_jump_target(pointer addr,pointer target) { u8 *ptr=(u8 *)addr; if(*ptr==0x0f) { assert(ptr[1]>=0x80&&ptr[1]<=0x8f); u32 *ptr2=(u32 *)(ptr+2); *ptr2=target-(u32)ptr2-4; } else if(*ptr==0xe8||*ptr==0xe9) { u32 *ptr2=(u32 *)(ptr+1); *ptr2=target-(u32)ptr2-4; } else { assert(*ptr==0xc7); /* mov immediate (store address) */ u32 *ptr2=(u32 *)(ptr+6); *ptr2=target; } } void *kill_pointer(void *stub) { u32 i_ptr=*((u32 *)(stub+6)); *((u32 *)i_ptr)=(u32)stub-(u32)i_ptr-4; return (void *)i_ptr; } pointer get_pointer(void *stub) { pointer i_ptr=*((u32 *)(stub+6)); return *((s32 *)i_ptr)+i_ptr+4; } // Find the "clean" entry point from a "dirty" entry point // by skipping past the call to verify_code pointer get_clean_addr(pointer addr) { u8 *ptr=(u8 *)addr; if(ptr[0]==0xB8) { assert(ptr[21]==0xE8); // call instruction if(ptr[26]==0xE9) return *(s32 *)(ptr+27)+addr+31; // follow jmp else return(addr+26); } /* 64-bit source pointer */ assert(ptr[26]==0xE8); // call instruction if(ptr[31]==0xE9) return *(s32 *)(ptr+32)+addr+36; // follow jmp else return(addr+31); } int verify_dirty(pointer addr) { u8 *ptr=(u8 *)addr; if(ptr[0]==0xB8) { u32 source=*(u32 *)(ptr+1); u32 copy=*(u32 *)(ptr+6); u32 len=*(u32 *)(ptr+11); //printf("source=%x source-rdram=%x\n",source,source-(int)rdram); assert(ptr[21]==0xE8); // call instruction //printf("verify_dirty: %x %x %x\n",source,copy,len); return !memcmp((void *)source,(void *)copy,len); } assert(ptr[0]==0x48&&ptr[1]==0xB8); /* 64-bit source pointer */ u64 source=*(u64 *)(ptr+2); u32 copy=*(u32 *)(ptr+11); u32 len=*(u32 *)(ptr+16); //printf("source=%x source-rdram=%x\n",source,source-(int)rdram); assert(ptr[26]==0xE8); // call instruction //printf("verify_dirty: %x %x %x\n",source,copy,len); return !memcmp((void *)source,(void *)copy,len); } // This doesn't necessarily find all clean entry points, just // guarantees that it's not dirty int isclean(pointer addr) { u8 *ptr=(u8 *)addr; if(ptr[0]==0xB8) { if(ptr[0]!=0xB8) return 1; // mov imm,%eax if(ptr[5]!=0xBB) return 1; // mov imm,%ebx if(ptr[10]!=0xB9) return 1; // mov imm,%ecx if(ptr[15]!=0x41) return 1; // rex prefix if(ptr[16]!=0xBC) return 1; // mov imm,%r12d if(ptr[21]!=0xE8) return 1; // call instruction }else{ if(ptr[0]!=0x48) return 1; // rex prefix if(ptr[1]!=0xB8) return 1; // mov imm,%rax if(ptr[10]!=0xBB) return 1; // mov imm,%ebx if(ptr[15]!=0xB9) return 1; // mov imm,%ecx if(ptr[20]!=0x41) return 1; // rex prefix if(ptr[21]!=0xBC) return 1; // mov imm,%r12d if(ptr[26]!=0xE8) return 1; // call instruction } return 0; } void get_bounds(pointer addr,u32 *start,u32 *end) { u8 *ptr=(u8 *)addr; if(ptr[0]==0xB8) { u32 source=*(u32 *)(ptr+1); //u32 copy=*(u32 *)(ptr+6); u32 len=*(u32 *)(ptr+11); assert(ptr[21]==0xE8); // call instruction *start=source; *end=source+len; }else{ assert(ptr[0]==0x48&&ptr[1]==0xB8); u64 source=*(u64 *)(ptr+2); //32 copy=*(u32 *)(ptr+11); u32 len=*(u32 *)(ptr+16); assert(ptr[26]==0xE8); // call instruction *start=source; *end=source+len; } } /* Register allocation */ // Note: registers are allocated clean (unmodified state) // if you intend to modify the register, you must call dirty_reg(). void alloc_reg(struct regstat *cur,int i,signed char reg) { int r,hr; int preferred_reg = (reg&3)+(reg>21)*4+(reg==24)+(reg==28)+(reg==32); if(reg==CCREG) preferred_reg=HOST_CCREG; // Don't allocate unused registers if((cur->u>>reg)&1) return; // see if it's already allocated for(hr=0;hrregmap[hr]==reg) return; } // Keep the same mapping if the register was already allocated in a loop preferred_reg = loop_reg(i,reg,preferred_reg); // Try to allocate the preferred register if(cur->regmap[preferred_reg]==-1) { cur->regmap[preferred_reg]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[preferred_reg]; if(r<64&&((cur->u>>r)&1)) { cur->regmap[preferred_reg]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==-1) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]; if(r>=0) { if((cur->u>>r)&1) if(i==0||(unneeded_reg[i-1]>>r)&1) {cur->regmap[hr]=-1;break;} } } // Try to allocate any available register, but prefer // registers that have not been used recently. if(i>0) { for(hr=0;hrregmap[hr]==-1) { if(regs[i-1].regmap[hr]!=rs1[i-1]&®s[i-1].regmap[hr]!=rs2[i-1]&®s[i-1].regmap[hr]!=rt1[i-1]&®s[i-1].regmap[hr]!=rt2[i-1]) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==-1) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); if(i>0) { // Don't evict the cycle count at entry points, otherwise the entry // stub will have to write it. if(bt[i]&&hsn[CCREG]>2) hsn[CCREG]=2; if(i>1&&hsn[CCREG]>2&&(itype[i-2]==RJUMP||itype[i-2]==UJUMP||itype[i-2]==CJUMP||itype[i-2]==SJUMP)) hsn[CCREG]=2; for(j=10;j>=3;j--) { // Alloc preferred register if available if(hsn[r=cur->regmap[preferred_reg]&63]==j) { for(hr=0;hrregmap[hr]&63)==r) { cur->regmap[hr]=-1; cur->dirty&=~(1<isdoingcp&=~(1<regmap[preferred_reg]=reg; return; } for(r=0;r<=MAXREG;r++) { if(hsn[r]==j&&r!=rs1[i-1]&&r!=rs2[i-1]&&r!=rt1[i-1]&&r!=rt2[i-1]) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==reg) return; } // Try to allocate any available register, starting with EDI, ESI, EBP... // We prefer EDI, ESI, EBP since the others are used for byte/halfword stores for(hr=HOST_REGS-1;hr>=0;hr--) { if(hr!=EXCLUDE_REG&&cur->regmap[hr]==-1) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;hr--) { r=cur->regmap[hr]; if(r>=0) { if((cur->u>>r)&1) { if(i==0||((unneeded_reg[i-1]>>r)&1)) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[0]&63],hsn[cur->regmap[1]&63],hsn[cur->regmap[2]&63],hsn[cur->regmap[3]&63],hsn[cur->regmap[5]&63],hsn[cur->regmap[6]&63],hsn[cur->regmap[7]&63]); if(i>0) { // Don't evict the cycle count at entry points, otherwise the entry // stub will have to write it. if(bt[i]&&hsn[CCREG]>2) hsn[CCREG]=2; if(i>1&&hsn[CCREG]>2&&(itype[i-2]==RJUMP||itype[i-2]==UJUMP||itype[i-2]==CJUMP||itype[i-2]==SJUMP)) hsn[CCREG]=2; for(j=10;j>=3;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j&&r!=rs1[i-1]&&r!=rs2[i-1]&&r!=rt1[i-1]&&r!=rt2[i-1]) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<=0;j--) { for(r=0;r<=MAXREG;r++) { if(hsn[r]==j) { for(hr=0;hrregmap[hr]==r+64) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[hr]==r) { cur->regmap[hr]=reg; cur->dirty&=~(1<isdoingcp&=~(1<regmap[n]==reg) { dirty=(cur->dirty>>n)&1; cur->regmap[n]=-1; } } cur->regmap[hr]=reg; cur->dirty&=~(1<dirty|=dirty<isdoingcp&=~(1<7||rs>7) output_rex(1,rs>>3,0,rt>>3); output_byte(0x89); output_modrm(3,rt&7,rs&7); } void emit_mov64(int rs,int rt) { assem_debug("mov %%%s,%%%s\n",regname[rs],regname[rt]); output_rex(1,rs>>3,0,rt>>3); output_byte(0x89); output_modrm(3,rt&7,rs&7); } void emit_add(int rs1,int rs2,int rt) { if(rs1==rt) { assem_debug("add %%%s,%%%s\n",regname[rs2],regname[rs1]); output_byte(0x01); output_modrm(3,rs1,rs2); }else if(rs2==rt) { assem_debug("add %%%s,%%%s\n",regname[rs1],regname[rs2]); output_byte(0x01); output_modrm(3,rs2,rs1); }else { assem_debug("lea (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]); output_byte(0x8D); if(rs1!=EBP) { output_modrm(0,4,rt); output_sib(0,rs2,rs1); }else if(rs2!=EBP) { output_modrm(0,4,rt); output_sib(0,rs1,rs2); }else /* lea 0(,%ebp,2) */{ output_modrm(0,4,rt); output_sib(1,EBP,5); output_w32(0); } } } void emit_adds(int rs1,int rs2,int rt) { emit_add(rs1,rs2,rt); } void emit_lea8(int rs1,int rt) { assem_debug("lea 0(%%%s,8),%%%s\n",regname[rs1],regname[rt]); output_byte(0x8D); output_modrm(0,4,rt); output_sib(3,rs1,5); output_w32(0); } void emit_leairrx1(int imm,int rs1,int rs2,int rt) { assem_debug("lea %x(%%%s,%%%s,1),%%%s\n",imm,regname[rs1],regname[rs2],regname[rt]); output_byte(0x8D); if(imm!=0||rs1==EBP) { output_modrm(2,4,rt); output_sib(0,rs2,rs1); output_w32(imm); }else{ output_modrm(0,4,rt); output_sib(0,rs2,rs1); } } void emit_leairrx4(int imm,int rs1,int rs2,int rt) { assem_debug("lea %x(%%%s,%%%s,4),%%%s\n",imm,regname[rs1],regname[rs2],regname[rt]); output_byte(0x8D); if(imm!=0||rs1==EBP) { output_modrm(2,4,rt); output_sib(2,rs2,rs1); output_w32(imm); }else{ output_modrm(0,4,rt); output_sib(2,rs2,rs1); } } void emit_neg(int rs, int rt) { if(rs!=rt) emit_mov(rs,rt); assem_debug("neg %%%s\n",regname[rt]); output_byte(0xF7); output_modrm(3,rt,3); } void emit_negs(int rs, int rt) { emit_neg(rs,rt); } void emit_sub(int rs1,int rs2,int rt) { if(rs1==rt) { assem_debug("sub %%%s,%%%s\n",regname[rs2],regname[rs1]); output_byte(0x29); output_modrm(3,rs1,rs2); } else if(rs2==rt) { emit_neg(rs2,rs2); emit_add(rs2,rs1,rs2); } else { emit_mov(rs1,rt); emit_sub(rt,rs2,rt); } } void emit_subs(int rs1,int rs2,int rt) { emit_sub(rs1,rs2,rt); } void emit_zeroreg(int rt) { output_byte(0x31); output_modrm(3,rt,rt); assem_debug("xor %%%s,%%%s\n",regname[rt],regname[rt]); } void emit_loadreg(int r, int hr) { int addr=(slave?(int)slave_reg:(int)master_reg)+(r<<2); if(r==CCREG) addr=slave?(int)&slave_cc:(int)&master_cc; assem_debug("mov %x+%d,%%%s\n",addr,r,regname[hr]); output_byte(0x8B); output_modrm(0,5,hr); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_storereg(int r, int hr) { int addr=(slave?(int)slave_reg:(int)master_reg)+(r<<2); if(r==CCREG) addr=slave?(int)&slave_cc:(int)&master_cc; assem_debug("mov %%%s,%x+%d\n",regname[hr],addr,r); output_byte(0x89); output_modrm(0,5,hr); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_test(int rs, int rt) { assem_debug("test %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x85); output_modrm(3,rs,rt); } void emit_test64(int rs, int rt) { assem_debug("test %%%s,%%%s\n",regname[rs],regname[rt]); output_rex(1,rt>>3,0,rs>>3); output_byte(0x85); output_modrm(3,rs,rt); } void emit_testimm(int rs,int imm) { assem_debug("test $0x%x,%%%s\n",imm,regname[rs]); if(imm<128&&imm>=-128&&rs<4) { output_byte(0xF6); output_modrm(3,rs,0); output_byte(imm); } else { output_byte(0xF7); output_modrm(3,rs,0); output_w32(imm); } } void emit_not(int rs,int rt) { if(rs!=rt) emit_mov(rs,rt); assem_debug("not %%%s\n",regname[rt]); output_byte(0xF7); output_modrm(3,rt,2); } void emit_and(unsigned int rs1,unsigned int rs2,unsigned int rt) { assert(rs1<8); assert(rs2<8); assert(rt<8); if(rs1==rt) { assem_debug("and %%%s,%%%s\n",regname[rs2],regname[rt]); output_byte(0x21); output_modrm(3,rs1,rs2); } else if(rs2==rt) { assem_debug("and %%%s,%%%s\n",regname[rs1],regname[rt]); output_byte(0x21); output_modrm(3,rs2,rs1); } else { emit_mov(rs1,rt); emit_and(rt,rs2,rt); } } void emit_or(unsigned int rs1,unsigned int rs2,unsigned int rt) { assert(rs1<8); assert(rs2<8); assert(rt<8); if(rs1==rt) { assem_debug("or %%%s,%%%s\n",regname[rs2],regname[rt]); output_byte(0x09); output_modrm(3,rs1,rs2); } else if(rs2==rt) { assem_debug("or %%%s,%%%s\n",regname[rs1],regname[rt]); output_byte(0x09); output_modrm(3,rs2,rs1); } else { emit_mov(rs1,rt); emit_or(rt,rs2,rt); } } void emit_or_and_set_flags(int rs1,int rs2,int rt) { emit_or(rs1,rs2,rt); } void emit_xor(unsigned int rs1,unsigned int rs2,unsigned int rt) { assert(rs1<8); assert(rs2<8); assert(rt<8); if(rs1==rt) { assem_debug("xor %%%s,%%%s\n",regname[rs2],regname[rt]); output_byte(0x31); output_modrm(3,rs1,rs2); } else if(rs2==rt) { assem_debug("xor %%%s,%%%s\n",regname[rs1],regname[rt]); output_byte(0x31); output_modrm(3,rs2,rs1); } else { emit_mov(rs1,rt); emit_xor(rt,rs2,rt); } } void emit_movimm(int imm,unsigned int rt) { assem_debug("mov $%d,%%%s\n",imm,regname[rt]); assert(rt<16); if(rt>=8) output_rex(0,0,0,1); output_byte(0xB8+(rt&7)); output_w32(imm); } void emit_movimm64(u64 imm,unsigned int rt) { assem_debug("movq $0x%llx,%%%s\n",imm,regname[rt]); assert(rt<16); output_rex(1,0,0,rt>>3); output_byte(0xB8+(rt&7)); output_w64(imm); } void emit_addimm(int rs,int imm,int rt) { if(rs==rt) { if(imm!=0) { assem_debug("add $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,0); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,0); output_w32(imm); } } } else { if(imm!=0) { assem_debug("lea %d(%%%s),%%%s\n",imm,regname[rs],regname[rt]); output_byte(0x8D); if(imm<128&&imm>=-128) { output_modrm(1,rs,rt); output_byte(imm); }else{ output_modrm(2,rs,rt); output_w32(imm); } }else{ emit_mov(rs,rt); } } } void emit_addimm64(int rs,int imm,int rt) { if(rs==rt) { if(imm!=0) { assem_debug("add $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_rex(1,0,0,rt>>3); output_byte(0x83); output_modrm(3,rt&7,0); output_byte(imm); } else { output_rex(1,0,0,rt>>3); output_byte(0x81); output_modrm(3,rt&7,0); output_w32(imm); } } } else { if(imm!=0) { assem_debug("lea %d(%%%s),%%%s\n",imm,regname[rs],regname[rt]); output_rex(1,rt>>3,0,rs>>3); output_byte(0x8D); if(imm<128&&imm>=-128) { output_modrm(1,rs&7,rt&7); output_byte(imm); }else{ output_modrm(2,rs&7,rt&7); output_w32(imm); } }else{ emit_mov(rs,rt); } } } void emit_addimm_and_set_flags(int imm,int rt) { assem_debug("add $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,0); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,0); output_w32(imm); } } void emit_addimm_no_flags(int imm,int rt) { if(imm!=0) { assem_debug("lea %d(%%%s),%%%s\n",imm,regname[rt],regname[rt]); output_byte(0x8D); if(imm<128&&imm>=-128) { output_modrm(1,rt,rt); output_byte(imm); }else{ output_modrm(2,rt,rt); output_w32(imm); } } } void emit_adcimm(int imm,unsigned int rt) { assem_debug("adc $%d,%%%s\n",imm,regname[rt]); assert(rt<8); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,2); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,2); output_w32(imm); } } void emit_sbbimm(int imm,unsigned int rt) { assem_debug("sbb $%d,%%%s\n",imm,regname[rt]); assert(rt<8); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,3); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,3); output_w32(imm); } } void emit_addimm64_32(int rsh,int rsl,int imm,int rth,int rtl) { if(rsh==rth&&rsl==rtl) { assem_debug("add $%d,%%%s\n",imm,regname[rtl]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rtl,0); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rtl,0); output_w32(imm); } assem_debug("adc $%d,%%%s\n",imm>>31,regname[rth]); output_byte(0x83); output_modrm(3,rth,2); output_byte(imm>>31); } else { emit_mov(rsh,rth); emit_mov(rsl,rtl); emit_addimm64_32(rth,rtl,imm,rth,rtl); } } void emit_sbb(int rs1,int rs2) { assem_debug("sbb %%%s,%%%s\n",regname[rs1],regname[rs2]); output_byte(0x19); output_modrm(3,rs2,rs1); } void emit_andimm(int rs,int imm,int rt) { if(imm==0) { emit_zeroreg(rt); } else if(rs==rt) { assem_debug("and $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,4); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,4); output_w32(imm); } } else { emit_mov(rs,rt); emit_andimm(rt,imm,rt); } } void emit_orimm(int rs,int imm,int rt) { if(rs==rt) { if(imm!=0) { assem_debug("or $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,1); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,1); output_w32(imm); } } } else { emit_mov(rs,rt); emit_orimm(rt,imm,rt); } } void emit_xorimm(int rs,int imm,int rt) { if(rs==rt) { if(imm!=0) { assem_debug("xor $%d,%%%s\n",imm,regname[rt]); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rt,6); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rt,6); output_w32(imm); } } } else { emit_mov(rs,rt); emit_xorimm(rt,imm,rt); } } void emit_shlimm(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shl %%%s,%d\n",regname[rt],imm); assert(imm>0); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,4); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_shlimm(rt,imm,rt); } } void emit_shlimm64(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shl %%%s,%d\n",regname[rt],imm); assert(imm>0); output_rex(1,0,0,rt>>3); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,4); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_shlimm64(rt,imm,rt); } } void emit_shrimm(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shr %%%s,%d\n",regname[rt],imm); assert(imm>0); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,5); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_shrimm(rt,imm,rt); } } void emit_shrimm64(int rs,unsigned int imm,int rt) { assert(rs==rt); if(rs==rt) { assem_debug("shr %%%s,%d\n",regname[rt],imm); assert(imm>0); output_rex(1,0,0,rt>>3); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,5); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_shrimm(rt,imm,rt); } } void emit_sarimm(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("sar %%%s,%d\n",regname[rt],imm); assert(imm>0); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,7); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_sarimm(rt,imm,rt); } } void emit_rorimm(int rs,unsigned int imm,int rt) { if(rs==rt) { assem_debug("ror %%%s,%d\n",regname[rt],imm); assert(imm>0); if(imm==1) output_byte(0xD1); else output_byte(0xC1); output_modrm(3,rt,1); if(imm>1) output_byte(imm); } else { emit_mov(rs,rt); emit_rorimm(rt,imm,rt); } } void emit_swapb(int rs,int rt) { if(rs==rt) { assem_debug("ror %%%s,8\n",regname[rt]+1); output_byte(0x66); output_byte(0xC1); output_modrm(3,rt,1); output_byte(8); } else { emit_mov(rs,rt); emit_swapb(rt,rt); } } void emit_shldimm(int rs,int rs2,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shld %%%s,%%%s,%d\n",regname[rt],regname[rs2],imm); assert(imm>0); output_byte(0x0F); output_byte(0xA4); output_modrm(3,rt,rs2); output_byte(imm); } else { emit_mov(rs,rt); emit_shldimm(rt,rs2,imm,rt); } } void emit_shrdimm(int rs,int rs2,unsigned int imm,int rt) { if(rs==rt) { assem_debug("shrd %%%s,%%%s,%d\n",regname[rt],regname[rs2],imm); assert(imm>0); output_byte(0x0F); output_byte(0xAC); output_modrm(3,rt,rs2); output_byte(imm); } else { emit_mov(rs,rt); emit_shrdimm(rt,rs2,imm,rt); } } void emit_shlcl(int r) { assem_debug("shl %%%s,%%cl\n",regname[r]); output_byte(0xD3); output_modrm(3,r,4); } void emit_shrcl(int r) { assem_debug("shr %%%s,%%cl\n",regname[r]); output_byte(0xD3); output_modrm(3,r,5); } void emit_sarcl(int r) { assem_debug("sar %%%s,%%cl\n",regname[r]); output_byte(0xD3); output_modrm(3,r,7); } void emit_shldcl(int r1,int r2) { assem_debug("shld %%%s,%%%s,%%cl\n",regname[r1],regname[r2]); output_byte(0x0F); output_byte(0xA5); output_modrm(3,r1,r2); } void emit_shrdcl(int r1,int r2) { assem_debug("shrd %%%s,%%%s,%%cl\n",regname[r1],regname[r2]); output_byte(0x0F); output_byte(0xAD); output_modrm(3,r1,r2); } void emit_cmpimm(int rs,int imm) { assem_debug("cmp $%d,%%%s\n",imm,regname[rs]); if(rs>=8) output_rex(0,0,0,rs>>3); if(imm<128&&imm>=-128) { output_byte(0x83); output_modrm(3,rs&7,7); output_byte(imm); } else { output_byte(0x81); output_modrm(3,rs&7,7); output_w32(imm); } } void emit_cmovne(u32 *addr,int rt) { assem_debug("cmovne %x,%%%s",(int)addr,regname[rt]); if(addr==&const_zero) assem_debug(" [zero]\n"); else if(addr==&const_one) assem_debug(" [one]\n"); else assem_debug("\n"); output_byte(0x0F); output_byte(0x45); output_modrm(0,5,rt); output_w32((int)addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_cmovl(u32 *addr,int rt) { assem_debug("cmovl %x,%%%s",(int)addr,regname[rt]); if(addr==&const_zero) assem_debug(" [zero]\n"); else if(addr==&const_one) assem_debug(" [one]\n"); else assem_debug("\n"); output_byte(0x0F); output_byte(0x4C); output_modrm(0,5,rt); output_w32((int)addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_cmovs(u32 *addr,int rt) { assem_debug("cmovs %x,%%%s",(int)addr,regname[rt]); if(addr==&const_zero) assem_debug(" [zero]\n"); else if(addr==&const_one) assem_debug(" [one]\n"); else assem_debug("\n"); output_byte(0x0F); output_byte(0x48); output_modrm(0,5,rt); output_w32((int)addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_cmovne_reg(int rs,int rt) { assem_debug("cmovne %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x45); output_modrm(3,rs,rt); } void emit_cmovl_reg(int rs,int rt) { assem_debug("cmovl %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x4C); output_modrm(3,rs,rt); } void emit_cmovle_reg(int rs,int rt) { assem_debug("cmovle %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x4E); output_modrm(3,rs,rt); } void emit_cmovs_reg(int rs,int rt) { assem_debug("cmovs %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x48); output_modrm(3,rs,rt); } void emit_cmovnc_reg(int rs,int rt) { assem_debug("cmovae %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x43); output_modrm(3,rs,rt); } void emit_cmova_reg(int rs,int rt) { assem_debug("cmova %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x47); output_modrm(3,rs,rt); } void emit_cmovp_reg(int rs,int rt) { assem_debug("cmovp %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x4A); output_modrm(3,rs,rt); } void emit_cmovnp_reg(int rs,int rt) { assem_debug("cmovnp %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x0F); output_byte(0x4B); output_modrm(3,rs,rt); } void emit_setl(int rt) { assem_debug("setl %%%s\n",regname[rt]); output_byte(0x0F); output_byte(0x9C); output_modrm(3,rt,2); } void emit_movzbl_reg(int rs, int rt) { if(rs<4&&rt<8) { assem_debug("movzbl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_byte(0x0F); output_byte(0xB6); output_modrm(3,rs,rt); } else { assem_debug("movzbl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_rex(0,rt>>3,0,rs>>3); output_byte(0x0F); output_byte(0xB6); output_modrm(3,rs,rt); } } void emit_movzwl_reg(int rs, int rt) { assem_debug("movzwl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_byte(0x0F); output_byte(0xB7); output_modrm(3,rs,rt); } void emit_movsbl_reg(int rs, int rt) { if(rs<4&&rt<8) { assem_debug("movsbl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_byte(0x0F); output_byte(0xBE); output_modrm(3,rs,rt); } else { assem_debug("movsbl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_rex(0,rt>>3,0,rs>>3); output_byte(0x0F); output_byte(0xBE); output_modrm(3,rs,rt); } } void emit_movswl_reg(int rs, int rt) { assem_debug("movswl %%%s,%%%s\n",regname[rs]+1,regname[rt]); output_byte(0x0F); output_byte(0xBF); output_modrm(3,rs,rt); } void emit_slti32(int rs,int imm,int rt) { if(rs!=rt) emit_zeroreg(rt); emit_cmpimm(rs,imm); if(rt<4) { emit_setl(rt); if(rs==rt) emit_movzbl_reg(rt,rt); } else { if(rs==rt) emit_movimm(0,rt); emit_cmovl(&const_one,rt); } } void emit_sltiu32(int rs,int imm,int rt) { if(rs!=rt) emit_zeroreg(rt); emit_cmpimm(rs,imm); if(rs==rt) emit_movimm(0,rt); emit_adcimm(0,rt); } void emit_slti64_32(int rsh,int rsl,int imm,int rt) { assert(rsh!=rt); emit_slti32(rsl,imm,rt); if(imm>=0) { emit_test(rsh,rsh); emit_cmovne(&const_zero,rt); emit_cmovs(&const_one,rt); } else { emit_cmpimm(rsh,-1); emit_cmovne(&const_zero,rt); emit_cmovl(&const_one,rt); } } void emit_sltiu64_32(int rsh,int rsl,int imm,int rt) { assert(rsh!=rt); emit_sltiu32(rsl,imm,rt); if(imm>=0) { emit_test(rsh,rsh); emit_cmovne(&const_zero,rt); } else { emit_cmpimm(rsh,-1); emit_cmovne(&const_one,rt); } } void emit_cmp(int rs,int rt) { assem_debug("cmp %%%s,%%%s\n",regname[rt],regname[rs]); output_byte(0x39); output_modrm(3,rs,rt); } void emit_set_gz32(int rs, int rt) { //assem_debug("set_gz32\n"); emit_cmpimm(rs,1); emit_movimm(1,rt); emit_cmovl(&const_zero,rt); } void emit_set_nz32(int rs, int rt) { //assem_debug("set_nz32\n"); emit_cmpimm(rs,1); emit_movimm(1,rt); emit_sbbimm(0,rt); } void emit_set_gz64_32(int rsh, int rsl, int rt) { //assem_debug("set_gz64\n"); emit_set_gz32(rsl,rt); emit_test(rsh,rsh); emit_cmovne(&const_one,rt); emit_cmovs(&const_zero,rt); } void emit_set_nz64_32(int rsh, int rsl, int rt) { //assem_debug("set_nz64\n"); emit_or_and_set_flags(rsh,rsl,rt); emit_cmovne(&const_one,rt); } void emit_set_if_less32(int rs1, int rs2, int rt) { //assem_debug("set if less (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]); if(rs1!=rt&&rs2!=rt) emit_zeroreg(rt); emit_cmp(rs1,rs2); if(rs1==rt||rs2==rt) emit_movimm(0,rt); emit_cmovl(&const_one,rt); } void emit_set_if_carry32(int rs1, int rs2, int rt) { //assem_debug("set if carry (%%%s,%%%s),%%%s\n",regname[rs1],regname[rs2],regname[rt]); if(rs1!=rt&&rs2!=rt) emit_zeroreg(rt); emit_cmp(rs1,rs2); if(rs1==rt||rs2==rt) emit_movimm(0,rt); emit_adcimm(0,rt); } void emit_set_if_less64_32(int u1, int l1, int u2, int l2, int rt) { //assem_debug("set if less64 (%%%s,%%%s,%%%s,%%%s),%%%s\n",regname[u1],regname[l1],regname[u2],regname[l2],regname[rt]); assert(u1!=rt); assert(u2!=rt); emit_cmp(l1,l2); emit_mov(u1,rt); emit_sbb(u2,rt); emit_movimm(0,rt); emit_cmovl(&const_one,rt); } void emit_set_if_carry64_32(int u1, int l1, int u2, int l2, int rt) { //assem_debug("set if carry64 (%%%s,%%%s,%%%s,%%%s),%%%s\n",regname[u1],regname[l1],regname[u2],regname[l2],regname[rt]); assert(u1!=rt); assert(u2!=rt); emit_cmp(l1,l2); emit_mov(u1,rt); emit_sbb(u2,rt); emit_movimm(0,rt); emit_adcimm(0,rt); } void emit_adc(int rs,int rt) { assem_debug("adc %%%s,%%%s\n",regname[rs],regname[rt]); output_byte(0x11); output_modrm(3,rt,rs); } void emit_sh2tst(int s1, int s2, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_test(s1,s2); emit_cmovne_reg(temp,sr); } void emit_sh2tstimm(int s, int imm, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_testimm(s,imm); //emit_addimm(sr,-1,temp); assem_debug("lea -1(%%%s),%%%s\n",regname[sr],regname[temp]); output_byte(0x8D); output_modrm(1,sr,temp); output_byte(0xFF); emit_cmovne_reg(temp,sr); } void emit_cmpeq(int s1, int s2, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_cmp(s1,s2); emit_cmovne_reg(temp,sr); } void emit_cmpeqimm(int s, int imm, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_cmpimm(s,imm); emit_cmovne_reg(temp,sr); } void emit_cmpge(int s1, int s2, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_cmp(s2,s1); emit_cmovl_reg(temp,sr); } void emit_cmpgt(int s1, int s2, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_cmp(s2,s1); emit_cmovle_reg(temp,sr); } void emit_cmphi(int s1, int s2, int sr, int temp) { emit_andimm(sr,~1,sr); emit_cmp(s1,s2); emit_adcimm(0,sr); } void emit_cmphs(int s1, int s2, int sr, int temp) { emit_orimm(sr,1,sr); emit_cmp(s2,s1); emit_sbbimm(0,sr); } void emit_dt(int t, int sr) { emit_addimm(t,-2,t); emit_shrimm(sr,1,sr); emit_addimm(t,1,t); emit_adc(sr,sr); } void emit_cmppz(int s, int sr) { emit_shrimm(sr,1,sr); emit_cmpimm(s,0x80000000); emit_adc(sr,sr); } void emit_cmppl(int s, int sr, int temp) { assert(temp>=0); emit_orimm(sr,1,sr); emit_addimm(sr,-1,temp); emit_test(s,s); emit_cmovle_reg(temp,sr); } void emit_addc(int s, int t, int sr) { emit_shrimm(sr,1,sr); emit_adc(s,t); emit_adc(sr,sr); } void emit_subc(int s, int t, int sr) { emit_shrimm(sr,1,sr); emit_sbb(s,t); emit_adc(sr,sr); } void emit_shrsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_shrimm(t,1,t); emit_adc(sr,sr); } void emit_sarsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_sarimm(t,1,t); emit_adc(sr,sr); } void emit_shlsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_shlimm(t,1,t); emit_adc(sr,sr); } void emit_rotl(int t) { assem_debug("rol %%%s\n",regname[t]); output_byte(0xD1); output_modrm(3,t,0); } void emit_rotlsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_rotl(t); emit_adc(sr,sr); } void emit_rotr(int t) { assem_debug("ror %%%s\n",regname[t]); output_byte(0xD1); output_modrm(3,t,1); } void emit_rotrsr(int t, int sr) { emit_shrimm(sr,1,sr); emit_rotr(t); emit_adc(sr,sr); } void emit_rotclsr(int t, int sr) { emit_shrimm(sr,1,sr); assem_debug("rcl %%%s\n",regname[t]); output_byte(0xD1); output_modrm(3,t,2); emit_adc(sr,sr); } void emit_rotcrsr(int t, int sr) { emit_shrimm(sr,1,sr); assem_debug("rcr %%%s\n",regname[t]); output_byte(0xD1); output_modrm(3,t,3); emit_adc(sr,sr); } void emit_call(int a) { assem_debug("call %x (%x+%x)\n",a,(int)out+5,a-(int)out-5); output_byte(0xe8); output_w32(a-(int)out-4); } void emit_jmp(int a) { assem_debug("jmp %x (%x+%x)\n",a,(int)out+5,a-(int)out-5); output_byte(0xe9); output_w32(a-(int)out-4); } void emit_jne(int a) { assem_debug("jne %x\n",a); output_byte(0x0f); output_byte(0x85); output_w32(a-(int)out-4); } void emit_jeq(int a) { assem_debug("jeq %x\n",a); output_byte(0x0f); output_byte(0x84); output_w32(a-(int)out-4); } void emit_js(int a) { assem_debug("js %x\n",a); output_byte(0x0f); output_byte(0x88); output_w32(a-(int)out-4); } void emit_jns(int a) { assem_debug("jns %x\n",a); output_byte(0x0f); output_byte(0x89); output_w32(a-(int)out-4); } void emit_jl(int a) { assem_debug("jl %x\n",a); output_byte(0x0f); output_byte(0x8c); output_w32(a-(int)out-4); } void emit_jge(int a) { assem_debug("jge %x\n",a); output_byte(0x0f); output_byte(0x8d); output_w32(a-(int)out-4); } void emit_jno(int a) { assem_debug("jno %x\n",a); output_byte(0x0f); output_byte(0x81); output_w32(a-(int)out-4); } void emit_jc(int a) { assem_debug("jc %x\n",a); output_byte(0x0f); output_byte(0x82); output_w32(a-(int)out-4); } void emit_pushimm(int imm) { assem_debug("push $%x\n",imm); output_byte(0x68); output_w32(imm); } //void emit_pusha() //{ // assem_debug("pusha\n"); // output_byte(0x60); //} //void emit_popa() //{ // assem_debug("popa\n"); // output_byte(0x61); //} void emit_pushreg(unsigned int r) { assem_debug("push %%%s\n",regname[r]); assert(r<8); output_byte(0x50+r); } void emit_popreg(unsigned int r) { assem_debug("pop %%%s\n",regname[r]); assert(r<8); output_byte(0x58+r); } void emit_callreg(unsigned int r) { assem_debug("call *%%%s\n",regname[r]); assert(r<8); output_byte(0xFF); output_modrm(3,r,2); } void emit_jmpreg(unsigned int r) { assem_debug("jmp *%%%s\n",regname[r]); assert(r<8); output_byte(0xFF); output_modrm(3,r,4); } void emit_jmpmem_indexed(u32 addr,unsigned int r) { assem_debug("jmp *%x(%%%s)\n",addr,regname[r]); assert(r<8); output_byte(0xFF); output_modrm(2,r,4); output_w32(addr); } void emit_cmpstr(int s1, int s2, int sr, int temp) { // Compare s1 and s2. If any byte is equal, set T. // Calculates the xor of the strings, then checks if any byte is // zero by subtracting 1 from each byte. If there is a carry/borrow // then a byte was zero. assert(temp>=0); emit_pushreg(s2); emit_xor(s1,s2,s2); emit_shrimm(sr,1,sr); emit_mov(s2,temp); emit_addimm_and_set_flags(0-0x01010101,temp); emit_adcimm(-1,temp); emit_not(s2,s2); emit_xor(temp,s2,temp); emit_andimm(temp,0x01010101,temp); emit_addimm_and_set_flags(-1,temp); emit_adc(sr,sr); emit_popreg(s2); } void emit_negc(int rs, int rt, int sr) { assert(rs>=0&&rs<8); if(rt<0) { emit_shrimm(sr,1,sr); // Get C flag emit_jc((pointer)out+10); // 6 emit_neg(rs,rs); // 2 emit_neg(rs,rs); // 2 emit_adc(sr,sr); // Save C flag }else{ if(rs!=rt) emit_mov(rs,rt); emit_shrimm(sr,1,sr); // Get C flag emit_jc((pointer)out+9); // 6 emit_addimm(rt,-1,rt); // 3 emit_adc(sr,sr); // Save C flag emit_not(rt,rt); } } void emit_readword(u64 addr, int rt) { if(addr-(u64)out+0x7FFFFFFA>0xFFFFFFFE) { //TODO: special eax case emit_movimm64(addr,rt); assem_debug("mov (%%%s),%%%s\n",regname[rt],regname[rt]); output_byte(0x8B); if(rt!=EBP) { output_modrm(0,rt,rt); } else { output_modrm(1,rt,rt); output_byte(0); } } else { assem_debug("mov %x,%%%s\n",addr,regname[rt]); output_byte(0x8B); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } } void emit_readword_indexed(int addr, int rs, int rt) { assem_debug("mov %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x8B); if(addr<128&&addr>=-128) { output_modrm(1,rs,rt); if(rs==ESP) output_sib(0,4,4); output_byte(addr); } else { output_modrm(2,rs,rt); if(rs==ESP) output_sib(0,4,4); output_w32(addr); } } void emit_readword_map(int addr, int map, int rt) { if(map<0) emit_readword(addr, rt); else { assem_debug("addr32 mov %x(,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x67); output_byte(0x8B); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_readword_indexed_map(int addr, int rs, int map, int rt) { assert(map>=0); if(map<0) emit_readword_indexed(addr, rs, rt); else { //assem_debug("addr32 mov %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assem_debug("mov %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assert(rs!=ESP); //output_byte(0x67); //addr32 output_byte(0x8B); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(2,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(2,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(2,map,rs); output_w32(addr); } } } void emit_movmem_indexedx4(int addr, int rs, int rt) { assem_debug("mov (%x,%%%s,4),%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x8B); output_modrm(0,4,rt); output_sib(2,rs,5); output_w32(addr); } void emit_movmem_indexedx4_addr32(int addr, int rs, int rt) { assem_debug("addr32 mov (%x,%%%s,4),%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x67); output_byte(0x8B); output_modrm(0,4,rt); output_sib(2,rs,5); output_w32(addr); } void emit_movmem_indexedx8(int addr, int rs, int rt) { assem_debug("mov (%x,%%%s,8),%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x8B); output_modrm(0,4,rt); output_sib(3,rs,5); output_w32(addr); } void emit_movmem_indexedx8_64(int addr, int rs, int rt) { assem_debug("mov (%x,%%%s,8),%%%s\n",addr,regname[rs],regname[rt]); output_rex(1,0,0,rt>>3); output_byte(0x8B); output_modrm(0,4,rt); output_sib(3,rs,5); output_w32(addr); } void emit_movsbl(u64 addr, int rt) { if(addr-(u64)out+0x7FFFFFF9>0xFFFFFFFE) { emit_movimm64(addr,rt); assem_debug("movsbl (%%%s),%%%s\n",regname[rt],regname[rt]); output_byte(0x0F); output_byte(0xBE); if(rt!=EBP) { output_modrm(0,rt,rt); } else { output_modrm(1,rt,rt); output_byte(0); } } else { assem_debug("movsbl %x,%%%s\n",addr,regname[rt]); output_byte(0x0F); output_byte(0xBE); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // rip-relative } } void emit_movsbl_indexed(int addr, int rs, int rt) { assem_debug("movsbl %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x0F); output_byte(0xBE); output_modrm(2,rs,rt); output_w32(addr); } void emit_movsbl_map(u64 addr, int map, int rt) { if(map<0) emit_movsbl(addr, rt); else { //FIXME assem_debug("addr32 movsbl %x(,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x67); output_byte(0x0F); output_byte(0xBE); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_movsbl_indexed_map(int addr, int rs, int map, int rt) { if(map<0) emit_movsbl_indexed(addr, rs, rt); else { //assem_debug("addr32 movsbl %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assem_debug("movsbl %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assert(rs!=ESP); //output_byte(0x67); output_byte(0x0F); output_byte(0xBE); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(2,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(2,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(2,map,rs); output_w32(addr); } } } void emit_movswl(u64 addr, int rt) { if(addr-(u64)out+0x7FFFFFF9>0xFFFFFFFE) { emit_movimm64(addr,rt); assem_debug("movswl (%%%s),%%%s\n",regname[rt],regname[rt]); output_byte(0x0F); output_byte(0xBF); if(rt!=EBP) { output_modrm(0,rt,rt); } else { output_modrm(1,rt,rt); output_byte(0); } } else { assem_debug("movswl %x,%%%s\n",addr,regname[rt]); output_byte(0x0F); output_byte(0xBF); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // rip-relative } } void emit_movswl_indexed(int addr, int rs, int rt) { assem_debug("movswl %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x0F); output_byte(0xBF); output_modrm(2,rs,rt); output_w32(addr); } void emit_movswl_map(u64 addr, int map, int rt) { if(map<0) emit_movswl(addr, rt); else { //FIXME assem_debug("addr32 movswl %x(,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x67); output_byte(0x0F); output_byte(0xBF); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_movswl_indexed_map(int addr, int rs, int map, int rt) { assert(map>=0); if(map<0) emit_movswl_indexed(addr, rs, rt); else { assem_debug("movswl %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assert(rs!=ESP); output_byte(0x0F); output_byte(0xBF); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(2,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(2,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(2,map,rs); output_w32(addr); } } } void emit_movzbl(int addr, int rt) { assem_debug("movzbl %x,%%%s\n",addr,regname[rt]); output_byte(0x0F); output_byte(0xB6); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_movzbl_indexed(int addr, int rs, int rt) { assem_debug("movzbl %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x0F); output_byte(0xB6); output_modrm(2,rs,rt); output_w32(addr); } void emit_movzbl_map(int addr, int map, int rt) { if(map<0) emit_movzbl(addr, rt); else { assem_debug("addr32 movzbl %x(,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x67); output_byte(0x0F); output_byte(0xB6); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_movzbl_indexed_map(int addr, int rs, int map, int rt) { if(map<0) emit_movzbl_indexed(addr, rs, rt); else { assem_debug("addr32 movzbl %x(%%%s,%%%s,4),%%%s\n",addr,regname[rs],regname[map],regname[rt]); assert(rs!=ESP); output_byte(0x67); output_byte(0x0F); output_byte(0xB6); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(2,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(2,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(2,map,rs); output_w32(addr); } } } void emit_movzwl(int addr, int rt) { assem_debug("movzwl %x,%%%s\n",addr,regname[rt]); output_byte(0x0F); output_byte(0xB7); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_movzwl_indexed(int addr, int rs, int rt) { assem_debug("movzwl %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x0F); output_byte(0xB7); output_modrm(2,rs,rt); output_w32(addr); } void emit_movzwl_map(int addr, int map, int rt) { if(map<0) emit_movzwl(addr, rt); else { assem_debug("addr32 movzwl %x(,%%%s,4),%%%s\n",addr,regname[map],regname[rt]); output_byte(0x67); output_byte(0x0F); output_byte(0xB7); output_modrm(0,4,rt); output_sib(2,map,5); output_w32(addr); } } void emit_movq(u64 addr, int rt) { if(addr-(u64)out+0x7FFFFFFA>0xFFFFFFFF) { assert(addr-(u64)out+0x7FFFFFFA<0x100000000); //TODO: special eax case emit_movimm64(addr,rt); //FIXME assem_debug("mov (%%%s),%%%s\n",regname[rt],regname[rt]); output_byte(0x8B); output_modrm(0,rt,rt); output_byte(0xCC);//remove } else { assem_debug("movq %llx,%%%s\n",addr,regname[rt]); output_rex(1,0,0,rt>>8); output_byte(0x8B); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } } void emit_xchg(int rs, int rt) { assem_debug("xchg %%%s,%%%s\n",regname[rs],regname[rt]); if(rs==EAX) { output_byte(0x90+rt); } else { output_byte(0x87); output_modrm(3,rs,rt); } } void emit_writeword(int rt, int addr) { assem_debug("movl %%%s,%x\n",regname[rt],addr); output_byte(0x89); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_writeword_indexed(int rt, int addr, int rs) { assem_debug("mov %%%s,%x+%%%s\n",regname[rt],addr,regname[rs]); output_byte(0x89); if(addr<128&&addr>=-128) { output_modrm(1,rs,rt); if(rs==ESP) output_sib(0,4,4); output_byte(addr); } else { output_modrm(2,rs,rt); if(rs==ESP) output_sib(0,4,4); output_w32(addr); } } #if 0 void emit_writeword_map(int rt, int addr, int map) { if(map<0) { emit_writeword(rt, addr+(int)rdram-0x80000000); } else { emit_writeword_indexed(rt, addr+(int)rdram-0x80000000, map); } } #endif void emit_writeword_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writeword_indexed(rt, addr, rs); else { //assem_debug("addr32 mov %%%s,%x(%%%s,%%%s,1)\n",regname[rt],addr,regname[rs],regname[map]); assem_debug("mov %%%s,%x(%%%s,%%%s,1)\n",regname[rt],addr,regname[rs],regname[map]); assert(rs!=ESP); //output_byte(0x67); output_byte(0x89); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(0,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(0,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(0,map,rs); output_w32(addr); } } } void emit_writehword(int rt, int addr) { assem_debug("movw %%%s,%x\n",regname[rt]+1,addr); output_byte(0x66); output_byte(0x89); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_writehword_indexed(int rt, int addr, int rs) { assem_debug("movw %%%s,%x+%%%s\n",regname[rt]+1,addr,regname[rs]); output_byte(0x66); output_byte(0x89); if(addr<128&&addr>=-128) { output_modrm(1,rs,rt); output_byte(addr); } else { output_modrm(2,rs,rt); output_w32(addr); } } #if 0 void emit_writehword_map(int rt, int addr, int map) { if(map<0) { emit_writehword(rt, addr+(int)rdram-0x80000000); } else { emit_writehword_indexed(rt, addr+(int)rdram-0x80000000, map); } } #endif void emit_writehword_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writehword_indexed(rt, addr, rs); else { assem_debug("movw %%%s,%x(%%%s,%%%s,1)\n",regname[rt]+1,addr,regname[rs],regname[map]); assert(rs!=ESP); output_byte(0x66); output_byte(0x89); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(0,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(0,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(0,map,rs); output_w32(addr); } } } void emit_writebyte(int rt, int addr) { assem_debug("movb %%%cl,%x\n",regname[rt][1],addr); if(rt>=4) output_rex(0,rt>>3,0,0); output_byte(0x88); output_modrm(0,5,rt); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_writebyte_indexed(int rt, int addr, int rs) { assem_debug("movb %%%cl,%x+%%%s\n",regname[rt][1],addr,regname[rs]); if(rt>=4||rs>=8) output_rex(0,rt>>3,0,rs>>3); output_byte(0x88); if(addr<128&&addr>=-128) { output_modrm(1,rs,rt); output_byte(addr); } else { output_modrm(2,rs,rt); output_w32(addr); } } #if 0 void emit_writebyte_map(int rt, int addr, int map) { if(map<0) { emit_writebyte(rt, addr+(int)rdram-0x80000000); } else { emit_writebyte_indexed(rt, addr+(int)rdram-0x80000000, map); } } #endif void emit_writebyte_indexed_map(int rt, int addr, int rs, int map, int temp) { if(map<0) emit_writebyte_indexed(rt, addr, rs); else { assem_debug("movb %%%cl,%x(%%%s,%%%s,1)\n",regname[rt][1],addr,regname[rs],regname[map]); assert(rs!=ESP); if(rt>=4||rs>=8||map>=8) output_rex(0,rt>>3,map>>3,rs>>3); output_byte(0x88); if(addr==0&&rs!=EBP) { output_modrm(0,4,rt); output_sib(0,map,rs); } else if(addr<128&&addr>=-128) { output_modrm(1,4,rt); output_sib(0,map,rs); output_byte(addr); } else { output_modrm(2,4,rt); output_sib(0,map,rs); output_w32(addr); } } } void emit_writeword_imm(int imm, int addr) { assem_debug("movl $%x,%x\n",imm,addr); output_byte(0xC7); output_modrm(0,5,0); output_w32(addr-(int)out-8); // Note: rip-relative in 64-bit mode output_w32(imm); } void emit_writeword_imm_esp(int imm, int addr) { assem_debug("mov $%x,%x(%%esp)\n",imm,addr); assert(addr>=-128&&addr<128); output_byte(0xC7); output_modrm(!!addr,4,0); output_sib(0,4,4); if(addr) output_byte(addr); output_w32(imm); } void emit_writedword_imm32(int imm, int addr) { assem_debug("movq $%x,%x\n",imm,addr); output_rex(1,0,0,0); output_byte(0xC7); output_modrm(0,5,0); output_w32(addr-(int)out-8); // Note: rip-relative in 64-bit mode output_w32(imm); // Note: This 32-bit value will be sign extended } void emit_writebyte_imm(int imm, int addr) { assem_debug("movb $%x,%x\n",imm,addr); assert(imm>=-128&&imm<128); output_byte(0xC6); output_modrm(0,5,0); output_w32(addr-(int)out-5); // Note: rip-relative in 64-bit mode output_byte(imm); } void emit_rmw_andimm(int addr, int map, int imm) { if(map<0) { assem_debug("andb $0x%x,(%%%s)\n",imm,regname[addr]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,addr,4); } else { assem_debug("andb $0x%x,(%%%s,%%%s,1)\n",imm,regname[addr],regname[map]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,4,4); if(addr!=EBP) { output_sib(0,map,addr); } else { assert(addr!=map); output_sib(0,addr,map); } } output_byte(imm); } void emit_rmw_xorimm(int addr, int map, int imm) { if(map<0) { assem_debug("xorb $0x%x,(%%%s)\n",imm,regname[addr]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,addr,6); } else { assem_debug("xorb $0x%x,(%%%s,%%%s,1)\n",imm,regname[addr],regname[map]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,4,6); if(addr!=EBP) { output_sib(0,map,addr); } else { assert(addr!=map); output_sib(0,addr,map); } } output_byte(imm); } void emit_rmw_orimm(int addr, int map, int imm) { if(map<0) { assem_debug("orb $0x%x,(%%%s)\n",imm,regname[addr]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,addr,1); } else { assem_debug("orb $0x%x,(%%%s,%%%s,1)\n",imm,regname[addr],regname[map]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,4,1); if(addr!=EBP) { output_sib(0,map,addr); } else { assert(addr!=map); output_sib(0,addr,map); } } output_byte(imm); } void emit_sh2tas(int addr, int map, int sr) { emit_shrimm(sr,1,sr); if(map<0) { assem_debug("cmpb $1,(%%%s)\n",regname[addr]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,addr,7); } else { assem_debug("cmpb $1,(%%%s,%%%s,1)\n",regname[addr],regname[map]); assert(addr!=ESP); output_byte(0x80); output_modrm(0,4,7); if(addr!=EBP) { output_sib(0,map,addr); } else { assert(addr!=map); output_sib(0,addr,map); } } output_byte(1); emit_adc(sr,sr); emit_rmw_orimm(addr,map,0x80); } void emit_mul(int rs) { assem_debug("mul %%%s\n",regname[rs]); output_byte(0xF7); output_modrm(3,rs,4); } void emit_imul(int rs) { assem_debug("imul %%%s\n",regname[rs]); output_byte(0xF7); output_modrm(3,rs,5); } void emit_multiply(int rs1,int rs2,int rt) { if(rs1==rt) { assem_debug("imul %%%s,%%%s\n",regname[rs2],regname[rt]); output_byte(0x0F); output_byte(0xAF); output_modrm(3,rs2,rt); } else { emit_mov(rs1,rt); emit_multiply(rt,rs2,rt); } } void emit_div(int rs) { assem_debug("div %%%s\n",regname[rs]); output_byte(0xF7); output_modrm(3,rs,6); } void emit_idiv(int rs) { assem_debug("idiv %%%s\n",regname[rs]); output_byte(0xF7); output_modrm(3,rs,7); } void emit_cdq() { assem_debug("cdq\n"); output_byte(0x99); } void emit_div0s(int s1, int s2, int sr, int temp) { emit_shlimm(sr,24,sr); emit_mov(s2,temp); assem_debug("bt %%%s,31\n",regname[s2]); output_byte(0x0f); output_byte(0xba); output_modrm(3,s2,4); output_byte(0x1f); assem_debug("rcr %%%s\n",regname[sr]); output_byte(0xD1); output_modrm(3,sr,3); emit_xor(temp,s1,temp); assem_debug("bt %%%s,31\n",regname[s1]); output_byte(0x0f); output_byte(0xba); output_modrm(3,s1,4); output_byte(0x1f); assem_debug("rcr %%%s,24\n",regname[sr]); output_byte(0xc1); output_modrm(3,sr,3); output_byte(24); assem_debug("bt %%%s,31\n",regname[temp]); output_byte(0x0f); output_byte(0xba); output_modrm(3,temp,4); output_byte(0x1f); emit_adc(sr,sr); } // Load return address void emit_load_return_address(unsigned int rt) { // (assumes this instruction will be followed by a 5-byte jmp instruction) emit_movimm((int)out+10,rt); } // Load 2 immediates optimizing for small code size void emit_mov2imm_compact(int imm1,unsigned int rt1,int imm2,unsigned int rt2) { emit_movimm(imm1,rt1); if(imm2-imm1<128&&imm2-imm1>=-128) emit_addimm(rt1,imm2-imm1,rt2); else emit_movimm(imm2,rt2); } // compare byte in memory void emit_cmpmem_imm_byte(pointer addr,int imm) { assert(imm<128&&imm>=-127); assem_debug("cmpb $%d,%x\n",imm,addr); output_byte(0x80); output_modrm(0,5,7); output_w32(addr-(int)out-5); // Note: rip-relative in 64-bit mode output_byte(imm); } // special case for checking invalid_code void emit_cmpmem_indexedsr12_imm(int addr,int r,int imm) { assert(imm<128&&imm>=-127); assert(r>=0&&r<8); emit_shrimm(r,12,r); assem_debug("cmp $%d,%x+%%%s\n",imm,addr,regname[r]); output_byte(0x80); output_modrm(2,r,7); output_w32(addr); output_byte(imm); } // special case for checking hash_table void emit_cmpmem_indexed(int addr,int rs,int rt) { assert(rs>=0&&rs<8); assert(rt>=0&&rt<8); assem_debug("cmp %x+%%%s,%%%s\n",addr,regname[rs],regname[rt]); output_byte(0x39); output_modrm(2,rs,rt); output_w32(addr); } // special case for checking memory_map in verify_mapping void emit_cmpmem(int addr,int rt) { assert(rt>=0&&rt<8); assem_debug("cmp %x,%%%s\n",addr,regname[rt]); output_byte(0x39); output_modrm(0,5,rt); output_w32((int)addr-(int)out-4); // Note: rip-relative in 64-bit mode } // Used to preload hash table entries void emit_prefetch(void *addr) { assem_debug("prefetch %x\n",(int)addr); output_byte(0x0F); output_byte(0x18); output_modrm(0,5,1); output_w32((int)addr-(int)out-4); // Note: rip-relative in 64-bit mode } /*void emit_submem(int r,int addr) { assert(r>=0&&r<8); assem_debug("sub %x,%%%s\n",addr,regname[r]); output_byte(0x2B); output_modrm(0,5,r); output_w32((int)addr); }*/ void emit_flds(int r) { assem_debug("flds (%%%s)\n",regname[r]); output_byte(0xd9); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_fldl(int r) { assem_debug("fldl (%%%s)\n",regname[r]); output_byte(0xdd); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_fucomip(unsigned int r) { assem_debug("fucomip %d\n",r); assert(r<8); output_byte(0xdf); output_byte(0xe8+r); } void emit_fchs() { assem_debug("fchs\n"); output_byte(0xd9); output_byte(0xe0); } void emit_fabs() { assem_debug("fabs\n"); output_byte(0xd9); output_byte(0xe1); } void emit_fsqrt() { assem_debug("fsqrt\n"); output_byte(0xd9); output_byte(0xfa); } void emit_fadds(int r) { assem_debug("fadds (%%%s)\n",regname[r]); output_byte(0xd8); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_faddl(int r) { assem_debug("faddl (%%%s)\n",regname[r]); output_byte(0xdc); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_fadd(int r) { assem_debug("fadd st%d\n",r); output_byte(0xd8); output_byte(0xc0+r); } void emit_fsubs(int r) { assem_debug("fsubs (%%%s)\n",regname[r]); output_byte(0xd8); if(r!=EBP) output_modrm(0,r,4); else {output_modrm(1,EBP,4);output_byte(0);} } void emit_fsubl(int r) { assem_debug("fsubl (%%%s)\n",regname[r]); output_byte(0xdc); if(r!=EBP) output_modrm(0,r,4); else {output_modrm(1,EBP,4);output_byte(0);} } void emit_fsub(int r) { assem_debug("fsub st%d\n",r); output_byte(0xd8); output_byte(0xe0+r); } void emit_fmuls(int r) { assem_debug("fmuls (%%%s)\n",regname[r]); output_byte(0xd8); if(r!=EBP) output_modrm(0,r,1); else {output_modrm(1,EBP,1);output_byte(0);} } void emit_fmull(int r) { assem_debug("fmull (%%%s)\n",regname[r]); output_byte(0xdc); if(r!=EBP) output_modrm(0,r,1); else {output_modrm(1,EBP,1);output_byte(0);} } void emit_fmul(int r) { assem_debug("fmul st%d\n",r); output_byte(0xd8); output_byte(0xc8+r); } void emit_fdivs(int r) { assem_debug("fdivs (%%%s)\n",regname[r]); output_byte(0xd8); if(r!=EBP) output_modrm(0,r,6); else {output_modrm(1,EBP,6);output_byte(0);} } void emit_fdivl(int r) { assem_debug("fdivl (%%%s)\n",regname[r]); output_byte(0xdc); if(r!=EBP) output_modrm(0,r,6); else {output_modrm(1,EBP,6);output_byte(0);} } void emit_fdiv(int r) { assem_debug("fdiv st%d\n",r); output_byte(0xd8); output_byte(0xf0+r); } void emit_fpop() { // fstp st(0) assem_debug("fpop\n"); output_byte(0xdd); output_byte(0xd8); } void emit_fildl(int r) { assem_debug("fildl (%%%s)\n",regname[r]); output_byte(0xdb); if(r!=EBP) output_modrm(0,r,0); else {output_modrm(1,EBP,0);output_byte(0);} } void emit_fildll(int r) { assem_debug("fildll (%%%s)\n",regname[r]); output_byte(0xdf); if(r!=EBP) output_modrm(0,r,5); else {output_modrm(1,EBP,5);output_byte(0);} } void emit_fistpl(int r) { assem_debug("fistpl (%%%s)\n",regname[r]); output_byte(0xdb); if(r!=EBP) output_modrm(0,r,3); else {output_modrm(1,EBP,3);output_byte(0);} } void emit_fistpll(int r) { assem_debug("fistpll (%%%s)\n",regname[r]); output_byte(0xdf); if(r!=EBP) output_modrm(0,r,7); else {output_modrm(1,EBP,7);output_byte(0);} } void emit_fstps(int r) { assem_debug("fstps (%%%s)\n",regname[r]); output_byte(0xd9); if(r!=EBP) output_modrm(0,r,3); else {output_modrm(1,EBP,3);output_byte(0);} } void emit_fstpl(int r) { assem_debug("fstpl (%%%s)\n",regname[r]); output_byte(0xdd); if(r!=EBP) output_modrm(0,r,3); else {output_modrm(1,EBP,3);output_byte(0);} } void emit_fnstcw_stack() { assem_debug("fnstcw (%%esp)\n"); output_byte(0xd9); output_modrm(0,4,7); output_sib(0,4,4); } void emit_fldcw_stack() { assem_debug("fldcw (%%esp)\n"); output_byte(0xd9); output_modrm(0,4,5); output_sib(0,4,4); } void emit_fldcw_indexed(int addr,int r) { assem_debug("fldcw %x(%%%s)\n",addr,regname[r]); output_byte(0xd9); output_modrm(0,4,5); output_sib(1,r,5); output_w32(addr); } void emit_fldcw(int addr) { assem_debug("fldcw %x\n",addr); output_byte(0xd9); output_modrm(0,5,5); output_w32(addr-(int)out-4); // Note: rip-relative in 64-bit mode } void emit_movss_load(unsigned int addr,unsigned int ssereg) { assem_debug("movss (%%%s),xmm%d\n",regname[addr],ssereg); assert(ssereg<8); output_byte(0xf3); output_byte(0x0f); output_byte(0x10); if(addr!=EBP) output_modrm(0,addr,ssereg); else {output_modrm(1,EBP,ssereg);output_byte(0);} } void emit_movsd_load(unsigned int addr,unsigned int ssereg) { assem_debug("movsd (%%%s),xmm%d\n",regname[addr],ssereg); assert(ssereg<8); output_byte(0xf2); output_byte(0x0f); output_byte(0x10); if(addr!=EBP) output_modrm(0,addr,ssereg); else {output_modrm(1,EBP,ssereg);output_byte(0);} } void emit_movd_store(unsigned int ssereg,unsigned int addr) { assem_debug("movd xmm%d,(%%%s)\n",ssereg,regname[addr]); assert(ssereg<8); output_byte(0x66); output_byte(0x0f); output_byte(0x7e); if(addr!=EBP) output_modrm(0,addr,ssereg); else {output_modrm(1,EBP,ssereg);output_byte(0);} } void emit_cvttps2dq(unsigned int ssereg1,unsigned int ssereg2) { assem_debug("cvttps2dq xmm%d,xmm%d\n",ssereg1,ssereg2); assert(ssereg1<8); assert(ssereg2<8); output_byte(0xf3); output_byte(0x0f); output_byte(0x5b); output_modrm(3,ssereg1,ssereg2); } void emit_cvttpd2dq(unsigned int ssereg1,unsigned int ssereg2) { assem_debug("cvttpd2dq xmm%d,xmm%d\n",ssereg1,ssereg2); assert(ssereg1<8); assert(ssereg2<8); output_byte(0x66); output_byte(0x0f); output_byte(0xe6); output_modrm(3,ssereg1,ssereg2); } unsigned int count_bits(u32 reglist) { int count=0; while(reglist) { count+=reglist&1; reglist>>=1; } return count; } // Save registers before function call // This code is executed infrequently so we try to minimize code size // by pushing registers onto the stack instead of writing them to their // usual locations void save_regs(u32 reglist) { reglist&=0xC7; // only save the caller-save registers, %eax, %ecx, %edx, %esi, %edi int hr; int count=count_bits(reglist); if(count) { for(hr=0;hr>hr)&1) { emit_pushreg(hr); } } } } if(slave) emit_addimm64(ESP,-(6-count)*8,ESP); // slave has master's return address on stack else emit_addimm64(ESP,-(7-count)*8,ESP); } // Restore registers after function call void restore_regs(u32 reglist) { reglist&=0xC7; // only save the caller-save registers, %eax, %ecx, %edx, %esi, %edi int hr; int count=count_bits(reglist); if(slave) emit_addimm64(ESP,(6-count)*8,ESP); else emit_addimm64(ESP,(7-count)*8,ESP); if(count) { for(hr=HOST_REGS-1;hr>=0;hr--) { if(hr!=EXCLUDE_REG) { if((reglist>>hr)&1) { emit_popreg(hr); } } } } } /* Stubs/epilogue */ emit_extjump(pointer addr, int target) { u8 *ptr=(u8 *)addr; if(*ptr==0x0f) { assert(ptr[1]>=0x80&&ptr[1]<=0x8f); addr+=2; } else { assert(*ptr==0xe8||*ptr==0xe9); addr++; } emit_movimm(target,EAX); emit_movimm(addr,EBX); //assert(addr>=0x7000000&&addr<0x7FFFFFF); //DEBUG > #ifdef DEBUG_CYCLE_COUNT emit_readword((int)&last_count,ECX); emit_add(HOST_CCREG,ECX,HOST_CCREG); emit_readword((int)&next_interupt,ECX); emit_writeword(HOST_CCREG,(int)&Count); emit_sub(HOST_CCREG,ECX,HOST_CCREG); emit_writeword(ECX,(int)&last_count); #endif //DEBUG < emit_jmp((pointer)dyna_linker); } do_readstub(int n) { assem_debug("do_readstub %x\n",start+stubs[n][3]*2); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); int rt; rt=get_reg(i_regmap,rt1[i]==TBIT?-1:rt1[i]); assert(rs>=0); if(addr<0) addr=rt; if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); if(rs!=EDI) emit_mov(rs,EDI); if(type==LOADB_STUB) emit_xorimm(EDI,1,EDI); /* int temp; int cc=get_reg(i_regmap,CCREG); if(cc<0) { if(addr==HOST_CCREG) { cc=0;temp=1; assert(cc!=HOST_CCREG); assert(temp!=HOST_CCREG); emit_loadreg(CCREG,cc); } else { cc=HOST_CCREG; emit_loadreg(CCREG,cc); temp=!addr; } } else { temp=!addr; }*/ if(type==LOADB_STUB) emit_call((int)MappedMemoryReadByte); if(type==LOADW_STUB) emit_call((int)MappedMemoryReadWord); if(type==LOADL_STUB) emit_call((int)MappedMemoryReadLong); if(type==LOADS_STUB) { // RTE instruction, pop PC and SR from stack int pc=get_reg(i_regmap,RTEMP); assert(pc>=0); if(rs==EAX||rs==ECX||rs==EDX||rs==ESI||rs==EDI) emit_mov(rs,12); //emit_writeword_indexed(rs,0,ESP); emit_call((int)MappedMemoryReadLong); if(rs==EAX||rs==ECX||rs==EDX||rs==ESI) emit_mov(12,rs); //emit_readword_indexed(0,ESP,rs); if(pc==EDI) { emit_mov(EAX,12); //emit_writeword_indexed(EAX,0,ESP); } else { if(pc==EAX||pc==ECX||pc==EDX||pc==ESI) emit_mov(EAX,12); //emit_writeword_indexed(EAX,0,ESP); else emit_mov(EAX,pc); if(rs==EDI) { emit_mov(12,EDI); //emit_readword_indexed(0,ESP,EAX); emit_addimm(EDI,4,EDI); }else emit_addimm(rs,4,EDI); } emit_call((int)MappedMemoryReadLong); assert(rt>=0); if(rt!=EAX) emit_mov(EAX,rt); if(pc==EAX||pc==ECX||pc==EDX||pc==ESI||pc==EDI) emit_mov(12,pc); //emit_readword_indexed(0,ESP,pc); } else if(type==LOADB_STUB) { if(rt>=0) emit_movsbl_reg(EAX,rt); } else if(type==LOADW_STUB) { if(rt>=0) emit_movswl_reg(EAX,rt); } else { if(rt!=EAX&&rt>=0) emit_mov(EAX,rt); } restore_regs(reglist); if(type==LOADS_STUB) emit_addimm(rs,8,rs); emit_jmp(stubs[n][2]); // return address } inline_readstub(int type, int i, u32 addr, signed char regmap[], int target, int adj, u32 reglist) { assem_debug("inline_readstub\n"); //int rs=get_reg(regmap,target); int rt=get_reg(regmap,target); //if(rs<0) rs=get_reg(regmap,-1); if(rt<0) rt=get_reg(regmap,-1); assert(rt>=0); save_regs(reglist); emit_movimm(addr,EDI); if(type==LOADB_STUB) emit_call((int)MappedMemoryReadByte); if(type==LOADW_STUB) emit_call((int)MappedMemoryReadWord); if(type==LOADL_STUB) emit_call((int)MappedMemoryReadLong); assert(type!=LOADS_STUB); if(type==LOADB_STUB) { if(rt>=0) emit_movsbl_reg(EAX,rt); } else if(type==LOADW_STUB) { if(rt>=0) emit_movswl_reg(EAX,rt); } else { if(rt!=EAX&&rt>=0) emit_mov(EAX,rt); } restore_regs(reglist); } do_writestub(int n) { assem_debug("do_writestub %x\n",start+stubs[n][3]*2); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); int rt=get_reg(i_regmap,rs1[i]); assert(rs>=0); assert(rt>=0); if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); // "FASTCALL" api: address in edi, data in esi if(rs!=EDI) { if(rt==EDI) { if(rs==ESI) emit_xchg(EDI,ESI); else { emit_mov(rt,ESI); emit_mov(rs,EDI); } } else { emit_mov(rs,EDI); if(rt!=ESI) emit_mov(rt,ESI); } } else if(rt!=ESI) emit_mov(rt,ESI); //if(type==STOREB_STUB) emit_xorimm(EAX,1,EAX); // WriteInvalidateByteSwapped does this /*int temp; int cc=get_reg(i_regmap,CCREG); if(cc<0) { if(addr==HOST_CCREG) { cc=0;temp=1; assert(cc!=HOST_CCREG); assert(temp!=HOST_CCREG); emit_loadreg(CCREG,cc); } else { cc=HOST_CCREG; emit_loadreg(CCREG,cc); temp=!addr; } } else { temp=!addr; }*/ if(type==STOREB_STUB) emit_call((int)WriteInvalidateByteSwapped); if(type==STOREW_STUB) emit_call((int)WriteInvalidateWord); if(type==STOREL_STUB) emit_call((int)WriteInvalidateLong); restore_regs(reglist); emit_jmp(stubs[n][2]); // return address } inline_writestub(int type, int i, u32 addr, signed char regmap[], int target, int adj, u32 reglist) { assem_debug("inline_writestub\n"); //int rs=get_reg(regmap,-1); int rt=get_reg(regmap,target); //assert(rs>=0); assert(rt>=0); save_regs(reglist); // "FASTCALL" api: address in eax, data in edx if(rt!=ESI) emit_mov(rt,ESI); emit_movimm(addr,EDI); // FIXME - should be able to move the existing value if(type==STOREB_STUB) emit_call((int)WriteInvalidateByte); if(type==STOREW_STUB) emit_call((int)WriteInvalidateWord); if(type==STOREL_STUB) emit_call((int)WriteInvalidateLong); restore_regs(reglist); } do_rmwstub(int n) { assem_debug("do_rmwstub %x\n",start+stubs[n][3]*2); set_jump_target(stubs[n][1],(int)out); int type=stubs[n][0]; int i=stubs[n][3]; int rs=stubs[n][4]; struct regstat *i_regs=(struct regstat *)stubs[n][5]; u32 reglist=stubs[n][7]; signed char *i_regmap=i_regs->regmap; int addr=get_reg(i_regmap,AGEN1+(i&1)); //int rt=get_reg(i_regmap,rs1[i]); assert(rs>=0); //assert(rt>=0); if(addr<0) addr=get_reg(i_regmap,-1); assert(addr>=0); save_regs(reglist); // "FASTCALL" api: address in eax, data in edx emit_xorimm(rs,1,rs); if(rs!=EDI) emit_mov(rs,EDI); if(rs==EAX||rs==ECX||rs==EDX||rs==ESI||rs==EDI) emit_mov(rs,12); //emit_writeword_indexed(rs,0,ESP); //if(i_regmap[HOST_CCREG]==CCREG) emit_storereg(CCREG,HOST_CCREG);//DEBUG /*if(i_regmap[HOST_CCREG]==CCREG) { emit_addimm(HOST_CCREG,CLOCK_DIVIDER*(stubs[n][6]),HOST_CCREG); output_byte(0x03); output_modrm(1,4,HOST_CCREG); output_sib(0,4,4); output_byte(12+16); emit_writeword(HOST_CCREG,(int)&MSH2->cycles); output_byte(0x2B); output_modrm(1,4,HOST_CCREG); output_sib(0,4,4); output_byte(12+16); emit_addimm(HOST_CCREG,-CLOCK_DIVIDER*(stubs[n][6]),HOST_CCREG); } if(i_regmap[HOST_CCREG]!=CCREG) { emit_loadreg(CCREG,ECX); emit_addimm(ECX,CLOCK_DIVIDER*(stubs[n][6]),ECX); output_byte(0x03); output_modrm(1,4,ECX); output_sib(0,4,4); output_byte(12+16); emit_writeword(ECX,(int)&MSH2->cycles); }*/ emit_call((int)MappedMemoryReadByte); emit_mov(EAX,ESI); if(rs==EAX||rs==ECX||rs==EDX||rs==ESI||rs==EDI) emit_mov(12,EDI); //emit_readword_indexed(0,ESP,EAX); else emit_mov(rs,EDI); if(type==RMWA_STUB) emit_andimm(ESI,imm[i],ESI); if(type==RMWX_STUB) emit_xorimm(ESI,imm[i],ESI); if(type==RMWO_STUB) emit_orimm(ESI,imm[i],ESI); if(type==RMWT_STUB) { // TAS.B emit_mov(ESI,12); //emit_writeword_indexed(EDX,0,ESP); emit_orimm(ESI,0x80,ESI); } //emit_call((int)MappedMemoryWriteByte); emit_call((int)WriteInvalidateByte); restore_regs(reglist); if(opcode2[i]==11) { // TAS.B signed char sr; sr=get_reg(i_regs->regmap,SR); assert(sr>=0); // Liveness analysis? emit_andimm(sr,~1,sr); /*assem_debug("cmp $%d,%d+%%%s\n",1,-16,regname[ESP]); output_byte(0x80); output_modrm(1,4,7); output_sib(0,4,4); output_byte(-16); output_byte(1);*/ emit_cmpimm(12,1); emit_adcimm(0,sr); } emit_jmp(stubs[n][2]); // return address } do_unalignedwritestub(int n) { set_jump_target(stubs[n][1],(int)out); output_byte(0xCC); emit_jmp(stubs[n][2]); // return address } void printregs(int edi,int esi,int ebp,int esp,int b,int d,int c,int a) { printf("regs: %x %x %x %x %x %x %x (%x)\n",a,b,c,d,ebp,esi,edi,(&edi)[-1]); } int do_dirty_stub(int i) { assem_debug("do_dirty_stub %x\n",start+i*2); u32 alignedlen=((((u32)source)+slen*2+2)&~2)-(u32)alignedsource; if((u64)source<=0xFFFFFFFF) emit_movimm(((u32)source)&~3,EAX); //alignedsource else emit_movimm64(((u64)source)&~3,EAX); //alignedsource emit_movimm((u32)copy,EBX); emit_movimm((((u32)source+slen*2+2)&~3)-((u32)source&~3),ECX); emit_movimm(start+i*2+slave,12); emit_call((int)&verify_code); int entry=(int)out; load_regs_entry(i); if(entry==(int)out) entry=instr_addr[i]; emit_jmp(instr_addr[i]); return entry; } /* Memory Map */ int do_map_r(int s,int ar,int map,int cache,int x,int a,int shift,int c,u32 addr) { if(c) { /*if((signed int)addr>=(signed int)0xC0000000) { emit_movq((int)(memory_map+(addr>>12)),map); } else*/ return -1; // No mapping } else { if(s!=map) emit_mov(s,map); emit_shrimm(map,12,map); // Schedule this while we wait on the load if(x) emit_xorimm(s,x,ar); //if(shift>=0) emit_lea8(s,shift); //if(~a) emit_andimm(s,a,ar); emit_movmem_indexedx8_64((int)memory_map,map,map); } return map; } int do_map_r_branch(int map, int c, u32 addr, int *jaddr) { if(!c) { emit_test64(map,map); *jaddr=(int)out; emit_js(0); } return map; } int gen_tlb_addr_r(int ar, int map) { if(map>=0) { emit_leairrx4(0,ar,map,ar); } } int do_map_w(int s,int ar,int map,int cache,int x,int c,u32 addr) { if(c) { if(can_direct_write(addr)) { emit_movq((int)(memory_map+(addr>>12)),map); } else return -1; // No mapping } else { if(s!=map) emit_mov(s,map); //if(s!=ar) emit_mov(s,ar); emit_shrimm(map,12,map); // Schedule this while we wait on the load if(x) emit_xorimm(s,x,ar); emit_movmem_indexedx8_64((int)memory_map,map,map); } emit_shlimm64(map,2,map); return map; } int do_map_w_branch(int map, int c, u32 addr, int *jaddr) { if(!c||can_direct_write(addr)) { *jaddr=(int)out; emit_jc(0); } } int gen_tlb_addr_w(int ar, int map) { if(map>=0) { emit_leairrx1(0,ar,map,ar); } } // We don't need this for x86 generate_map_const(u32 addr,int reg) { // void *mapaddr=memory_map+(addr>>12); } /* Special assem */ void do_preload_rhash(int r) { emit_movimm(0xf8,r); } void do_preload_rhtbl(int r) { // Don't need this for x86 } void do_rhash(int rs,int rh) { emit_and(rs,rh,rh); } void do_miniht_load(int ht,int rh) { // Don't need this for x86. The load and compare can be combined into // a single instruction (below) } void do_miniht_jump(int rs,int rh,int ht) { emit_cmpmem_indexed(slave?(u32)mini_ht_slave:(u32)mini_ht_master,rh,rs); emit_jne(jump_vaddr_reg[slave][rs]); emit_readword_indexed(slave?(u32)mini_ht_slave+4:(u32)mini_ht_master+4,rh,rh); emit_jmpreg(rh); } void do_miniht_insert(int return_address,int rt,int temp) { emit_movimm(return_address,rt); // PC into link register //emit_writeword_imm(return_address,(int)&mini_ht[(return_address&0xFF)>>8][0]); if(slave) emit_writeword(rt,(int)&mini_ht_slave[(return_address&0xFF)>>3][0]); else emit_writeword(rt,(int)&mini_ht_master[(return_address&0xFF)>>3][0]); add_to_linker((int)out,return_address,1); if(slave) emit_writeword_imm(0,(int)&mini_ht_slave[(return_address&0xFF)>>3][1]); else emit_writeword_imm(0,(int)&mini_ht_master[(return_address&0xFF)>>3][1]); } void wb_valid(signed char pre[],signed char entry[],u32 dirty_pre,u32 dirty,u64 u) { //if(dirty_pre==dirty) return; int hr,reg,new_hr; for(hr=0;hr>(reg&63))&1) { if(reg>=0) { if(((dirty_pre&~dirty)>>hr)&1) { if(reg>=0&® #include /* Thread handle structure. */ struct thd_s { int running; pthread_t thd; void (*func)(void *); void *arg; pthread_mutex_t mutex; pthread_cond_t cond; }; static struct thd_s thread_handle[YAB_NUM_THREADS]; static pthread_key_t hnd_key; static pthread_once_t hnd_key_once; static void make_key() { pthread_key_create(&hnd_key, NULL); } static void *wrapper(void *hnd) { struct thd_s *hnds = (struct thd_s *)hnd; pthread_mutex_lock(&hnds->mutex); /* Set the handle for the thread, and call the actual thread function. */ pthread_setspecific(hnd_key, hnd); hnds->func(hnds->arg); pthread_mutex_unlock(&hnds->mutex); return NULL; } int YabThreadStart(unsigned int id, void (*func)(void *), void *arg) { /* Create the key to access the thread handle if we haven't made it yet. */ pthread_once(&hnd_key_once, make_key); /* Make sure we aren't trying to start a thread twice. */ if(thread_handle[id].running) { fprintf(stderr, "YabThreadStart: Thread %u is already started!\n", id); return -1; } /* Create the mutex and condvar for the thread. */ if(pthread_mutex_init(&thread_handle[id].mutex, NULL)) { fprintf(stderr, "YabThreadStart: Error creating mutex\n"); return -1; } if(pthread_cond_init(&thread_handle[id].cond, NULL)) { fprintf(stderr, "YabThreadStart: Error creating condvar\n"); pthread_mutex_destroy(&thread_handle[id].mutex); return -1; } thread_handle[id].func = func; thread_handle[id].arg = arg; /* Create the thread. */ if(pthread_create(&thread_handle[id].thd, NULL, wrapper, &thread_handle[id])) { fprintf(stderr, "YabThreadStart: Couldn't start thread\n"); pthread_cond_destroy(&thread_handle[id].cond); pthread_mutex_destroy(&thread_handle[id].mutex); return -1; } thread_handle[id].running = 1; return 0; } void YabThreadWait(unsigned int id) { /* Make sure the thread is running. */ if(!thread_handle[id].running) return; /* Join the thread to wait for it to finish. */ pthread_join(thread_handle[id].thd, NULL); /* Cleanup... */ pthread_cond_destroy(&thread_handle[id].cond); pthread_mutex_destroy(&thread_handle[id].mutex); thread_handle[id].thd = NULL; thread_handle[id].func = NULL; thread_handle[id].running = 0; } void YabThreadYield(void) { sched_yield(); } void YabThreadSleep(void) { struct thd_s *thd = (struct thd_s *)pthread_getspecific(hnd_key); /* Wait on the condvar... */ pthread_cond_wait(&thd->cond, &thd->mutex); } void YabThreadSleep(unsigned int id) { /* Wait on the condvar... */ pthread_cond_wait(&thread_handle[id].cond, &thread_handle[id].mutex); } void YabThreadWake(unsigned int id) { if(!thread_handle[id].running) return; pthread_cond_signal(&thread_handle[id].cond); } yabause-0.9.13.1/src/sh2core.c000644 001750 001750 00000206060 12256006201 017704 0ustar00guillaumeguillaume000000 000000 /* Copyright 2003-2005 Guillaume Duhamel Copyright 2004-2005, 2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // SH2 Shared Code #include #include "sh2core.h" #include "debug.h" #include "memory.h" #include "yabause.h" #if defined(SH2_DYNAREC) #include "sh2_dynarec/sh2_dynarec.h" #endif SH2_struct *MSH2=NULL; SH2_struct *SSH2=NULL; SH2_struct *CurrentSH2; SH2Interface_struct *SH2Core=NULL; extern SH2Interface_struct *SH2CoreList[]; void OnchipReset(SH2_struct *context); void FRTExec(u32 cycles); void WDTExec(u32 cycles); u8 SCIReceiveByte(void); void SCITransmitByte(u8); ////////////////////////////////////////////////////////////////////////////// int SH2Init(int coreid) { int i; // MSH2 if ((MSH2 = (SH2_struct *)calloc(1, sizeof(SH2_struct))) == NULL) return -1; if (SH2TrackInfLoopInit(MSH2) != 0) return -1; MSH2->onchip.BCR1 = 0x0000; MSH2->isslave = 0; // SSH2 if ((SSH2 = (SH2_struct *)calloc(1, sizeof(SH2_struct))) == NULL) return -1; if (SH2TrackInfLoopInit(SSH2) != 0) return -1; SSH2->onchip.BCR1 = 0x8000; SSH2->isslave = 1; // So which core do we want? if (coreid == SH2CORE_DEFAULT) coreid = 0; // Assume we want the first one // Go through core list and find the id for (i = 0; SH2CoreList[i] != NULL; i++) { if (SH2CoreList[i]->id == coreid) { // Set to current core SH2Core = SH2CoreList[i]; break; } } if ((SH2Core == NULL) || (SH2Core->Init() != 0)) { free(MSH2); free(SSH2); MSH2 = SSH2 = NULL; return -1; } return 0; } ////////////////////////////////////////////////////////////////////////////// void SH2DeInit() { if (SH2Core) SH2Core->DeInit(); SH2Core = NULL; if (MSH2) { SH2TrackInfLoopDeInit(MSH2); free(MSH2); } MSH2 = NULL; if (SSH2) { SH2TrackInfLoopDeInit(SSH2); free(SSH2); } SSH2 = NULL; } ////////////////////////////////////////////////////////////////////////////// void SH2Reset(SH2_struct *context) { int i; // Reset general registers for (i = 0; i < 15; i++) SH2Core->SetGPR(context, i, 0x00000000); SH2Core->SetSR(context, 0x000000F0); SH2Core->SetGBR(context, 0x00000000); SH2Core->SetVBR(context, 0x00000000); SH2Core->SetMACH(context, 0x00000000); SH2Core->SetMACL(context, 0x00000000); SH2Core->SetPR(context, 0x00000000); // Internal variables context->delay = 0x00000000; context->cycles = 0; context->isIdle = 0; context->frc.leftover = 0; context->frc.shift = 3; context->wdt.isenable = 0; context->wdt.isinterval = 1; context->wdt.shift = 1; context->wdt.leftover = 0; // Reset Interrupts memset((void *)context->interrupts, 0, sizeof(interrupt_struct) * MAX_INTERRUPTS); SH2Core->SetInterrupts(context, 0, context->interrupts); // Core specific reset SH2Core->Reset(context); // Reset Onchip modules OnchipReset(context); // Reset backtrace context->bt.numbacktrace = 0; } ////////////////////////////////////////////////////////////////////////////// void SH2PowerOn(SH2_struct *context) { u32 VBR = SH2Core->GetVBR(context); SH2Core->SetPC(context, MappedMemoryReadLong(VBR)); SH2Core->SetGPR(context, 15, MappedMemoryReadLong(VBR+4)); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL SH2Exec(SH2_struct *context, u32 cycles) { CurrentSH2 = context; SH2Core->Exec(context, cycles); FRTExec(cycles); WDTExec(cycles); if (UNLIKELY(context->cycles < cycles)) context->cycles = 0; else context->cycles -= cycles; } ////////////////////////////////////////////////////////////////////////////// void SH2SendInterrupt(SH2_struct *context, u8 vector, u8 level) { SH2Core->SendInterrupt(context, vector, level); } ////////////////////////////////////////////////////////////////////////////// void SH2NMI(SH2_struct *context) { context->onchip.ICR |= 0x8000; SH2SendInterrupt(context, 0xB, 0x10); } ////////////////////////////////////////////////////////////////////////////// void SH2Step(SH2_struct *context) { if (SH2Core) { u32 tmp = SH2Core->GetPC(context); // Execute 1 instruction SH2Exec(context, context->cycles+1); // Sometimes it doesn't always execute one instruction, // let's make sure it did if (tmp == SH2Core->GetPC(context)) SH2Exec(context, context->cycles+1); } } ////////////////////////////////////////////////////////////////////////////// int SH2StepOver(SH2_struct *context, void (*func)(void *, u32, void *)) { if (SH2Core) { u32 tmp = SH2Core->GetPC(context); u16 inst=MappedMemoryReadWord(context->regs.PC); // If instruction is jsr, bsr, or bsrf, step over it if ((inst & 0xF000) == 0xB000 || // BSR (inst & 0xF0FF) == 0x0003 || // BSRF (inst & 0xF0FF) == 0x400B) // JSR { // Set breakpoint after at PC + 4 context->stepOverOut.callBack = func; context->stepOverOut.type = SH2ST_STEPOVER; context->stepOverOut.enabled = 1; context->stepOverOut.address = context->regs.PC+4; return 1; } else { // Execute 1 instruction instead SH2Exec(context, context->cycles+1); // Sometimes it doesn't always execute one instruction, // let's make sure it did if (tmp == SH2Core->GetPC(context)) SH2Exec(context, context->cycles+1); } } return 0; } ////////////////////////////////////////////////////////////////////////////// void SH2StepOut(SH2_struct *context, void (*func)(void *, u32, void *)) { if (SH2Core) { context->stepOverOut.callBack = func; context->stepOverOut.type = SH2ST_STEPOUT; context->stepOverOut.enabled = 1; context->stepOverOut.address = 0; } } ////////////////////////////////////////////////////////////////////////////// int SH2TrackInfLoopInit(SH2_struct *context) { context->trackInfLoop.maxNum = 100; if ((context->trackInfLoop.match = calloc(context->trackInfLoop.maxNum, sizeof(tilInfo_struct))) == NULL) return -1; return 0; } ////////////////////////////////////////////////////////////////////////////// void SH2TrackInfLoopDeInit(SH2_struct *context) { if (context->trackInfLoop.match) free(context->trackInfLoop.match); } ////////////////////////////////////////////////////////////////////////////// void SH2TrackInfLoopStart(SH2_struct *context) { context->trackInfLoop.enabled = 1; } ////////////////////////////////////////////////////////////////////////////// void SH2TrackInfLoopStop(SH2_struct *context) { context->trackInfLoop.enabled = 0; } ////////////////////////////////////////////////////////////////////////////// void SH2TrackInfLoopClear(SH2_struct *context) { memset(context->trackInfLoop.match, 0, sizeof(tilInfo_struct) * context->trackInfLoop.maxNum); context->trackInfLoop.num = 0; } ////////////////////////////////////////////////////////////////////////////// void SH2GetRegisters(SH2_struct *context, sh2regs_struct * r) { if (r != NULL) { SH2Core->GetRegisters(context, r); } } ////////////////////////////////////////////////////////////////////////////// void SH2SetRegisters(SH2_struct *context, sh2regs_struct * r) { if (r != NULL) { SH2Core->SetRegisters(context, r); } } ////////////////////////////////////////////////////////////////////////////// void SH2WriteNotify(u32 start, u32 length) { if (SH2Core->WriteNotify) SH2Core->WriteNotify(start, length); } ////////////////////////////////////////////////////////////////////////////// void SH2SetBreakpointCallBack(SH2_struct *context, void (*func)(void *, u32, void *), void *userdata) { context->bp.BreakpointCallBack = func; context->bp.BreakpointUserData = userdata; } ////////////////////////////////////////////////////////////////////////////// int SH2AddCodeBreakpoint(SH2_struct *context, u32 addr) { int i; if (context->bp.numcodebreakpoints < MAX_BREAKPOINTS) { // Make sure it isn't already on the list for (i = 0; i < context->bp.numcodebreakpoints; i++) { if (addr == context->bp.codebreakpoint[i].addr) return -1; } context->bp.codebreakpoint[context->bp.numcodebreakpoints].addr = addr; context->bp.numcodebreakpoints++; return 0; } return -1; } ////////////////////////////////////////////////////////////////////////////// static void SH2SortCodeBreakpoints(SH2_struct *context) { int i, i2; u32 tmp; for (i = 0; i < (MAX_BREAKPOINTS-1); i++) { for (i2 = i+1; i2 < MAX_BREAKPOINTS; i2++) { if (context->bp.codebreakpoint[i].addr == 0xFFFFFFFF && context->bp.codebreakpoint[i2].addr != 0xFFFFFFFF) { tmp = context->bp.codebreakpoint[i].addr; context->bp.codebreakpoint[i].addr = context->bp.codebreakpoint[i2].addr; context->bp.codebreakpoint[i2].addr = tmp; } } } } ////////////////////////////////////////////////////////////////////////////// int SH2DelCodeBreakpoint(SH2_struct *context, u32 addr) { int i, i2; LOG("Deleting breakpoint %08X...\n", addr); if (context->bp.numcodebreakpoints > 0) { for (i = 0; i < context->bp.numcodebreakpoints; i++) { if (context->bp.codebreakpoint[i].addr == addr) { context->bp.codebreakpoint[i].addr = 0xFFFFFFFF; SH2SortCodeBreakpoints(context); context->bp.numcodebreakpoints--; LOG("Remaining breakpoints: \n"); for (i2 = 0; i2 < context->bp.numcodebreakpoints; i2++) { LOG("%08X", context->bp.codebreakpoint[i2].addr); } return 0; } } } LOG("Failed deleting breakpoint\n"); return -1; } ////////////////////////////////////////////////////////////////////////////// codebreakpoint_struct *SH2GetBreakpointList(SH2_struct *context) { return context->bp.codebreakpoint; } ////////////////////////////////////////////////////////////////////////////// void SH2ClearCodeBreakpoints(SH2_struct *context) { int i; for (i = 0; i < MAX_BREAKPOINTS; i++) { context->bp.codebreakpoint[i].addr = 0xFFFFFFFF; } context->bp.numcodebreakpoints = 0; } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL SH2MemoryBreakpointReadByte(u32 addr) { int i; for (i = 0; i < CurrentSH2->bp.nummemorybreakpoints; i++) { if (CurrentSH2->bp.memorybreakpoint[i].addr == (addr & 0x0FFFFFFF)) { if (CurrentSH2->bp.BreakpointCallBack && CurrentSH2->bp.inbreakpoint == 0) { CurrentSH2->bp.inbreakpoint = 1; CurrentSH2->bp.BreakpointCallBack(CurrentSH2, 0, CurrentSH2->bp.BreakpointUserData); CurrentSH2->bp.inbreakpoint = 0; } return CurrentSH2->bp.memorybreakpoint[i].oldreadbyte(addr); } } // Use the closest match if address doesn't match for (i = 0; i < MSH2->bp.nummemorybreakpoints; i++) { if (((MSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) return MSH2->bp.memorybreakpoint[i].oldreadbyte(addr); } for (i = 0; i < SSH2->bp.nummemorybreakpoints; i++) { if (((SSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) return SSH2->bp.memorybreakpoint[i].oldreadbyte(addr); } return 0; } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL SH2MemoryBreakpointReadWord(u32 addr) { int i; for (i = 0; i < CurrentSH2->bp.nummemorybreakpoints; i++) { if (CurrentSH2->bp.memorybreakpoint[i].addr == (addr & 0x0FFFFFFF)) { if (CurrentSH2->bp.BreakpointCallBack && CurrentSH2->bp.inbreakpoint == 0) { CurrentSH2->bp.inbreakpoint = 1; CurrentSH2->bp.BreakpointCallBack(CurrentSH2, 0, CurrentSH2->bp.BreakpointUserData); CurrentSH2->bp.inbreakpoint = 0; } return CurrentSH2->bp.memorybreakpoint[i].oldreadword(addr); } } // Use the closest match if address doesn't match for (i = 0; i < MSH2->bp.nummemorybreakpoints; i++) { if (((MSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) return MSH2->bp.memorybreakpoint[i].oldreadword(addr); } for (i = 0; i < SSH2->bp.nummemorybreakpoints; i++) { if (((SSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) return SSH2->bp.memorybreakpoint[i].oldreadword(addr); } return 0; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL SH2MemoryBreakpointReadLong(u32 addr) { int i; for (i = 0; i < CurrentSH2->bp.nummemorybreakpoints; i++) { if (CurrentSH2->bp.memorybreakpoint[i].addr == (addr & 0x0FFFFFFF)) { if (CurrentSH2->bp.BreakpointCallBack && CurrentSH2->bp.inbreakpoint == 0) { CurrentSH2->bp.inbreakpoint = 1; CurrentSH2->bp.BreakpointCallBack(CurrentSH2, 0, CurrentSH2->bp.BreakpointUserData); CurrentSH2->bp.inbreakpoint = 0; } return CurrentSH2->bp.memorybreakpoint[i].oldreadlong(addr); } } // Use the closest match if address doesn't match for (i = 0; i < MSH2->bp.nummemorybreakpoints; i++) { if (((MSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) return MSH2->bp.memorybreakpoint[i].oldreadlong(addr); } for (i = 0; i < SSH2->bp.nummemorybreakpoints; i++) { if (((SSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) return SSH2->bp.memorybreakpoint[i].oldreadlong(addr); } return 0; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2MemoryBreakpointWriteByte(u32 addr, u8 val) { int i; for (i = 0; i < CurrentSH2->bp.nummemorybreakpoints; i++) { if (CurrentSH2->bp.memorybreakpoint[i].addr == (addr & 0x0FFFFFFF)) { if (CurrentSH2->bp.BreakpointCallBack && CurrentSH2->bp.inbreakpoint == 0) { CurrentSH2->bp.inbreakpoint = 1; CurrentSH2->bp.BreakpointCallBack(CurrentSH2, 0, CurrentSH2->bp.BreakpointUserData); CurrentSH2->bp.inbreakpoint = 0; } CurrentSH2->bp.memorybreakpoint[i].oldwritebyte(addr, val); return; } } // Use the closest match if address doesn't match for (i = 0; i < MSH2->bp.nummemorybreakpoints; i++) { if (((MSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) { MSH2->bp.memorybreakpoint[i].oldwritebyte(addr, val); return; } } for (i = 0; i < SSH2->bp.nummemorybreakpoints; i++) { if (((SSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) { SSH2->bp.memorybreakpoint[i].oldwritebyte(addr, val); return; } } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2MemoryBreakpointWriteWord(u32 addr, u16 val) { int i; for (i = 0; i < CurrentSH2->bp.nummemorybreakpoints; i++) { if (CurrentSH2->bp.memorybreakpoint[i].addr == (addr & 0x0FFFFFFF)) { if (CurrentSH2->bp.BreakpointCallBack && CurrentSH2->bp.inbreakpoint == 0) { CurrentSH2->bp.inbreakpoint = 1; CurrentSH2->bp.BreakpointCallBack(CurrentSH2, 0, CurrentSH2->bp.BreakpointUserData); CurrentSH2->bp.inbreakpoint = 0; } CurrentSH2->bp.memorybreakpoint[i].oldwriteword(addr, val); return; } } // Use the closest match if address doesn't match for (i = 0; i < MSH2->bp.nummemorybreakpoints; i++) { if (((MSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) { MSH2->bp.memorybreakpoint[i].oldwriteword(addr, val); return; } } for (i = 0; i < SSH2->bp.nummemorybreakpoints; i++) { if (((SSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) { SSH2->bp.memorybreakpoint[i].oldwriteword(addr, val); return; } } } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL SH2MemoryBreakpointWriteLong(u32 addr, u32 val) { int i; for (i = 0; i < CurrentSH2->bp.nummemorybreakpoints; i++) { if (CurrentSH2->bp.memorybreakpoint[i].addr == (addr & 0x0FFFFFFF)) { if (CurrentSH2->bp.BreakpointCallBack && CurrentSH2->bp.inbreakpoint == 0) { CurrentSH2->bp.inbreakpoint = 1; CurrentSH2->bp.BreakpointCallBack(CurrentSH2, 0, CurrentSH2->bp.BreakpointUserData); CurrentSH2->bp.inbreakpoint = 0; } CurrentSH2->bp.memorybreakpoint[i].oldwritelong(addr, val); return; } } // Use the closest match if address doesn't match for (i = 0; i < MSH2->bp.nummemorybreakpoints; i++) { if (((MSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) { MSH2->bp.memorybreakpoint[i].oldwritelong(addr, val); return; } } for (i = 0; i < SSH2->bp.nummemorybreakpoints; i++) { if (((SSH2->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) { SSH2->bp.memorybreakpoint[i].oldwritelong(addr, val); return; } } } ////////////////////////////////////////////////////////////////////////////// static int CheckForMemoryBreakpointDupes(SH2_struct *context, u32 addr, u32 flag, int *which) { int i; for (i = 0; i < context->bp.nummemorybreakpoints; i++) { if (((context->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((addr >> 16) & 0xFFF)) { // See it actually was using the same operation flag if (context->bp.memorybreakpoint[i].flags & flag) { *which = i; return 1; } } } return 0; } ////////////////////////////////////////////////////////////////////////////// int SH2AddMemoryBreakpoint(SH2_struct *context, u32 addr, u32 flags) { int which; int i; if (flags == 0) return -1; if (context->bp.nummemorybreakpoints < MAX_BREAKPOINTS) { // Only regular addresses are supported at this point(Sorry, no onchip!) switch (addr >> 29) { case 0x0: case 0x1: case 0x5: break; default: return -1; } addr &= 0x0FFFFFFF; // Make sure it isn't already on the list for (i = 0; i < context->bp.nummemorybreakpoints; i++) { if (addr == context->bp.memorybreakpoint[i].addr) return -1; } context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].addr = addr; context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].flags = flags; context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldreadbyte = ReadByteList[(addr >> 16) & 0xFFF]; context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldreadword = ReadWordList[(addr >> 16) & 0xFFF]; context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldreadlong = ReadLongList[(addr >> 16) & 0xFFF]; context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldwritebyte = WriteByteList[(addr >> 16) & 0xFFF]; context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldwriteword = WriteWordList[(addr >> 16) & 0xFFF]; context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldwritelong = WriteLongList[(addr >> 16) & 0xFFF]; if (flags & BREAK_BYTEREAD) { // Make sure function isn't already being breakpointed by another breakpoint if (!CheckForMemoryBreakpointDupes(context, addr, BREAK_BYTEREAD, &which)) ReadByteList[(addr >> 16) & 0xFFF] = &SH2MemoryBreakpointReadByte; else // fix old memory access function context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldreadbyte = context->bp.memorybreakpoint[which].oldreadbyte; } if (flags & BREAK_WORDREAD) { // Make sure function isn't already being breakpointed by another breakpoint if (!CheckForMemoryBreakpointDupes(context, addr, BREAK_WORDREAD, &which)) ReadWordList[(addr >> 16) & 0xFFF] = &SH2MemoryBreakpointReadWord; else // fix old memory access function context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldreadword = context->bp.memorybreakpoint[which].oldreadword; } if (flags & BREAK_LONGREAD) { // Make sure function isn't already being breakpointed by another breakpoint if (!CheckForMemoryBreakpointDupes(context, addr, BREAK_LONGREAD, &which)) ReadLongList[(addr >> 16) & 0xFFF] = &SH2MemoryBreakpointReadLong; else // fix old memory access function context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldreadword = context->bp.memorybreakpoint[which].oldreadword; } if (flags & BREAK_BYTEWRITE) { // Make sure function isn't already being breakpointed by another breakpoint if (!CheckForMemoryBreakpointDupes(context, addr, BREAK_BYTEWRITE, &which)) WriteByteList[(addr >> 16) & 0xFFF] = &SH2MemoryBreakpointWriteByte; else // fix old memory access function context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldwritebyte = context->bp.memorybreakpoint[which].oldwritebyte; } if (flags & BREAK_WORDWRITE) { // Make sure function isn't already being breakpointed by another breakpoint if (!CheckForMemoryBreakpointDupes(context, addr, BREAK_WORDWRITE, &which)) WriteWordList[(addr >> 16) & 0xFFF] = &SH2MemoryBreakpointWriteWord; else // fix old memory access function context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldwriteword = context->bp.memorybreakpoint[which].oldwriteword; } if (flags & BREAK_LONGWRITE) { // Make sure function isn't already being breakpointed by another breakpoint if (!CheckForMemoryBreakpointDupes(context, addr, BREAK_LONGWRITE, &which)) WriteLongList[(addr >> 16) & 0xFFF] = &SH2MemoryBreakpointWriteLong; else // fix old memory access function context->bp.memorybreakpoint[context->bp.nummemorybreakpoints].oldwritelong = context->bp.memorybreakpoint[which].oldwritelong; } context->bp.nummemorybreakpoints++; return 0; } return -1; } ////////////////////////////////////////////////////////////////////////////// static void SH2SortMemoryBreakpoints(SH2_struct *context) { int i, i2; memorybreakpoint_struct tmp; for (i = 0; i < (MAX_BREAKPOINTS-1); i++) { for (i2 = i+1; i2 < MAX_BREAKPOINTS; i2++) { if (context->bp.memorybreakpoint[i].addr == 0xFFFFFFFF && context->bp.memorybreakpoint[i2].addr != 0xFFFFFFFF) { memcpy(&tmp, context->bp.memorybreakpoint+i, sizeof(memorybreakpoint_struct)); memcpy(context->bp.memorybreakpoint+i, context->bp.memorybreakpoint+i2, sizeof(memorybreakpoint_struct)); memcpy(context->bp.memorybreakpoint+i2, &tmp, sizeof(memorybreakpoint_struct)); } } } } ////////////////////////////////////////////////////////////////////////////// int SH2DelMemoryBreakpoint(SH2_struct *context, u32 addr) { int i, i2; if (context->bp.nummemorybreakpoints > 0) { for (i = 0; i < context->bp.nummemorybreakpoints; i++) { if (context->bp.memorybreakpoint[i].addr == addr) { // Remove memory access piggyback function to memory access function table // Make sure no other breakpoints need the breakpoint functions first for (i2 = 0; i2 < context->bp.nummemorybreakpoints; i2++) { if (((context->bp.memorybreakpoint[i].addr >> 16) & 0xFFF) == ((context->bp.memorybreakpoint[i2].addr >> 16) & 0xFFF) && i != i2) { // Clear the flags context->bp.memorybreakpoint[i].flags &= ~context->bp.memorybreakpoint[i2].flags; } } if (context->bp.memorybreakpoint[i].flags & BREAK_BYTEREAD) ReadByteList[(addr >> 16) & 0xFFF] = context->bp.memorybreakpoint[i].oldreadbyte; if (context->bp.memorybreakpoint[i].flags & BREAK_WORDREAD) ReadWordList[(addr >> 16) & 0xFFF] = context->bp.memorybreakpoint[i].oldreadword; if (context->bp.memorybreakpoint[i].flags & BREAK_LONGREAD) ReadLongList[(addr >> 16) & 0xFFF] = context->bp.memorybreakpoint[i].oldreadlong; if (context->bp.memorybreakpoint[i].flags & BREAK_BYTEWRITE) WriteByteList[(addr >> 16) & 0xFFF] = context->bp.memorybreakpoint[i].oldwritebyte; if (context->bp.memorybreakpoint[i].flags & BREAK_WORDWRITE) WriteWordList[(addr >> 16) & 0xFFF] = context->bp.memorybreakpoint[i].oldwriteword; if (context->bp.memorybreakpoint[i].flags & BREAK_LONGWRITE) WriteLongList[(addr >> 16) & 0xFFF] = context->bp.memorybreakpoint[i].oldwritelong; context->bp.memorybreakpoint[i].addr = 0xFFFFFFFF; SH2SortMemoryBreakpoints(context); context->bp.nummemorybreakpoints--; return 0; } } } return -1; } ////////////////////////////////////////////////////////////////////////////// memorybreakpoint_struct *SH2GetMemoryBreakpointList(SH2_struct *context) { return context->bp.memorybreakpoint; } ////////////////////////////////////////////////////////////////////////////// void SH2ClearMemoryBreakpoints(SH2_struct *context) { int i; for (i = 0; i < MAX_BREAKPOINTS; i++) { context->bp.memorybreakpoint[i].addr = 0xFFFFFFFF; context->bp.memorybreakpoint[i].flags = 0; context->bp.memorybreakpoint[i].oldreadbyte = NULL; context->bp.memorybreakpoint[i].oldreadword = NULL; context->bp.memorybreakpoint[i].oldreadlong = NULL; context->bp.memorybreakpoint[i].oldwritebyte = NULL; context->bp.memorybreakpoint[i].oldwriteword = NULL; context->bp.memorybreakpoint[i].oldwritelong = NULL; } context->bp.nummemorybreakpoints = 0; } ////////////////////////////////////////////////////////////////////////////// void SH2HandleBackTrace(SH2_struct *context) { u16 inst = context->instruction; if ((inst & 0xF000) == 0xB000 || // BSR (inst & 0xF0FF) == 0x0003 || // BSRF (inst & 0xF0FF) == 0x400B) // JSR { if (context->bt.numbacktrace < sizeof(context->bt.addr)/sizeof(u32)) { context->bt.addr[context->bt.numbacktrace] = context->regs.PC; context->bt.numbacktrace++; } } else if (inst == 0x000B) // RTS { if (context->bt.numbacktrace > 0) context->bt.numbacktrace--; } } ////////////////////////////////////////////////////////////////////////////// u32 *SH2GetBacktraceList(SH2_struct *context, int *size) { *size = context->bt.numbacktrace; return context->bt.addr; } ////////////////////////////////////////////////////////////////////////////// void SH2HandleStepOverOut(SH2_struct *context) { if (context->stepOverOut.enabled) { switch ((int)context->stepOverOut.type) { case SH2ST_STEPOVER: // Step Over if (context->regs.PC == context->stepOverOut.address) { context->stepOverOut.enabled = 0; context->stepOverOut.callBack(context, context->regs.PC, (void *)context->stepOverOut.type); } break; case SH2ST_STEPOUT: // Step Out { u16 inst; if (context->stepOverOut.levels < 0 && context->regs.PC == context->regs.PR) { context->stepOverOut.enabled = 0; context->stepOverOut.callBack(context, context->regs.PC, (void *)context->stepOverOut.type); return; } inst = context->instruction;; if ((inst & 0xF000) == 0xB000 || // BSR (inst & 0xF0FF) == 0x0003 || // BSRF (inst & 0xF0FF) == 0x400B) // JSR context->stepOverOut.levels++; else if (inst == 0x000B || // RTS inst == 0x002B) // RTE context->stepOverOut.levels--; break; } default: break; } } } ////////////////////////////////////////////////////////////////////////////// void SH2HandleTrackInfLoop(SH2_struct *context) { if (context->trackInfLoop.enabled) { // Look for specific bf/bt/bra instructions that branch to address < PC if ((context->instruction & 0x8B80) == 0x8B80 || // bf (context->instruction & 0x8F80) == 0x8F80 || // bf/s (context->instruction & 0x8980) == 0x8980 || // bt (context->instruction & 0x8D80) == 0x8D80 || // bt/s (context->instruction & 0xA800) == 0xA800) // bra { int i; // See if it's already on match list for (i = 0; i < context->trackInfLoop.num; i++) { if (context->regs.PC == context->trackInfLoop.match[i].addr) { context->trackInfLoop.match[i].count++; return; } } if (context->trackInfLoop.num >= context->trackInfLoop.maxNum) { context->trackInfLoop.match = realloc(context->trackInfLoop.match, sizeof(tilInfo_struct) * (context->trackInfLoop.maxNum * 2)); context->trackInfLoop.maxNum *= 2; } // Add new i=context->trackInfLoop.num; context->trackInfLoop.match[i].addr = context->regs.PC; context->trackInfLoop.match[i].count = 1; context->trackInfLoop.num++; } } } ////////////////////////////////////////////////////////////////////////////// // Onchip specific ////////////////////////////////////////////////////////////////////////////// void OnchipReset(SH2_struct *context) { context->onchip.SMR = 0x00; context->onchip.BRR = 0xFF; context->onchip.SCR = 0x00; context->onchip.TDR = 0xFF; context->onchip.SSR = 0x84; context->onchip.RDR = 0x00; context->onchip.TIER = 0x01; context->onchip.FTCSR = 0x00; context->onchip.FRC.all = 0x0000; context->onchip.OCRA = 0xFFFF; context->onchip.OCRB = 0xFFFF; context->onchip.TCR = 0x00; context->onchip.TOCR = 0xE0; context->onchip.FICR = 0x0000; context->onchip.IPRB = 0x0000; context->onchip.VCRA = 0x0000; context->onchip.VCRB = 0x0000; context->onchip.VCRC = 0x0000; context->onchip.VCRD = 0x0000; context->onchip.DRCR0 = 0x00; context->onchip.DRCR1 = 0x00; context->onchip.WTCSR = 0x18; context->onchip.WTCNT = 0x00; context->onchip.RSTCSR = 0x1F; context->onchip.SBYCR = 0x60; context->onchip.CCR = 0x00; context->onchip.ICR = 0x0000; context->onchip.IPRA = 0x0000; context->onchip.VCRWDT = 0x0000; context->onchip.DVCR = 0x00000000; context->onchip.VCRDIV = 0x00000000; context->onchip.BARA.all = 0x00000000; context->onchip.BAMRA.all = 0x00000000; context->onchip.BBRA = 0x0000; context->onchip.BARB.all = 0x00000000; context->onchip.BAMRB.all = 0x00000000; context->onchip.BDRB.all = 0x00000000; context->onchip.BDMRB.all = 0x00000000; context->onchip.BBRB = 0x0000; context->onchip.BRCR = 0x0000; context->onchip.CHCR0 = 0x00000000; context->onchip.CHCR1 = 0x00000000; context->onchip.DMAOR = 0x00000000; context->onchip.BCR1 &= 0x8000; // preserve MASTER bit context->onchip.BCR1 |= 0x03F0; context->onchip.BCR2 = 0x00FC; context->onchip.WCR = 0xAAFF; context->onchip.MCR = 0x0000; context->onchip.RTCSR = 0x0000; context->onchip.RTCNT = 0x0000; context->onchip.RTCOR = 0x0000; } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL OnchipReadByte(u32 addr) { switch(addr) { case 0x000: // LOG("Serial Mode Register read: %02X\n", CurrentSH2->onchip.SMR); return CurrentSH2->onchip.SMR; case 0x001: // LOG("Bit Rate Register read: %02X\n", CurrentSH2->onchip.BRR); return CurrentSH2->onchip.BRR; case 0x002: // LOG("Serial Control Register read: %02X\n", CurrentSH2->onchip.SCR); return CurrentSH2->onchip.SCR; case 0x003: // LOG("Transmit Data Register read: %02X\n", CurrentSH2->onchip.TDR); return CurrentSH2->onchip.TDR; case 0x004: // LOG("Serial Status Register read: %02X\n", CurrentSH2->onchip.SSR); /* // if Receiver is enabled, clear SSR's TDRE bit, set SSR's RDRF and update RDR. if (CurrentSH2->onchip.SCR & 0x10) { CurrentSH2->onchip.RDR = SCIReceiveByte(); CurrentSH2->onchip.SSR = (CurrentSH2->onchip.SSR & 0x7F) | 0x40; } // if Transmitter is enabled, clear SSR's RDRF bit, and set SSR's TDRE bit. else if (CurrentSH2->onchip.SCR & 0x20) { CurrentSH2->onchip.SSR = (CurrentSH2->onchip.SSR & 0xBF) | 0x80; } */ return CurrentSH2->onchip.SSR; case 0x005: // LOG("Receive Data Register read: %02X PC = %08X\n", CurrentSH2->onchip.RDR, SH2Core->GetPC(CurrentSH2)); return CurrentSH2->onchip.RDR; case 0x010: return CurrentSH2->onchip.TIER; case 0x011: return CurrentSH2->onchip.FTCSR; case 0x012: return CurrentSH2->onchip.FRC.part.H; case 0x013: return CurrentSH2->onchip.FRC.part.L; case 0x014: if (!(CurrentSH2->onchip.TOCR & 0x10)) return CurrentSH2->onchip.OCRA >> 8; else return CurrentSH2->onchip.OCRB >> 8; case 0x015: if (!(CurrentSH2->onchip.TOCR & 0x10)) return CurrentSH2->onchip.OCRA & 0xFF; else return CurrentSH2->onchip.OCRB & 0xFF; case 0x016: return CurrentSH2->onchip.TCR; case 0x017: return CurrentSH2->onchip.TOCR; case 0x018: return CurrentSH2->onchip.FICR >> 8; case 0x019: return CurrentSH2->onchip.FICR & 0xFF; case 0x060: return CurrentSH2->onchip.IPRB >> 8; case 0x062: return CurrentSH2->onchip.VCRA >> 8; case 0x063: return CurrentSH2->onchip.VCRA & 0xFF; case 0x064: return CurrentSH2->onchip.VCRB >> 8; case 0x065: return CurrentSH2->onchip.VCRB & 0xFF; case 0x066: return CurrentSH2->onchip.VCRC >> 8; case 0x067: return CurrentSH2->onchip.VCRC & 0xFF; case 0x068: return CurrentSH2->onchip.VCRD >> 8; case 0x080: return CurrentSH2->onchip.WTCSR; case 0x081: return CurrentSH2->onchip.WTCNT; case 0x092: return CurrentSH2->onchip.CCR; case 0x0E0: return CurrentSH2->onchip.ICR >> 8; case 0x0E1: return CurrentSH2->onchip.ICR & 0xFF; case 0x0E2: return CurrentSH2->onchip.IPRA >> 8; case 0x0E3: return CurrentSH2->onchip.IPRA & 0xFF; case 0x0E4: return CurrentSH2->onchip.VCRWDT >> 8; case 0x0E5: return CurrentSH2->onchip.VCRWDT & 0xFF; default: LOG("Unhandled Onchip byte read %08X\n", (int)addr); break; } return 0; } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL OnchipReadWord(u32 addr) { switch(addr) { case 0x060: return CurrentSH2->onchip.IPRB; case 0x062: return CurrentSH2->onchip.VCRA; case 0x064: return CurrentSH2->onchip.VCRB; case 0x066: return CurrentSH2->onchip.VCRC; case 0x068: return CurrentSH2->onchip.VCRD; case 0x0E0: return CurrentSH2->onchip.ICR; case 0x0E2: return CurrentSH2->onchip.IPRA; case 0x0E4: return CurrentSH2->onchip.VCRWDT; case 0x1E2: // real BCR1 register is located at 0x1E2-0x1E3; Sega Rally OK return CurrentSH2->onchip.BCR1; case 0x1E6: return CurrentSH2->onchip.BCR2; case 0x1EA: return CurrentSH2->onchip.WCR; case 0x1EE: return CurrentSH2->onchip.MCR; case 0x1F2: return CurrentSH2->onchip.RTCSR; case 0x1F6: return CurrentSH2->onchip.RTCNT; case 0x1FA: return CurrentSH2->onchip.RTCOR; default: LOG("Unhandled Onchip word read %08X\n", (int)addr); return 0; } return 0; } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL OnchipReadLong(u32 addr) { switch(addr) { case 0x100: case 0x120: return CurrentSH2->onchip.DVSR; case 0x104: // DVDNT case 0x124: return CurrentSH2->onchip.DVDNTL; case 0x108: case 0x128: return CurrentSH2->onchip.DVCR; case 0x10C: case 0x12C: return CurrentSH2->onchip.VCRDIV; case 0x110: case 0x130: return CurrentSH2->onchip.DVDNTH; case 0x114: case 0x134: return CurrentSH2->onchip.DVDNTL; case 0x118: // Acts as a separate register, but is set to the same value case 0x138: // as DVDNTH after division return CurrentSH2->onchip.DVDNTUH; case 0x11C: // Acts as a separate register, but is set to the same value case 0x13C: // as DVDNTL after division return CurrentSH2->onchip.DVDNTUL; case 0x180: return CurrentSH2->onchip.SAR0; case 0x184: return CurrentSH2->onchip.DAR0; case 0x188: return CurrentSH2->onchip.TCR0; case 0x18C: return CurrentSH2->onchip.CHCR0; case 0x190: return CurrentSH2->onchip.SAR1; case 0x194: return CurrentSH2->onchip.DAR1; case 0x198: return CurrentSH2->onchip.TCR1; case 0x19C: return CurrentSH2->onchip.CHCR1; case 0x1A0: return CurrentSH2->onchip.VCRDMA0; case 0x1A8: return CurrentSH2->onchip.VCRDMA1; case 0x1B0: return CurrentSH2->onchip.DMAOR; case 0x1E0: return CurrentSH2->onchip.BCR1; case 0x1E4: return CurrentSH2->onchip.BCR2; case 0x1E8: return CurrentSH2->onchip.WCR; case 0x1EC: return CurrentSH2->onchip.MCR; case 0x1F0: return CurrentSH2->onchip.RTCSR; case 0x1F4: return CurrentSH2->onchip.RTCNT; case 0x1F8: return CurrentSH2->onchip.RTCOR; default: LOG("Unhandled Onchip long read %08X\n", (int)addr); return 0; } return 0; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL OnchipWriteByte(u32 addr, u8 val) { switch(addr) { case 0x000: // LOG("Serial Mode Register write: %02X\n", val); CurrentSH2->onchip.SMR = val; return; case 0x001: // LOG("Bit Rate Register write: %02X\n", val); CurrentSH2->onchip.BRR = val; return; case 0x002: // LOG("Serial Control Register write: %02X\n", val); // If Transmitter is getting disabled, set TDRE if (!(val & 0x20)) CurrentSH2->onchip.SSR |= 0x80; CurrentSH2->onchip.SCR = val; return; case 0x003: // LOG("Transmit Data Register write: %02X. PC = %08X\n", val, SH2Core->GetPC(CurrentSH2)); CurrentSH2->onchip.TDR = val; return; case 0x004: // LOG("Serial Status Register write: %02X\n", val); if (CurrentSH2->onchip.SCR & 0x20) { // Transmitter Mode // If the TDRE bit cleared, let's do a transfer if (!(val & 0x80)) SCITransmitByte(CurrentSH2->onchip.TDR); // Generate an interrupt if need be here } return; case 0x010: CurrentSH2->onchip.TIER = (val & 0x8E) | 0x1; return; case 0x011: CurrentSH2->onchip.FTCSR = (CurrentSH2->onchip.FTCSR & (val & 0xFE)) | (val & 0x1); return; case 0x012: CurrentSH2->onchip.FRC.part.H = val; return; case 0x013: CurrentSH2->onchip.FRC.part.L = val; return; case 0x014: if (!(CurrentSH2->onchip.TOCR & 0x10)) CurrentSH2->onchip.OCRA = (val << 8) | (CurrentSH2->onchip.OCRA & 0xFF); else CurrentSH2->onchip.OCRB = (val << 8) | (CurrentSH2->onchip.OCRB & 0xFF); return; case 0x015: if (!(CurrentSH2->onchip.TOCR & 0x10)) CurrentSH2->onchip.OCRA = (CurrentSH2->onchip.OCRA & 0xFF00) | val; else CurrentSH2->onchip.OCRB = (CurrentSH2->onchip.OCRB & 0xFF00) | val; return; case 0x016: CurrentSH2->onchip.TCR = val & 0x83; switch (val & 3) { case 0: CurrentSH2->frc.shift = 3; break; case 1: CurrentSH2->frc.shift = 5; break; case 2: CurrentSH2->frc.shift = 7; break; case 3: LOG("FRT external input clock not implemented.\n"); break; } return; case 0x017: CurrentSH2->onchip.TOCR = 0xE0 | (val & 0x13); return; case 0x060: CurrentSH2->onchip.IPRB = (val << 8); return; case 0x061: return; case 0x062: CurrentSH2->onchip.VCRA = ((val & 0x7F) << 8) | (CurrentSH2->onchip.VCRA & 0x00FF); return; case 0x063: CurrentSH2->onchip.VCRA = (CurrentSH2->onchip.VCRA & 0xFF00) | (val & 0x7F); return; case 0x064: CurrentSH2->onchip.VCRB = ((val & 0x7F) << 8) | (CurrentSH2->onchip.VCRB & 0x00FF); return; case 0x065: CurrentSH2->onchip.VCRB = (CurrentSH2->onchip.VCRB & 0xFF00) | (val & 0x7F); return; case 0x066: CurrentSH2->onchip.VCRC = ((val & 0x7F) << 8) | (CurrentSH2->onchip.VCRC & 0x00FF); return; case 0x067: CurrentSH2->onchip.VCRC = (CurrentSH2->onchip.VCRC & 0xFF00) | (val & 0x7F); return; case 0x068: CurrentSH2->onchip.VCRD = (val & 0x7F) << 8; return; case 0x069: return; case 0x071: CurrentSH2->onchip.DRCR0 = val & 0x3; return; case 0x072: CurrentSH2->onchip.DRCR1 = val & 0x3; return; case 0x091: CurrentSH2->onchip.SBYCR = val & 0xDF; return; case 0x092: CurrentSH2->onchip.CCR = val & 0xCF; return; case 0x0E0: CurrentSH2->onchip.ICR = ((val & 0x1) << 8) | (CurrentSH2->onchip.ICR & 0xFEFF); return; case 0x0E1: CurrentSH2->onchip.ICR = (CurrentSH2->onchip.ICR & 0xFFFE) | (val & 0x1); return; case 0x0E2: CurrentSH2->onchip.IPRA = (val << 8) | (CurrentSH2->onchip.IPRA & 0x00FF); return; case 0x0E3: CurrentSH2->onchip.IPRA = (CurrentSH2->onchip.IPRA & 0xFF00) | (val & 0xF0); return; case 0x0E4: CurrentSH2->onchip.VCRWDT = ((val & 0x7F) << 8) | (CurrentSH2->onchip.VCRWDT & 0x00FF); return; case 0x0E5: CurrentSH2->onchip.VCRWDT = (CurrentSH2->onchip.VCRWDT & 0xFF00) | (val & 0x7F); return; default: LOG("Unhandled Onchip byte write %08X\n", (int)addr); } } ////////////////////////////////////////////////////////////////////////////// void FASTCALL OnchipWriteWord(u32 addr, u16 val) { switch(addr) { case 0x060: CurrentSH2->onchip.IPRB = val & 0xFF00; return; case 0x062: CurrentSH2->onchip.VCRA = val & 0x7F7F; return; case 0x064: CurrentSH2->onchip.VCRB = val & 0x7F7F; return; case 0x066: CurrentSH2->onchip.VCRC = val & 0x7F7F; return; case 0x068: CurrentSH2->onchip.VCRD = val & 0x7F7F; return; case 0x080: // This and RSTCSR have got to be the most wackiest register // mappings I've ever seen if (val >> 8 == 0xA5) { // WTCSR switch (val & 7) { case 0: CurrentSH2->wdt.shift = 1; break; case 1: CurrentSH2->wdt.shift = 6; break; case 2: CurrentSH2->wdt.shift = 7; break; case 3: CurrentSH2->wdt.shift = 8; break; case 4: CurrentSH2->wdt.shift = 9; break; case 5: CurrentSH2->wdt.shift = 10; break; case 6: CurrentSH2->wdt.shift = 12; break; case 7: CurrentSH2->wdt.shift = 13; break; } CurrentSH2->wdt.isenable = (val & 0x20); CurrentSH2->wdt.isinterval = (~val & 0x40); CurrentSH2->onchip.WTCSR = (u8)val | 0x18; } else if (val >> 8 == 0x5A) { // WTCNT CurrentSH2->onchip.WTCNT = (u8)val; } return; case 0x082: if (val == 0xA500) // clear WOVF bit CurrentSH2->onchip.RSTCSR &= 0x7F; else if (val >> 8 == 0x5A) // RSTE and RSTS bits CurrentSH2->onchip.RSTCSR = (CurrentSH2->onchip.RSTCSR & 0x80) | (val & 0x60) | 0x1F; return; case 0x092: CurrentSH2->onchip.CCR = val & 0xCF; return; case 0x0E0: CurrentSH2->onchip.ICR = val & 0x0101; return; case 0x0E2: CurrentSH2->onchip.IPRA = val & 0xFFF0; return; case 0x0E4: CurrentSH2->onchip.VCRWDT = val & 0x7F7F; return; case 0x108: case 0x128: CurrentSH2->onchip.DVCR = val & 0x3; return; case 0x148: CurrentSH2->onchip.BBRA = val & 0xFF; return; case 0x178: CurrentSH2->onchip.BRCR = val & 0xF4DC; return; default: LOG("Unhandled Onchip word write %08X(%04X)\n", (int)addr, val); return; } } ////////////////////////////////////////////////////////////////////////////// void FASTCALL OnchipWriteLong(u32 addr, u32 val) { switch (addr) { case 0x100: case 0x120: CurrentSH2->onchip.DVSR = val; return; case 0x104: // 32-bit / 32-bit divide operation case 0x124: { s32 divisor = (s32) CurrentSH2->onchip.DVSR; if (divisor == 0) { // Regardless of what DVDNTL is set to, the top 3 bits // are used to create the new DVDNTH value if (val & 0x80000000) { CurrentSH2->onchip.DVDNTL = 0x80000000; CurrentSH2->onchip.DVDNTH = 0xFFFFFFFC | ((val >> 29) & 0x3); } else { CurrentSH2->onchip.DVDNTL = 0x7FFFFFFF; CurrentSH2->onchip.DVDNTH = 0 | (val >> 29); } CurrentSH2->onchip.DVDNTUL = CurrentSH2->onchip.DVDNTL; CurrentSH2->onchip.DVDNTUH = CurrentSH2->onchip.DVDNTH; CurrentSH2->onchip.DVCR |= 1; if (CurrentSH2->onchip.DVCR & 0x2) SH2SendInterrupt(CurrentSH2, CurrentSH2->onchip.VCRDIV & 0x7F, (MSH2->onchip.IPRA >> 12) & 0xF); } else { s32 quotient = ((s32) val) / divisor; s32 remainder = ((s32) val) % divisor; CurrentSH2->onchip.DVDNTL = quotient; CurrentSH2->onchip.DVDNTUL = quotient; CurrentSH2->onchip.DVDNTH = remainder; CurrentSH2->onchip.DVDNTUH = remainder; } return; } case 0x108: case 0x128: CurrentSH2->onchip.DVCR = val & 0x3; return; case 0x10C: case 0x12C: CurrentSH2->onchip.VCRDIV = val & 0xFFFF; return; case 0x110: case 0x130: CurrentSH2->onchip.DVDNTH = val; return; case 0x114: case 0x134: { // 64-bit / 32-bit divide operation s32 divisor = (s32) CurrentSH2->onchip.DVSR; s64 dividend = CurrentSH2->onchip.DVDNTH; dividend <<= 32; dividend |= val; if (divisor == 0) { if (CurrentSH2->onchip.DVDNTH & 0x80000000) { CurrentSH2->onchip.DVDNTL = 0x80000000; CurrentSH2->onchip.DVDNTH = CurrentSH2->onchip.DVDNTH << 3; // fix me } else { CurrentSH2->onchip.DVDNTL = 0x7FFFFFFF; CurrentSH2->onchip.DVDNTH = CurrentSH2->onchip.DVDNTH << 3; // fix me } CurrentSH2->onchip.DVDNTUL = CurrentSH2->onchip.DVDNTL; CurrentSH2->onchip.DVDNTUH = CurrentSH2->onchip.DVDNTH; CurrentSH2->onchip.DVCR |= 1; if (CurrentSH2->onchip.DVCR & 0x2) SH2SendInterrupt(CurrentSH2, CurrentSH2->onchip.VCRDIV & 0x7F, (MSH2->onchip.IPRA >> 12) & 0xF); } else { s64 quotient = dividend / divisor; s32 remainder = dividend % divisor; if (quotient > 0x7FFFFFFF) { CurrentSH2->onchip.DVCR |= 1; CurrentSH2->onchip.DVDNTL = 0x7FFFFFFF; CurrentSH2->onchip.DVDNTH = 0xFFFFFFFE; // fix me if (CurrentSH2->onchip.DVCR & 0x2) SH2SendInterrupt(CurrentSH2, CurrentSH2->onchip.VCRDIV & 0x7F, (MSH2->onchip.IPRA >> 12) & 0xF); } else if ((s32)(quotient >> 32) < -1) { CurrentSH2->onchip.DVCR |= 1; CurrentSH2->onchip.DVDNTL = 0x80000000; CurrentSH2->onchip.DVDNTH = 0xFFFFFFFE; // fix me if (CurrentSH2->onchip.DVCR & 0x2) SH2SendInterrupt(CurrentSH2, CurrentSH2->onchip.VCRDIV & 0x7F, (MSH2->onchip.IPRA >> 12) & 0xF); } else { CurrentSH2->onchip.DVDNTL = quotient; CurrentSH2->onchip.DVDNTH = remainder; } CurrentSH2->onchip.DVDNTUL = CurrentSH2->onchip.DVDNTL; CurrentSH2->onchip.DVDNTUH = CurrentSH2->onchip.DVDNTH; } return; } case 0x118: case 0x138: CurrentSH2->onchip.DVDNTUH = val; return; case 0x11C: case 0x13C: CurrentSH2->onchip.DVDNTUL = val; return; case 0x140: CurrentSH2->onchip.BARA.all = val; return; case 0x144: CurrentSH2->onchip.BAMRA.all = val; return; case 0x180: CurrentSH2->onchip.SAR0 = val; return; case 0x184: CurrentSH2->onchip.DAR0 = val; return; case 0x188: CurrentSH2->onchip.TCR0 = val & 0xFFFFFF; return; case 0x18C: CurrentSH2->onchip.CHCR0 = val & 0xFFFF; // If the DMAOR DME bit is set and AE and NMIF bits are cleared, // and CHCR's DE bit is set and TE bit is cleared, // do a dma transfer if ((CurrentSH2->onchip.DMAOR & 7) == 1 && (val & 0x3) == 1) DMAExec(); return; case 0x190: CurrentSH2->onchip.SAR1 = val; return; case 0x194: CurrentSH2->onchip.DAR1 = val; return; case 0x198: CurrentSH2->onchip.TCR1 = val & 0xFFFFFF; return; case 0x19C: CurrentSH2->onchip.CHCR1 = val & 0xFFFF; // If the DMAOR DME bit is set and AE and NMIF bits are cleared, // and CHCR's DE bit is set and TE bit is cleared, // do a dma transfer if ((CurrentSH2->onchip.DMAOR & 7) == 1 && (val & 0x3) == 1) DMAExec(); return; case 0x1A0: CurrentSH2->onchip.VCRDMA0 = val & 0xFFFF; return; case 0x1A8: CurrentSH2->onchip.VCRDMA1 = val & 0xFFFF; return; case 0x1B0: CurrentSH2->onchip.DMAOR = val & 0xF; // If the DMAOR DME bit is set and AE and NMIF bits are cleared, // and CHCR's DE bit is set and TE bit is cleared, // do a dma transfer if ((val & 7) == 1) DMAExec(); return; case 0x1E0: CurrentSH2->onchip.BCR1 &= 0x8000; CurrentSH2->onchip.BCR1 |= val & 0x1FF7; return; case 0x1E4: CurrentSH2->onchip.BCR2 = val & 0xFC; return; case 0x1E8: CurrentSH2->onchip.WCR = val; return; case 0x1EC: CurrentSH2->onchip.MCR = val & 0xFEFC; return; case 0x1F0: CurrentSH2->onchip.RTCSR = val & 0xF8; return; case 0x1F8: CurrentSH2->onchip.RTCOR = val & 0xFF; return; default: LOG("Unhandled Onchip long write %08X\n", (int)addr); break; } } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL AddressArrayReadLong(u32 addr) { return CurrentSH2->AddressArray[(addr & 0x3FC) >> 2]; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL AddressArrayWriteLong(u32 addr, u32 val) { CurrentSH2->AddressArray[(addr & 0x3FC) >> 2] = val; } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL DataArrayReadByte(u32 addr) { return T2ReadByte(CurrentSH2->DataArray, addr & 0xFFF); } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL DataArrayReadWord(u32 addr) { return T2ReadWord(CurrentSH2->DataArray, addr & 0xFFF); } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL DataArrayReadLong(u32 addr) { return T2ReadLong(CurrentSH2->DataArray, addr & 0xFFF); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL DataArrayWriteByte(u32 addr, u8 val) { T2WriteByte(CurrentSH2->DataArray, addr & 0xFFF, val); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL DataArrayWriteWord(u32 addr, u16 val) { T2WriteWord(CurrentSH2->DataArray, addr & 0xFFF, val); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL DataArrayWriteLong(u32 addr, u32 val) { T2WriteLong(CurrentSH2->DataArray, addr & 0xFFF, val); } ////////////////////////////////////////////////////////////////////////////// void FRTExec(u32 cycles) { u32 frcold; u32 frctemp; u32 mask; frcold = frctemp = (u32)CurrentSH2->onchip.FRC.all; mask = (1 << CurrentSH2->frc.shift) - 1; // Increment FRC frctemp += ((cycles + CurrentSH2->frc.leftover) >> CurrentSH2->frc.shift); CurrentSH2->frc.leftover = (cycles + CurrentSH2->frc.leftover) & mask; // Check to see if there is or was a Output Compare A match if (frctemp >= CurrentSH2->onchip.OCRA && frcold < CurrentSH2->onchip.OCRA) { // Do we need to trigger an interrupt? if (CurrentSH2->onchip.TIER & 0x8) SH2SendInterrupt(CurrentSH2, CurrentSH2->onchip.VCRC & 0x7F, (CurrentSH2->onchip.IPRB & 0xF00) >> 8); // Do we need to clear the FRC? if (CurrentSH2->onchip.FTCSR & 0x1) { frctemp = 0; CurrentSH2->frc.leftover = 0; } // Set OCFA flag CurrentSH2->onchip.FTCSR |= 0x8; } // Check to see if there is or was a Output Compare B match if (frctemp >= CurrentSH2->onchip.OCRB && frcold < CurrentSH2->onchip.OCRB) { // Do we need to trigger an interrupt? if (CurrentSH2->onchip.TIER & 0x4) SH2SendInterrupt(CurrentSH2, CurrentSH2->onchip.VCRC & 0x7F, (CurrentSH2->onchip.IPRB & 0xF00) >> 8); // Set OCFB flag CurrentSH2->onchip.FTCSR |= 0x4; } // If FRC overflows, set overflow flag if (frctemp > 0xFFFF) { // Do we need to trigger an interrupt? if (CurrentSH2->onchip.TIER & 0x2) SH2SendInterrupt(CurrentSH2, (CurrentSH2->onchip.VCRD >> 8) & 0x7F, (CurrentSH2->onchip.IPRB & 0xF00) >> 8); CurrentSH2->onchip.FTCSR |= 2; } // Write new FRC value CurrentSH2->onchip.FRC.all = frctemp; } ////////////////////////////////////////////////////////////////////////////// void WDTExec(u32 cycles) { u32 wdttemp; u32 mask; if (!CurrentSH2->wdt.isenable || CurrentSH2->onchip.WTCSR & 0x80 || CurrentSH2->onchip.RSTCSR & 0x80) return; wdttemp = (u32)CurrentSH2->onchip.WTCNT; mask = (1 << CurrentSH2->wdt.shift) - 1; wdttemp += ((cycles + CurrentSH2->wdt.leftover) >> CurrentSH2->wdt.shift); CurrentSH2->wdt.leftover = (cycles + CurrentSH2->wdt.leftover) & mask; // Are we overflowing? if (wdttemp > 0xFF) { // Obviously depending on whether or not we're in Watchdog or Interval // Modes, they'll handle an overflow differently. if (CurrentSH2->wdt.isinterval) { // Interval Timer Mode // Set OVF flag CurrentSH2->onchip.WTCSR |= 0x80; // Trigger interrupt SH2SendInterrupt(CurrentSH2, (CurrentSH2->onchip.VCRWDT >> 8) & 0x7F, (CurrentSH2->onchip.IPRA >> 4) & 0xF); } else { // Watchdog Timer Mode(untested) LOG("Watchdog timer(WDT mode) overflow not implemented\n"); } } // Write new WTCNT value CurrentSH2->onchip.WTCNT = (u8)wdttemp; } ////////////////////////////////////////////////////////////////////////////// void DMAExec(void) { // If AE and NMIF bits are set, we can't continue if (CurrentSH2->onchip.DMAOR & 0x6) return; if ((CurrentSH2->onchip.CHCR0 & 0x1) && (CurrentSH2->onchip.CHCR1 & 0x1)) { // both channel wants DMA if (CurrentSH2->onchip.DMAOR & 0x8) { // round robin priority LOG("dma\t: FIXME: two channel dma - round robin priority not properly implemented\n"); DMATransfer(&CurrentSH2->onchip.CHCR0, &CurrentSH2->onchip.SAR0, &CurrentSH2->onchip.DAR0, &CurrentSH2->onchip.TCR0, &CurrentSH2->onchip.VCRDMA0); DMATransfer(&CurrentSH2->onchip.CHCR1, &CurrentSH2->onchip.SAR1, &CurrentSH2->onchip.DAR1, &CurrentSH2->onchip.TCR1, &CurrentSH2->onchip.VCRDMA1); } else { // channel 0 > channel 1 priority DMATransfer(&CurrentSH2->onchip.CHCR0, &CurrentSH2->onchip.SAR0, &CurrentSH2->onchip.DAR0, &CurrentSH2->onchip.TCR0, &CurrentSH2->onchip.VCRDMA0); DMATransfer(&CurrentSH2->onchip.CHCR1, &CurrentSH2->onchip.SAR1, &CurrentSH2->onchip.DAR1, &CurrentSH2->onchip.TCR1, &CurrentSH2->onchip.VCRDMA1); } } else { // only one channel wants DMA if (CurrentSH2->onchip.CHCR0 & 0x1) { // DMA for channel 0 DMATransfer(&CurrentSH2->onchip.CHCR0, &CurrentSH2->onchip.SAR0, &CurrentSH2->onchip.DAR0, &CurrentSH2->onchip.TCR0, &CurrentSH2->onchip.VCRDMA0); return; } if (CurrentSH2->onchip.CHCR1 & 0x1) { // DMA for channel 1 DMATransfer(&CurrentSH2->onchip.CHCR1, &CurrentSH2->onchip.SAR1, &CurrentSH2->onchip.DAR1, &CurrentSH2->onchip.TCR1, &CurrentSH2->onchip.VCRDMA1); return; } } } ////////////////////////////////////////////////////////////////////////////// void DMATransfer(u32 *CHCR, u32 *SAR, u32 *DAR, u32 *TCR, u32 *VCRDMA) { int size; u32 i, i2; if (!(*CHCR & 0x2)) { // TE is not set int srcInc; int destInc; switch(*CHCR & 0x3000) { case 0x0000: srcInc = 0; break; case 0x1000: srcInc = 1; break; case 0x2000: srcInc = -1; break; default: srcInc = 0; break; } switch(*CHCR & 0xC000) { case 0x0000: destInc = 0; break; case 0x4000: destInc = 1; break; case 0x8000: destInc = -1; break; default: destInc = 0; break; } switch (size = ((*CHCR & 0x0C00) >> 10)) { case 0: for (i = 0; i < *TCR; i++) { MappedMemoryWriteByte(*DAR, MappedMemoryReadByte(*SAR)); *SAR += srcInc; *DAR += destInc; } *TCR = 0; break; case 1: destInc *= 2; srcInc *= 2; for (i = 0; i < *TCR; i++) { MappedMemoryWriteWord(*DAR, MappedMemoryReadWord(*SAR)); *SAR += srcInc; *DAR += destInc; } *TCR = 0; break; case 2: destInc *= 4; srcInc *= 4; for (i = 0; i < *TCR; i++) { MappedMemoryWriteLong(*DAR, MappedMemoryReadLong(*SAR)); *DAR += destInc; *SAR += srcInc; } *TCR = 0; break; case 3: destInc *= 4; srcInc *= 4; for (i = 0; i < *TCR; i+=4) { for(i2 = 0; i2 < 4; i2++) { MappedMemoryWriteLong(*DAR, MappedMemoryReadLong(*SAR)); *DAR += destInc; *SAR += srcInc; } } *TCR = 0; break; } SH2WriteNotify(destInc<0?*DAR:*DAR-i*destInc,i*abs(destInc)); } if (*CHCR & 0x4) SH2SendInterrupt(CurrentSH2, *VCRDMA, (CurrentSH2->onchip.IPRA & 0xF00) >> 8); // Set Transfer End bit *CHCR |= 0x2; } ////////////////////////////////////////////////////////////////////////////// // Input Capture Specific ////////////////////////////////////////////////////////////////////////////// void FASTCALL MSH2InputCaptureWriteWord(UNUSED u32 addr, UNUSED u16 data) { // Set Input Capture Flag MSH2->onchip.FTCSR |= 0x80; // Copy FRC register to FICR MSH2->onchip.FICR = MSH2->onchip.FRC.all; // Time for an Interrupt? if (MSH2->onchip.TIER & 0x80) SH2SendInterrupt(MSH2, (MSH2->onchip.VCRC >> 8) & 0x7F, (MSH2->onchip.IPRB >> 8) & 0xF); } ////////////////////////////////////////////////////////////////////////////// void FASTCALL SSH2InputCaptureWriteWord(UNUSED u32 addr, UNUSED u16 data) { // Set Input Capture Flag SSH2->onchip.FTCSR |= 0x80; // Copy FRC register to FICR SSH2->onchip.FICR = SSH2->onchip.FRC.all; // Time for an Interrupt? if (SSH2->onchip.TIER & 0x80) SH2SendInterrupt(SSH2, (SSH2->onchip.VCRC >> 8) & 0x7F, (SSH2->onchip.IPRB >> 8) & 0xF); } ////////////////////////////////////////////////////////////////////////////// // SCI Specific ////////////////////////////////////////////////////////////////////////////// u8 SCIReceiveByte(void) { return 0; } ////////////////////////////////////////////////////////////////////////////// void SCITransmitByte(UNUSED u8 val) { } ////////////////////////////////////////////////////////////////////////////// int SH2SaveState(SH2_struct *context, FILE *fp) { int offset; IOCheck_struct check; sh2regs_struct regs; // Write header if (context->isslave == 0) offset = StateWriteHeader(fp, "MSH2", 1); else { offset = StateWriteHeader(fp, "SSH2", 1); ywrite(&check, (void *)&yabsys.IsSSH2Running, 1, 1, fp); } // Write registers SH2GetRegisters(context, ®s); ywrite(&check, (void *)®s, sizeof(sh2regs_struct), 1, fp); // Write onchip registers ywrite(&check, (void *)&context->onchip, sizeof(Onchip_struct), 1, fp); // Write internal variables // FIXME: write the clock divisor rather than the shift amount for // backward compatibility (fix this next time the save state version // is updated) context->frc.shift = 1 << context->frc.shift; ywrite(&check, (void *)&context->frc, sizeof(context->frc), 1, fp); { u32 div = context->frc.shift; context->frc.shift = 0; while ((div >>= 1) != 0) context->frc.shift++; } context->NumberOfInterrupts = SH2Core->GetInterrupts(context, context->interrupts); ywrite(&check, (void *)context->interrupts, sizeof(interrupt_struct), MAX_INTERRUPTS, fp); ywrite(&check, (void *)&context->NumberOfInterrupts, sizeof(u32), 1, fp); ywrite(&check, (void *)context->AddressArray, sizeof(u32), 0x100, fp); ywrite(&check, (void *)context->DataArray, sizeof(u8), 0x1000, fp); ywrite(&check, (void *)&context->delay, sizeof(u32), 1, fp); ywrite(&check, (void *)&context->cycles, sizeof(u32), 1, fp); ywrite(&check, (void *)&context->isslave, sizeof(u8), 1, fp); ywrite(&check, (void *)&context->isIdle, sizeof(u8), 1, fp); ywrite(&check, (void *)&context->instruction, sizeof(u16), 1, fp); return StateFinishHeader(fp, offset); } ////////////////////////////////////////////////////////////////////////////// int SH2LoadState(SH2_struct *context, FILE *fp, UNUSED int version, int size) { IOCheck_struct check; sh2regs_struct regs; if (context->isslave == 1) yread(&check, (void *)&yabsys.IsSSH2Running, 1, 1, fp); // Read registers yread(&check, (void *)®s, sizeof(sh2regs_struct), 1, fp); SH2SetRegisters(context, ®s); // Read onchip registers yread(&check, (void *)&context->onchip, sizeof(Onchip_struct), 1, fp); // Read internal variables yread(&check, (void *)&context->frc, sizeof(context->frc), 1, fp); { // FIXME: backward compatibility hack (see SH2SaveState() comment) u32 div = context->frc.shift; context->frc.shift = 0; while ((div >>= 1) != 0) context->frc.shift++; } yread(&check, (void *)context->interrupts, sizeof(interrupt_struct), MAX_INTERRUPTS, fp); yread(&check, (void *)&context->NumberOfInterrupts, sizeof(u32), 1, fp); SH2Core->SetInterrupts(context, context->NumberOfInterrupts, context->interrupts); yread(&check, (void *)context->AddressArray, sizeof(u32), 0x100, fp); yread(&check, (void *)context->DataArray, sizeof(u8), 0x1000, fp); yread(&check, (void *)&context->delay, sizeof(u32), 1, fp); yread(&check, (void *)&context->cycles, sizeof(u32), 1, fp); yread(&check, (void *)&context->isslave, sizeof(u8), 1, fp); yread(&check, (void *)&context->isIdle, sizeof(u8), 1, fp); yread(&check, (void *)&context->instruction, sizeof(u16), 1, fp); #if defined(SH2_DYNAREC) if(SH2Core->id==2) { invalidate_all_pages(); if (context->isslave == 1) { // If the slave SH2 isn't running, make sure the dynarec stops it if(!yabsys.IsSSH2Running) SH2Core->Reset(SSH2); } } #endif return size; } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/yglshader.c000644 001750 001750 00000045741 12256006174 020341 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Guillaume Duhamel Copyright 2005-2006 Theo Berkau Copyright 2011 Shinya Miyamoto This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef HAVE_LIBGL #include #include #include "ygl.h" #include "yui.h" #include "vidshared.h" #ifdef WIN32 #include #include #elif HAVE_GLXGETPROCADDRESS #include #endif extern float vdp1wratio; extern float vdp1hratio; extern int GlHeight; extern int GlWidth; static void Ygl_printShaderError( GLuint shader ) { GLsizei bufSize; glGetShaderiv(shader, GL_INFO_LOG_LENGTH , &bufSize); if (bufSize > 1) { GLchar *infoLog; infoLog = (GLchar *)malloc(bufSize); if (infoLog != NULL) { GLsizei length; glGetShaderInfoLog(shader, bufSize, &length, infoLog); printf("Shaderlog:\n%s\n", infoLog); free(infoLog); } } } static GLuint _prgid[PG_MAX] ={0}; /*------------------------------------------------------------------------------------ * VDP1 GlowShading Operation * ----------------------------------------------------------------------------------*/ const GLchar Yglprg_vdp1_gouraudshading_v[] = \ "attribute vec4 grcolor;\n" \ "varying vec4 vtxcolor;\n" \ "void main() {\n" \ " vtxcolor=grcolor;\n" \ " gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n" \ " gl_Position = ftransform();\n" \ "}\n"; const GLchar * pYglprg_vdp1_gouraudshading_v[] = {Yglprg_vdp1_gouraudshading_v, NULL}; const GLchar Yglprg_vdp1_gouraudshading_f[] = \ "uniform sampler2D sprite;\n" \ "varying vec4 vtxcolor;\n" \ "void main() {\n" \ " vec2 addr = gl_TexCoord[0].st;\n" \ " addr.s = addr.s / (gl_TexCoord[0].q);\n" \ " addr.t = addr.t / (gl_TexCoord[0].q);\n" \ " vec4 spriteColor = texture2D(sprite,addr);\n" \ " gl_FragColor = spriteColor; \n" \ " gl_FragColor += vec4(vtxcolor.r,vtxcolor.g,vtxcolor.b,0.0);\n" \ " gl_FragColor.a = spriteColor.a;\n" \ "}\n"; const GLchar * pYglprg_vdp1_gouraudshading_f[] = {Yglprg_vdp1_gouraudshading_f, NULL}; int Ygl_uniformGlowShading(void * p ) { YglProgram * prg; prg = p; if( prg->vertexAttribute != NULL ) { glEnableVertexAttribArray(prg->vaid); glVertexAttribPointer(prg->vaid,4, GL_FLOAT, GL_FALSE, 0, prg->vertexAttribute); } return 0; } int Ygl_cleanupGlowShading(void * p ) { YglProgram * prg; prg = p; glDisableVertexAttribArray(prg->vaid); return 0; } /*------------------------------------------------------------------------------------ * VDP1 GlowShading and Half Trans Operation * ----------------------------------------------------------------------------------*/ static int id_sprite; static int id_fbo; static int id_fbowidth; static int id_fboheight; const GLchar Yglprg_vdp1_gouraudshading_hf_v[] = \ "attribute vec4 grcolor;\n" \ "varying vec4 vtxcolor;\n" \ "void main() {\n" \ " vtxcolor=grcolor;\n" \ " gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n" \ " gl_Position = ftransform();\n" \ "}\n"; const GLchar * pYglprg_vdp1_gouraudshading_hf_v[] = {Yglprg_vdp1_gouraudshading_hf_v, NULL}; const GLchar Yglprg_vdp1_gouraudshading_hf_f[] = \ "uniform sampler2D sprite;\n" \ "uniform sampler2D fbo;\n" \ "uniform int fbowidth;\n" \ "uniform int fbohegiht;\n" \ "varying vec4 vtxcolor;\n" \ "void main() {\n" \ " vec2 addr = gl_TexCoord[0].st;\n" \ " vec2 faddr = vec2( gl_FragCoord.x/float(fbowidth), gl_FragCoord.y/float(fbohegiht)); \n " \ " addr.s = addr.s / (gl_TexCoord[0].q);\n" \ " addr.t = addr.t / (gl_TexCoord[0].q);\n" \ " vec4 spriteColor = texture2D(sprite,addr);\n" \ " vec4 fboColor = texture2D(fbo,faddr);\n" \ " spriteColor += vec4(vtxcolor.r,vtxcolor.g,vtxcolor.b,0.0);\n" \ " if( fboColor.a > 0.0 && spriteColor.a > 0.0 )\n" \ " { \n " \ " gl_FragColor = spriteColor*0.5 + fboColor*0.5;\n " \ " gl_FragColor.a = fboColor.a;\n "\ " }else{\n" \ " gl_FragColor = spriteColor;\n" \ " }\n" \ "}\n"; const GLchar * pYglprg_vdp1_gouraudshading_hf_f[] = {Yglprg_vdp1_gouraudshading_hf_f, NULL}; int Ygl_uniformGlowShadingHalfTrans(void * p ) { YglProgram * prg; prg = p; if( prg->vertexAttribute != NULL ) { glEnableVertexAttribArray(prg->vaid); glVertexAttribPointer(prg->vaid,4, GL_FLOAT, GL_FALSE, 0, prg->vertexAttribute); } glUniform1i(id_sprite, 0); glUniform1i(id_fbo, 1); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D,_Ygl->vdp1FrameBuff[_Ygl->drawframe]); glUniform1i(id_fbowidth, GlWidth); glUniform1i(id_fboheight, GlHeight); glActiveTexture(GL_TEXTURE0); return 0; } int Ygl_cleanupGlowShadingHalfTrans(void * p ) { YglProgram * prg; prg = p; glDisableVertexAttribArray(prg->vaid); return 0; } /*------------------------------------------------------------------------------------ * VDP1 Half Trans Operation * ----------------------------------------------------------------------------------*/ static int id_hf_sprite; static int id_hf_fbo; static int id_hf_fbowidth; static int id_hf_fboheight; const GLchar Yglprg_vdp1_halftrans_v[] = \ "void main() {\n" \ " gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n" \ " gl_Position = ftransform();\n" \ "}\n"; const GLchar * pYglprg_vdp1_halftrans_v[] = {Yglprg_vdp1_halftrans_v, NULL}; const GLchar Yglprg_vdp1_halftrans_f[] = \ "uniform sampler2D sprite;\n" \ "uniform sampler2D fbo;\n" \ "uniform int fbowidth;\n" \ "uniform int fbohegiht;\n" \ "void main() {\n" \ " vec2 addr = gl_TexCoord[0].st;\n" \ " vec2 faddr = vec2( gl_FragCoord.x/float(fbowidth), gl_FragCoord.y/float(fbohegiht)); \n " \ " addr.s = addr.s / (gl_TexCoord[0].q);\n" \ " addr.t = addr.t / (gl_TexCoord[0].q);\n" \ " vec4 spriteColor = texture2D(sprite,addr);\n" \ " vec4 fboColor = texture2D(fbo,faddr);\n" \ " if( fboColor.a > 0.0 && spriteColor.a > 0.0 )\n" \ " { \n " \ " gl_FragColor = spriteColor*0.5 + fboColor*0.5;\n " \ " gl_FragColor.a = fboColor.a;\n "\ " }else{\n" \ " gl_FragColor = spriteColor;\n" \ " }\n" \ "}\n"; const GLchar * pYglprg_vdp1_halftrans_f[] = {Yglprg_vdp1_halftrans_f, NULL}; int Ygl_uniformHalfTrans(void * p ) { YglProgram * prg; prg = p; glUniform1i(id_hf_sprite, 0); glUniform1i(id_hf_fbo, 1); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D,_Ygl->vdp1FrameBuff[_Ygl->drawframe]); glUniform1i(id_hf_fbowidth, GlWidth); glUniform1i(id_hf_fboheight, GlHeight); glActiveTexture(GL_TEXTURE0); return 0; } int Ygl_cleanupHalfTrans(void * p ) { YglProgram * prg; prg = p; return 0; } /*------------------------------------------------------------------------------------ * VDP1 UserClip Operation * ----------------------------------------------------------------------------------*/ int Ygl_uniformStartUserClip(void * p ) { YglProgram * prg; prg = p; if( prg->ux1 != -1 ) { GLint vertices[12]; glColorMask( GL_FALSE,GL_FALSE,GL_FALSE,GL_FALSE ); glStencilMask(0xffffffff); glClearStencil(0); glClear(GL_STENCIL_BUFFER_BIT); glEnable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS,0x1,0x01); glStencilOp(GL_REPLACE,GL_REPLACE,GL_REPLACE); glDisable(GL_TEXTURE_2D); glColor4f(0.0f,0.0f,0.0f,1.0f); // render vertices[0] = (int)((float)prg->ux1 * vdp1wratio); vertices[1] = (int)((float)prg->uy1 * vdp1hratio); vertices[2] = (int)((float)(prg->ux2+1) * vdp1wratio); vertices[3] = (int)((float)prg->uy1 * vdp1hratio); vertices[4] = (int)((float)(prg->ux2+1) * vdp1wratio); vertices[5] = (int)((float)(prg->uy2+1) * vdp1hratio); vertices[6] = (int)((float)prg->ux1 * vdp1wratio); vertices[7] = (int)((float)prg->uy1 * vdp1hratio); vertices[8] = (int)((float)(prg->ux2+1) * vdp1wratio); vertices[9] = (int)((float)(prg->uy2+1) * vdp1hratio); vertices[10] = (int)((float)prg->ux1 * vdp1wratio); vertices[11] = (int)((float)(prg->uy2+1) * vdp1hratio); glVertexPointer(2, GL_INT, 0, vertices); glDrawArrays(GL_TRIANGLES, 0, 6); glColorMask( GL_TRUE,GL_TRUE,GL_TRUE,GL_TRUE ); glStencilFunc(GL_ALWAYS,0,0x0); glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); glDisable(GL_STENCIL_TEST); glEnable(GL_TEXTURE_2D); glColor4f(1.0f,1.0f,1.0f,1.0f); } glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); if( prg->uClipMode == 0x02 ) { glStencilFunc(GL_EQUAL,0x1,0xFF); }else if( prg->uClipMode == 0x03 ) { glStencilFunc(GL_EQUAL,0x0,0xFF); }else{ glStencilFunc(GL_ALWAYS,0,0xFF); } return 0; } int Ygl_cleanupStartUserClip(void * p ){return 0;} int Ygl_uniformEndUserClip(void * p ) { YglProgram * prg; prg = p; glDisable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS,0,0xFF); return 0; } int Ygl_cleanupEndUserClip(void * p ){return 0;} int Ygl_uniformStartVDP2Window(void * p ) { YglProgram * prg; prg = p; glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); if( prg->bwin0 && !prg->bwin1 ) { if( prg->logwin0 ) { glStencilFunc(GL_EQUAL,0x01,0x01); }else{ glStencilFunc(GL_NOTEQUAL,0x01,0x01); } }else if( !prg->bwin0 && prg->bwin1 ) { if( prg->logwin1 ) { glStencilFunc(GL_EQUAL,0x02,0x02); }else{ glStencilFunc(GL_NOTEQUAL,0x02,0x02); } }else if( prg->bwin0 && prg->bwin1 ) { // and if( prg->winmode == 0x0 ) { glStencilFunc(GL_EQUAL,0x03,0x03); // OR }else if( prg->winmode == 0x01 ) { glStencilFunc(GL_LEQUAL,0x01,0x03); } } return 0; } int Ygl_cleanupStartVDP2Window(void * p ){return 0;} int Ygl_uniformEndVDP2Window(void * p ) { YglProgram * prg; prg = p; glDisable(GL_STENCIL_TEST); glStencilFunc(GL_ALWAYS,0,0xFF); return 0; } int Ygl_cleanupEndVDP2Window(void * p ){return 0;} /*------------------------------------------------------------------------------------ * VDP2 Draw Frame buffer Operation * ----------------------------------------------------------------------------------*/ static int idvdp1FrameBuffer; static int idfrom; static int idto; static int idcoloroffset; const GLchar Yglprg_vdp1_drawfb_v[] = \ "void main() {\n" \ " gl_TexCoord[0] = gl_MultiTexCoord0;\n" \ " gl_Position = ftransform();\n" \ "}\n"; const GLchar * pYglprg_vdp2_drawfb_v[] = {Yglprg_vdp1_drawfb_v, NULL}; const GLchar Yglprg_vdp2_drawfb_f[] = \ "uniform sampler2D vdp1FrameBuffer;\n" \ "uniform float from; \n" \ "uniform float to; \n" \ "uniform vec4 coloroffset;\n" \ "void main() {\n" \ " vec2 addr = gl_TexCoord[0].st;\n" \ " vec4 fbColor = texture2D(vdp1FrameBuffer,addr);\n" \ " int additional = int(fbColor.a * 255.0);\n" \ " float alpha = float(int(additional/8)*8)/255.0; \n" \ " float depth = (fbColor.a-alpha)*255.0/10.0 + 0.05; \n" \ " if( depth < from || depth > to ){ discard; return; }\n" \ " gl_FragColor = fbColor; \n" \ " gl_FragColor += vec4(coloroffset.r,coloroffset.g,coloroffset.b,0.0);\n" \ " gl_FragColor.a = alpha;\n" \ " gl_FragDepth = depth;\n" \ "}\n"; const GLchar * pYglprg_vdp2_drawfb_f[] = {Yglprg_vdp2_drawfb_f, NULL}; int Ygl_uniformVDP2DrawFramebuffer( float from, float to , float * offsetcol ) { glUseProgram(_prgid[PG_VDP2_DRAWFRAMEBUFF]); glUniform1i(idvdp1FrameBuffer, 0); glActiveTexture(GL_TEXTURE0); glUniform1f(idfrom,from); glUniform1f(idto,to); glUniform4f(idcoloroffset,offsetcol[0],offsetcol[1],offsetcol[2],offsetcol[3]); return 0; } /*------------------------------------------------------------------------------------ * VDP2 Add Blend operaiotn * ----------------------------------------------------------------------------------*/ int Ygl_uniformAddBlend(void * p ) { glBlendFunc(GL_ONE,GL_ONE); return 0; } int Ygl_cleanupAddBlend(void * p ) { glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); return 0; } int YglGetProgramId( int prg ) { return _prgid[prg]; } int YglInitShader( int id, const GLchar * vertex[], const GLchar * frag[] ) { GLint compiled,linked; GLuint vshader; GLuint fshader; _prgid[id] = glCreateProgram(); if (_prgid[id] == 0 ) return -1; vshader = glCreateShader(GL_VERTEX_SHADER); fshader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(vshader, 1, vertex, NULL); glCompileShader(vshader); glGetShaderiv(vshader, GL_COMPILE_STATUS, &compiled); if (compiled == GL_FALSE) { printf( "Compile error in vertex shader.\n"); Ygl_printShaderError(vshader); _prgid[id] = 0; return -1; } glShaderSource(fshader, 1, frag, NULL); glCompileShader(fshader); glGetShaderiv(fshader, GL_COMPILE_STATUS, &compiled); if (compiled == GL_FALSE) { printf( "Compile error in fragment shader.\n"); Ygl_printShaderError(fshader); _prgid[id] = 0; return -1; } glAttachShader(_prgid[id], vshader); glAttachShader(_prgid[id], fshader); glLinkProgram(_prgid[id]); glGetProgramiv(_prgid[id], GL_LINK_STATUS, &linked); if (linked == GL_FALSE) { printf("Link error..\n"); Ygl_printShaderError(_prgid[id]); _prgid[id] = 0; return -1; } return 0; } int YglProgramInit() { // _prgid[PG_NORMAL] = 0; // if( YglInitShader( PG_VFP1_GOURAUDSAHDING, pYglprg_vdp1_gouraudshading_v, pYglprg_vdp1_gouraudshading_f ) != 0 ) return 0; _prgid[PG_VFP1_STARTUSERCLIP] = 0; _prgid[PG_VFP1_ENDUSERCLIP] = 0; _prgid[PG_VDP2_ADDBLEND] = 0; // if( YglInitShader( PG_VDP2_DRAWFRAMEBUFF, pYglprg_vdp2_drawfb_v, pYglprg_vdp2_drawfb_f ) != 0 ) return 0; idvdp1FrameBuffer = glGetUniformLocation(_prgid[PG_VDP2_DRAWFRAMEBUFF], (const GLchar *)"vdp1FrameBuffer"); idfrom = glGetUniformLocation(_prgid[PG_VDP2_DRAWFRAMEBUFF], (const GLchar *)"from"); idto = glGetUniformLocation(_prgid[PG_VDP2_DRAWFRAMEBUFF], (const GLchar *)"to"); idcoloroffset = glGetUniformLocation(_prgid[PG_VDP2_DRAWFRAMEBUFF], (const GLchar *)"coloroffset"); // if( YglInitShader( PG_VFP1_HALFTRANS, pYglprg_vdp1_halftrans_v, pYglprg_vdp1_halftrans_f ) != 0 ) return 0; id_hf_sprite = glGetUniformLocation(_prgid[PG_VFP1_HALFTRANS], (const GLchar *)"sprite"); id_hf_fbo = glGetUniformLocation(_prgid[PG_VFP1_HALFTRANS], (const GLchar *)"fbo"); id_hf_fbowidth = glGetUniformLocation(_prgid[PG_VFP1_HALFTRANS], (const GLchar *)"fbowidth"); id_hf_fboheight = glGetUniformLocation(_prgid[PG_VFP1_HALFTRANS], (const GLchar *)"fbohegiht"); if( YglInitShader( PG_VFP1_GOURAUDSAHDING_HALFTRANS, pYglprg_vdp1_gouraudshading_hf_v, pYglprg_vdp1_gouraudshading_hf_f ) != 0 ) return 0; id_sprite = glGetUniformLocation(_prgid[PG_VFP1_GOURAUDSAHDING_HALFTRANS], (const GLchar *)"sprite"); id_fbo = glGetUniformLocation(_prgid[PG_VFP1_GOURAUDSAHDING_HALFTRANS], (const GLchar *)"fbo"); id_fbowidth = glGetUniformLocation(_prgid[PG_VFP1_GOURAUDSAHDING_HALFTRANS], (const GLchar *)"fbowidth"); id_fboheight = glGetUniformLocation(_prgid[PG_VFP1_GOURAUDSAHDING_HALFTRANS], (const GLchar *)"fbohegiht"); return 0; } int YglProgramChange( YglLevel * level, int prgid ) { YglProgram* tmp; level->prgcurrent++; if( level->prgcurrent >= level->prgcount) { level->prgcount++; tmp = (YglProgram*)malloc(sizeof(YglProgram)*level->prgcount); if( tmp == NULL ) return -1; memset(tmp,0,sizeof(YglProgram)*level->prgcount); memcpy(tmp,level->prg,sizeof(YglProgram)*(level->prgcount-1)); level->prg = tmp; level->prg[level->prgcurrent].currentQuad = 0; level->prg[level->prgcurrent].maxQuad = 12 * 64; if ((level->prg[level->prgcurrent].quads = (int *) malloc(level->prg[level->prgcurrent].maxQuad * sizeof(int))) == NULL) return -1; if ((level->prg[level->prgcurrent].textcoords = (float *) malloc(level->prg[level->prgcurrent].maxQuad * sizeof(float) * 2)) == NULL) return -1; if ((level->prg[level->prgcurrent].vertexAttribute = (float *) malloc(level->prg[level->prgcurrent].maxQuad * sizeof(float)*2)) == NULL) return -1; } level->prg[level->prgcurrent].prgid=prgid; level->prg[level->prgcurrent].prg=_prgid[prgid]; if( prgid == PG_VFP1_GOURAUDSAHDING ) { GLuint id; level->prg[level->prgcurrent].setupUniform = Ygl_uniformGlowShading; level->prg[level->prgcurrent].cleanupUniform = Ygl_cleanupGlowShading; id = glGetUniformLocation(_prgid[PG_VFP1_GOURAUDSAHDING], (const GLchar *)"sprite"); glUniform1i(id, 0); level->prg[level->prgcurrent].vaid = 0; level->prg[level->prgcurrent].vaid = glGetAttribLocation(_prgid[PG_VFP1_GOURAUDSAHDING],(const GLchar *)"grcolor"); } else if( prgid == PG_VFP1_STARTUSERCLIP ) { level->prg[level->prgcurrent].setupUniform = Ygl_uniformStartUserClip; level->prg[level->prgcurrent].cleanupUniform = Ygl_cleanupStartUserClip; } else if( prgid == PG_VFP1_ENDUSERCLIP ) { level->prg[level->prgcurrent].setupUniform = Ygl_uniformEndUserClip; level->prg[level->prgcurrent].cleanupUniform = Ygl_cleanupEndUserClip; } else if( prgid == PG_VFP1_HALFTRANS ) { level->prg[level->prgcurrent].setupUniform = Ygl_uniformHalfTrans; level->prg[level->prgcurrent].cleanupUniform = Ygl_cleanupHalfTrans; } else if( prgid == PG_VFP1_GOURAUDSAHDING_HALFTRANS ) { level->prg[level->prgcurrent].setupUniform = Ygl_uniformGlowShadingHalfTrans; level->prg[level->prgcurrent].cleanupUniform = Ygl_cleanupGlowShadingHalfTrans; level->prg[level->prgcurrent].vaid = glGetAttribLocation(_prgid[PG_VFP1_GOURAUDSAHDING],(const GLchar *)"grcolor"); }else if( prgid == PG_VDP2_ADDBLEND ) { level->prg[level->prgcurrent].setupUniform = Ygl_uniformAddBlend; level->prg[level->prgcurrent].cleanupUniform = Ygl_cleanupAddBlend; }else if( prgid == PG_VDP2_STARTWINDOW ) { level->prg[level->prgcurrent].setupUniform = Ygl_uniformStartVDP2Window; level->prg[level->prgcurrent].cleanupUniform = Ygl_cleanupStartVDP2Window; }else if( prgid == PG_VDP2_ENDWINDOW ) { level->prg[level->prgcurrent].setupUniform = Ygl_uniformEndVDP2Window; level->prg[level->prgcurrent].cleanupUniform = Ygl_cleanupEndVDP2Window; }else{ level->prg[level->prgcurrent].setupUniform = NULL; level->prg[level->prgcurrent].cleanupUniform = NULL; } return 0; } #endif yabause-0.9.13.1/src/q68/q68-disasm.c000644 001750 001750 00000074166 12256006177 020677 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-disasm.c: MC68000 disassembly routines Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "q68.h" #include "q68-const.h" #include "q68-internal.h" /*************************************************************************/ /********************** 68k instruction disassembly **********************/ /*************************************************************************/ /* Disassembly table. The first entry matching a given instruction is used. */ static const struct { uint16_t mask, test; // Entry matches when (opcode & mask) == test const char *format; // Instruction format } instructions[] = { /* ALU immediate */ {0xFFFF, 0x003C, "ORI.B #,CCR"}, {0xFFC0, 0x0000, "ORI.B #,"}, {0xFFFF, 0x007C, "ORI.W #,SR"}, {0xFFC0, 0x0040, "ORI.W #,"}, {0xFFC0, 0x0080, "ORI.L #,"}, {0xFFFF, 0x023C, "ANDI.B #,CCR"}, {0xFFC0, 0x0200, "ANDI.B #,"}, {0xFFFF, 0x027C, "ANDI.W #,SR"}, {0xFFC0, 0x0240, "ANDI.W #,"}, {0xFFC0, 0x0280, "ANDI.L #,"}, {0xFFC0, 0x0400, "SUBI.B #,"}, {0xFFC0, 0x0440, "SUBI.W #,"}, {0xFFC0, 0x0480, "SUBI.L #,"}, {0xFFC0, 0x0600, "ADDI.B #,"}, {0xFFC0, 0x0640, "ADDI.W #,"}, {0xFFC0, 0x0680, "ADDI.L #,"}, {0xFFFF, 0x0A3C, "EORI.B #,CCR"}, {0xFFC0, 0x0A00, "EORI.B #,"}, {0xFFFF, 0x0A7C, "EORI.W #,SR"}, {0xFFC0, 0x0A40, "EORI.W #,"}, {0xFFC0, 0x0A80, "EORI.L #,"}, {0xFFC0, 0x0C00, "CMPI.B #,"}, {0xFFC0, 0x0C40, "CMPI.W #,"}, {0xFFC0, 0x0C80, "CMPI.L #,"}, /* Bit twiddling and MOVEP */ {0xF1F8, 0x0108, "MOVEP.W (A),D"}, {0xF1F8, 0x0148, "MOVEP.L (A),D"}, {0xF1F8, 0x0188, "MOVEP.W D,(A)"}, {0xF1F8, 0x01C8, "MOVEP.L D,(A)"}, {0xFFC0, 0x0800, "BTST #,"}, {0xFFC0, 0x0840, "BCHG #,"}, {0xFFC0, 0x0880, "BCLR #,"}, {0xFFC0, 0x08C0, "BSET #,"}, {0xF1C0, 0x0100, "BTST D,"}, {0xF1C0, 0x0140, "BCHG D,"}, {0xF1C0, 0x0180, "BCLR D,"}, {0xF1C0, 0x01C0, "BSET D,"}, /* MOVE */ {0xF1C0, 0x1040, "MOVEA.B ,A"}, {0xF000, 0x1000, "MOVE.B ,"}, {0xF1C0, 0x2040, "MOVEA.L ,A"}, {0xF000, 0x2000, "MOVE.L ,"}, {0xF1C0, 0x3040, "MOVEA.W ,A"}, {0xF000, 0x3000, "MOVE.W ,"}, /* Miscellaneous */ {0xFFC0, 0x4000, "NEGX.B "}, {0xFFC0, 0x4040, "NEGX.W "}, {0xFFC0, 0x4080, "NEGX.L "}, {0xFFC0, 0x40C0, "MOVE.W SR,"}, {0xFFC0, 0x4200, "CLR.B "}, {0xFFC0, 0x4240, "CLR.W "}, {0xFFC0, 0x4280, "CLR.L "}, {0xFFC0, 0x42C0, "???"}, {0xFFC0, 0x4400, "NEG.B "}, {0xFFC0, 0x4440, "NEG.W "}, {0xFFC0, 0x4480, "NEG.L "}, {0xFFC0, 0x44C0, "MOVE.W CCR,"}, {0xFFC0, 0x4600, "NOT.B "}, {0xFFC0, 0x4640, "NOT.W "}, {0xFFC0, 0x4680, "NOT.L "}, {0xFFC0, 0x46C0, "MOVE.W ,SR"}, {0xFFF8, 0x4808, "???"}, {0xFFC0, 0x4800, "NBCD.B "}, {0xFFF8, 0x4840, "SWAP.W D"}, {0xFFF8, 0x4848, "???"}, {0xFFC0, 0x4840, "PEA.L "}, {0xFFF8, 0x4880, "EXT.W D"}, {0xFFF8, 0x48A0, "MOVEM.W ,-(A)"}, {0xFFC0, 0x4880, "MOVEM.W ,"}, {0xFFF8, 0x48C0, "EXT.L D"}, {0xFFF8, 0x48E0, "MOVEM.L ,-(A)"}, {0xFFC0, 0x48C0, "MOVEM.L ,"}, {0xFFC0, 0x4A00, "TST.B "}, {0xFFC0, 0x4A40, "TST.W "}, {0xFFC0, 0x4A80, "TST.L "}, {0xFFFF, 0x4AFC, "ILLEGAL"}, {0xFFC0, 0x4AC0, "TAS "}, {0xFFC0, 0x4C80, "MOVEM.W ,"}, {0xFFC0, 0x4CC0, "MOVEM.L ,"}, {0xFFF0, 0x4E40, "TRAP #"}, {0xFFF8, 0x4E50, "LINK #,A"}, {0xFFF8, 0x4E58, "UNLK A"}, {0xFFF8, 0x4E60, "MOVE USP,A"}, {0xFFF8, 0x4E68, "MOVE A,USP"}, {0xFFFF, 0x4E70, "RESET"}, {0xFFFF, 0x4E71, "NOP"}, {0xFFFF, 0x4E72, "STOP"}, {0xFFFF, 0x4E73, "RTE"}, {0xFFFF, 0x4E75, "RTS"}, {0xFFFF, 0x4E76, "TRAPV"}, {0xFFFF, 0x4E77, "RTR"}, {0xFFC0, 0x4E80, "JSR "}, {0xFFC0, 0x4EC0, "JMP "}, {0xF1C0, 0x4180, "CHK.W D,"}, {0xF1C0, 0x41C0, "LEA.L ,A"}, /* ADDQ/SUBQ/Scc/DBcc */ {0xF1C0, 0x5000, "ADDQ.B #,"}, {0xF1C0, 0x5040, "ADDQ.W #,"}, {0xF1C0, 0x5080, "ADDQ.L #,"}, {0xF1C0, 0x5100, "SUBQ.B #,"}, {0xF1C0, 0x5140, "SUBQ.W #,"}, {0xF1C0, 0x5180, "SUBQ.L #,"}, {0xFFF8, 0x50C8, "DBT D,"}, {0xFFC0, 0x50C0, "ST.B "}, {0xFFF8, 0x51C8, "DBRA D,"}, {0xFFC0, 0x51C0, "SF.B "}, {0xFFF8, 0x52C8, "DBHI D,"}, {0xFFC0, 0x52C0, "SHI.B "}, {0xFFF8, 0x53C8, "DBLS D,"}, {0xFFC0, 0x53C0, "SLS.B "}, {0xFFF8, 0x54C8, "DBCC D,"}, {0xFFC0, 0x54C0, "SCC.B "}, {0xFFF8, 0x55C8, "DBCS D,"}, {0xFFC0, 0x55C0, "SCS.B "}, {0xFFF8, 0x56C8, "DBNE D,"}, {0xFFC0, 0x56C0, "SNE.B "}, {0xFFF8, 0x57C8, "DBEQ D,"}, {0xFFC0, 0x57C0, "SEQ.B "}, {0xFFF8, 0x58C8, "DBVC D,"}, {0xFFC0, 0x58C0, "SVC.B "}, {0xFFF8, 0x59C8, "DBVS D,"}, {0xFFC0, 0x59C0, "SVS.B "}, {0xFFF8, 0x5AC8, "DBPL D,"}, {0xFFC0, 0x5AC0, "SPL.B "}, {0xFFF8, 0x5BC8, "DBMI D,"}, {0xFFC0, 0x5BC0, "SMI.B "}, {0xFFF8, 0x5CC8, "DBLT D,"}, {0xFFC0, 0x5CC0, "SLT.B "}, {0xFFF8, 0x5DC8, "DBGE D,"}, {0xFFC0, 0x5DC0, "SGE.B "}, {0xFFF8, 0x5EC8, "DBLE D,"}, {0xFFC0, 0x5EC0, "SLE.B "}, {0xFFF8, 0x5FC8, "DBGT D,"}, {0xFFC0, 0x5FC0, "SGT.B "}, /* BRA/BSR/Bcc */ {0xFFFF, 0x6000, "BRA.W "}, {0xFF00, 0x6000, "BRA.S "}, {0xFFFF, 0x6100, "BSR.W "}, {0xFF00, 0x6100, "BSR.S "}, {0xFFFF, 0x6200, "BHI.W "}, {0xFF00, 0x6200, "BHI.S "}, {0xFFFF, 0x6300, "BLS.W "}, {0xFF00, 0x6300, "BLS.S "}, {0xFFFF, 0x6400, "BCC.W "}, {0xFF00, 0x6400, "BCC.S "}, {0xFFFF, 0x6500, "BCS.W "}, {0xFF00, 0x6500, "BCS.S "}, {0xFFFF, 0x6600, "BNE.W "}, {0xFF00, 0x6600, "BNE.S "}, {0xFFFF, 0x6700, "BEQ.W "}, {0xFF00, 0x6700, "BEQ.S "}, {0xFFFF, 0x6800, "BVC.W "}, {0xFF00, 0x6800, "BVC.S "}, {0xFFFF, 0x6900, "BVS.W "}, {0xFF00, 0x6900, "BVS.S "}, {0xFFFF, 0x6A00, "BPL.W "}, {0xFF00, 0x6A00, "BPL.S "}, {0xFFFF, 0x6B00, "BMI.W "}, {0xFF00, 0x6B00, "BMI.S "}, {0xFFFF, 0x6C00, "BLT.W "}, {0xFF00, 0x6C00, "BLT.S "}, {0xFFFF, 0x6D00, "BGE.W "}, {0xFF00, 0x6D00, "BGE.S "}, {0xFFFF, 0x6E00, "BLE.W "}, {0xFF00, 0x6E00, "BLE.S "}, {0xFFFF, 0x6F00, "BGT.W "}, {0xFF00, 0x6F00, "BGT.S "}, /* MOVEQ */ {0xF100, 0x7000, "MOVEQ #,D"}, /* ALU non-immediate,ABCD/SBCD etc. */ {0xF1F8, 0x8100, "SBCD.B D,D"}, {0xF1F8, 0x8108, "SBCD.B -(A),-(A)"}, {0xF1F0, 0x8140, "???"}, {0xF1F0, 0x8180, "???"}, {0xF1C0, 0x8000, "OR.B ,D"}, {0xF1C0, 0x8040, "OR.W ,D"}, {0xF1C0, 0x8080, "OR.L ,D"}, {0xF1C0, 0x80C0, "DIVU ,D"}, {0xF1C0, 0x8100, "OR.B D,"}, {0xF1C0, 0x8140, "OR.W D,"}, {0xF1C0, 0x8180, "OR.L D,"}, {0xF1C0, 0x81C0, "DIVS ,D"}, {0xF1F8, 0x9100, "SUBX.B D,D"}, {0xF1F8, 0x9108, "SUBX.B -(A),-(A)"}, {0xF1F8, 0x9140, "SUBX.W D,D"}, {0xF1F8, 0x9148, "SUBX.W -(A),-(A)"}, {0xF1F8, 0x9180, "SUBX.L D,D"}, {0xF1F8, 0x9188, "SUBX.L -(A),-(A)"}, {0xF1C0, 0x9000, "SUB.B ,D"}, {0xF1C0, 0x9040, "SUB.W ,D"}, {0xF1C0, 0x9080, "SUB.L ,D"}, {0xF1C0, 0x90C0, "SUBA.W ,A"}, {0xF1C0, 0x9100, "SUB.B D,"}, {0xF1C0, 0x9140, "SUB.W D,"}, {0xF1C0, 0x9180, "SUB.L D,"}, {0xF1C0, 0x91C0, "SUBA.L ,A"}, {0xF1F8, 0xB108, "CMPM.B -(A),-(A)"}, {0xF1F8, 0xB148, "CMPM.W -(A),-(A)"}, {0xF1F8, 0xB188, "CMPM.L -(A),-(A)"}, {0xF1C0, 0xB000, "CMP.B ,D"}, {0xF1C0, 0xB040, "CMP.W ,D"}, {0xF1C0, 0xB080, "CMP.L ,D"}, {0xF1C0, 0xB0C0, "CMPA.W ,A"}, {0xF1C0, 0xB100, "CMP.B D,"}, {0xF1C0, 0xB140, "CMP.W D,"}, {0xF1C0, 0xB180, "CMP.L D,"}, {0xF1C0, 0xB1C0, "CMPA.L ,A"}, {0xF1F8, 0xC100, "ABCD.B D,D"}, {0xF1F8, 0xC108, "ABCD.B -(A),-(A)"}, {0xF1F8, 0xC140, "EXG.L D,D"}, {0xF1F8, 0xC148, "EXG.L A,A"}, {0xF1F8, 0xC180, "???"}, {0xF1F8, 0xC188, "EXG.L A,D"}, {0xF1C0, 0xC000, "AND.B ,D"}, {0xF1C0, 0xC040, "AND.W ,D"}, {0xF1C0, 0xC080, "AND.L ,D"}, {0xF1C0, 0xC0C0, "MULU ,D"}, {0xF1C0, 0xC100, "AND.B D,"}, {0xF1C0, 0xC140, "AND.W D,"}, {0xF1C0, 0xC180, "AND.L D,"}, {0xF1C0, 0xC1C0, "MULS ,D"}, {0xF1F8, 0xD100, "ADDX.B D,D"}, {0xF1F8, 0xD108, "ADDX.B -(A),-(A)"}, {0xF1F8, 0xD140, "ADDX.W D,D"}, {0xF1F8, 0xD148, "ADDX.W -(A),-(A)"}, {0xF1F8, 0xD180, "ADDX.L D,D"}, {0xF1F8, 0xD188, "ADDX.L -(A),-(A)"}, {0xF1C0, 0xD000, "ADD.B ,D"}, {0xF1C0, 0xD040, "ADD.W ,D"}, {0xF1C0, 0xD080, "ADD.L ,D"}, {0xF1C0, 0xD0C0, "ADDA.W ,A"}, {0xF1C0, 0xD100, "ADD.B D,"}, {0xF1C0, 0xD140, "ADD.W D,"}, {0xF1C0, 0xD180, "ADD.L D,"}, {0xF1C0, 0xD1C0, "ADDA.L ,A"}, /* Shift/rotate instructions */ {0xF1F8, 0xE000, "ASR.B #,D"}, {0xF1F8, 0xE008, "LSR.B #,D"}, {0xF1F8, 0xE010, "ROXR.B #,D"}, {0xF1F8, 0xE018, "ROR.B #,D"}, {0xF1F8, 0xE020, "ASR.B D,D"}, {0xF1F8, 0xE028, "LSR.B D,D"}, {0xF1F8, 0xE030, "ROXR.B D,D"}, {0xF1F8, 0xE038, "ROR.B D,D"}, {0xF1F8, 0xE040, "ASR.W #,D"}, {0xF1F8, 0xE048, "LSR.W #,D"}, {0xF1F8, 0xE050, "ROXR.W #,D"}, {0xF1F8, 0xE058, "ROR.W #,D"}, {0xF1F8, 0xE060, "ASR.W D,D"}, {0xF1F8, 0xE068, "LSR.W D,D"}, {0xF1F8, 0xE070, "ROXR.W D,D"}, {0xF1F8, 0xE078, "ROR.W D,D"}, {0xF1F8, 0xE080, "ASR.L #,D"}, {0xF1F8, 0xE088, "LSR.L #,D"}, {0xF1F8, 0xE090, "ROXR.L #,D"}, {0xF1F8, 0xE098, "ROR.L #,D"}, {0xF1F8, 0xE0A0, "ASR.L D,D"}, {0xF1F8, 0xE0A8, "LSR.L D,D"}, {0xF1F8, 0xE0B0, "ROXR.L D,D"}, {0xF1F8, 0xE0B8, "ROR.L D,D"}, {0xF1F8, 0xE100, "ASL.B #,D"}, {0xF1F8, 0xE108, "LSL.B #,D"}, {0xF1F8, 0xE110, "ROXL.B #,D"}, {0xF1F8, 0xE118, "ROL.B #,D"}, {0xF1F8, 0xE120, "ASL.B D,D"}, {0xF1F8, 0xE128, "LSL.B D,D"}, {0xF1F8, 0xE130, "ROXL.B D,D"}, {0xF1F8, 0xE138, "ROL.B D,D"}, {0xF1F8, 0xE140, "ASL.W #,D"}, {0xF1F8, 0xE148, "LSL.W #,D"}, {0xF1F8, 0xE150, "ROXL.W #,D"}, {0xF1F8, 0xE158, "ROL.W #,D"}, {0xF1F8, 0xE160, "ASL.W D,D"}, {0xF1F8, 0xE168, "LSL.W D,D"}, {0xF1F8, 0xE170, "ROXL.W D,D"}, {0xF1F8, 0xE178, "ROL.W D,D"}, {0xF1F8, 0xE180, "ASL.L #,D"}, {0xF1F8, 0xE188, "LSL.L #,D"}, {0xF1F8, 0xE190, "ROXL.L #,D"}, {0xF1F8, 0xE198, "ROL.L #,D"}, {0xF1F8, 0xE1A0, "ASL.L D,D"}, {0xF1F8, 0xE1A8, "LSL.L D,D"}, {0xF1F8, 0xE1B0, "ROXL.L D,D"}, {0xF1F8, 0xE1B8, "ROL.L D,D"}, {0xFFC0, 0xE0C0, "ASR.W "}, {0xFFC0, 0xE1C0, "ASL.W "}, {0xFFC0, 0xE2C0, "LSR.W "}, {0xFFC0, 0xE3C0, "LSL.W "}, {0xFFC0, 0xE4C0, "ROXR.W "}, {0xFFC0, 0xE5C0, "ROXL.W "}, {0xFFC0, 0xE6C0, "ROR.W "}, {0xFFC0, 0xE7C0, "ROL.W "}, }; /*************************************************************************/ /** * q68_disassemble: Disassembles the instruction at the given address. * Returns "???" if the address or opcode is invalid. * * [Parameters] * state: Processor state block * address: Address of instruction to disassemble * nwords_ret: Pointer to variable to receive length in words of the * instruction (NULL permitted) * [Return value] * String containined disassembled instruction * [Notes] * The returned string is only valid until the next call to this function. */ const char *q68_disassemble(Q68State *state, uint32_t address, int *nwords_ret) { const uint32_t base_address = address; static char outbuf[1000]; if (address % 2 != 0) { // Odd addresses are invalid if (nwords_ret) { *nwords_ret = 1; } return "???"; } uint16_t opcode = READU16(state, address); address += 2; const char *format = NULL; int i; for (i = 0; i < lenof(instructions); i++) { if ((opcode & instructions[i].mask) == instructions[i].test) { format = instructions[i].format; break; } } if (!format) { if (nwords_ret) { *nwords_ret = 1; } return "???"; } int outlen = 0; #define APPEND_CHAR(ch) do { \ if (outlen < sizeof(outbuf)-1) { \ outbuf[outlen++] = (ch); \ outbuf[outlen] = 0; \ } \ } while (0) #define APPEND(fmt,...) do { \ outlen += snprintf(&outbuf[outlen], sizeof(outbuf)-outlen, \ fmt , ## __VA_ARGS__); \ if (outlen > sizeof(outbuf)-1) { \ outlen = sizeof(outbuf)-1; \ } \ } while (0) int inpos = 0; while (format[inpos] != 0) { if (format[inpos] == '<') { char tagbuf[100]; int end = inpos+1; for (; format[end] != 0 && format[end] != '>'; end++) { if (end - (inpos+1) >= sizeof(tagbuf)) { break; } } memcpy(tagbuf, &format[inpos+1], end - (inpos+1)); tagbuf[end - (inpos+1)] = 0; if (format[end] != 0) { end++; } inpos = end; if (strncmp(tagbuf,"ea",2) == 0) { int mode, reg; char size; // 'b', 'w', or 'l' if (strncmp(tagbuf,"ea2",3) == 0) { // 2nd EA of MOVE insns mode = opcode>>6 & 7; reg = opcode>>9 & 7; size = tagbuf[4]; } else { mode = opcode>>3 & 7; reg = opcode>>0 & 7; size = tagbuf[3]; } switch (mode) { case 0: APPEND("D%d", reg); break; case 1: APPEND("A%d", reg); break; case 2: APPEND("(A%d)", reg); break; case 3: APPEND("(A%d)+", reg); break; case 4: APPEND("-(A%d)", reg); break; case 5: { int16_t disp = READS16(state, address); address += 2; APPEND("%d(A%d)", disp, reg); break; } case 6: { uint16_t ext = READU16(state, address); address += 2; const int iregtype = ext>>15; const int ireg = ext>>12 & 7; const int iregsize = ext>>11; const int8_t disp = ext & 0xFF; APPEND("%d(A%d,%c%d.%c)", disp, reg, iregtype ? 'A' : 'D', ireg, iregsize ? 'l' : 'w'); break; } case 7: switch (reg) { case 0: { const uint16_t abs = READU16(state, address); address += 2; APPEND("($%X).w", abs); break; } case 1: { const uint32_t abs = READU32(state, address); address += 4; APPEND("($%X).l", abs); break; } case 2: { int16_t disp = READS16(state, address); address += 2; APPEND("$%X(PC)", (base_address+2) + disp); break; } case 3: { uint16_t ext = READU16(state, address); address += 2; const int iregtype = ext>>15; const int ireg = ext>>12 & 7; const int iregsize = ext>>11; const int8_t disp = ext & 0xFF; APPEND("$%X(PC,%c%d.%c)", (base_address+2) + disp, iregtype ? 'A' : 'D', ireg, iregsize ? 'l' : 'w'); break; } case 4: { uint32_t imm; if (size == 'l') { imm = READU32(state, address); address += 4; } else { imm = READU16(state, address); address += 2; } APPEND("#%s%X", imm<10 ? "" : "$", imm); break; } default: APPEND("???"); break; } } } else if (strcmp(tagbuf,"reg") == 0) { APPEND("%d", opcode>>9 & 7); } else if (strcmp(tagbuf,"reg0") == 0) { APPEND("%d", opcode>>0 & 7); } else if (strcmp(tagbuf,"count") == 0) { APPEND("%d", opcode>>9 & 7 ?: 8); } else if (strcmp(tagbuf,"trap") == 0) { APPEND("%d", opcode>>0 & 15); } else if (strcmp(tagbuf,"quick8") == 0) { APPEND("%d", (int8_t)(opcode & 0xFF)); } else if (strncmp(tagbuf,"imm8",4) == 0) { uint8_t imm8 = READU16(state, address); // Upper 8 bits ignored imm8 &= 0xFF; address += 2; if (tagbuf[4] == 'd') { APPEND("%d", imm8); } else if (tagbuf[4] == 'x') { APPEND("$%02X", imm8); } else { APPEND("%s%X", imm8<10 ? "" : "$", imm8); } } else if (strncmp(tagbuf,"imm16",5) == 0) { uint16_t imm16 = READU16(state, address); address += 2; if (tagbuf[5] == 'd') { APPEND("%d", imm16); } else if (tagbuf[5] == 'x') { APPEND("$%04X", imm16); } else { APPEND("%s%X", imm16<10 ? "" : "$", imm16); } } else if (strcmp(tagbuf,"pcrel8") == 0) { int8_t disp8 = opcode & 0xFF; APPEND("$%X", (base_address+2) + disp8); } else if (strcmp(tagbuf,"pcrel16") == 0) { int16_t disp16 = READS16(state, address); address += 2; APPEND("$%X", (base_address+2) + disp16); } else if (strcmp(tagbuf,"reglist") == 0 || strcmp(tagbuf,"tsilger") == 0) { uint16_t reglist = READU16(state, address); address += 2; if (strcmp(tagbuf,"tsilger") == 0) { // "reglist" backwards /* Predecrement-mode register list, so flip it around */ uint16_t temp = reglist; reglist = 0; while (temp) { reglist <<= 1; if (temp & 1) { reglist |= 1; } temp >>= 1; } } char listbuf[3*16]; // Buffer for generating register list unsigned int listlen = 0; // strlen(listbuf) unsigned int last = 0; // State of the previous bit unsigned int regnum = 0; // Current register number (0-15) while (reglist) { if (reglist & 1) { if (last) { if (listlen >= 3 && listbuf[listlen-3] == '-') { listlen -= 2; } else { listbuf[listlen++] = '-'; } } else { if (listlen > 0) { listbuf[listlen++] = '/'; } } listbuf[listlen++] = regnum<8 ? 'D' : 'A'; listbuf[listlen++] = '0' + (regnum % 8); } last = reglist & 1; regnum++; reglist >>= 1; } listbuf[listlen] = 0; APPEND("%s", listbuf); } else { APPEND("<%s>", tagbuf); } } else { APPEND_CHAR(format[inpos]); inpos++; } } if (nwords_ret) { *nwords_ret = (address - base_address) / 2; } return outbuf; } /*************************************************************************/ /*********************** Execution tracing support ***********************/ /*************************************************************************/ /* Processor state block to use in tracing */ static Q68State *state; /* File pointer for trace output */ static FILE *logfile; /* Cycle accumulator */ static uint64_t total_cycles; /* Range of cycles to trace */ static const uint64_t trace_start = 000000000ULL; // First cycle to trace static const uint64_t trace_stop = 600000000ULL; // Last cycle to trace + 1 /*-----------------------------------------------------------------------*/ /** * q68_trace_init: Initialize the tracing code. * * [Parameters] * state: Processor state block * [Return value] * None */ void q68_trace_init(Q68State *state_) { state = state_; } /*-----------------------------------------------------------------------*/ /** * q68_trace_add_cycles: Add the given number of cycles to the global * accumulator. * * [Parameters] * cycles: Number of cycles to add * [Return value] * None */ extern void q68_trace_add_cycles(int32_t cycles) { total_cycles += cycles; } /*-----------------------------------------------------------------------*/ #ifdef PSP /** * HEXIT: Helper routine for q68_trace() to print a value in hexadecimal. * See q68_trace() for why we don't just use printf(). */ static inline void HEXIT(char * const ptr, uint32_t val, int ndigits) { while (ndigits-- > 0) { const int digit = val & 0xF; val >>= 4; ptr[ndigits] = (digit>9 ? digit+7+'0' : digit+'0'); } } #endif /*----------------------------------*/ /** * q68_trace: Output a trace for the instruction at the current PC. * * [Parameters] * None * [Return value] * None */ void q68_trace(void) { const uint64_t cycles = total_cycles + state->cycles; if (cycles < trace_start) { /* Before first instruction: do nothing */ } else if (cycles >= trace_stop) { /* After last instruction: close log file if it's open */ if (logfile) { #ifdef __linux__ pclose(logfile); #else fclose(logfile); #endif logfile = NULL; } } else { if (!logfile) { #ifdef __linux__ logfile = popen("gzip -3 >q68.log.gz", "w"); #else logfile = fopen("q68.log", "w"); #endif if (UNLIKELY(!logfile)) { perror("Failed to open trace logfile"); return; } setvbuf(logfile, NULL, _IOFBF, 65536); } int nwords = 1, i; const char *disassembled = q68_disassemble(state, state->PC, &nwords); #ifdef PSP // because the cleaner fprintf() version is just too slow int dislen = strlen(disassembled); static char buf1[] = "......: .... .... .... .......................... SR=.... ..... [..........]\n"; static char buf2[] = " D: ........ ........ ........ ........ ........ ........ ........ ........\n" " A: ........ ........ ........ ........ ........ ........ ........ ........\n"; if (nwords > 3) { // We can only fit 3 words on the line nwords = 3; } HEXIT(&buf1[0], state->PC, 6); for (i = 0; i < nwords; i++) { HEXIT(&buf1[8+5*i], READU16(state, state->PC+2*i), 4); } if (i < 3) { memset(&buf1[8+5*i], ' ', 4+5*(2-i)); } if (dislen > 26) { // Pathologically long text needs special handling fprintf(logfile, "%.22s %-26s SR=%04X %c%c%c%c%c [%10lld]\n", buf1, disassembled, (int)state->SR, state->SR & SR_X ? 'X' : '.', state->SR & SR_N ? 'N' : '.', state->SR & SR_Z ? 'Z' : '.', state->SR & SR_V ? 'V' : '.', state->SR & SR_C ? 'C' : '.', (unsigned long long)cycles); } else { memcpy(&buf1[24], disassembled, dislen); if (dislen < 26) { memset(&buf1[24+dislen], ' ', 26-dislen); } HEXIT(&buf1[55], state->SR, 4); buf1[60] = state->SR & SR_X ? 'X' : '.'; buf1[61] = state->SR & SR_N ? 'N' : '.'; buf1[62] = state->SR & SR_Z ? 'Z' : '.'; buf1[63] = state->SR & SR_V ? 'V' : '.'; buf1[64] = state->SR & SR_C ? 'C' : '.'; snprintf(&buf1[68], sizeof(buf1)-68, "%10lld]\n", (unsigned long long)cycles); fwrite(buf1, 1, strlen(buf1), logfile); } for (i = 0; i < 8; i++) { HEXIT(&buf2[ 7+9*i], state->D[i], 8); HEXIT(&buf2[86+9*i], state->A[i], 8); } fwrite(buf2, 1, sizeof(buf2)-1, logfile); #else // !PSP char hexbuf[100]; int hexlen = 0; if (nwords > 3) { // We can only fit 3 words on the line nwords = 3; } for (i = 0; i < nwords && hexlen < sizeof(hexbuf)-5; i++) { hexlen += snprintf(hexbuf+hexlen, sizeof(hexbuf)-hexlen, "%s%04X", hexlen==0 ? "" : " ", (int)READU16(state, state->PC+2*i)); } fprintf(logfile, "%06X: %-14s %-26s SR=%04X %c%c%c%c%c [%10llu]\n" " D: %08X %08X %08X %08X %08X %08X %08X %08X\n" " A: %08X %08X %08X %08X %08X %08X %08X %08X\n", (int)state->PC, hexbuf, disassembled, (int)state->SR, state->SR & SR_X ? 'X' : '.', state->SR & SR_N ? 'N' : '.', state->SR & SR_Z ? 'Z' : '.', state->SR & SR_V ? 'V' : '.', state->SR & SR_C ? 'C' : '.', (unsigned long long)cycles, (int)state->D[0], (int)state->D[1], (int)state->D[2], (int)state->D[3], (int)state->D[4], (int)state->D[5], (int)state->D[6], (int)state->D[7], (int)state->A[0], (int)state->A[1], (int)state->A[2], (int)state->A[3], (int)state->A[4], (int)state->A[5], (int)state->A[6], (int)state->A[7] ); #endif // PSP } // current_cycles >= trace_start && current_cycles < trace_stop } /*************************************************************************/ /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68-jit.h000644 001750 001750 00000005476 12256006177 020210 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-jit.h: Dynamic translation header for Q68 Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Q68_JIT_H #define Q68_JIT_H /*************************************************************************/ /* Info structure for translated code blocks */ struct Q68JitEntry_ { Q68JitEntry *next, *prev; // Hash table collision chain pointers Q68State *state; // Associated processor state block uint32_t m68k_start; // Code start address in 68000 address space // (zero indicates a free entry) uint32_t m68k_end; // Code end address in 68000 address space OpcodeFunc *native_code; // Pointer to native code uint32_t native_length; // Length of native code (bytes) uint32_t native_size; // Size of native code buffer (bytes) void *exec_address; // Next execution address (NULL if not started) uint32_t timestamp; // Time this entry was added uint8_t running; // Nonzero if entry is currently running uint8_t must_clear; // Nonzero if entry must be cleared on completion }; /* Hash function */ #define JIT_HASH(addr) ((uint32_t)(addr) % Q68_JIT_TABLE_SIZE) /*************************************************************************/ /** * TIMESTAMP_COMPARE: Compare two timestamps. * * [Parameters] * a, b: Timestamps to compare * reference: Reference timestamp by which the comparison is made * [Return value] * -1 if a < b (i.e. "a is older than b") * 0 if a == b * 1 if a > b */ #ifdef __GNUC__ __attribute__((const)) #endif static inline int TIMESTAMP_COMPARE(uint32_t reference, uint32_t a, uint32_t b) { const uint32_t age_a = reference - a; const uint32_t age_b = reference - b; return age_a > age_b ? -1 : age_a < age_b ? 1 : 0; } /*************************************************************************/ #endif // Q68_JIT_H /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68-const.h000644 001750 001750 00000015407 12256006177 020543 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-const.h: Constants used in MC68000 emulation Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Q68_CONST_H #define Q68_CONST_H /*************************************************************************/ /* Configuration constants */ /* Maximum size in bytes of a 68k code block for translation */ #ifndef Q68_JIT_MAX_BLOCK_SIZE # define Q68_JIT_MAX_BLOCK_SIZE 4096 #endif /* Size of pages used in checking for writes to already-translated code * (1 page = 1<SR >> SR_I0_SHIFT) & 7) #define SR_SET_I(state,val) ((state)->SR &= ~(7 << SR_I0_SHIFT), \ (state)->SR |= ((val) & 7) << SR_I0_SHIFT) /*-----------------------------------------------------------------------*/ /* Exception numbers */ #define EX_INITIAL_SP 0 #define EX_INITIAL_PC 1 #define EX_BUS_ERROR 2 #define EX_ADDRESS_ERROR 3 #define EX_ILLEGAL_INSTRUCTION 4 #define EX_DIVIDE_BY_ZERO 5 #define EX_CHK 6 #define EX_TRAPV 7 #define EX_PRIVILEGE_VIOLATION 8 #define EX_TRACE 9 #define EX_LINE_1010 10 #define EX_LINE_1111 11 #define EX_SPURIOUS_INTERRUPT 24 #define EX_LEVEL_1_INTERRUPT 25 #define EX_LEVEL_2_INTERRUPT 26 #define EX_LEVEL_3_INTERRUPT 27 #define EX_LEVEL_4_INTERRUPT 28 #define EX_LEVEL_5_INTERRUPT 29 #define EX_LEVEL_6_INTERRUPT 30 #define EX_LEVEL_7_INTERRUPT 31 #define EX_TRAP 32 // 16 vectors (EX_TRAP+0 .. EX_TRAP+15) /*-----------------------------------------------------------------------*/ /* Bits in the fault status word for bus/address error exceptions */ #define FAULT_STATUS_IN (1<<3) // Instruction/Not (0 = instruction, 1 = not) #define FAULT_STATUS_IN_INSN (0) #define FAULT_STATUS_IN_DATA (FAULT_STATUS_IN) #define FAULT_STATUS_RW (1<<4) // Read/Write (0 = write, 1 = read) #define FAULT_STATUS_RW_READ (FAULT_STATUS_RW) #define FAULT_STATUS_RW_WRITE (0) /*-----------------------------------------------------------------------*/ /* Condition codes for conditional instructions */ #define COND_T 0 #define COND_F 1 #define COND_HI 2 #define COND_LS 3 #define COND_CC 4 // also HS #define COND_CS 5 // also LO #define COND_NE 6 #define COND_EQ 7 #define COND_VC 8 #define COND_VS 9 #define COND_PL 10 #define COND_MI 11 #define COND_GE 12 #define COND_LT 13 #define COND_GT 14 #define COND_LE 15 /*-----------------------------------------------------------------------*/ /* Size codes in opcode bits 6-7 */ #define SIZE_B 0 #define SIZE_W 1 #define SIZE_L 2 /* Macro to convert size codes to equivalent byte counts */ #define SIZE_TO_BYTES(size) ((size) + 1 + ((size) == SIZE_L)) /*-----------------------------------------------------------------------*/ /* Effective address modes */ #define EA_DATA_REG 0 // Data Register Direct #define EA_ADDRESS_REG 1 // Address Register Direct #define EA_INDIRECT 2 // Address Register Indirect #define EA_POSTINCREMENT 3 // Address Register Indirect with Postincrement #define EA_PREDECREMENT 4 // Address Register Indirect with Predecrement #define EA_DISPLACEMENT 5 // Address Register Indirect with Displacement #define EA_INDEX 6 // Address Register Indirect with Index #define EA_MISC 7 #define EA_MISC_ABSOLUTE_W 0 // Absolute Short #define EA_MISC_ABSOLUTE_L 1 // Absolute Long #define EA_MISC_PCREL 2 // Program Counter Indirect with Displacement #define EA_MISC_PCREL_INDEX 3 // Program Counter Indirect with Index #define EA_MISC_IMMEDIATE 4 // Immediate /* Macros to retrieve the mode and register number from an opcode */ #define EA_MODE(opcode) ((opcode)>>3 & 7) #define EA_REG(opcode) ((opcode)>>0 & 7) /*************************************************************************/ /*************************************************************************/ #endif // Q68_CONST_H /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68-jit-psp.h000644 001750 001750 00000056312 12256006177 021003 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-jit-psp.h: PSP dynamic translation header for Q68 Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Q68_JIT_PSP_H #define Q68_JIT_PSP_H #include /*************************************************************************/ /** * JIT_CALL: Run translated code from the given (native) address for the * given number of cycles. * * [Parameters] * state: Processor state block * cycles: Number of clock cycles to execute * address_ptr: Pointer to address of native code to execute; must be * updated on return with the next address to execute * or NULL if the end of the block was reached * [Return value] * Number of clock cycles actually executed */ static inline int JIT_CALL(Q68State *state, int cycles, void **address_ptr) { register Q68State *__state asm("s0") = state; register int __cycles asm("s1") = cycles; register int __cycles_out asm("s2"); register void * __address asm("v0") = *address_ptr; asm(".set push; .set noreorder\n" "jalr %[address]\n" "move $s2, $zero\n" ".set pop" : "=r" (__cycles), [address] "=r" (__address), "=r" (__cycles_out) : "r" (__state), "0" (__cycles), "1" (__address) : "at", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "s3", "s4", "s5", "s6", "s7", "ra", "memory" ); *address_ptr = __address; return __cycles_out; } /*************************************************************************/ /** * JIT_FIXUP_BRANCH: Modify a branch instruction at the given offset to * jump to the given target. * * [Parameters] * entry: Block being translated * offset: Offset within entry->native_code of branch instruction * (as returned in *branch_offset EMIT parameter) * target: Target offset within entry->native_code * [Return value] * None */ static inline void JIT_FIXUP_BRANCH(Q68JitEntry *entry, uint32_t offset, uint32_t target) { int32_t disp_4 = (target - (offset + 4)) / 4; if (disp_4 >= -32768 && disp_4 <= 32767) { *(int16_t *)((uint8_t *)entry->native_code + offset) = disp_4; } } /*************************************************************************/ /*************************************************************************/ /* * The remaining macros are all used to insert a specific operation into * the native code stream. For simplicity, we define the actual code for * each operation in a separate assembly file, and use memcpy() to copy * from the assembled code to the output code stream. The GEN_EMIT macro * below is used to generate each of the JIT_EMIT_* functions; each * function JIT_EMIT_xxx copies JIT_PSPSIZE_xxx bytes from JIT_PSP_xxx to * the code stream, expanding the code buffer if necessary. */ /* Sub-macros: */ #define GEN_NAMESIZE(name) \ extern const uint8_t JIT_PSP_##name[]; \ extern const uint32_t JIT_PSPSIZE_##name; #define GEN_PARAM(name,param) \ extern const uint32_t JIT_PSPPARAM_##name##_##param; #define GEN_FUNC_TOP(name) \ if (UNLIKELY(entry->native_size - entry->native_length \ < JIT_PSPSIZE_##name)) { \ if (!expand_buffer(entry)) { \ return; \ } \ } \ if (JIT_PSPSIZE_##name > 0) { \ memcpy((uint8_t *)entry->native_code + entry->native_length, \ JIT_PSP_##name, JIT_PSPSIZE_##name); \ } #define GEN_COPY_PARAM(name,type,param) \ *(type *)((uint8_t *)entry->native_code + entry->native_length \ + JIT_PSPPARAM_##name##_##param) = param; #define GEN_FUNC_BOTTOM(name) \ entry->native_length += JIT_PSPSIZE_##name; #define GEN_EMIT(name) \ GEN_NAMESIZE(name) \ static void JIT_EMIT_##name(Q68JitEntry *entry) { \ GEN_FUNC_TOP(name) \ GEN_FUNC_BOTTOM(name) \ } #define GEN_EMIT_1(name,type1,param1) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_FUNC_BOTTOM(name) \ } #define GEN_EMIT_2(name,type1,param1,type2,param2) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ GEN_PARAM(name,param2) \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ type2 param2) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_COPY_PARAM(name, type2, param2) \ GEN_FUNC_BOTTOM(name) \ } #define GEN_EMIT_3(name,type1,param1,type2,param2,type3,param3) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ GEN_PARAM(name,param2) \ GEN_PARAM(name,param3) \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ type2 param2, type3 param3) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_COPY_PARAM(name, type2, param2) \ GEN_COPY_PARAM(name, type3, param3) \ GEN_FUNC_BOTTOM(name) \ } #define GEN_EMIT_4(name,type1,param1,type2,param2,type3,param3,type4,param4) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ GEN_PARAM(name,param2) \ GEN_PARAM(name,param3) \ GEN_PARAM(name,param4) \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ type2 param2, type3 param3, type4 param4) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_COPY_PARAM(name, type2, param2) \ GEN_COPY_PARAM(name, type3, param3) \ GEN_COPY_PARAM(name, type4, param4) \ GEN_FUNC_BOTTOM(name) \ } #define GEN_EMIT_5(name,type1,param1,type2,param2,type3,param3,type4,param4,type5,param5) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ GEN_PARAM(name,param2) \ GEN_PARAM(name,param3) \ GEN_PARAM(name,param4) \ GEN_PARAM(name,param5) \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ type2 param2, type3 param3, \ type4 param4, type5 param5) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_COPY_PARAM(name, type2, param2) \ GEN_COPY_PARAM(name, type3, param3) \ GEN_COPY_PARAM(name, type4, param4) \ GEN_COPY_PARAM(name, type5, param5) \ GEN_FUNC_BOTTOM(name) \ } /* These versions include a hidden "disp_4" parameter, and pass the value of * disp_4_formula for that parameter. */ #define GEN_EMIT_disp(name,disp_4_formula) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,disp_4) \ static void __real_JIT_EMIT_##name(Q68JitEntry *entry, int16_t disp_4) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, int16_t, disp_4) \ GEN_FUNC_BOTTOM(name) \ } \ static void JIT_EMIT_##name(Q68JitEntry *entry) { \ __real_JIT_EMIT_##name(entry, (disp_4_formula)); \ } #define GEN_EMIT_1_disp(name,type1,param1,disp_4_formula) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ GEN_PARAM(name,disp_4) \ static void __real_JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ int16_t disp_4) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_COPY_PARAM(name, int16_t, disp_4) \ GEN_FUNC_BOTTOM(name) \ } \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1) { \ __real_JIT_EMIT_##name(entry, param1, (disp_4_formula)); \ } #define GEN_EMIT_2_disp(name,type1,param1,type2,param2,disp_4_formula) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ GEN_PARAM(name,param2) \ GEN_PARAM(name,disp_4) \ static void __real_JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ type2 param2, int16_t disp_4) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_COPY_PARAM(name, type2, param2) \ GEN_COPY_PARAM(name, int16_t, disp_4) \ GEN_FUNC_BOTTOM(name) \ } \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ type2 param2) { \ __real_JIT_EMIT_##name(entry, param1, param2, (disp_4_formula)); \ } /*-----------------------------------------------------------------------*/ /* Code prologue and epilogue */ GEN_EMIT(PROLOGUE) extern const int JIT_PSPOFS_TERMINATE; extern const int JIT_PSPOFS_EXCEPTION; extern const int JIT_PSPOFS_ADDRESS_ERROR_EA; extern const int JIT_PSPOFS_ADDRESS_ERROR_SP; GEN_EMIT(EPILOGUE) #ifdef Q68_TRACE /* Trace the current instruction */ GEN_EMIT(TRACE) #endif /* Add the specified number of cycles to the cycle counter */ GEN_EMIT_1(ADD_CYCLES, int16_t, cycles) /* Check the cycle limit and interrupt execution if necessary */ GEN_EMIT(CHECK_CYCLES) /* Add the specified amount to the program counter and/or check whether * to abort */ GEN_EMIT_1(ADVANCE_PC, int16_t, value) GEN_EMIT_1_disp(ADVANCE_PC_CHECK_ABORT, int16_t, value, (JIT_PSPOFS_TERMINATE - (entry->native_length + 4)) / 4) GEN_EMIT_disp(CHECK_ABORT, (JIT_PSPOFS_TERMINATE - (entry->native_length + 4)) / 4) /* Exception raising */ GEN_EMIT_1_disp(EXCEPTION, int16_t, num, (JIT_PSPOFS_EXCEPTION - (entry->native_length + 4)) / 4) GEN_EMIT_2_disp(CHECK_ALIGNED_EA, uint16_t, opcode, uint16_t, status, (JIT_PSPOFS_ADDRESS_ERROR_EA - (entry->native_length + 4)) / 4) GEN_EMIT_2_disp(CHECK_ALIGNED_SP, uint16_t, opcode, uint16_t, status, (JIT_PSPOFS_ADDRESS_ERROR_SP - entry->native_length) / 4) GEN_EMIT_disp(CHECK_SUPER, (JIT_PSPOFS_EXCEPTION - (entry->native_length + 4)) / 4) /*-----------------------------------------------------------------------*/ /* Resolve an effective address */ GEN_EMIT_1(RESOLVE_INDIRECT, int16_t, reg4) GEN_EMIT_3(RESOLVE_POSTINC, int16_t, reg4, int16_t, size, int16_t, reg4_b) #define JIT_EMIT_RESOLVE_POSTINC(entry,reg4,size) do { \ int16_t __reg4 = (reg4); \ JIT_EMIT_RESOLVE_POSTINC((entry), __reg4, (size), __reg4); \ } while (0) GEN_EMIT(RESOLVE_POSTINC_A7_B) GEN_EMIT_3(RESOLVE_PREDEC, int16_t, reg4, int16_t, nsize, int16_t, reg4_b) #define JIT_EMIT_RESOLVE_PREDEC(entry,reg4,size) do { \ int16_t __reg4 = (reg4); \ JIT_EMIT_RESOLVE_PREDEC((entry), __reg4, -(size), __reg4); \ } while (0) GEN_EMIT(RESOLVE_PREDEC_A7_B) GEN_EMIT_2(RESOLVE_DISP, int16_t, reg4, int16_t, disp) GEN_EMIT_3(RESOLVE_INDEX_W, int16_t, reg4, int16_t, ireg4, int16_t, disp) GEN_EMIT_3(RESOLVE_INDEX_L, int16_t, reg4, int16_t, ireg4, int16_t, disp) GEN_EMIT_2(RESOLVE_ABSOLUTE, uint16_t, addr_hi, uint16_t, addr_lo) #define JIT_EMIT_RESOLVE_ABSOLUTE(entry,addr) do { \ uint32_t __addr = (addr); \ JIT_EMIT_RESOLVE_ABSOLUTE((entry), __addr>>16, __addr & 0xFFFF); \ } while (0) GEN_EMIT_3(RESOLVE_ABS_INDEX_W, int16_t, ireg4, uint16_t, addr_hi, uint16_t, addr_lo) #define JIT_EMIT_RESOLVE_ABS_INDEX_W(entry,addr,ireg4) do { \ uint32_t __addr = (addr); \ JIT_EMIT_RESOLVE_ABS_INDEX_W((entry), (ireg4), __addr>>16, \ (addr) & 0xFFFF); \ } while (0) GEN_EMIT_3(RESOLVE_ABS_INDEX_L, int16_t, ireg4, uint16_t, addr_hi, uint16_t, addr_lo) #define JIT_EMIT_RESOLVE_ABS_INDEX_L(entry,addr,ireg4) do { \ uint32_t __addr = (addr); \ JIT_EMIT_RESOLVE_ABS_INDEX_L((entry), (ireg4), __addr>>16, \ __addr & 0xFFFF); \ } while (0) /* Retrieve various things as operand 1 */ GEN_EMIT_1(GET_OP1_REGISTER, int16_t, reg4) GEN_EMIT(GET_OP1_EA_B) GEN_EMIT(GET_OP1_EA_W) GEN_EMIT(GET_OP1_EA_L) GEN_EMIT_1(GET_OP1_IMMED_16S, int16_t, value_lo) GEN_EMIT_1(GET_OP1_IMMED_16U, uint16_t, value_lo) GEN_EMIT_1(GET_OP1_IMMED_16HI, uint16_t, value_hi) GEN_EMIT_2(GET_OP1_IMMED_32, uint16_t, value_hi, uint16_t, value_lo) #define JIT_EMIT_GET_OP1_IMMEDIATE(entry,value) do { \ Q68JitEntry * const __entry = (entry); \ const int32_t __value = (value); \ if (value >= -32768 && value <= 32767) { \ JIT_EMIT_GET_OP1_IMMED_16S(__entry, __value); \ } else if (value >= 0 && value <= 65535) { \ JIT_EMIT_GET_OP1_IMMED_16U(__entry, __value); \ } else if ((value & 0xFFFF) == 0) { \ JIT_EMIT_GET_OP1_IMMED_16HI(__entry, __value>>16); \ } else { \ JIT_EMIT_GET_OP1_IMMED_32(__entry, __value>>16, __value & 0xFFFF); \ } \ } while (0) GEN_EMIT(GET_OP1_CCR) GEN_EMIT(GET_OP1_SR) /* Retrieve various things as operand 2 */ GEN_EMIT_1(GET_OP2_REGISTER, int16_t, reg4) GEN_EMIT(GET_OP2_EA_B) GEN_EMIT(GET_OP2_EA_W) GEN_EMIT(GET_OP2_EA_L) GEN_EMIT_1(GET_OP2_IMMED_16S, int16_t, value_lo) GEN_EMIT_1(GET_OP2_IMMED_16U, uint16_t, value_lo) GEN_EMIT_1(GET_OP2_IMMED_16HI, uint16_t, value_hi) GEN_EMIT_2(GET_OP2_IMMED_32, uint16_t, value_hi, uint16_t, value_lo) #define JIT_EMIT_GET_OP2_IMMEDIATE(entry,value) do { \ Q68JitEntry * const __entry = (entry); \ const int32_t __value = (value); \ if (value >= -32768 && value <= 32767) { \ JIT_EMIT_GET_OP2_IMMED_16S(__entry, __value); \ } else if (value >= 0 && value <= 65535) { \ JIT_EMIT_GET_OP2_IMMED_16U(__entry, __value); \ } else if ((value & 0xFFFF) == 0) { \ JIT_EMIT_GET_OP2_IMMED_16HI(__entry, __value>>16); \ } else { \ JIT_EMIT_GET_OP2_IMMED_32(__entry, __value>>16, __value & 0xFFFF); \ } \ } while (0) GEN_EMIT(GET_OP2_CCR) GEN_EMIT(GET_OP2_SR) /* Update various things from result */ GEN_EMIT_1(SET_REGISTER_B, int16_t, reg4) GEN_EMIT_1(SET_REGISTER_W, int16_t, reg4) GEN_EMIT_1(SET_REGISTER_L, int16_t, reg4) GEN_EMIT_1(SET_AREG_W, int16_t, reg4) GEN_EMIT(SET_EA_B) GEN_EMIT(SET_EA_W) GEN_EMIT(SET_EA_L) GEN_EMIT(SET_CCR) GEN_EMIT(SET_SR) /* Stack operations */ GEN_EMIT(PUSH_L) GEN_EMIT(POP_L) /* Condition code setting */ GEN_EMIT(SETCC_ADD_B) GEN_EMIT(SETCC_ADD_W) GEN_EMIT(SETCC_ADD_L) GEN_EMIT(SETCC_ADDX_B) GEN_EMIT(SETCC_ADDX_W) GEN_EMIT(SETCC_ADDX_L) GEN_EMIT(SETCC_SUB_B) GEN_EMIT(SETCC_SUB_W) GEN_EMIT(SETCC_SUB_L) GEN_EMIT(SETCC_SUBX_B) GEN_EMIT(SETCC_SUBX_W) GEN_EMIT(SETCC_SUBX_L) GEN_EMIT(SETCC_CMP_B) GEN_EMIT(SETCC_CMP_W) GEN_EMIT(SETCC_CMP_L) GEN_EMIT(SETCC_LOGIC_B) GEN_EMIT(SETCC_LOGIC_W) GEN_EMIT(SETCC_LOGIC_L) /* Condition testing */ GEN_EMIT(TEST_T) GEN_EMIT(TEST_F) GEN_EMIT(TEST_HI) GEN_EMIT(TEST_LS) GEN_EMIT(TEST_CC) GEN_EMIT(TEST_CS) GEN_EMIT(TEST_NE) GEN_EMIT(TEST_EQ) GEN_EMIT(TEST_VC) GEN_EMIT(TEST_VS) GEN_EMIT(TEST_PL) GEN_EMIT(TEST_MI) GEN_EMIT(TEST_GE) GEN_EMIT(TEST_LT) GEN_EMIT(TEST_GT) GEN_EMIT(TEST_LE) /* ALU operations */ GEN_EMIT(MOVE_B) GEN_EMIT(MOVE_W) GEN_EMIT(MOVE_L) GEN_EMIT(ADD_B) GEN_EMIT(ADD_W) GEN_EMIT(ADD_L) GEN_EMIT(ADDA_W) GEN_EMIT(ADDX_B) GEN_EMIT(ADDX_W) GEN_EMIT(ADDX_L) GEN_EMIT(SUB_B) GEN_EMIT(SUB_W) GEN_EMIT(SUB_L) GEN_EMIT(SUBA_W) GEN_EMIT(SUBX_B) GEN_EMIT(SUBX_W) GEN_EMIT(SUBX_L) GEN_EMIT(MULS_W) GEN_EMIT(MULU_W) GEN_EMIT(DIVS_W) GEN_EMIT(DIVU_W) GEN_EMIT(AND_B) GEN_EMIT(AND_W) GEN_EMIT(AND_L) GEN_EMIT(OR_B) GEN_EMIT(OR_W) GEN_EMIT(OR_L) GEN_EMIT(EOR_B) GEN_EMIT(EOR_W) GEN_EMIT(EOR_L) GEN_EMIT(EXT_W) GEN_EMIT(EXT_L) GEN_EMIT(SWAP) /* BCD operations */ GEN_EMIT(ABCD) GEN_EMIT(SBCD) /* Bit-twiddling operations */ GEN_EMIT(BTST_B) GEN_EMIT(BTST_L) GEN_EMIT(BCHG) GEN_EMIT(BCLR) GEN_EMIT(BSET) /* Shift/rotate operations */ GEN_EMIT(ASL_B) GEN_EMIT(ASL_W) GEN_EMIT(ASL_L) GEN_EMIT(ASR_B) GEN_EMIT(ASR_W) GEN_EMIT(ASR_L) GEN_EMIT(LSL_B) GEN_EMIT(LSL_W) GEN_EMIT(LSL_L) GEN_EMIT(LSR_B) GEN_EMIT(LSR_W) GEN_EMIT(LSR_L) GEN_EMIT(ROXL_B) GEN_EMIT(ROXL_W) GEN_EMIT(ROXL_L) GEN_EMIT(ROXR_B) GEN_EMIT(ROXR_W) GEN_EMIT(ROXR_L) GEN_EMIT(ROL_B) GEN_EMIT(ROL_W) GEN_EMIT(ROL_L) GEN_EMIT(ROR_B) GEN_EMIT(ROR_W) GEN_EMIT(ROR_L) /* Conditional and branch operations ("branch_offset" parameter receives * the native offset of the branch to update when resolving, or -1 if not * supported) */ GEN_EMIT(Scc) GEN_EMIT(ADD_CYCLES_Scc_Dn) GEN_EMIT_4(DBcc, int16_t, reg4, int16_t, reg4_b, uint16_t, target_hi, uint16_t, target_lo) #define JIT_EMIT_DBcc(entry,reg4,target) do { \ int16_t __reg4 = (reg4); \ uint32_t __target = (target); \ JIT_EMIT_DBcc((entry), __reg4, __reg4, __target>>16, __target & 0xFFFF); \ } while (0) GEN_EMIT_5(DBcc_native, int16_t, reg4, int16_t, reg4_b, uint16_t, target_hi, uint16_t, target_lo, int16_t, native_disp_4_4) #define JIT_EMIT_DBcc_native(entry,reg4,target,offset) do { \ Q68JitEntry *__entry = (entry); \ int16_t __reg4 = (reg4); \ uint32_t __target = (target); \ int32_t __fragment_end = __entry->native_length + JIT_PSPSIZE_DBcc_native; \ JIT_EMIT_DBcc_native(__entry, __reg4, __reg4, \ __target>>16, __target & 0xFFFF, \ ((offset) - __fragment_end + 4) / 4); \ } while (0) GEN_EMIT_3(Bcc_common, uint16_t, target_hi, uint16_t, target_lo, int16_t, disp_4) static void JIT_EMIT_Bcc(Q68JitEntry *entry, uint32_t target, int32_t *branch_offset) { *branch_offset = entry->native_length + JIT_PSPPARAM_Bcc_common_disp_4; int32_t disp_4 = (JIT_PSPOFS_TERMINATE - (*branch_offset + 4)) / 4; JIT_EMIT_Bcc_common(entry, target>>16, target & 0xFFFF, disp_4); } static void JIT_EMIT_Bcc_native(Q68JitEntry *entry, uint32_t target, int32_t offset) { uint32_t branch_offset = entry->native_length + JIT_PSPPARAM_Bcc_common_disp_4; int32_t disp_4 = (offset - (branch_offset + 4)) / 4; /* Displacement is assumed to be within range (+/-128k) */ JIT_EMIT_Bcc_common(entry, target>>16, target & 0xFFFF, disp_4); } GEN_EMIT_4(BSR, uint16_t, return_addr_hi, uint16_t, return_addr_lo, uint16_t, target_hi, uint16_t, target_lo) #define JIT_EMIT_BSR(entry,return_addr,target) do { \ uint32_t __return_addr = (return_addr); \ uint32_t __target = (target); \ JIT_EMIT_BSR((entry), __return_addr>>16, __return_addr & 0xFFFF, \ __target>>16, __target & 0xFFFF); \ } while (0) GEN_EMIT(JMP) GEN_EMIT_2(JSR, uint16_t, return_addr_hi, uint16_t, return_addr_lo) #define JIT_EMIT_JSR(entry,return_addr) do { \ uint32_t __return_addr = (return_addr); \ JIT_EMIT_JSR((entry), __return_addr>>16, __return_addr & 0xFFFF); \ } while (0) /* MOVEM-related operations */ GEN_EMIT_1(STORE_DEC_W, int16_t, reg4) GEN_EMIT_1(STORE_DEC_L, int16_t, reg4) GEN_EMIT_1(STORE_INC_W, int16_t, reg4) GEN_EMIT_1(STORE_INC_L, int16_t, reg4) GEN_EMIT_1(LOAD_INC_W, int16_t, reg4) GEN_EMIT_1(LOAD_INC_L, int16_t, reg4) GEN_EMIT_1(LOADA_INC_W, int16_t, reg4) GEN_EMIT_1(MOVEM_WRITEBACK, int16_t, reg4) /* Miscellaneous operations */ GEN_EMIT(CHK_W) GEN_EMIT_1(LEA, int16_t, reg4) GEN_EMIT(PEA) GEN_EMIT(TAS) GEN_EMIT_1(MOVE_FROM_USP, int16_t, reg4) GEN_EMIT_1(MOVE_TO_USP, int16_t, reg4) GEN_EMIT_1(STOP, uint16_t, newSR) GEN_EMIT(TRAPV) GEN_EMIT(RTS) GEN_EMIT(RTR) GEN_EMIT(RTE) GEN_EMIT_3(MOVEP_READ_W, int16_t, areg4, int16_t, disp, int16_t, dreg4) GEN_EMIT_3(MOVEP_READ_L, int16_t, areg4, int16_t, disp, int16_t, dreg4) GEN_EMIT_3(MOVEP_WRITE_W, int16_t, areg4, int16_t, disp, int16_t, dreg4) GEN_EMIT_3(MOVEP_WRITE_L, int16_t, areg4, int16_t, disp, int16_t, dreg4) GEN_EMIT_2(EXG, int16_t, reg1_4, int16_t, reg2_4) /*************************************************************************/ #endif // Q68_JIT_PSP_H /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68-core.c000644 001750 001750 00000237370 12256006177 020345 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-core.c: Q68 execution core Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "q68.h" #include "q68-const.h" #include "q68-internal.h" /* Define this to export two tables of counters: * uint32_t q68_ops[128]; // Corresponds to opcode_table[] * uint32_t q68_4xxx_ops[32]; // Corresponds to opcode_4xxx_table[] * Each entry will be incremented each time the corresponding function is * called. */ // #define COUNT_OPCODES /*************************************************************************/ /* * The following table maps each 68000 instruction to its implementing * function in this file: * * || Instruction | Func. || Instruction | Func. || Instruction | Func. || * ++-------------+--------++-------------+--------++-------------+--------++ * || ABCD | op_BCD || EOR | op_alu || NOT | op4alu || * || ADD | op_alu || EORI | op_imm || OR | op_alu || * || ADDA | op_alu || EORI to CCR | op_imm || ORI | op_imm || * || ADDI | op_imm || EORI to SR | op_imm || ORI to CCR | op_imm || * || ADDQ | opADSQ || EXG | op_EXG || ORI to SR | op_imm || * || ADDX | opADSX || EXT | op_EXT || PEA | op_PEA || * || AND | op_alu || ILLEGAL | op_TAS || RESET | op4E7x || * || ANDI | op_imm || JMP | opjump || ROL | opshft || * || ANDI to CCR | op_imm || JSR | opjump || ROR | opshft || * || ANDI to SR | op_imm || LEA | op_LEA || ROXL | opshft || * || ASL | opshft || LINK | opLINK || ROXR | opshft || * || ASR | opshft || LSL | opshft || RTE | op4E7x || * || Bcc | op_Bcc || LSR | opshft || RTR | op4E7x || * || BCHG | op_bit || MOVE | opMOVE || RTS | op4E7x || * || BCLR | op_bit || MOVEA | opMOVE || SBCD | op_BCD || * || BRA | op_Bcc || MOVE to CCR | opMVSR || Scc | op_Scc || * || BSET | op_bit || MOVE from SR| opMVSR || STOP | op4E7x || * || BSR | op_Bcc || MOVE to SR | opMVSR || SUB | op_alu || * || BTST | op_bit || MOVE USP | opMUSP || SUBA | op_alu || * || CHK | op_CHK || MOVEM | (*1) || SUBI | op_imm || * || CLR | op4alu || MOVEP | opMOVP || SUBQ | opADSQ || * || CMP | op_alu || MOVEQ | opMOVQ || SUBX | opADSX || * || CMPA | op_alu || MULS | op_MUL || SWAP | opSWAP || * || CMPI | op_imm || MULU | op_MUL || TAS | op_TAS || * || CMPM | opCMPM || NBCD | opNBCD || TRAP | opTRAP || * || DBcc | opDBcc || NEG | op4alu || TRAPV | op4E7x || * || DIVS | op_DIV || NEGX | op4alu || TST | op4alu || * || DIVU | op_DIV || NOP | op4E7X || UNLK | opUNLK || * * (*1) MOVEM is implemented by two instructions, op_STM (MOVEM reglist,mem) * and op_LDM (MOVEM mem,reglist). * * Cycle counts were taken from: * http://linas.org/mirrors/www.nvg.ntnu.no/2002.09.16/amiga/MC680x0_Sections/mc68000timing.HTML */ /*************************************************************************/ /* Forward declarations for helper functions and instruction implementations */ static void set_SR(Q68State *state, uint16_t value); static inline void check_interrupt(Q68State *state); static int take_exception(Q68State *state, uint8_t num); static inline int op_ill(Q68State *state, uint32_t opcode); static int ea_resolve(Q68State *state, uint32_t opcode, int size, int access_type); /* Note: Allowing these to be inlined actually slows down the code by ~0.5% * (at least on x86). FIXME: how about PSP? */ static NOINLINE uint32_t ea_get(Q68State *state, uint32_t opcode, int size, int is_rmw, int *cycles_ret); static NOINLINE void ea_set(Q68State *state, uint32_t opcode, int size, uint32_t data); static int op_imm(Q68State *state, uint32_t opcode); static int op_bit(Q68State *state, uint32_t opcode); static int opMOVE(Q68State *state, uint32_t opcode); static int op4xxx(Q68State *state, uint32_t opcode); static int op_CHK(Q68State *state, uint32_t opcode); static int op_LEA(Q68State *state, uint32_t opcode); static int opADSQ(Q68State *state, uint32_t opcode); static int op_Scc(Q68State *state, uint32_t opcode); static int opDBcc(Q68State *state, uint32_t opcode); static int op_Bcc(Q68State *state, uint32_t opcode); static int opMOVQ(Q68State *state, uint32_t opcode); static int op_alu(Q68State *state, uint32_t opcode); static int op_DIV(Q68State *state, uint32_t opcode); static int opAxxx(Q68State *state, uint32_t opcode); static int op_MUL(Q68State *state, uint32_t opcode); static int opshft(Q68State *state, uint32_t opcode); static int opFxxx(Q68State *state, uint32_t opcode); static int op4alu(Q68State *state, uint32_t opcode); static int opMVSR(Q68State *state, uint32_t opcode); static int opNBCD(Q68State *state, uint32_t opcode); static int op_PEA(Q68State *state, uint32_t opcode); static int opSWAP(Q68State *state, uint32_t opcode); static int op_TAS(Q68State *state, uint32_t opcode); static int op_EXT(Q68State *state, uint32_t opcode); static int op_STM(Q68State *state, uint32_t opcode); static int op_LDM(Q68State *state, uint32_t opcode); static int opmisc(Q68State *state, uint32_t opcode); static int opTRAP(Q68State *state, uint32_t opcode); static int opLINK(Q68State *state, uint32_t opcode); static int opUNLK(Q68State *state, uint32_t opcode); static int opMUSP(Q68State *state, uint32_t opcode); static int op4E7x(Q68State *state, uint32_t opcode); static int opjump(Q68State *state, uint32_t opcode); static int opMOVP(Q68State *state, uint32_t opcode); static int opADSX(Q68State *state, uint32_t opcode); static int op_BCD(Q68State *state, uint32_t opcode); static int opCMPM(Q68State *state, uint32_t opcode); static int op_EXG(Q68State *state, uint32_t opcode); /*-----------------------------------------------------------------------*/ /* Main table of instruction implemenation functions; table index is bits * 15-12 and 8-6 of the opcode (ABCD ...E FG.. .... -> 0ABC DEFG). */ static OpcodeFunc * const opcode_table[128] = { op_imm, op_imm, op_imm, op_imm, op_bit, op_bit, op_bit, op_bit, // 00 opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 10 opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 20 opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 30 op4xxx, op4xxx, op4xxx, op4xxx, op_ill, op_ill, op_CHK, op_LEA, // 40 opADSQ, opADSQ, opADSQ, op_Scc, opADSQ, opADSQ, opADSQ, op_Scc, // 50 op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, // 60 opMOVQ, opMOVQ, opMOVQ, opMOVQ, op_ill, op_ill, op_ill, op_ill, // 70 op_alu, op_alu, op_alu, op_DIV, op_alu, op_alu, op_alu, op_DIV, // 80 op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // 90 opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, // A0 op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // B0 op_alu, op_alu, op_alu, op_MUL, op_alu, op_alu, op_alu, op_MUL, // C0 op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // D0 opshft, opshft, opshft, opshft, opshft, opshft, opshft, opshft, // E0 opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, // F0 }; /* Subtable for instructions in the $4xxx (miscellaneous) group; table index * is bits 11-9 and 7-6 of the opcode (1000 ABC0 DE.. .... -> 000A BCDE). */ static OpcodeFunc * const opcode_4xxx_table[32] = { op4alu, op4alu, op4alu, opMVSR, // 40xx op4alu, op4alu, op4alu, op_ill, // 42xx op4alu, op4alu, op4alu, opMVSR, // 44xx op4alu, op4alu, op4alu, opMVSR, // 46xx opNBCD, op_PEA, op_STM, op_STM, // 48xx op4alu, op4alu, op4alu, op_TAS, // 4Axx op_ill, op_ill, op_LDM, op_LDM, // 4Cxx op_ill, opmisc, opjump, opjump, // 4Exx }; /* Sub-subtable for instructions in the $4E40-$4E7F range, used by opmisc(); * index is bits 5-3 of the opcode. */ static OpcodeFunc * const opcode_4E4x_table[8] = { opTRAP, opTRAP, opLINK, opUNLK, opMUSP, opMUSP, op4E7x, op_ill, }; #ifdef COUNT_OPCODES /* Counters for opcode groups. */ uint32_t q68_ops[128], q68_4xxx_ops[32]; #endif /*************************************************************************/ /************************** Interface functions **************************/ /*************************************************************************/ /** * q68_reset: Reset the virtual processor. * * [Parameters] * state: Processor state block * [Return value] * None */ void q68_reset(Q68State *state) { int i; for (i = 0; i < 8; i++) { state->D[i] = 0; state->A[i] = 0; } state->PC = 0; state->SR = SR_S; SR_SET_I(state, 7); state->USP = 0; state->SSP = 0; state->current_PC = 0; state->ea_addr = 0; state->exception = 0; state->fault_addr = 0; state->fault_opcode = 0; state->fault_status = 0; state->jit_running = NULL; #ifdef Q68_USE_JIT q68_jit_reset(state); #endif #ifdef Q68_TRACE q68_trace_init(state); #endif state->A[7] = READU32(state, 0x000000); state->PC = READU32(state, 0x000004); state->halted = 0; } /*-----------------------------------------------------------------------*/ /** * q68_run: Execute instructions for the given number of clock cycles. * * [Parameters] * state: Processor state block * cycles: Number of clock cycles to execute * [Return value] * Number of clock cycles executed (may be greater than "cycles") */ int q68_run(Q68State *state, int cycles) { /* Check for pending interrupts */ check_interrupt(state); /* Run the virtual processor */ state->cycles = 0; while (state->cycles < cycles) { if (UNLIKELY(state->halted)) { /* If we're halted, consume all remaining cycles */ state->cycles = cycles; break; } if (UNLIKELY(state->exception)) { int exception = state->exception; state->exception = 0; state->cycles += take_exception(state, exception); if (state->cycles >= cycles) { break; } } #ifdef Q68_USE_JIT if (!state->jit_running) { state->jit_running = q68_jit_find(state, state->PC); if (UNLIKELY(!state->jit_running)) { state->jit_running = q68_jit_translate(state, state->PC); } } if (state->jit_running) { q68_jit_run(state, cycles, &state->jit_running); } else { #endif #ifndef Q68_DISABLE_ADDRESS_ERROR if (UNLIKELY(state->PC & 1)) { state->fault_addr = state->PC; state->fault_status = FAULT_STATUS_IN_INSN | FAULT_STATUS_RW_READ; state->cycles += take_exception(state, EX_ADDRESS_ERROR); continue; } #endif #ifdef Q68_TRACE q68_trace(); #endif const unsigned int opcode = IFETCH(state); state->current_PC = state->PC; #ifndef Q68_DISABLE_ADDRESS_ERROR state->fault_opcode = opcode; #endif const unsigned int index = (opcode>>9 & 0x78) | (opcode>>6 & 0x07); #ifdef COUNT_OPCODES q68_ops[index]++; #endif state->cycles += (*opcode_table[index])(state, opcode); #ifdef Q68_USE_JIT } #endif } // while (state->cycles < cycles && !state->halted) #ifdef Q68_TRACE q68_trace_add_cycles(state->cycles); #endif return state->cycles; } /*************************************************************************/ /************************** Instruction helpers **************************/ /*************************************************************************/ /** * set_SR: Set the processor's status register, performing any necessary * additional actions (such as switching user/supervisor stacks). * * [Parameters] * state: Processor state block * value: New SR value * [Return value] * None */ static void set_SR(Q68State *state, uint16_t value) { const uint16_t old_value = state->SR; state->SR = value; if ((old_value ^ value) & SR_S) { if (value & SR_S) { // Switched to supervisor mode state->USP = state->A[7]; state->A[7] = state->SSP; } else { // Switched to user mode state->SSP = state->A[7]; state->A[7] = state->USP; } } check_interrupt(state); } /*************************************************************************/ /** * check_interrupt: Check whether an unmasked interrupt is pending, and * raise the appropriate exception if so. * * [Parameters] * state: Processor state block * [Return value] * None */ static inline void check_interrupt(Q68State *state) { const int irq = state->irq & 7; // Just to be safe if (UNLIKELY(irq > SR_GET_I(state) || irq == 7 // Level 7 is the non-maskable interrupt )) { if (state->halted != Q68_HALTED_DOUBLE_FAULT) { state->irq = 0; state->halted = 0; state->exception = EX_LEVEL_1_INTERRUPT + (irq-1); } } } /*-----------------------------------------------------------------------*/ /** * take_exception: Take an exception. * * [Parameters] * state: Processor state block * num: Exception number * [Return value] * Clock cycles used */ static int take_exception(Q68State *state, uint8_t num) { static const int exception_cycles[256] = { [EX_BUS_ERROR ] = 50, [EX_ADDRESS_ERROR ] = 50, [EX_ILLEGAL_INSTRUCTION ] = 34, [EX_DIVIDE_BY_ZERO ] = 42, [EX_CHK ] = 44, [EX_TRAPV ] = 34, [EX_PRIVILEGE_VIOLATION ] = 34, [EX_TRACE ] = 34, [EX_LINE_1010 ] = 34, // These two are assumed to be [EX_LINE_1111 ] = 34, // equal to ILLEGAL_INSTRUCTION [EX_SPURIOUS_INTERRUPT ] = 44, [EX_LEVEL_1_INTERRUPT ] = 44, [EX_LEVEL_2_INTERRUPT ] = 44, [EX_LEVEL_3_INTERRUPT ] = 44, [EX_LEVEL_4_INTERRUPT ] = 44, [EX_LEVEL_5_INTERRUPT ] = 44, [EX_LEVEL_6_INTERRUPT ] = 44, [EX_LEVEL_7_INTERRUPT ] = 44, [EX_TRAP+ 0 ] = 38, [EX_TRAP+ 1 ] = 38, [EX_TRAP+ 2 ] = 38, [EX_TRAP+ 3 ] = 38, [EX_TRAP+ 4 ] = 38, [EX_TRAP+ 5 ] = 38, [EX_TRAP+ 6 ] = 38, [EX_TRAP+ 7 ] = 38, [EX_TRAP+ 8 ] = 38, [EX_TRAP+ 9 ] = 38, [EX_TRAP+10 ] = 38, [EX_TRAP+11 ] = 38, [EX_TRAP+12 ] = 38, [EX_TRAP+13 ] = 38, [EX_TRAP+14 ] = 38, [EX_TRAP+15 ] = 38, }; /* Clear this out ahead of time in case we hit a double fault */ state->jit_running = NULL; if (!(state->SR & SR_S)) { state->USP = state->A[7]; state->A[7] = state->SSP; } #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->halted = Q68_HALTED_DOUBLE_FAULT; // Oops! return 0; } #endif PUSH32(state, state->PC); PUSH16(state, state->SR); if (num == EX_BUS_ERROR || num == EX_ADDRESS_ERROR) { PUSH16(state, state->fault_opcode); PUSH32(state, state->fault_addr); PUSH16(state, state->fault_status); } state->SR |= SR_S; if (num >= EX_LEVEL_1_INTERRUPT && num <= EX_LEVEL_7_INTERRUPT) { SR_SET_I(state, (num - EX_LEVEL_1_INTERRUPT) + 1); } state->PC = READU32(state, num*4); #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->PC & 1) { /* FIXME: Does a real 68000 double fault here or just take an * address error exception? */ state->halted = Q68_HALTED_DOUBLE_FAULT; return 0; } #endif return exception_cycles[num]; } /*************************************************************************/ /** * op_ill: Handle a generic illegal opcode. * * [Parameters] * state: Processor state block * opcode: Instruction opcode * [Return value] * Clock cycles used */ static inline int op_ill(Q68State *state, uint32_t opcode) { state->exception = EX_ILLEGAL_INSTRUCTION; return 0; } /*************************************************************************/ /*************************************************************************/ /** * ea_resolve: Resolve the address for the memory-reference EA indicated * by opcode[5:0], storing it in state->ea_addr. Behavior is undefined if * the EA is a direct register reference. * * [Parameters] * state: Processor state block * opcode: Instruction opcode * size: Access size (SIZE_*) * access_type: Access type (ACCESS_*) * [Return value] * Clock cycles used (negative indicates an illegal EA) */ static int ea_resolve(Q68State *state, uint32_t opcode, int size, int access_type) { const unsigned int mode = EA_MODE(opcode); const unsigned int reg = EA_REG(opcode); const unsigned int bytes = SIZE_TO_BYTES(size); static const int base_cycles[8] = {0, 0, 4, 4, 6, 8, 10, 0}; int cycles = base_cycles[mode] + (size==SIZE_L ? 4 : 0); switch (mode) { case EA_INDIRECT: state->ea_addr = state->A[reg]; break; case EA_POSTINCREMENT: state->ea_addr = state->A[reg]; state->A[reg] += bytes; if (bytes == 1 && reg == 7) { // A7 must stay even state->A[reg] += 1; } break; case EA_PREDECREMENT: if (access_type == ACCESS_WRITE) { /* 2-cycle penalty not applied to write-only accesses * (MOVE and MOVEM) */ cycles -= 2; } state->A[reg] -= bytes; if (bytes == 1 && reg == 7) { // A7 must stay even state->A[reg] -= 1; } state->ea_addr = state->A[reg]; break; case EA_DISPLACEMENT: state->ea_addr = state->A[reg] + (int16_t)IFETCH(state); break; case EA_INDEX: { const uint16_t ext = IFETCH(state); const unsigned int ireg = ext >> 12; // 0..15 const int32_t index = (ext & 0x0800) ? (int32_t)state->DA[ireg] : (int16_t)state->DA[ireg]; const int32_t disp = (int32_t)((int8_t)ext); state->ea_addr = state->A[reg] + index + disp; break; } default: /* case EA_MISC */ switch (reg) { case EA_MISC_ABSOLUTE_W: cycles += 8; state->ea_addr = (int16_t)IFETCH(state); break; case EA_MISC_ABSOLUTE_L: cycles += 12; state->ea_addr = IFETCH(state) << 16; state->ea_addr |= (uint16_t)IFETCH(state); break; case EA_MISC_PCREL: if (access_type != ACCESS_READ) { return -1; } else { cycles += 8; state->ea_addr = state->current_PC + (int16_t)IFETCH(state); } break; case EA_MISC_PCREL_INDEX: if (access_type != ACCESS_READ) { return -1; } else { cycles += 10; const uint16_t ext = IFETCH(state); const unsigned int ireg = ext >> 12; // 0..15 const int32_t index = (ext & 0x0800) ? (int32_t)state->DA[ireg] : (int16_t)state->DA[ireg]; const int32_t disp = (int32_t)((int8_t)ext); state->ea_addr = state->current_PC + index + disp; } break; case EA_MISC_IMMEDIATE: if (access_type != ACCESS_READ) { return -1; } else { cycles += 4; state->ea_addr = state->PC; if (size == SIZE_B) { state->ea_addr++; // Point at the lower byte } state->PC += (size==SIZE_L ? 4 : 2); } break; default: return -1; } } return cycles; } /*-----------------------------------------------------------------------*/ /** * ea_get: Read an unsigned value from the EA indicated by opcode[5:0]. * * If the EA selector is invalid for the access size and mode, an illegal * instruction exception is raised. If the EA is a memory reference, the * size is word or long, and the address is odd, an address error * exception is raised. In either case, the error is indicated by a * negative value returned in *cycles_ret. * * [Parameters] * state: Processor state block * opcode: Instruction opcode * size: Access size (SIZE_*) * is_rmw: Nonzero if the operand will be modified and written back * cycles_ret: Pointer to variable to receive clock cycles used * (negative indicates that an exception occurred) * [Return value] * Value read (undefined if an exception occurs) */ static uint32_t ea_get(Q68State *state, uint32_t opcode, int size, int is_rmw, int *cycles_ret) { const unsigned int reg = EA_REG(opcode); switch (EA_MODE(opcode)) { case EA_DATA_REG: *cycles_ret = 0; return size==SIZE_B ? (uint8_t) state->D[reg] : size==SIZE_W ? (uint16_t)state->D[reg] : state->D[reg]; case EA_ADDRESS_REG: if (size == SIZE_B) { /* An.b not permitted */ state->exception = EX_ILLEGAL_INSTRUCTION; *cycles_ret = -1; return 0; } else { *cycles_ret = 0; return size==SIZE_W ? (uint16_t)state->A[reg] : state->A[reg]; } default: { *cycles_ret = ea_resolve(state, opcode, size, is_rmw ? ACCESS_MODIFY : ACCESS_READ); if (*cycles_ret < 0) { state->exception = EX_ILLEGAL_INSTRUCTION; return 0; } if (size == SIZE_B) { return READU8(state, state->ea_addr); } else { #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->ea_addr & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->ea_addr; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ; *cycles_ret = -1; return 0; } #endif return size==SIZE_W ? READU16(state, state->ea_addr) : READU32(state, state->ea_addr); } } } } /*-----------------------------------------------------------------------*/ /** * ea_set: Update a value at the EA indicated by opcode[5:0]. If the * EA is a memory reference, uses the previously resolved address in * state->ea_addr rather than resolving the address again. Behavior is * undefined if the previous ea_resolve() or ea_get() failed (or if no * previous call was made). * * If the EA is a memory reference, the size is word or long, and the * address is odd, an address error exception is raised instead. * * [Parameters] * state: Processor state block * opcode: Instruction opcode * size: Access size (SIZE_*) * data: Value to store * [Return value] * None */ static void ea_set(Q68State *state, uint32_t opcode, int size, uint32_t data) { const unsigned int reg = EA_REG(opcode); switch (EA_MODE(opcode)) { case EA_DATA_REG: switch (size) { case SIZE_B: *(BYTE_OFS + (uint8_t *)&state->D[reg]) = data; break; case SIZE_W: *(WORD_OFS + (uint16_t *)&state->D[reg]) = data; break; default: state->D[reg] = data; break; } return; case EA_ADDRESS_REG: if (size == SIZE_W) { state->A[reg] = (int16_t)data; // Sign-extended for address regs } else { // must be SIZE_L state->A[reg] = data; } return; default: { if (size == SIZE_B) { WRITE8(state, state->ea_addr, data); } else { #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->ea_addr & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->ea_addr; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE; return; } else #endif if (size == SIZE_W) { WRITE16(state, state->ea_addr, data); } else { WRITE32(state, state->ea_addr, data); } } return; } } } /*************************************************************************/ /*********************** Major instruction groups ************************/ /*************************************************************************/ /** * op_imm: Immediate instructions (format 0000 xxx0 xxxx xxxx). */ static int op_imm(Q68State *state, uint32_t opcode) { /* Check for bit-twiddling and illegal opcodes first */ enum {OR = 0, AND, SUB, ADD, _BIT, EOR, CMP, _ILL} aluop; aluop = opcode>>9 & 7; if (aluop == _BIT) { return op_bit(state, opcode); } else if (aluop == _ILL) { return op_ill(state, opcode); } /* Get the instruction size */ INSN_GET_SIZE; if (size == 3) { return op_ill(state, opcode); } const int bytes = SIZE_TO_BYTES(size); const int shift = bytes*8 - 1; const uint32_t valuemask = ~(~1 << shift); /* Fetch the immediate value */ uint32_t imm = (uint16_t)IFETCH(state); if (size == SIZE_B) { imm &= 0xFF; } else if (size == SIZE_L) { imm = imm<<16 | (uint16_t)IFETCH(state); } /* Fetch the EA operand (which may be SR or CCR) */ int use_SR; int cycles; uint32_t ea_val; if ((aluop==OR || aluop==AND || aluop==EOR) && (opcode & 0x3F) == 0x3C) { /* xxxI #imm,SR (or CCR) use the otherwise-invalid form of an * immediate value destination */ if (size == SIZE_W && !(state->SR & SR_S)) { state->exception = EX_PRIVILEGE_VIOLATION; return 0; } use_SR = 1; cycles = 8; // Total instruction time is 20 cycles switch (size) { case SIZE_B: ea_val = state->SR & 0xFF; break; case SIZE_W: ea_val = state->SR; break; default: return op_ill(state, opcode); } } else { use_SR = 0; ea_val = ea_get(state, opcode, size, 1, &cycles); if (cycles < 0) { return 0; } } /* Perform the operation */ uint32_t result; if (aluop == ADD || aluop == SUB) { INSN_CLEAR_XCC(); } else { INSN_CLEAR_CC(); } switch (aluop) { case OR: result = ea_val | imm; break; case AND: result = ea_val & imm; break; case EOR: result = ea_val ^ imm; break; case CMP: if (size == SIZE_L) { // CMPI takes less time in most cases if (EA_MODE(opcode) != EA_DATA_REG) { cycles -= 8; } else { cycles -= 2; } } else { if (EA_MODE(opcode) != EA_DATA_REG) { cycles -= 4; } } /* fall through to... */ case SUB: { result = (ea_val - imm) & valuemask; if (((imm ^ ea_val) & (result ^ ea_val)) >> shift) { state->SR |= SR_V; } if ((int)((imm >> shift) - (ea_val >> shift) + (result >> shift)) > 0) { state->SR |= SR_C; if (aluop != CMP) { state->SR |= SR_X; } } break; } default: // case ADD result = (ea_val + imm) & valuemask; if (((ea_val ^ result) & (imm ^ result)) >> shift) { state->SR |= SR_V; } if ((int)((ea_val >> shift) + (imm >> shift) - (result >> shift)) > 0) { state->SR |= SR_X | SR_C; } break; } INSN_SETNZ_SHIFT(result); /* Update the EA operand (if not CMPI) */ if (aluop != CMP) { if (use_SR) { if (size == SIZE_W) { set_SR(state, result); } else { state->SR &= 0xFF00; state->SR |= result; } } else { ea_set(state, opcode, size, result); } } /* All done */ return (size==SIZE_L ? 16 : 8) + (EA_MODE(opcode) == EA_DATA_REG ? 0 : 4) + cycles; } /*************************************************************************/ /** * op_bit: Bit-twiddling instructions (formats 0000 rrr1 xxxx xxxx and * 0000 1000 xxxx xxxx). */ static int op_bit(Q68State *state, uint32_t opcode) { /* Check early for MOVEP (coded as BTST/BCHG/BCLR/BSET Dn,An) */ if (EA_MODE(opcode) == EA_ADDRESS_REG) { if (opcode & 0x0100) { return opMOVP(state, opcode); } else { return op_ill(state, opcode); } } enum {BTST = 0, BCHG = 1, BCLR = 2, BSET = 3} op = opcode>>6 & 3; int cycles; /* Get the bit number to operate on */ unsigned int bitnum; if (opcode & 0x0100) { /* Bit number in register */ INSN_GET_REG; bitnum = state->D[reg]; cycles = 0; } else { bitnum = IFETCH(state); cycles = 4; } /* EA operand is 32 bits when coming from a register, 8 when from memory */ int size; switch (EA_MODE(opcode)) { case EA_DATA_REG: size = SIZE_L; bitnum %= 32; break; default: size = SIZE_B; bitnum %= 8; break; } int cycles_tmp; uint32_t value = ea_get(state, opcode, size, 1, &cycles_tmp); if (cycles_tmp < 0) { return 0; } cycles += cycles_tmp; if (size == SIZE_L && (op == BCLR || op == BTST)) { cycles += 2; } /* Perform the operation */ if ((value >> bitnum) & 1) { state->SR &= ~SR_Z; } else { state->SR |= SR_Z; } switch (op) { case BTST: /* Nothing to do */ break; case BCHG: value ^= 1 << bitnum; break; case BCLR: value &= ~(1 << bitnum); break; case BSET: value |= 1 << bitnum; break; } /* Update EA operand (if not BTST) */ if (op != BTST) { ea_set(state, opcode, size, value); } /* Return cycle count; note that the times for BCHG.L, BCLR.L, and * BSET.L are maximums (though how they vary is undocumented) */ return (op==BTST ? 4 : 8) + cycles; } /*************************************************************************/ /** * opMOVE: MOVE.[bwl] instruction (format {01,10,11}xx xxxx xxxx xxxx). */ static int opMOVE(Q68State *state, uint32_t opcode) { const int size = (opcode>>12==1 ? SIZE_B : opcode>>12==2 ? SIZE_L : SIZE_W); int cycles_src; const uint32_t data = ea_get(state, opcode, size, 0, &cycles_src); if (cycles_src < 0) { return 0; } /* Rearrange the opcode bits so we can pass the destination EA to * ea_resolve() */ const uint32_t dummy_opcode = (opcode>>9 & 7) | (opcode>>3 & 0x38); int cycles_dest; if (EA_MODE(dummy_opcode) <= EA_ADDRESS_REG) { cycles_dest = 0; } else { cycles_dest = ea_resolve(state, dummy_opcode, size, ACCESS_WRITE); if (cycles_dest < 0) { return op_ill(state, opcode); } } /* Update condition codes if the target is not an address register */ if (EA_MODE(dummy_opcode) != EA_ADDRESS_REG) { INSN_CLEAR_CC(); INSN_SETNZ(size==SIZE_B ? (int8_t)data : size==SIZE_W ? (int16_t)data : (int32_t)data); } /* Update the destination EA and return */ ea_set(state, dummy_opcode, size, data); return 4 + cycles_src + cycles_dest; } /*************************************************************************/ /** * op4xxx: Miscellaneous instructions (format 0100 xxx0 xxxx xxxx). */ static int op4xxx(Q68State *state, uint32_t opcode) { const unsigned int index = (opcode>>7 & 0x1C) | (opcode>>6 & 3); #ifdef COUNT_OPCODES q68_4xxx_ops[index]++; #endif return (*opcode_4xxx_table[index])(state, opcode); } /*************************************************************************/ /** * op_CHK: CHK instruction (format 0100 rrr1 10xx xxxx). */ static int op_CHK(Q68State *state, uint32_t opcode) { INSN_GET_REG; int size = SIZE_W; // Bit 7 == 0 indicates long mode on the 68020 int cycles; int32_t upper; // Yes, it's signed if (EA_MODE(opcode) == EA_ADDRESS_REG) { return op_ill(state, opcode); } upper = ea_get(state, opcode, size, 0, &cycles); if (cycles < 0) { return 0; } if (size == SIZE_W) { upper = (int32_t)(int16_t)upper; } int32_t value; if (size == SIZE_W) { value = (int32_t)(int16_t)state->D[reg]; } else { value = (int32_t)state->D[reg]; } if (value < 0) { state->SR |= SR_N; state->exception = EX_CHK; return cycles; } else if (value > upper) { state->SR &= ~SR_N; state->exception = EX_CHK; return cycles; } return 10 + cycles; } /*************************************************************************/ /** * op_LEA: LEA instruction (format 0100 rrr1 11xx xxxx). */ static int op_LEA(Q68State *state, uint32_t opcode) { INSN_GET_REG; /* Register, predecrement, postincrement, immediate modes are illegal */ if (EA_MODE(opcode) == EA_DATA_REG || EA_MODE(opcode) == EA_ADDRESS_REG || EA_MODE(opcode) == EA_POSTINCREMENT || EA_MODE(opcode) == EA_PREDECREMENT || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE) ) { return op_ill(state, opcode); } int cycles = ea_resolve(state, opcode, SIZE_W, ACCESS_READ); if (cycles < 0) { return op_ill(state, opcode); } if (cycles % 4 == 2) { // d(An,ix) and d(PC,ix) take 2 extra cycles cycles += 2; } state->A[reg] = state->ea_addr; return cycles; } /*************************************************************************/ /** * opADSQ: ADDQ and SUBQ instructions (format 0101 iiix xxxx xxxx). */ static int opADSQ(Q68State *state, uint32_t opcode) { const int is_sub = opcode & 0x0100; INSN_GET_COUNT; INSN_GET_SIZE; if (EA_MODE(opcode) == EA_ADDRESS_REG && size == 1) { size = 2; // ADDQ.W #imm,An is equivalent to ADDQ.L #imm,An } const int bytes = SIZE_TO_BYTES(size); const int shift = bytes*8 - 1; const uint32_t valuemask = ~(~1 << shift); int cycles; uint32_t data = ea_get(state, opcode, size, 1, &cycles); if (cycles < 0) { return 0; } uint32_t result; if (is_sub) { result = data - count; } else { result = data + count; } result &= valuemask; if (EA_MODE(opcode) != EA_ADDRESS_REG) { INSN_CLEAR_XCC(); INSN_SETNZ_SHIFT(result); if ((is_sub ? ~result & data : result & ~data) >> shift) { state->SR |= SR_V; } if ((is_sub ? result & ~data : ~result & data) >> shift) { state->SR |= SR_X | SR_C; } } ea_set(state, opcode, size, result); return (size==SIZE_L || EA_MODE(opcode) == EA_ADDRESS_REG ? 8 : 4) + (EA_MODE(opcode) >= EA_INDIRECT ? 4 : 0) + cycles; } /*************************************************************************/ /** * op_Scc: Scc instruction (format 0101 cccc 11xx xxxx). */ static int op_Scc(Q68State *state, uint32_t opcode) { if (EA_MODE(opcode) == EA_ADDRESS_REG) { /* DBcc Dn,disp is coded as Scc An with an extension word */ return opDBcc(state, opcode); } INSN_GET_COND; const int is_true = INSN_COND_TRUE(cond); /* From the cycle counts, it looks like this is a standard read/write * access rather than a write-only access */ int cycles; if (EA_MODE(opcode) == EA_DATA_REG) { cycles = 0; } else { cycles = ea_resolve(state, opcode, SIZE_B, ACCESS_MODIFY); if (cycles < 0) { return op_ill(state, opcode); } } ea_set(state, opcode, SIZE_B, is_true ? 0xFF : 0x00); if (EA_MODE(opcode) == EA_DATA_REG) { /* Scc Dn is a special case */ return is_true ? 6 : 4; } else { return 8 + cycles; } } /*-----------------------------------------------------------------------*/ /** * op_DBcc: DBcc instruction (format 0101 cccc 1100 1xxx). */ static int opDBcc(Q68State *state, uint32_t opcode) { INSN_GET_COND; const int is_true = INSN_COND_TRUE(cond); INSN_GET_REG0; INSN_GET_IMM16; if (is_true) { return 12; } else if (--(*(WORD_OFS + (int16_t *)&state->D[reg0])) == -1) { return 14; } else { state->PC = state->current_PC + imm16; return 10; } } /*************************************************************************/ /** * op_Bcc: Conditional branch instructions (format 0110 cccc dddd dddd). */ static int op_Bcc(Q68State *state, uint32_t opcode) { INSN_GET_COND; INSN_GET_DISP8; int cycles = 0; if (disp == 0) { disp = (int16_t)IFETCH(state); cycles = 4; } if (cond == COND_F) { /* BF is really BSR */ #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->A[7]; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE; return 0; } #endif PUSH32(state, state->PC); state->PC = state->current_PC + disp; return 18; } else if (INSN_COND_TRUE(cond)) { state->PC = state->current_PC + disp; return 10; } else { return 8 + cycles; } } /*************************************************************************/ /** * opMOVQ: MOVEQ instruction (format 0111 rrr0 iiii iiii). */ static int opMOVQ(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_IMM8; state->D[reg] = imm8; INSN_CLEAR_CC(); INSN_SETNZ(imm8); return 4; } /*************************************************************************/ /** * op_alu: Non-immediate ALU instructions (format 1ooo rrrx xxxx xxxx for * ooo = 000, 001, 011, 100, 101). */ static int op_alu(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_SIZE; /* Pass off special and invalid instructions early */ if (size != 3) { if ((opcode & 0xB130) == 0x9100) { /* ADDX/SUBX are coded as ADD/SUB.* Dn, */ return opADSX(state, opcode); } if ((opcode & 0xB1F0) == 0x8100) { /* ABCD/SBCD are coded as AND/OR.b Dn, */ return op_BCD(state, opcode); } if ((opcode & 0xF130) == 0xC100) { /* EXG is coded as AND.[wl] Dn, */ return op_EXG(state, opcode); } if ((opcode & 0xF130) == 0x8100) { /* OR.[wl] Dn, is invalid on the 68000 (later PACK/UNPK) */ return op_ill(state, opcode); } if ((opcode & 0xF138) == 0xB108 && (opcode>>6 & 3) != 3) { /* CMPM is coded as EOR.* Dn, */ return opCMPM(state, opcode); } } const int bytes = SIZE_TO_BYTES(size); const int shift = bytes*8 - 1; const uint32_t valuemask = ~(~1 << shift); int ea_dest = opcode & 0x100; int areg_dest = 0; // For ADDA/SUBA/CMPA enum {OR, AND, EOR, CMP, SUB, ADD} aluop; /* Find the instruction for the opcode group */ switch (opcode>>12) { case 0x8: aluop = OR; break; case 0x9: aluop = SUB; break; case 0xB: aluop = (((opcode>>6)+1) & 7) <= 4 ? CMP : EOR; break; case 0xC: aluop = AND; break; default: aluop = ADD; break; // case 0xD } /* Handle the special formats of ADDA/SUBA/CMPA */ if ((aluop == ADD || aluop == SUB || aluop == CMP) && size == 3) { size = ea_dest ? SIZE_L : SIZE_W; ea_dest = 0; areg_dest = 1; } /* Retrieve the register and EA values */ uint32_t reg_val = areg_dest ? state->A[reg] : (state->D[reg] & valuemask); int cycles; uint32_t ea_val = ea_get(state, opcode, size, ea_dest, &cycles); if (cycles < 0) { return 0; } if (size == SIZE_L || areg_dest) { cycles += 4; } if (ea_dest) { cycles += 4; } else if ((aluop == CMP && areg_dest) || (size == SIZE_L && (EA_MODE(opcode) <= EA_ADDRESS_REG || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE)))) { cycles -= 2; } /* Perform the actual computation */ uint32_t result; if (!areg_dest || aluop == CMP) { if (aluop == ADD || aluop == SUB) { INSN_CLEAR_XCC(); } else { INSN_CLEAR_CC(); } } switch (aluop) { case OR: result = reg_val | ea_val; break; case AND: result = reg_val & ea_val; break; case EOR: result = reg_val ^ ea_val; break; case CMP: /* fall through to... */ case SUB: { uint32_t src, dest; if (areg_dest) { /* CMPA/SUBA keep all 32 bits, and SUBA doesn't * touch flags */ src = ea_val; dest = reg_val; result = reg_val - ea_val; if (aluop == SUB) { break; } } else { if (ea_dest) { src = reg_val; dest = ea_val; } else { src = ea_val; dest = reg_val; } result = (dest - src) & valuemask; } if (((src ^ dest) & (result ^ dest)) >> shift) { state->SR |= SR_V; } if ((int)((src >> shift) - (dest >> shift) + (result >> shift)) > 0) { state->SR |= SR_C; if (aluop != CMP) { state->SR |= SR_X; } } break; } default: // case ADD if (areg_dest) { /* ADDA keeps all 32 bits and doesn't touch flags */ result = reg_val + ea_val; break; } result = (reg_val + ea_val) & valuemask; if (((reg_val ^ result) & (ea_val ^ result)) >> shift) { state->SR |= SR_V; } if ((int)((reg_val >> shift) + (ea_val >> shift) - (result >> shift)) > 0) { state->SR |= SR_X | SR_C; } break; } // switch (aluop) if (!areg_dest || aluop == CMP) { INSN_SETNZ_SHIFT(result); } /* Store the result in the proper place (if the instruction is not CMP) */ if (aluop != CMP) { if (ea_dest) { ea_set(state, opcode, size, result); } else if (areg_dest) { state->A[reg] = result; } else if (size == SIZE_B) { *(BYTE_OFS + (uint8_t *)&state->D[reg]) = result; } else if (size == SIZE_W) { *(WORD_OFS + (uint16_t *)&state->D[reg]) = result; } else { // size == SIZE_L state->D[reg] = result; } } return 4 + cycles; } /*************************************************************************/ /** * op_DIV: DIVU and DIVS instructions (format 1000 rrrx 11xx xxxx). */ static int op_DIV(Q68State *state, uint32_t opcode) { INSN_GET_REG; const int sign = opcode & (1<<8); state->SR &= ~SR_C; // Always cleared, even on exception int cycles; const uint16_t divisor = ea_get(state, opcode, SIZE_W, 0, &cycles); if (cycles < 0) { return 0; } if (divisor == 0) { state->exception = EX_DIVIDE_BY_ZERO; return cycles; } int32_t quotient, remainder; if (sign) { quotient = (int32_t)state->D[reg] / (int16_t)divisor; remainder = (int32_t)state->D[reg] % (int16_t)divisor; if (quotient < -0x8000 || quotient > 0x7FFF) { state->SR |= SR_V; } else { state->SR &= ~SR_V; } } else { quotient = state->D[reg] / divisor; remainder = state->D[reg] % divisor; if (quotient & 0xFFFF0000) { state->SR |= SR_V; } else { state->SR &= ~SR_V; } } if (!(state->SR & SR_V)) { state->D[reg] = (quotient & 0xFFFF) | (remainder << 16); if (quotient & 0x8000) { state->SR |= SR_N; } else { state->SR &= ~SR_N; } if (quotient == 0) { state->SR |= SR_Z; } else { state->SR &= ~SR_Z; } } /* The 68000 docs say that the timing difference between best and * worst cases is less than 10%, so we just return the worst case */ return (sign ? 158 : 140) + cycles; } /*************************************************************************/ /** * opAxxx: $Axxx illegal instruction set (format 1010 xxxx xxxx xxxx). */ static int opAxxx(Q68State *state, uint32_t opcode) { state->exception = EX_LINE_1010; return 0; } /*************************************************************************/ /** * op_MUL: MULU and MULS instructions (format 1100 rrrx 11xx xxxx). */ static int op_MUL(Q68State *state, uint32_t opcode) { INSN_GET_REG; const int sign = opcode & (1<<8); int cycles; const uint16_t data = ea_get(state, opcode, SIZE_W, 0, &cycles); if (cycles < 0) { return 0; } if (sign) { state->D[reg] = (int16_t)state->D[reg] * (int16_t)data; } else { state->D[reg] = (uint16_t)state->D[reg] * data; } INSN_CLEAR_CC(); INSN_SETNZ(state->D[reg]); /* Precise timing varies with the effective address; the algorithm is * implemented below for reference, but for typical usage it's probably * not important to be exact */ #ifdef MUL_PRECISE_TIMING // not normally defined if (sign) { uint32_t temp; for (temp = (uint32_t)data << 1; temp != 0; temp >>= 1) { if ((temp & 3) == 1 || (temp & 3) == 2) { cycles += 2; } } } else { unsigned int temp; for (temp = data; temp != 0; temp >>= 1) { if (temp & 1) { cycles += 2; } } } return 38 + cycles; #else // !MUL_PRECISE_TIMING return 54 + cycles; #endif } /*************************************************************************/ /** * opshft: Shift and rotate instructions (format 1110 xxxx xxxx xxxx). */ static int opshft(Q68State *state, uint32_t opcode) { const int is_left = opcode & 0x0100; INSN_GET_SIZE; INSN_GET_COUNT; INSN_GET_REG0; int is_memory; int type; // Shift/rotate type (0=ASL/ASR, 1=LSL/LSR, ...) uint32_t data; int cycles; if (size == 3) { /* Memory shift/rotate */ is_memory = 1; if ((opcode & 0x0800) || EA_MODE(opcode) <= EA_ADDRESS_REG) { return op_ill(state, opcode); } size = SIZE_W; type = opcode>>9 & 3; count = 1; data = ea_get(state, opcode, size, 1, &cycles); if (cycles < 0) { return 0; } } else { /* Register shift/rotate */ is_memory = 0; type = opcode>>3 & 3; if (opcode & 0x0020) { INSN_GET_REG; count = state->D[reg] & 63; } data = size==SIZE_B ? (uint8_t) state->D[reg0] : size==SIZE_W ? (uint16_t)state->D[reg0] : state->D[reg0]; cycles = 0; } cycles += count*2; INSN_CLEAR_CC(); if (count > 0) { const int nbits = (size==SIZE_B ? 8 : size==SIZE_W ? 16 : 32); switch (type) { case 0: // ASL/ASR state->SR &= ~SR_X; if (is_left) { int V = 0, C; /* Have to shift bit by bit to detect overflow */ for (; count > 0; count--) { C = (data >> (nbits-1)) & 1; data <<= 1; V |= (C ^ (data >> (nbits-1))) & 1; } if (V) { state->SR |= SR_V; } if (C) { state->SR |= SR_X | SR_C; } } else { if (size == SIZE_B) { // Sign extend if necessary data = (int8_t)data; } else if (size == SIZE_W) { data = (int16_t)data; } if (count > nbits) { count = 32; // Some systems break with a shift count >32 } data = (int32_t)data >> (count-1); if (data & 1) { state->SR |= SR_X | SR_C; } data = (int32_t)data >> 1; } break; case 1: // LSL/LSR state->SR &= ~SR_X; if (count > nbits) { data = 0; } else if (is_left) { data <<= count-1; if ((data >> (nbits-1)) & 1) { state->SR |= SR_X | SR_C; } data <<= 1; } else { data = (int32_t)data >> (count-1); if (data & 1) { state->SR |= SR_X | SR_C; } data = (int32_t)data >> 1; } break; case 2: { // ROXL/ROXR uint32_t X = (state->SR >> SR_X_SHIFT) & 1; state->SR &= ~SR_X; if (is_left) { for (; count > 0; count--) { const int new_X = (data >> (nbits-1)) & 1; data = (data << 1) | X; X = new_X; } } else { for (; count > 0; count--) { const int new_X = data & 1; data = (data >> 1) | (X << (nbits-1)); X = new_X; } } if (X) { state->SR |= SR_C | SR_X; } break; } default: { // (case 3) ROL/ROR count %= nbits; if (is_left) { data = (data << count) | (data >> (nbits - count)); if ((data >> (nbits-1)) & 1) { state->SR |= SR_C; } data <<= 1; } else { data = (data >> count) | (data << (nbits - count)); if (data & 1) { state->SR |= SR_C; } data = (int32_t)data >> 1; } break; } } // switch (type) } else { // count == 0 if (type == 2 && (state->SR & SR_X)) { state->SR |= SR_C; } } INSN_SETNZ(size==SIZE_B ? (int8_t) data : size==SIZE_W ? (int16_t)data : data); if (is_memory) { ea_set(state, opcode, size, data); } else { switch (size) { case SIZE_B: *(BYTE_OFS + (uint8_t *)&state->D[reg0]) = data; break; case SIZE_W: *(WORD_OFS + (uint16_t *)&state->D[reg0]) = data; break; default: state->D[reg0] = data; break; } } return (size==SIZE_L ? 8 : 6) + cycles; } /*************************************************************************/ /** * opFxxx: $Fxxx illegal instruction set (format 1111 xxxx xxxx xxxx). */ static int opFxxx(Q68State *state, uint32_t opcode) { state->exception = EX_LINE_1111; return 0; } /*************************************************************************/ /*********************** $4xxx group instructions ************************/ /*************************************************************************/ /** * op4alu: Single-operand ALU instructions in the $4xxx opcode range * (format 0100 ooo0 ssxx xxxx for ooo = 000, 001, 010, 011, 101). */ static int op4alu(Q68State *state, uint32_t opcode) { INSN_GET_SIZE; const int bytes = SIZE_TO_BYTES(size); const int shift = bytes*8 - 1; const uint32_t valuemask = ~(~1 << shift); enum {NEGX = 0, CLR = 1, NEG = 2, NOT = 3, TST = 5} aluop; aluop = opcode>>9 & 7; if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed return op_ill(state, opcode); } /* Retrieve the EA value */ int cycles; uint32_t value = ea_get(state, opcode, size, 1, &cycles); if (cycles < 0) { return 0; } if (aluop != TST) { if (EA_MODE(opcode) == EA_DATA_REG) { if (size == SIZE_L) { cycles += 2; } } else { cycles += (size == SIZE_L) ? 8 : 4; } } /* Perform the actual computation */ uint32_t result; if (aluop == NEGX) { state->SR &= ~(SR_N | SR_V | SR_C); // Z is never set, only cleared } else { INSN_CLEAR_CC(); } switch (aluop) { case NEGX: { int X = (state->SR >> SR_X_SHIFT) & 1; result = (0 - value - X) & valuemask; if (result != 0) { state->SR &= ~SR_Z; } goto NEG_common; } case NEG: result = (0 - value) & valuemask; if (result == 0) { state->SR |= SR_Z; } else { state->SR &= ~SR_Z; } NEG_common: if (result >> shift) { state->SR |= SR_N; } if ((value & result) >> shift) { state->SR |= SR_V; } if ((value | result) != 0) { state->SR |= SR_X | SR_C; } else { state->SR &= ~SR_X; } break; case CLR: result = 0; state->SR |= SR_Z; break; case NOT: result = ~value & valuemask; INSN_SETNZ_SHIFT(result); break; default: // case TST result = value; // Avoid a compiler warning INSN_SETNZ_SHIFT(value); break; } // switch (aluop) /* Store the result in the proper place (if the instruction is not TST) */ if (aluop != TST) { ea_set(state, opcode, size, result); } return 4 + cycles; } /*************************************************************************/ /** * opMVSR: MOVE to/from SR/CCR instructions (format 0100 0xx0 11xx xxxx). */ static int opMVSR(Q68State *state, uint32_t opcode) { int is_CCR; int ea_dest; int cycles; switch (opcode>>9 & 3) { case 0: // MOVE SR, is_CCR = 0; ea_dest = 1; cycles = (EA_MODE(opcode) == EA_DATA_REG) ? 6 : 8; break; case 1: // Undefined (MOVE CCR, on 68010) return op_ill(state, opcode); case 2: // MOVE ,CCR is_CCR = 1; ea_dest = 0; cycles = 12; break; default: // MOVE ,SR (case 3) if (!(state->SR & SR_S)) { state->exception = EX_PRIVILEGE_VIOLATION; return 0; } is_CCR = 0; ea_dest = 0; cycles = 12; break; } if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed return op_ill(state, opcode); } /* Motorola docs say the address is read before being written, even * for the SR, format; also, the access size is a word even for * CCR operations. */ int cycles_tmp; uint16_t value = ea_get(state, opcode, SIZE_W, ea_dest, &cycles_tmp); if (cycles_tmp < 0) { return 0; } cycles += cycles_tmp; if (ea_dest) { uint16_t value = state->SR; if (is_CCR) { value &= 0x00FF; } ea_set(state, opcode, SIZE_W, value); } else { if (!is_CCR) { set_SR(state, value); } } return cycles; } /*************************************************************************/ /** * opNBCD: NBCD instruction (format 0100 1000 00xx xxxx). */ static int opNBCD(Q68State *state, uint32_t opcode) { if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed return op_ill(state, opcode); } int cycles; int value = ea_get(state, opcode, SIZE_B, 1, &cycles); if (cycles < 0) { return 0; } int result; int X = (state->SR >> SR_X_SHIFT) & 1; state->SR &= ~(SR_X | SR_C); // Z is never set, only cleared /* Slightly convoluted to match what a real 68000 does (see SBCD) */ int res_low = 0 - (value & 0x0F) - X; int borrow = 0; if (res_low < 0) { res_low += 10; borrow = 1<<4; } int res_high = 0 - (value & 0xF0) - borrow; if (res_high < 0) { res_high += 10<<4; state->SR |= SR_X | SR_C; } result = res_high + res_low; if (result < 0) { state->SR |= SR_X | SR_C; } result &= 0xFF; if (result != 0) { state->SR &= ~SR_Z; } ea_set(state, opcode, SIZE_B, result); return (EA_MODE(opcode) == EA_DATA_REG ? 6 : 8) + cycles; } /*************************************************************************/ /** * op_PEA: PEA instruction (format 0100 1000 01xx xxxx). */ static int op_PEA(Q68State *state, uint32_t opcode) { /* SWAP is coded as PEA Dn */ if (EA_MODE(opcode) == EA_DATA_REG) { return opSWAP(state, opcode); } if (EA_MODE(opcode) == EA_DATA_REG || EA_MODE(opcode) == EA_ADDRESS_REG || EA_MODE(opcode) == EA_POSTINCREMENT || EA_MODE(opcode) == EA_PREDECREMENT || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE) ) { return op_ill(state, opcode); } int cycles = ea_resolve(state, opcode, SIZE_W, ACCESS_READ); if (cycles < 0) { return op_ill(state, opcode); } if (cycles % 4 == 2) { // d(An,ix) and d(PC,ix) take 2 extra cycles cycles += 2; } #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->A[7]; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE; return 0; } #endif PUSH32(state, state->ea_addr); return 8 + cycles; } /*************************************************************************/ /** * opSWAP: SWAP instruction (format 0100 1000 0100 0rrr). */ static int opSWAP(Q68State *state, uint32_t opcode) { INSN_GET_REG0; state->D[reg0] = state->D[reg0]>>16 | state->D[reg0]<<16; INSN_CLEAR_CC(); INSN_SETNZ(state->D[reg0]); return 4; } /*************************************************************************/ /** * op_TAS: TAS instruction (format 0100 1010 11xx xxxx). Also covers the * ILLEGAL instruction (format 0100 1010 1111 1100). */ static int op_TAS(Q68State *state, uint32_t opcode) { if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed return op_ill(state, opcode); } int cycles; int8_t value = ea_get(state, opcode, SIZE_B, 1, &cycles); if (cycles < 0) { /* Note that the ILLEGAL instruction is coded as TAS #imm, so it * will be rejected as unwriteable by ea_get() */ return 0; } INSN_CLEAR_CC(); INSN_SETNZ(value); ea_set(state, opcode, SIZE_B, value | 0x80); return (EA_MODE(opcode) == EA_DATA_REG ? 4 : 10) + cycles; } /*************************************************************************/ /** * op_EXT: EXT instruction (format 0100 1000 1s00 0rrr). */ static int op_EXT(Q68State *state, uint32_t opcode) { INSN_GET_REG0; INSN_CLEAR_CC(); if (opcode & 0x0040) { int16_t value = (int16_t)state->D[reg0]; state->D[reg0] = (int32_t)value; INSN_SETNZ(value); } else { int8_t value = (int16_t)state->D[reg0]; *(WORD_OFS + (int16_t *)&state->D[reg0]) = (int16_t)value; INSN_SETNZ(value); } return 4; } /*************************************************************************/ /** * op_STM: MOVEM reglist, (i.e. STore Multiple) instruction (format * 0100 1000 1sxx xxxx). */ static int op_STM(Q68State *state, uint32_t opcode) { /* EXT.* is coded as MOVEM.* reglist,Dn */ if (EA_MODE(opcode) == EA_DATA_REG) { return op_EXT(state, opcode); } unsigned int regmask = IFETCH(state); int size = (opcode & 0x0040) ? SIZE_L : SIZE_W; if (EA_MODE(opcode) <= EA_ADDRESS_REG || EA_MODE(opcode) == EA_POSTINCREMENT // Not allowed for store ) { return op_ill(state, opcode); } /* Avoid modifying the register during address resolution */ uint16_t safe_ea; if (EA_MODE(opcode) == EA_PREDECREMENT) { safe_ea = EA_INDIRECT<<3 | EA_REG(opcode); } else { safe_ea = opcode; } int cycles = ea_resolve(state, safe_ea, SIZE_W, ACCESS_WRITE); if (cycles < 0) { return op_ill(state, opcode); } #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->ea_addr & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->ea_addr; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE; return 0; } #endif if (EA_MODE(opcode) == EA_PREDECREMENT) { /* Register order is reversed in predecrement mode */ int reg; for (reg = 15; reg >= 0; reg--, regmask >>= 1) { if (regmask & 1) { if (size == SIZE_W) { state->ea_addr -= 2; WRITE16(state, state->ea_addr, state->DA[reg]); cycles += 4; } else { state->ea_addr -= 4; WRITE32(state, state->ea_addr, state->DA[reg]); cycles += 8; } } } state->A[EA_REG(opcode)] = state->ea_addr; } else { int reg; for (reg = 0; reg < 16; reg++, regmask >>= 1) { if (regmask & 1) { if (size == SIZE_W) { WRITE16(state, state->ea_addr, state->DA[reg]); state->ea_addr += 2; cycles += 4; } else { WRITE32(state, state->ea_addr, state->DA[reg]); state->ea_addr += 4; cycles += 8; } } } } return 4 + cycles; } /*-----------------------------------------------------------------------*/ /** * op_LDM: MOVEM ,reglist (i.e. LoaD Multiple) instruction (format * 0100 1100 1sxx xxxx). */ static int op_LDM(Q68State *state, uint32_t opcode) { unsigned int regmask = IFETCH(state); int size = (opcode & 0x0040) ? SIZE_L : SIZE_W; if (EA_MODE(opcode) <= EA_ADDRESS_REG || EA_MODE(opcode) == EA_PREDECREMENT // Not allowed for load ) { return op_ill(state, opcode); } /* Avoid modifying the register during address resolution */ uint16_t safe_ea; if (EA_MODE(opcode) == EA_POSTINCREMENT) { safe_ea = EA_INDIRECT<<3 | EA_REG(opcode); } else { safe_ea = opcode; } int cycles = ea_resolve(state, safe_ea, SIZE_W, ACCESS_READ); if (cycles < 0) { return op_ill(state, opcode); } #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->ea_addr & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->ea_addr; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ; return 0; } #endif int reg; for (reg = 0; reg < 16; reg++, regmask >>= 1) { if (regmask & 1) { if (size == SIZE_W) { int16_t value = READS16(state, state->ea_addr); if (reg < 8) { *(WORD_OFS + (uint16_t *)&state->D[reg]) = value; } else { state->A[reg-8] = (int32_t)value; } state->ea_addr += 2; cycles += 4; } else { state->DA[reg] = READU32(state, state->ea_addr); state->ea_addr += 4; cycles += 8; } } } if (EA_MODE(opcode) == EA_POSTINCREMENT) { state->A[EA_REG(opcode)] = state->ea_addr; } return 8 + cycles; } /*************************************************************************/ /** * opmisc: $4xxx-group misc. instructions (format 0100 1110 01xx xxxx). */ static int opmisc(Q68State *state, uint32_t opcode) { const unsigned int index = (opcode>>3 & 7); return (*opcode_4E4x_table[index])(state, opcode); } /*-----------------------------------------------------------------------*/ /** * opTRAP: TRAP #n instruction (format 0100 1110 0100 nnnn). */ static int opTRAP(Q68State *state, uint32_t opcode) { state->exception = EX_TRAP + (opcode & 0x000F); return 0; } /*-----------------------------------------------------------------------*/ /** * opLINK: LINK instruction (format 0100 1110 0101 0rrr). */ static int opLINK(Q68State *state, uint32_t opcode) { INSN_GET_REG0; int16_t disp = IFETCH(state); #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->A[7]; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE; return 0; } #endif PUSH32(state, state->A[reg0]); state->A[reg0] = state->A[7]; state->A[7] += disp; return 16; } /*-----------------------------------------------------------------------*/ /** * opUNLK: UNLK instruction (format 0100 1110 0101 1rrr). */ static int opUNLK(Q68State *state, uint32_t opcode) { INSN_GET_REG0; /* FIXME: What happens if A7 is used as the register? I.e. does the * postincrement happen before or after the value is written to * the destination register? The Motorola docs could be read * both ways, so going by the literal operation sequence here. */ state->A[7] = state->A[reg0]; #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->A[7]; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ; return 0; } #endif state->A[reg0] = READU32(state, state->A[7]); state->A[7] += 4; return 12; } /*-----------------------------------------------------------------------*/ /** * opMUSP: MOVE An,USP and MOVE USP,An instructions (format * 0100 1110 0110 xrrr). */ static int opMUSP(Q68State *state, uint32_t opcode) { if (!(state->SR & SR_S)) { state->exception = EX_PRIVILEGE_VIOLATION; return 0; } INSN_GET_REG0; if (opcode & 0x0008) { state->USP = state->A[reg0]; } else { state->A[reg0] = state->USP; } return 4; } /*-----------------------------------------------------------------------*/ /** * op4E7x: Instructions with opcodes $4E70-$4E77 that don't fit anywhere * else. */ static int op4E7x(Q68State *state, uint32_t opcode) { switch (opcode & 7) { case 0: // $4E70 RESET if (!(state->SR & SR_S)) { state->exception = EX_PRIVILEGE_VIOLATION; return 0; } return 132; case 1: // $4E71 NOP return 4; case 2: // $4E72 STOP if (!(state->SR & SR_S)) { state->exception = EX_PRIVILEGE_VIOLATION; return 0; } state->halted = 1; set_SR(state, IFETCH(state)); return 4; case 3: { // $4E73 RTE if (!(state->SR & SR_S)) { state->exception = EX_PRIVILEGE_VIOLATION; return 0; } #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->A[7]; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ; return 0; } #endif uint16_t new_SR = POP16(state); state->PC = POP32(state); set_SR(state, new_SR); return 20; } case 5: // $4E75 RTS #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->A[7]; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ; return 0; } #endif state->PC = POP32(state); return 16; case 6: // $4E76 TRAPV if (state->SR & SR_V) { state->exception = EX_TRAPV; return 0; } return 4; case 7: { // $4E77 RTR #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->A[7]; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ; return 0; } #endif state->SR &= 0xFF00; state->SR |= POP16(state) & 0x00FF; state->PC = POP32(state); return 20; } default: // $4E74 RTD is 68010 only return op_ill(state, opcode); } } /*************************************************************************/ /** * opjump: JSR and JMP instructions (format 0100 1110 1xxx xxxx). */ static int opjump(Q68State *state, uint32_t opcode) { int is_jsr = ~opcode & 0x0040; /* JMP is essentially identical to LEA PC, and has the same * constraints. JSR is equivalent to MOVE.L PC,-(A7) followed by a * JMP to the address. Both use a separate timing table, however. */ int cycles; switch (EA_MODE(opcode)) { case EA_INDIRECT: cycles = 8; break; case EA_DISPLACEMENT: cycles = 10; break; case EA_INDEX: cycles = 14; break; case EA_MISC: switch (EA_REG(opcode)) { case EA_MISC_ABSOLUTE_W: cycles = 10; break; case EA_MISC_ABSOLUTE_L: cycles = 12; break; case EA_MISC_PCREL: cycles = 10; break; case EA_MISC_PCREL_INDEX: cycles = 14; break; default: return op_ill(state, opcode); } break; default: return op_ill(state, opcode); } ea_resolve(state, opcode, SIZE_W, ACCESS_READ); // cannot fail if (is_jsr) { #ifndef Q68_DISABLE_ADDRESS_ERROR if (state->A[7] & 1) { state->exception = EX_ADDRESS_ERROR; state->fault_addr = state->A[7]; state->fault_status = FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE; return 0; } #endif cycles += 8; PUSH32(state, state->PC); } state->PC = state->ea_addr; return cycles; } /*************************************************************************/ /******************* Other miscellaneous instructions ********************/ /*************************************************************************/ /** * opMOVP: MOVEP instruction (0000 rrr1 xx00 1rrr). */ static int opMOVP(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_REG0; int to_memory = opcode & 0x0080; int is_long = opcode & 0x0040; int16_t disp = IFETCH(state); uint32_t addr = state->A[reg0] + disp; if (to_memory) { uint32_t data = state->D[reg]; if (is_long) { WRITE8(state, addr+0, data>>24); WRITE8(state, addr+2, data>>16); WRITE8(state, addr+4, data>> 8); WRITE8(state, addr+6, data>> 0); } else { WRITE8(state, addr+0, data>> 8); WRITE8(state, addr+2, data>> 0); } } else { uint32_t data; if (is_long) { data = READU8(state, addr+0) << 24; data |= READU8(state, addr+2) << 16; data |= READU8(state, addr+4) << 8; data |= READU8(state, addr+6) << 0; } else { data = READU8(state, addr+0) << 8; data |= READU8(state, addr+2) << 0; } state->D[reg] = data; } return is_long ? 24 : 16; } /*************************************************************************/ /** * opADSX: ADDX/SUBX instructions (1x01 rrr1 ss00 xrrr). */ static int opADSX(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_SIZE; INSN_GET_REG0; const int is_add = opcode & 0x4000; const int is_memory = opcode & 0x0008; const int bytes = SIZE_TO_BYTES(size); const int shift = bytes*8 - 1; const uint32_t valuemask = ~(~1 << shift); const uint16_t src_ea = (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg0; const uint16_t dest_ea = (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg; int dummy; uint32_t src = ea_get(state, src_ea, size, 0, &dummy); uint32_t dest = ea_get(state, dest_ea, size, 1, &dummy); uint32_t result; int X = (state->SR >> SR_X_SHIFT) & 1; state->SR &= ~(SR_X | SR_N | SR_V | SR_C); // Z is never set, only cleared if (is_add) { result = (dest + src + X) & valuemask; if (((src ^ result) & (dest ^ result)) >> shift) { state->SR |= SR_V; } if ((int)((src >> shift) + (dest >> shift) - (result >> shift)) > 0) { state->SR |= SR_X | SR_C; } } else { result = (dest - src - X) & valuemask; if (((src ^ dest) & (result ^ dest)) >> shift) { state->SR |= SR_V; } if ((int)((src >> shift) - (dest >> shift) + (result >> shift)) > 0) { state->SR |= SR_X | SR_C; } } if (result >> shift) { state->SR |= SR_N; } if (result != 0) { state->SR &= ~SR_Z; } ea_set(state, dest_ea, size, result); return (is_memory ? (size==SIZE_L ? 30 : 18) : (size==SIZE_L ? 8 : 4)); } /*-----------------------------------------------------------------------*/ /** * op_BCD: ABCD/SBCD instructions (1x00 rrr1 0000 xrrr). */ static int op_BCD(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_REG0; const int is_add = opcode & 0x4000; const int is_memory = opcode & 0x0008; const uint16_t src_ea = (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg0; const uint16_t dest_ea = (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg; int dummy; uint8_t src = ea_get(state, src_ea, SIZE_B, 0, &dummy); uint8_t dest = ea_get(state, dest_ea, SIZE_B, 1, &dummy); int result; int X = (state->SR >> SR_X_SHIFT) & 1; state->SR &= ~(SR_X | SR_C); // Z is never set, only cleared if (is_add) { result = (dest & 0x0F) + (src & 0x0F) + X; if (result >= 10) { /* This seems to be correct w.r.t. a real 68000 and invalid data * (e.g. 0x0F + 0x0F): it only checks for a carry of 1 */ result += 6; } result += (dest & 0xF0) + (src & 0xF0); if (result >= 10<<4) { result -= 10<<4; state->SR |= SR_X | SR_C; } } else { /* Slightly convoluted to match what a real 68000 does */ int res_low = (dest & 0x0F) - (src & 0x0F) - X; int borrow = 0; if (res_low < 0) { res_low += 10; borrow = 1<<4; } int res_high = (dest & 0xF0) - (src & 0xF0) - borrow; if (res_high < 0) { res_high += 10<<4; state->SR |= SR_X | SR_C; } result = res_high + res_low; if (result < 0) { state->SR |= SR_X | SR_C; } } result &= 0xFF; if (result != 0) { state->SR &= ~SR_Z; } ea_set(state, dest_ea, SIZE_B, result); return is_memory ? 18 : 6; } /*-----------------------------------------------------------------------*/ /** * opCMPM: CMPM instructions (1011 rrr1 ss00 1rrr). */ static int opCMPM(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_SIZE; INSN_GET_REG0; const int bytes = SIZE_TO_BYTES(size); const int shift = bytes*8 - 1; const uint32_t valuemask = ~(~1 << shift); const uint16_t src_ea = EA_POSTINCREMENT<<3 | reg0; const uint16_t dest_ea = EA_POSTINCREMENT<<3 | reg; int dummy; uint32_t src = ea_get(state, src_ea, size, 0, &dummy); uint32_t dest = ea_get(state, dest_ea, size, 0, &dummy); uint32_t result = (dest - src) & valuemask; INSN_CLEAR_XCC(); INSN_SETNZ_SHIFT(result); if (((src ^ dest) & (result ^ dest)) >> shift) { state->SR |= SR_V; } if ((int)((src >> shift) - (dest >> shift) + (result >> shift)) > 0) { state->SR |= SR_C; } return size==SIZE_L ? 20 : 12; } /*************************************************************************/ /** * op_EXG: EXG instruction (1100 rrr1 xx00 1rrr). */ static int op_EXG(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_REG0; const int mode = opcode & 0xF8; if (mode == 0x40) { const uint32_t tmp = state->D[reg]; state->D[reg] = state->D[reg0]; state->D[reg0] = tmp; } else if (mode == 0x48) { const uint32_t tmp = state->A[reg]; state->A[reg] = state->A[reg0]; state->A[reg0] = tmp; } else if (mode == 0x88) { const uint32_t tmp = state->D[reg]; state->D[reg] = state->A[reg0]; state->A[reg0] = tmp; } else { return op_ill(state, opcode); } return 6; } /*************************************************************************/ /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68.h000644 001750 001750 00000021775 12256006177 017424 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68.h: Q68 main header Copyright 2009-2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Q68_H #define Q68_H #include #include /*************************************************************************/ /****************** Exported definitions and data types ******************/ /*************************************************************************/ /* Memory read/write function types. The size of the operation is not * specified here, but is rather determined by which callback the function * is assigned to. */ /** * Q68ReadFunc: Read data from memory. * * [Parameters] * address: Address to read from * [Return value] * Value read (zero-extended to 32 bits) */ typedef uint32_t Q68ReadFunc(uint32_t address); /** * Q68WriteFunc: Write data to memory. * * [Parameters] * address: Address to write to * data: Value to write * [Return value] * None */ typedef void Q68WriteFunc(uint32_t address, uint32_t data); /*************************************************************************/ /* Virtual processor state (opaque) */ typedef struct Q68State_ Q68State; /*************************************************************************/ /************************** Emulator interface ***************************/ /*************************************************************************/ /** * q68_create: Create a new virtual processor. The virtual processor is * created uninitialized; before starting the processor, the caller must * set the IRQ level and read/write callbacks, then call q68_reset(). * * [Parameters] * None * [Return value] * Processor state block on success, NULL on error */ extern Q68State *q68_create(void); /** * q68_create_ex: Create a new virtual processor, using the specified * functions for all memory allocation. * * [Parameters] * malloc_func: Function for allocating a memory block * realloc_func: Function for adjusting the size of a memory block * free_func: Function for freeing a memory block * [Return value] * Processor state block on success, NULL on error */ extern Q68State *q68_create_ex(void *(*malloc_func)(size_t size), void *(*realloc_func)(void *ptr, size_t size), void (*free_func)(void *ptr)); /** * q68_destroy: Free all resources used by a virtual processor. * * [Parameters] * state: Processor state block * [Return value] * None */ extern void q68_destroy(Q68State *state); /*----------------------------------*/ /** * q68_set_irq: Set the interrupt request (IRQ) input to the processor. * * [Parameters] * state: Processor state block * irq: IRQ level (0-7) * [Return value] * None */ extern void q68_set_irq(Q68State *state, int irq); /** * q68_set_{readb,readw,writeb,writew}_func: Set the read/write callback * functions called by the virtual processor on memory accesses. * * For the read functions, only the lower 8 (readb) or 16 (readw) bits of * the return value are used; the function does not need to sign-extend or * zero-extend the value. Similarly, the value passed to the write * functions will only have the low 8 (writeb) or 16 (writew) bits valid, * and the function should ignore the upper bits of the value. * * For the word access functions (readw and writew), the address is * guaranteed to be even, so the function does not need to check for this * itself. (However, see the Q68_DISABLE_ADDRESS_ERROR configuration * option in q68-internal.h.) * * [Parameters] * state: Processor state block * func: Callback function to set * [Return value] * None */ extern void q68_set_readb_func(Q68State *state, Q68ReadFunc func); extern void q68_set_readw_func(Q68State *state, Q68ReadFunc func); extern void q68_set_writeb_func(Q68State *state, Q68WriteFunc func); extern void q68_set_writew_func(Q68State *state, Q68WriteFunc func); /** * q68_set_jit_flush_func: Set a function to be used to flush the native * CPU's caches after a block of 68k code has been translated into native * code. If not set, no cache flushing is performed. This function has no * effect if dynamic translation is not enabled. * * [Parameters] * state: Processor state block * flush_func: Function for flushing the native CPU's caches (NULL if none) * [Return value] * None */ extern void q68_set_jit_flush_func(Q68State *state, void (*flush_func)(void)); /*----------------------------------*/ /** * q68_get_{dreg,areg,pc,sr,usp,ssp}: Return the current value of the * specified register. * * [Parameters] * state: Processor state block * num: Register number (q68_get_dreg() and q68_get_areg() only) * [Return value] * Register value */ extern uint32_t q68_get_dreg(const Q68State *state, int num); extern uint32_t q68_get_areg(const Q68State *state, int num); extern uint32_t q68_get_pc(const Q68State *state); extern uint16_t q68_get_sr(const Q68State *state); extern uint32_t q68_get_usp(const Q68State *state); extern uint32_t q68_get_ssp(const Q68State *state); /** * q68_set_{dreg,areg,pc,sr,usp,ssp}: Set the value of the specified * register. * * [Parameters] * state: Processor state block * num: Register number (q68_set_dreg() and q68_set_areg() only) * value: Value to set * [Return value] * None */ extern void q68_set_dreg(Q68State *state, int num, uint32_t value); extern void q68_set_areg(Q68State *state, int num, uint32_t value); extern void q68_set_pc(Q68State *state, uint32_t value); extern void q68_set_sr(Q68State *state, uint16_t value); extern void q68_set_usp(Q68State *state, uint32_t value); extern void q68_set_ssp(Q68State *state, uint32_t value); /*----------------------------------*/ /** * q68_touch_memory: Clear any cached translations covering the given * address range. Users should call this function whenever 68000-accessible * memory is modified by an external agent. * * [Parameters] * state: Processor state block * address: 68000 address of modified data * size: Size of modified data (in bytes) * [Return value] * None */ extern void q68_touch_memory(Q68State *state, uint32_t address, uint32_t size); /*-----------------------------------------------------------------------*/ /** * q68_reset: Reset the virtual processor. * * [Parameters] * state: Processor state block * [Return value] * None */ extern void q68_reset(Q68State *state); /** * q68_run: Execute instructions for the given number of clock cycles. * * [Parameters] * state: Processor state block * cycles: Number of clock cycles to execute * [Return value] * Number of clock cycles executed (may be greater than "cycles") */ extern int q68_run(Q68State *state, int cycles); /*-----------------------------------------------------------------------*/ /** * q68_disassemble: Disassemble the instruction at the given address. * Returns "???" if the address or opcode is invalid. * * [Parameters] * state: Processor state block * address: Address of instruction to disassemble * [Return value] * String containined disassembled instruction * [Notes] * The returned string is only valid until the next call to this function. */ extern const char *q68_disassemble(Q68State *state, uint32_t address, int *nwords_ret); /*----------------------------------*/ /** * q68_trace_init: Initialize the tracing code. * * [Parameters] * state: Processor state block * [Return value] * None */ extern void q68_trace_init(Q68State *state_); /** * q68_trace_add_cycles: Add the given number of cycles to the global * accumulator. * * [Parameters] * cycles: Number of cycles to add * [Return value] * None */ extern void q68_trace_add_cycles(int32_t cycles); /** * q68_trace: Output a trace for the instruction at the current PC. * * [Parameters] * None * [Return value] * None */ extern void q68_trace(void); /*************************************************************************/ /*************************************************************************/ #endif // Q68_H /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68.c000644 001750 001750 00000022004 12256006177 017401 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68.c: Quick-and-dirty MC68000 emulator with dynamic translation support Copyright 2009-2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "q68.h" #include "q68-internal.h" /*************************************************************************/ /* * The source code for Q68 is divided into the following files: * * q68.c (this file) -- Main interface function definitions * q68.h -------------- Interface definition (users should include this header) * q68-const.h -------- 68000-related constants (status register bits, etc.) * q68-core.c --------- Processor execution core * q68-disasm.c ------- 68000 instruction disassembly and tracing support * (for debugging) * q68-internal.h ----- General definitions and declarations for internal use * q68-jit.c ---------- Dynamic ("just-in-time") translation support * q68-jit.h ---------- Declarations used only by the JIT code * q68-jit-psp.[hS] --- JIT implementation for the PSP's Allegrex processor * q68-jit-x86.[hS] --- JIT implementation for the Intel x86 architecture * (both 32-bit and 64-bit environments supported) */ /*************************************************************************/ /*************************************************************************/ /** * q68_create: Create a new virtual processor. The virtual processor is * created uninitialized; before starting the processor, the caller must * set the IRQ level and read/write callbacks, then call q68_reset(). * * [Parameters] * None * [Return value] * Processor state block on success, NULL on error */ Q68State *q68_create(void) { return q68_create_ex(malloc, realloc, free); } /*-----------------------------------------------------------------------*/ /** * q68_create_ex: Create a new virtual processor, using the specified * functions for all memory allocation. * * [Parameters] * malloc_func: Function for allocating a memory block * realloc_func: Function for adjusting the size of a memory block * free_func: Function for freeing a memory block * [Return value] * Processor state block on success, NULL on error */ Q68State *q68_create_ex(void *(*malloc_func)(size_t size), void *(*realloc_func)(void *ptr, size_t size), void (*free_func)(void *ptr)) { Q68State *state; state = (*malloc_func)(sizeof(*state)); if (!state) { return NULL; } state->malloc_func = malloc_func; state->realloc_func = realloc_func; state->free_func = free_func; #ifdef Q68_USE_JIT if (!q68_jit_init(state)) { state->free_func(state); return NULL; } #endif state->halted = Q68_HALTED_DOUBLE_FAULT; // Let's initialize this, at least return state; } /*-----------------------------------------------------------------------*/ /** * q68_destroy: Free all resources used by a virtual processor. * * [Parameters] * state: Processor state block * [Return value] * None */ void q68_destroy(Q68State *state) { #ifdef Q68_USE_JIT q68_jit_cleanup(state); #endif state->free_func(state); } /*************************************************************************/ /** * q68_set_irq: Set the interrupt request (IRQ) input to the processor. * * [Parameters] * state: Processor state block * irq: IRQ level (0-7) * [Return value] * None */ void q68_set_irq(Q68State *state, int irq) { state->irq = irq & 7; } /*-----------------------------------------------------------------------*/ /** * q68_set_{readb,readw,writeb,writew}_func: Set the read/write callback * functions called by the virtual processor on memory accesses. * * For the read functions, only the lower 8 (readb) or 16 (readw) bits of * the return value are used; the function does not need to sign-extend or * zero-extend the value. Similarly, the value passed to the write * functions will only have the low 8 (writeb) or 16 (writew) bits valid, * and the function should ignore the upper bits of the value. * * For the word access functions (readw and writew), the address is * guaranteed to be even, so the function does not need to check for this * itself. (However, see the Q68_DISABLE_ADDRESS_ERROR configuration * option in q68-internal.h.) * * [Parameters] * state: Processor state block * func: Callback function to set * [Return value] * None */ void q68_set_readb_func(Q68State *state, Q68ReadFunc func) { state->readb_func = func; } void q68_set_readw_func(Q68State *state, Q68ReadFunc func) { state->readw_func = func; } void q68_set_writeb_func(Q68State *state, Q68WriteFunc func) { state->writeb_func = func; } void q68_set_writew_func(Q68State *state, Q68WriteFunc func) { state->writew_func = func; } /*-----------------------------------------------------------------------*/ /** * q68_set_jit_flush_func: Set a function to be used to flush the native * CPU's caches after a block of 68k code has been translated into native * code. If not set, no cache flushing is performed. This function has no * effect if dynamic translation is not enabled. * * [Parameters] * state: Processor state block * flush_func: Function for flushing the native CPU's caches (NULL if none) * [Return value] * None */ void q68_set_jit_flush_func(Q68State *state, void (*flush_func)(void)) { state->jit_flush = flush_func; } /*************************************************************************/ /** * q68_get_{dreg,areg,pc,sr,usp,ssp}: Return the current value of the * specified register. * * [Parameters] * state: Processor state block * num: Register number (q68_get_dreg() and q68_get_areg() only) * [Return value] * Register value */ uint32_t q68_get_dreg(const Q68State *state, int num) { return state->D[num]; } uint32_t q68_get_areg(const Q68State *state, int num) { return state->A[num]; } uint32_t q68_get_pc(const Q68State *state) { return state->PC; } uint16_t q68_get_sr(const Q68State *state) { return state->SR; } uint32_t q68_get_usp(const Q68State *state) { return state->USP; } uint32_t q68_get_ssp(const Q68State *state) { return state->SSP; } /*-----------------------------------------------------------------------*/ /** * q68_set_{dreg,areg,pc,sr,usp,ssp}: Set the value of the specified * register. * * [Parameters] * state: Processor state block * num: Register number (q68_set_dreg() and q68_set_areg() only) * value: Value to set * [Return value] * None */ void q68_set_dreg(Q68State *state, int num, uint32_t value) { state->D[num] = value; } void q68_set_areg(Q68State *state, int num, uint32_t value) { state->A[num] = value; } void q68_set_pc(Q68State *state, uint32_t value) { state->PC = value; } void q68_set_sr(Q68State *state, uint16_t value) { state->SR = value; } void q68_set_usp(Q68State *state, uint32_t value) { state->USP = value; } void q68_set_ssp(Q68State *state, uint32_t value) { state->SSP = value; } /*************************************************************************/ /** * q68_touch_memory: Clear any cached translations covering the given * address range. Users should call this function whenever 68000-accessible * memory is modified by an external agent. * * [Parameters] * state: Processor state block * address: 68000 address of modified data * size: Size of modified data (in bytes) * [Return value] * None */ void q68_touch_memory(Q68State *state, uint32_t address, uint32_t size) { #ifdef Q68_USE_JIT const uint32_t first_page = address >> Q68_JIT_PAGE_BITS; const uint32_t last_page = (address + (size-1)) >> Q68_JIT_PAGE_BITS; uint32_t page; for (page = first_page; page <= last_page; page++) { if (UNLIKELY(JIT_PAGE_TEST(state, page))) { q68_jit_clear_page(state, page << Q68_JIT_PAGE_BITS); } } #endif } /*************************************************************************/ /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68-internal.h000644 001750 001750 00000051640 12256006177 021230 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-internal.h: Internal declarations/definitions used by Q68 Copyright 2009-2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Q68_INTERNAL_H #define Q68_INTERNAL_H #ifndef Q68_CONST_H # include "q68-const.h" #endif /*************************************************************************/ /******************** Defines controlling compilation ********************/ /*************************************************************************/ /** * Q68_USE_JIT: When defined, enables the use of dynamic translation. * Defining this on a CPU without dynamic translation support will result * in a compilation error. * * See q68-jit.h for additional settings specific to dynamic translation. */ // #define Q68_USE_JIT /** * Q68_JIT_OPTIMIZE_FLAGS: When defined, allows the dynamic translator to * skip setting the condition flags (XNZVC) after an instruction when it * can prove that doing so will have no effect on program execution (e.g. * because the following instruction overwrites the flags). This will * cause an instruction-by-instruction execution trace to differ from an * actual 68000, though the program behavior will remain unchanged * (barring any bugs in the optimization). */ #define Q68_JIT_OPTIMIZE_FLAGS /** * Q68_JIT_LOOSE_TIMING: When defined, allows the dynamic translator to * take some leeway in checking execution timing, such that instructions * may continue to be executed even after the cycle limit has been reached. * This allows the translated code to execute more quickly, but will * slightly alter the timing of responses to external events such as * interrupts. */ #define Q68_JIT_LOOSE_TIMING /** * Q68_OPTIMIZE_IDLE: When defined, optimizes certain idle loops to * improve performance in JIT mode. Enabling this option slightly alters * execution timing. */ #define Q68_OPTIMIZE_IDLE /** * Q68_JIT_VERBOSE: When defined, outputs some status messages considered * useful in debugging or optimizing the JIT core. */ // #define Q68_JIT_VERBOSE /** * Q68_DISABLE_ADDRESS_ERROR: When defined, disables the generation of * address error exceptions for unaligned word/longword accesses. The * behavior for such an access will depend on how the host system handles * unaligned accesses, and may include the host program itself crashing. */ // #define Q68_DISABLE_ADDRESS_ERROR /** * Q68_TRACE: When defined, the execution of every instruction will be * traced to the file "q68.log" in the current directory. (On Linux, the * trace will be written in compressed form to "q68.log.gz".) */ // #define Q68_TRACE /*************************************************************************/ /************************* Generic helper macros *************************/ /*************************************************************************/ /* Offset of a byte and word within a native long */ #ifdef WORDS_BIGENDIAN # define BYTE_OFS 3 # define WORD_OFS 1 #else # define BYTE_OFS 0 # define WORD_OFS 0 #endif /* Return the length of an array */ #define lenof(a) (sizeof((a)) / sizeof(*(a))) /* Compiler attribute to mark functions as not to be inlined */ #ifdef __GNUC__ # define NOINLINE __attribute__((noinline)) #else # define NOINLINE /*nothing*/ #endif /* Macro to tell the compiler that a certain condition is likely or * unlikely to occur */ #ifdef __GNUC__ # define LIKELY(x) (__builtin_expect(!!(x), 1)) # define UNLIKELY(x) (__builtin_expect(!!(x), 0)) #else # define LIKELY(x) (x) # define UNLIKELY(x) (x) #endif /* Debug/error message macro. DMSG("message",...) prints to stderr a line * in the form: * func_name(file:line): message * printf()-style format tokens and arguments are allowed, and no newline * is required at the end. The format string must be a literal string * constant. */ #define DMSG(msg,...) \ fprintf(stderr, "%s(%s:%d): " msg "\n", __FUNCTION__, __FILE__, __LINE__ \ , ## __VA_ARGS__) /*************************************************************************/ /******************* Processor state block definition ********************/ /*************************************************************************/ typedef struct Q68JitEntry_ Q68JitEntry; // For JIT code typedef struct Q68JitBlacklist_ Q68JitBlacklist; // For JIT code struct Q68State_ { /**** Data directly reflecting the current CPU state ****/ /* Registers */ union { struct { uint32_t D[8]; uint32_t A[8]; }; uint32_t DA[16]; // For fast accessing (A0 = D8, A1 = D9, etc.) }; uint32_t PC; uint32_t SR; uint32_t USP, SSP; /* "Current PC" for this instruction (address of opcode + 2) */ uint32_t current_PC; /* Effective address used by the current instruction */ uint32_t ea_addr; /* Pending exception, to be taken after the current instruction has * been processed (zero if none) */ unsigned int exception; /* Auxiliary exception data used for a bus error or address error */ uint32_t fault_addr; uint16_t fault_opcode; uint16_t fault_status; /* Nonzero if virtual processor is halted (due to either a STOP * instruction or a double fault); see Q68_HALTED_* */ unsigned int halted; /* Current interrupt request level */ unsigned int irq; /* Number of clock cycles executed so far */ uint32_t cycles; /**** Environment settings ****/ /* Native memory allocation functions */ void *(*malloc_func)(size_t size); void *(*realloc_func)(void *ptr, size_t size); void (*free_func)(void *ptr); /* 68k memory read/write functions */ Q68ReadFunc *readb_func, *readw_func; Q68WriteFunc *writeb_func, *writew_func; /* Native cache flushing function (for JIT) */ void (*jit_flush)(void); /**** JIT-related data ****/ /* Currently executing JIT block (NULL = none) */ Q68JitEntry *jit_running; /* Nonzero if JIT routine needs to abort because the underlying 68000 * code was modified (e.g. by a self-modifying routine) */ unsigned int jit_abort; /* Hash table of translated code segments, hashed by 68000 start address */ Q68JitEntry *jit_table; // Record buffer Q68JitEntry **jit_hashchain; // Hash collision chains /* Total size of translated data */ int32_t jit_total_data; // Signed to protect against its going negative /* Internal timestamp used by JIT LRU logic */ uint32_t jit_timestamp; /* List of self-modifying blocks not to be translated (zero in both * address fields indicates a free entry) */ struct { uint32_t m68k_start; // Code start address in 68000 address space uint32_t m68k_end; // Code end address in 68000 address space uint32_t timestamp; // Time this entry was added } jit_blacklist[Q68_JIT_BLACKLIST_SIZE]; /* Nonzero if the current PC is inside a blacklisted block */ unsigned int jit_in_blist; /* When jit_in_blacklist != 0, indicates the relevant jit_blacklist[] * array entry index */ unsigned int jit_blist_num; /* Call stack for efficient handling of subroutine calls */ unsigned int jit_callstack_top; // Index of next entry to write struct { uint32_t return_PC; // PC on return from subroutine (0 = free) Q68JitEntry *return_entry; // JIT block containing native code void *return_native; // Native address to jump to } jit_callstack[Q68_JIT_CALLSTACK_SIZE]; /* Buffer for tracking translated code blocks */ uint8_t jit_pages[1<<(24-(Q68_JIT_PAGE_BITS+3))]; }; /*-----------------------------------------------------------------------*/ /* Constants used in the Q68State.halted field */ enum { Q68_HALTED_NONE = 0, // Processor is running normally Q68_HALTED_STOP, // Processor halted because of a STOP instruction Q68_HALTED_DOUBLE_FAULT, // Processor halted because of a double fault }; /*-----------------------------------------------------------------------*/ /* Macros for accessing Q68State.jit_pages[] (page is assumed to be valid) */ #define JIT_PAGE_SET(state,page) \ ((state)->jit_pages[(page)>>3] |= 1<<((page)&7)) #define JIT_PAGE_CLEAR(state,page) \ ((state)->jit_pages[(page)>>3] &= ~(1<<((page)&7))) #define JIT_PAGE_TEST(state,page) \ ((state)->jit_pages[(page)>>3] & 1<<((page)&7)) /*************************************************************************/ /************************ JIT interface functions ************************/ /*************************************************************************/ /** * q68_jit_init: Allocate memory for JIT data. Must be called before any * other JIT function. * * [Parameters] * state: Processor state block * [Return value] * Nonzero on success, zero on error */ extern int q68_jit_init(Q68State *state); /** * q68_jit_reset: Reset the dynamic translation state, clearing out all * previously stored data. * * [Parameters] * state: Processor state block * [Return value] * None */ extern void q68_jit_reset(Q68State *state); /** * q68_jit_cleanup: Destroy all JIT-related data. * * [Parameters] * state: Processor state block * [Return value] * None */ extern void q68_jit_cleanup(Q68State *state); /** * q68_jit_translate: Dynamically translate a block of instructions * starting at the given address. If a translation already exists for the * given address, it is cleared. * * [Parameters] * state: Processor state block * address: Start address in 68000 address space * [Return value] * Translated block to be passed to q68_jit_run(), or NULL on error */ extern Q68JitEntry *q68_jit_translate(Q68State *state, uint32_t address); /** * q68_jit_find: Find the translated block for a given address, if any. * * [Parameters] * state: Processor state block * address: Start address in 68000 address space * [Return value] * Translated block to be passed to q68_jit_run(), or NULL if no such * block exists */ extern Q68JitEntry *q68_jit_find(Q68State *state, uint32_t address); /** * q68_jit_run: Run translated 68000 code. * * [Parameters] * state: Processor state block * cycle_limit: Clock cycle limit on execution (code will stop when * state->cycles >= cycles) * address_ptr: Pointer to translated block to execute; will be cleared * to NULL on return if the end of the block was reached * [Return value] * None */ extern void q68_jit_run(Q68State *state, uint32_t cycle_limit, Q68JitEntry **entry_ptr); /** * q68_jit_clear: Clear any translation beginning at the given address. * * [Parameters] * state: Processor state block * address: Start address in 68000 address space * [Return value] * None */ extern void q68_jit_clear(Q68State *state, uint32_t address); /** * q68_jit_clear_page: Clear any translation which occurs in the JIT page * containing the given address. Intended for use on writes from external * sources. * * [Parameters] * state: Processor state block * address: Address to which data was written * [Return value] * None */ extern void q68_jit_clear_page(Q68State *state, uint32_t address); /** * q68_jit_clear_write: Clear any translation which includes the given * address, and (if there is at least one such translation) blacklist the * address from being translated. * * [Parameters] * state: Processor state block * address: Address to which data was written * size: Size of data written * [Return value] * None */ extern void q68_jit_clear_write(Q68State *state, uint32_t address, uint32_t size); /*************************************************************************/ /************************ Internal-use constants *************************/ /*************************************************************************/ /* Effective address types for ea_type() */ enum { EA_xREG, // Data/address register EA_MEM, // Memory reference EA_IMM, // Immediate value }; /* Effective address access types for ea_resolve() */ enum { ACCESS_READ = 0, ACCESS_WRITE, ACCESS_MODIFY, // For the read access of a read-write operation }; /*************************************************************************/ /*************** Opcode implementation function prototype ****************/ /*************************************************************************/ /** * OpcodeFunc: Type of a function implementing one or a group of * instructions. * * [Parameters] * state: Processor state block * opcode: Instruction opcode * [Return value] * Clock cycles used */ typedef int OpcodeFunc(Q68State *state, uint32_t opcode); /*************************************************************************/ /******************* Memory access functions (inline) ********************/ /*************************************************************************/ /** * READ[SU]{8,16,32}: Read a value from memory. * * [Parameters] * state: Processor state block * addr: Address to read or write * [Return value] * Value read */ static inline int32_t READS8(Q68State *state, uint32_t addr) { return (int8_t) state->readb_func(addr & 0xFFFFFF); } static inline uint32_t READU8(Q68State *state, uint32_t addr) { return state->readb_func(addr & 0xFFFFFF); } static inline int32_t READS16(Q68State *state, uint32_t addr) { return (int16_t) state->readw_func(addr & 0xFFFFFF); } static inline uint32_t READU16(Q68State *state, uint32_t addr) { return state->readw_func(addr & 0xFFFFFF); } static inline int32_t READS32(Q68State *state, uint32_t addr) { addr &= 0xFFFFFF; int32_t value = (int32_t) state->readw_func(addr) << 16; addr += 2; addr &= 0xFFFFFF; value |= state->readw_func(addr); return value; } static inline uint32_t READU32(Q68State *state, uint32_t addr) { addr &= 0xFFFFFF; uint32_t value = state->readw_func(addr) << 16; addr += 2; addr &= 0xFFFFFF; value |= state->readw_func(addr); return value; } /*-----------------------------------------------------------------------*/ /** * WRITE{8,16,32}: Write a value to memory. * * [Parameters] * state: Processor state block * addr: Address to read or write * data: Value to write * [Return value] * None */ static inline void WRITE8(Q68State *state, uint32_t addr, uint8_t data) { addr &= 0xFFFFFF; #ifdef Q68_USE_JIT if (UNLIKELY(JIT_PAGE_TEST(state, addr >> Q68_JIT_PAGE_BITS))) { q68_jit_clear_write(state, addr, 1); } #endif state->writeb_func(addr, data); } static inline void WRITE16(Q68State *state, uint32_t addr, uint16_t data) { addr &= 0xFFFFFF; #ifdef Q68_USE_JIT if (UNLIKELY(JIT_PAGE_TEST(state, addr >> Q68_JIT_PAGE_BITS))) { q68_jit_clear_write(state, addr, 2); } #endif state->writew_func(addr, data); } static inline void WRITE32(Q68State *state, uint32_t addr, uint32_t data) { WRITE16(state, addr, data>>16); WRITE16(state, addr+2, data); } /*-----------------------------------------------------------------------*/ /** * IFETCH: Retrieve and return the 16-bit word at the PC, incrementing the * PC by 2. * * [Parameters] * state: Processor state block * [Return value] * 16-bit value read */ static inline uint32_t IFETCH(Q68State *state) { uint32_t data = READU16(state, state->PC); state->PC += 2; return data; } /*-----------------------------------------------------------------------*/ /** * PUSH{16,32}, POP{16,32}: Push values onto or pop them off the stack. * * [Parameters] * state: Processor state block * data: Value to push (PUSH only) * [Return value] * Value popped (unsigned, POP only) */ static inline void PUSH16(Q68State *state, uint16_t data) { state->A[7] -= 2; WRITE16(state, state->A[7], data); } static inline void PUSH32(Q68State *state, uint32_t data) { state->A[7] -= 4; WRITE32(state, state->A[7], data); } static inline uint32_t POP16(Q68State *state) { const uint32_t data = READU16(state, state->A[7]); state->A[7] += 2; return data; } static inline uint32_t POP32(Q68State *state) { const uint32_t data = READU32(state, state->A[7]); state->A[7] += 4; return data; } /*************************************************************************/ /******************* Instruction implementation macros *******************/ /*************************************************************************/ /* INSN_GET_REG: uint32_t reg = opcode[11:9] */ #define INSN_GET_REG \ uint32_t reg = (opcode>>9) & 7 /* INSN_GET_COUNT: uint32_t count = opcode[11:9] || 8 */ #define INSN_GET_COUNT \ uint32_t count = (opcode>>9) & 7 ?: 8 /* INSN_GET_COND: uint32_t cond = opcode[11:8] */ #define INSN_GET_COND \ uint32_t cond = (opcode>>8) & 15 /* INSN_GET_SIZE: uint32_t size = opcode[7:6] */ #define INSN_GET_SIZE \ uint32_t size = (opcode>>6) & 3 /* INSN_GET_REG0: uint32_t reg0 = opcode[2:0] */ #define INSN_GET_REG0 \ uint32_t reg0 = opcode & 7 /* INSN_GET_IMM8: const int32_t imm8 = SIGN_EXTEND(opcode[7:0]) */ #define INSN_GET_IMM8 \ const int32_t imm8 = (int32_t)(int8_t)opcode /* INSN_GET_IMM16: const int32_t imm16 = *++PC; */ #define INSN_GET_IMM16 \ const int32_t imm16 = (int16_t)IFETCH(state) /* INSN_GET_DISP8: int32_t disp = SIGN_EXTEND(opcode[7:0]) */ #define INSN_GET_DISP8 \ int32_t disp = (int32_t)(int8_t)opcode /*-----------------------------------------------------------------------*/ /* INSN_COND_TRUE: Evaluate whether the given condition code is satisfied. */ #define INSN_COND_TRUE(cond) __extension__({ \ const unsigned int __cond = (cond); \ const unsigned int group = __cond >> 1; \ const unsigned int onoff = __cond & 1; \ (group==COND_LS>>1 ? (state->SR & SR_C) >> SR_C_SHIFT \ | (state->SR & SR_Z) >> SR_Z_SHIFT : \ group==COND_CS>>1 ? (state->SR & SR_C) >> SR_C_SHIFT : \ group==COND_EQ>>1 ? (state->SR & SR_Z) >> SR_Z_SHIFT : \ group==COND_VS>>1 ? (state->SR & SR_V) >> SR_V_SHIFT : \ group==COND_MI>>1 ? (state->SR & SR_N) >> SR_N_SHIFT : \ group==COND_LT>>1 ? (state->SR & SR_N) >> SR_N_SHIFT \ ^ (state->SR & SR_V) >> SR_V_SHIFT : \ group==COND_LE>>1 ? (state->SR & SR_Z) >> SR_Z_SHIFT \ | ((state->SR & SR_N) >> SR_N_SHIFT \ ^ (state->SR & SR_V) >> SR_V_SHIFT) : \ 0 /* COND_T or COND_F */ \ ) == onoff; \ }) /*-----------------------------------------------------------------------*/ /* INSN_CLEAR_XCC, INSN_CLEAR_CC: Clear all condition codes, or all except * the X flag. */ #define INSN_CLEAR_XCC() (state->SR &= ~(SR_X|SR_N|SR_Z|SR_V|SR_C)) #define INSN_CLEAR_CC() (state->SR &= ~( SR_N|SR_Z|SR_V|SR_C)) /* INSN_SETNZ: Set the N and Z flags according to the given 32-bit result. * Assumes the flags are currently cleared. */ #define INSN_SETNZ(result) do { \ const int32_t __result = (result); \ if (__result < 0) { \ state->SR |= SR_N; \ } else if (__result == 0) { \ state->SR |= SR_Z; \ } \ } while (0) /* INSN_SETNZ_SHIFT: Like INSN_SETNZ, but assumes the presence of a "shift" * variable containing the number of bits of "result" less one. */ #define INSN_SETNZ_SHIFT(result) do { \ const int32_t __result = (result); \ if (__result >> shift) { \ state->SR |= SR_N; \ } else if (__result == 0) { \ state->SR |= SR_Z; \ } \ } while (0) /*************************************************************************/ /*************************************************************************/ #endif // Q68_INTERNAL_H /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68-jit-x86.h000644 001750 001750 00000035524 12256006177 020630 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-jit-x86.h: x86 (32/64-bit) dynamic translation header for Q68 Copyright 2009 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef Q68_JIT_X86_H #define Q68_JIT_X86_H /*************************************************************************/ /** * JIT_CALL: Run translated code from the given (native) address for the * given number of cycles. * * [Parameters] * state: Processor state block * cycles: Number of clock cycles to execute * address_ptr: Pointer to address of native code to execute; must be * updated on return with the next address to execute * or NULL if the end of the block was reached * [Return value] * Number of clock cycles actually executed */ static inline int JIT_CALL(Q68State *state, int cycles, void **address_ptr) { asm( #ifdef CPU_X64 /* GCC doesn't know we're actually calling a function here, so make * sure we don't accidentally overwrite the x64 redzone */ "sub $128, %%rsp; call *%[address]; add $128, %%rsp" #else /* x86 doesn't have a redzone, so we can just do a direct call */ "call *%[address]" #endif : [cycles] "=S" (cycles), [address] "=D" (*address_ptr) : [state] "b" (state), "0" (cycles), "1" (*address_ptr) #ifdef CPU_X64 : "rax", "rcx", "rdx", "r8", "r9", "r10", "r11" #else : "eax", "ecx", "edx" #endif , "memory" ); return cycles; } /*************************************************************************/ /** * JIT_FIXUP_BRANCH: Modify a branch instruction at the given offset to * jump to the given target. * * [Parameters] * entry: Block being translated * offset: Offset within entry->native_code of branch instruction * (as returned in *branch_offset EMIT parameter) * target: Target offset within entry->native_code * [Return value] * None */ static inline void JIT_FIXUP_BRANCH(Q68JitEntry *entry, uint32_t offset, uint32_t target) { /* Not supported on x86/x64 */ } /*************************************************************************/ /* * The remaining macros are all used to insert a specific operation into * the native code stream. For simplicity, we define the actual code for * each operation in a separate assembly file, and use memcpy() to copy * from the assembled code to the output code stream. The GEN_EMIT macro * below is used to generate each of the JIT_EMIT_* functions; each * function JIT_EMIT_xxx copies JIT_X86SIZE_xxx bytes from JIT_X86_xxx to * the code stream, expanding the code buffer if necessary. */ /* Sub-macros (platform-dependent): */ #ifdef CPU_X64 #define GEN_NAMESIZE(name) \ extern const uint8_t JIT_X64_##name[]; \ extern const uint32_t JIT_X64SIZE_##name; #define GEN_PARAM(name,param) \ extern const uint32_t JIT_X64PARAM_##name##_##param; #define GEN_FUNC_TOP(name) \ if (UNLIKELY(entry->native_size - entry->native_length \ < JIT_X64SIZE_##name)) { \ if (!expand_buffer(entry)) { \ return; \ } \ } \ if (JIT_X64SIZE_##name > 0) { \ memcpy((uint8_t *)entry->native_code + entry->native_length, \ JIT_X64_##name, JIT_X64SIZE_##name); \ } #define GEN_COPY_PARAM(name,type,param) \ *(type *)((uint8_t *)entry->native_code + entry->native_length \ + JIT_X64PARAM_##name##_##param) = param; #define GEN_FUNC_BOTTOM(name) \ entry->native_length += JIT_X64SIZE_##name; #else // CPU_X86 #define GEN_NAMESIZE(name) \ extern const uint8_t JIT_X86_##name[]; \ extern const uint32_t JIT_X86SIZE_##name; #define GEN_PARAM(name,param) \ extern const uint32_t JIT_X86PARAM_##name##_##param; #define GEN_FUNC_TOP(name) \ if (UNLIKELY(entry->native_size - entry->native_length \ < JIT_X86SIZE_##name)) { \ if (!expand_buffer(entry)) { \ return; \ } \ } \ if (JIT_X86SIZE_##name > 0) { \ memcpy((uint8_t *)entry->native_code + entry->native_length, \ JIT_X86_##name, JIT_X86SIZE_##name); \ } #define GEN_COPY_PARAM(name,type,param) \ *(type *)((uint8_t *)entry->native_code + entry->native_length \ + JIT_X86PARAM_##name##_##param) = param; #define GEN_FUNC_BOTTOM(name) \ entry->native_length += JIT_X86SIZE_##name; #endif // X64/X86 #define GEN_EMIT(name) \ GEN_NAMESIZE(name) \ static void JIT_EMIT_##name(Q68JitEntry *entry) { \ GEN_FUNC_TOP(name) \ GEN_FUNC_BOTTOM(name) \ } #define GEN_EMIT_1(name,type1,param1) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_FUNC_BOTTOM(name) \ } #define GEN_EMIT_2(name,type1,param1,type2,param2) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ GEN_PARAM(name,param2) \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ type2 param2) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_COPY_PARAM(name, type2, param2) \ GEN_FUNC_BOTTOM(name) \ } #define GEN_EMIT_3(name,type1,param1,type2,param2,type3,param3) \ GEN_NAMESIZE(name) \ GEN_PARAM(name,param1) \ GEN_PARAM(name,param2) \ GEN_PARAM(name,param3) \ static void JIT_EMIT_##name(Q68JitEntry *entry, type1 param1, \ type2 param2, type3 param3) { \ GEN_FUNC_TOP(name) \ GEN_COPY_PARAM(name, type1, param1) \ GEN_COPY_PARAM(name, type2, param2) \ GEN_COPY_PARAM(name, type3, param3) \ GEN_FUNC_BOTTOM(name) \ } /*-----------------------------------------------------------------------*/ /* Code prologue and epilogue */ GEN_EMIT(PROLOGUE) GEN_EMIT(EPILOGUE) #ifdef Q68_TRACE /* Trace the current instruction */ GEN_EMIT(TRACE) #endif /* Add the specified number of cycles to the cycle counter */ GEN_EMIT_1(ADD_CYCLES, int32_t, cycles) /* Check the cycle limit and interrupt execution if necessary */ GEN_EMIT(CHECK_CYCLES) /* Add the specified amount to the program counter and/or check whether * to abort */ GEN_EMIT_1(ADVANCE_PC, int32_t, value) GEN_EMIT_1(ADVANCE_PC_CHECK_ABORT, int32_t, value) GEN_EMIT(CHECK_ABORT) /* Exception raising */ GEN_EMIT_1(EXCEPTION, uint32_t, num) GEN_EMIT_2(CHECK_ALIGNED_EA, uint16_t, opcode, uint16_t, status) GEN_EMIT_2(CHECK_ALIGNED_SP, uint16_t, opcode, uint16_t, status) GEN_EMIT(CHECK_SUPER) /*-----------------------------------------------------------------------*/ /* Resolve an effective address */ GEN_EMIT_1(RESOLVE_INDIRECT, uint8_t, reg4) GEN_EMIT_2(RESOLVE_POSTINC, uint8_t, reg4, uint8_t, size) GEN_EMIT(RESOLVE_POSTINC_A7_B) GEN_EMIT_2(RESOLVE_PREDEC, uint8_t, reg4, uint8_t, size) GEN_EMIT(RESOLVE_PREDEC_A7_B) GEN_EMIT_2(RESOLVE_DISP, uint8_t, reg4, uint32_t, disp) GEN_EMIT_3(RESOLVE_INDEX_W, uint8_t, reg4, uint8_t, ireg4, uint8_t, disp) GEN_EMIT_3(RESOLVE_INDEX_L, uint8_t, reg4, uint8_t, ireg4, uint8_t, disp) GEN_EMIT_1(RESOLVE_ABSOLUTE, uint32_t, addr) GEN_EMIT_2(RESOLVE_ABS_INDEX_W, uint32_t, addr, uint8_t, ireg4) GEN_EMIT_2(RESOLVE_ABS_INDEX_L, uint32_t, addr, uint8_t, ireg4) /* Retrieve various things as operand 1 */ GEN_EMIT_1(GET_OP1_REGISTER, uint8_t, reg4) GEN_EMIT(GET_OP1_EA_B) GEN_EMIT(GET_OP1_EA_W) GEN_EMIT(GET_OP1_EA_L) GEN_EMIT_1(GET_OP1_IMMEDIATE, uint32_t, value) GEN_EMIT(GET_OP1_CCR) GEN_EMIT(GET_OP1_SR) /* Retrieve various things as operand 2 */ GEN_EMIT_1(GET_OP2_REGISTER, uint8_t, reg4) GEN_EMIT(GET_OP2_EA_B) GEN_EMIT(GET_OP2_EA_W) GEN_EMIT(GET_OP2_EA_L) GEN_EMIT_1(GET_OP2_IMMEDIATE, uint32_t, value) GEN_EMIT(GET_OP2_CCR) GEN_EMIT(GET_OP2_SR) /* Update various things from result */ GEN_EMIT_1(SET_REGISTER_B, uint8_t, reg4) GEN_EMIT_1(SET_REGISTER_W, uint8_t, reg4) GEN_EMIT_1(SET_REGISTER_L, uint8_t, reg4) GEN_EMIT_1(SET_AREG_W, uint8_t, reg4) GEN_EMIT(SET_EA_B) GEN_EMIT(SET_EA_W) GEN_EMIT(SET_EA_L) GEN_EMIT(SET_CCR) GEN_EMIT(SET_SR) /* Stack operations */ GEN_EMIT(PUSH_L) GEN_EMIT(POP_L) /* Condition code setting */ GEN_EMIT(SETCC_ADD_B) GEN_EMIT(SETCC_ADD_W) GEN_EMIT(SETCC_ADD_L) GEN_EMIT(SETCC_ADDX_B) GEN_EMIT(SETCC_ADDX_W) GEN_EMIT(SETCC_ADDX_L) GEN_EMIT(SETCC_SUB_B) GEN_EMIT(SETCC_SUB_W) GEN_EMIT(SETCC_SUB_L) GEN_EMIT(SETCC_SUBX_B) GEN_EMIT(SETCC_SUBX_W) GEN_EMIT(SETCC_SUBX_L) GEN_EMIT(SETCC_CMP_B) GEN_EMIT(SETCC_CMP_W) GEN_EMIT(SETCC_CMP_L) GEN_EMIT(SETCC_LOGIC_B) GEN_EMIT(SETCC_LOGIC_W) GEN_EMIT(SETCC_LOGIC_L) /* Condition testing */ GEN_EMIT(TEST_T) GEN_EMIT(TEST_F) GEN_EMIT(TEST_HI) GEN_EMIT(TEST_LS) GEN_EMIT(TEST_CC) GEN_EMIT(TEST_CS) GEN_EMIT(TEST_NE) GEN_EMIT(TEST_EQ) GEN_EMIT(TEST_VC) GEN_EMIT(TEST_VS) GEN_EMIT(TEST_PL) GEN_EMIT(TEST_MI) GEN_EMIT(TEST_GE) GEN_EMIT(TEST_LT) GEN_EMIT(TEST_GT) GEN_EMIT(TEST_LE) /* ALU operations */ GEN_EMIT(MOVE_B) GEN_EMIT(MOVE_W) GEN_EMIT(MOVE_L) GEN_EMIT(ADD_B) GEN_EMIT(ADD_W) GEN_EMIT(ADD_L) GEN_EMIT(ADDA_W) GEN_EMIT(ADDX_B) GEN_EMIT(ADDX_W) GEN_EMIT(ADDX_L) GEN_EMIT(SUB_B) GEN_EMIT(SUB_W) GEN_EMIT(SUB_L) GEN_EMIT(SUBA_W) GEN_EMIT(SUBX_B) GEN_EMIT(SUBX_W) GEN_EMIT(SUBX_L) GEN_EMIT(MULS_W) GEN_EMIT(MULU_W) GEN_EMIT(DIVS_W) GEN_EMIT(DIVU_W) GEN_EMIT(AND_B) GEN_EMIT(AND_W) GEN_EMIT(AND_L) GEN_EMIT(OR_B) GEN_EMIT(OR_W) GEN_EMIT(OR_L) GEN_EMIT(EOR_B) GEN_EMIT(EOR_W) GEN_EMIT(EOR_L) GEN_EMIT(EXT_W) GEN_EMIT(EXT_L) GEN_EMIT(SWAP) /* BCD operations */ GEN_EMIT(ABCD) GEN_EMIT(SBCD) /* Bit-twiddling operations */ GEN_EMIT(BTST_B) GEN_EMIT(BTST_L) GEN_EMIT(BCHG) GEN_EMIT(BCLR) GEN_EMIT(BSET) /* Shift/rotate operations */ GEN_EMIT(ASL_B) GEN_EMIT(ASL_W) GEN_EMIT(ASL_L) GEN_EMIT(ASR_B) GEN_EMIT(ASR_W) GEN_EMIT(ASR_L) GEN_EMIT(LSL_B) GEN_EMIT(LSL_W) GEN_EMIT(LSL_L) GEN_EMIT(LSR_B) GEN_EMIT(LSR_W) GEN_EMIT(LSR_L) GEN_EMIT(ROXL_B) GEN_EMIT(ROXL_W) GEN_EMIT(ROXL_L) GEN_EMIT(ROXR_B) GEN_EMIT(ROXR_W) GEN_EMIT(ROXR_L) GEN_EMIT(ROL_B) GEN_EMIT(ROL_W) GEN_EMIT(ROL_L) GEN_EMIT(ROR_B) GEN_EMIT(ROR_W) GEN_EMIT(ROR_L) /* Conditional and branch operations ("branch_offset" parameter receives * the native offset of the branch to update when resolving, or -1 if not * supported) */ GEN_EMIT(Scc) GEN_EMIT(ADD_CYCLES_Scc_Dn) GEN_EMIT_2(DBcc, uint8_t, reg4, int32_t, target) GEN_EMIT_3(DBcc_native, uint8_t, reg4, int32_t, target, int32_t, native_disp) #ifdef CPU_X64 # define SIZE_DBcc_native JIT_X64SIZE_DBcc_native #else # define SIZE_DBcc_native JIT_X86SIZE_DBcc_native #endif #define JIT_EMIT_DBcc_native(entry,reg4,target,offset) do { \ Q68JitEntry *__entry = (entry); \ int32_t __fragment_end = __entry->native_length + SIZE_DBcc_native; \ JIT_EMIT_DBcc_native(__entry, (reg4), (target), \ (offset) - __fragment_end); \ } while (0) GEN_EMIT_1(Bcc, int32_t, target) #define JIT_EMIT_Bcc(entry,target,branch_offset) do { \ JIT_EMIT_Bcc((entry), (target)); \ *(branch_offset) = -1; \ } while (0) GEN_EMIT_2(Bcc_native, int32_t, target, int32_t, native_disp) #ifdef CPU_X64 # define SIZE_Bcc_native JIT_X64SIZE_Bcc_native #else # define SIZE_Bcc_native JIT_X86SIZE_Bcc_native #endif #define JIT_EMIT_Bcc_native(entry,target,native) do { \ Q68JitEntry *__entry = (entry); \ int32_t __fragment_end = __entry->native_length + SIZE_Bcc_native; \ JIT_EMIT_Bcc_native(__entry, (target), (offset) - __fragment_end); \ } while (0) GEN_EMIT_2(BSR, uint32_t, return_addr, int32_t, target) GEN_EMIT(JMP) GEN_EMIT_1(JSR, uint32_t, return_addr) /* MOVEM-related operations */ GEN_EMIT_1(STORE_DEC_W, uint8_t, reg4) GEN_EMIT_1(STORE_DEC_L, uint8_t, reg4) GEN_EMIT_1(STORE_INC_W, uint8_t, reg4) GEN_EMIT_1(STORE_INC_L, uint8_t, reg4) GEN_EMIT_1(LOAD_INC_W, uint8_t, reg4) GEN_EMIT_1(LOAD_INC_L, uint8_t, reg4) GEN_EMIT_1(LOADA_INC_W, uint8_t, reg4) GEN_EMIT_1(MOVEM_WRITEBACK, uint8_t, reg4) /* Miscellaneous operations */ GEN_EMIT(CHK_W) GEN_EMIT_1(LEA, uint8_t, reg4) GEN_EMIT(PEA) GEN_EMIT(TAS) GEN_EMIT_1(MOVE_FROM_USP, uint8_t, reg4) GEN_EMIT_1(MOVE_TO_USP, uint8_t, reg4) GEN_EMIT_1(STOP, uint16_t, newSR) GEN_EMIT(TRAPV) GEN_EMIT(RTS) GEN_EMIT(RTR) GEN_EMIT(RTE) GEN_EMIT_3(MOVEP_READ_W, uint8_t, areg4, int32_t, disp, uint8_t, dreg4) GEN_EMIT_3(MOVEP_READ_L, uint8_t, areg4, int32_t, disp, uint8_t, dreg4) GEN_EMIT_3(MOVEP_WRITE_W, uint8_t, areg4, int32_t, disp, uint8_t, dreg4) GEN_EMIT_3(MOVEP_WRITE_L, uint8_t, areg4, int32_t, disp, uint8_t, dreg4) GEN_EMIT_2(EXG, uint8_t, reg1_4, uint8_t, reg2_4) /*************************************************************************/ #endif // Q68_JIT_X86_H /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/q68/q68-jit-x86.S000644 001750 001750 00000171302 12256006177 020576 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-jit-x86.S: x86 (32/64-bit) dynamic translation implementation for Q68 Copyright 2009-2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q68-const.h" /*************************************************************************/ /* * Register usage on x86 and x64 is as follows (for x86, read %rXX as %eXX): * * %rax -- result, temporary * %rbx -- Q68State structure pointer * %rcx -- temporary * %rdx -- operand 2 * %rsi -- cumulative cycle count * %rdi -- operand 1, (on termination) pointer to next address to execute * * Additionally, the cycle limit is pushed onto the stack at the start of * execution. */ /*************************************************************************/ /* Define handy macros so we can use the same source on both x86 and x64 */ #ifdef CPU_X64 /* External routine calling macros (indirect calls only) */ .macro CALL1 address, arg1 push %rsi push %rdi mov \arg1, %rdi call \address pop %rdi pop %rsi .endm .macro CALL2 address, arg1, arg2 push %rsi push %rdi mov \arg1, %rdi mov \arg2, %rsi call \address pop %rdi pop %rsi .endm /* Label/size/parameter definition macros */ #define DEFLABEL(name) .globl JIT_X64_##name; JIT_X64_##name: #define DEFSIZE(name) .globl JIT_X64SIZE_##name; \ JIT_X64SIZE_##name: .int . - JIT_X64_##name #define DEFPARAM(name,param,label,offset) \ .globl JIT_X64PARAM_##name##_##param; \ JIT_X64PARAM_##name##_##param: \ .int label - JIT_X64_##name + (offset) /* Q68State structure offsets */ Q68State_D = 0 Q68State_A = 32 Q68State_PC = 64 Q68State_SR = 68 Q68State_USP = 72 Q68State_SSP = 76 Q68State_current_PC = 80 Q68State_ea_addr = 84 Q68State_exception = 88 Q68State_fault_addr = 92 Q68State_fault_opcode = 96 Q68State_fault_status = 98 Q68State_halted = 100 Q68State_irq = 104 Q68State_cycles = 108 Q68State_malloc_func = 112 Q68State_realloc_func = 120 Q68State_free_func = 128 Q68State_readb_func = 136 Q68State_readw_func = 144 Q68State_writeb_func = 152 Q68State_writew_func = 160 Q68State_jit_flush = 168 Q68State_jit_running = 176 Q68State_jit_abort = 184 Q68State_jit_table = 192 Q68State_jit_hashchain = 200 Q68State_jit_total_data = 208 Q68State_jit_timestamp = 212 Q68State_jit_blacklist = 216 Q68State_jit_in_blist = Q68State_jit_blacklist + (12 * Q68_JIT_BLACKLIST_SIZE) Q68State_jit_blist_num = Q68State_jit_in_blist + 4 Q68State_jit_callstack_top = Q68State_jit_blist_num + 4 Q68State_jit_callstack = (Q68State_jit_callstack_top + 7) & ~7 Q68State_jit_pages = Q68State_jit_callstack + (24 * Q68_JIT_CALLSTACK_SIZE) #else // CPU_X86 /* Register name translation macros (we don't handle any 64-bit data except * when protected by #ifdef CPU_X64, so this is safe) */ #define rax eax #define rbx ebx #define rcx ecx #define rdx edx #define rsp esp #define rbp ebp #define rsi esi #define rdi edi /* External routine calling macros (indirect calls only) */ .macro CALL1 address, arg1 push \arg1 call \address pop %rcx .endm .macro CALL2 address, arg1, arg2 push \arg2 push \arg1 call \address pop %rcx pop %rcx .endm /* Label/size/parameter definition macros */ #define DEFLABEL(name) .globl JIT_X86_##name; JIT_X86_##name: #define DEFSIZE(name) .globl JIT_X86SIZE_##name; \ JIT_X86SIZE_##name: .int . - JIT_X86_##name #define DEFPARAM(name,param,label,offset) \ .globl JIT_X86PARAM_##name##_##param; \ JIT_X86PARAM_##name##_##param: \ .int label - JIT_X86_##name + (offset) /* Q68State structure offsets */ Q68State_D = 0 Q68State_A = 32 Q68State_PC = 64 Q68State_SR = 68 Q68State_USP = 72 Q68State_SSP = 76 Q68State_current_PC = 80 Q68State_ea_addr = 84 Q68State_exception = 88 Q68State_fault_addr = 92 Q68State_fault_opcode = 96 Q68State_fault_status = 98 Q68State_halted = 100 Q68State_irq = 104 Q68State_cycles = 108 Q68State_malloc_func = 112 Q68State_realloc_func = 116 Q68State_free_func = 120 Q68State_readb_func = 124 Q68State_readw_func = 128 Q68State_writeb_func = 132 Q68State_writew_func = 136 Q68State_jit_flush = 140 Q68State_jit_running = 144 Q68State_jit_abort = 148 Q68State_jit_table = 152 Q68State_jit_hashchain = 156 Q68State_jit_total_data = 160 Q68State_jit_timestamp = 164 Q68State_jit_blacklist = 168 Q68State_jit_in_blist = Q68State_jit_blacklist + (12 * Q68_JIT_BLACKLIST_SIZE) Q68State_jit_blist_num = Q68State_jit_in_blist + 4 Q68State_jit_callstack_top = Q68State_jit_blist_num + 4 Q68State_jit_callstack = Q68State_jit_callstack_top + 4 Q68State_jit_pages = Q68State_jit_callstack + (12 * Q68_JIT_CALLSTACK_SIZE) #endif // X64/X86 /*************************************************************************/ /* Shorthand for referencing Q68State fields */ #define D0 Q68State_D+0*4(%rbx) #define D1 Q68State_D+1*4(%rbx) #define D2 Q68State_D+2*4(%rbx) #define D3 Q68State_D+3*4(%rbx) #define D4 Q68State_D+4*4(%rbx) #define D5 Q68State_D+5*4(%rbx) #define D6 Q68State_D+6*4(%rbx) #define D7 Q68State_D+7*4(%rbx) #define A0 Q68State_A+0*4(%rbx) #define A1 Q68State_A+1*4(%rbx) #define A2 Q68State_A+2*4(%rbx) #define A3 Q68State_A+3*4(%rbx) #define A4 Q68State_A+4*4(%rbx) #define A5 Q68State_A+5*4(%rbx) #define A6 Q68State_A+6*4(%rbx) #define A7 Q68State_A+7*4(%rbx) #define PC Q68State_PC(%rbx) #define SR Q68State_SR(%rbx) #define USP Q68State_USP(%rbx) #define SSP Q68State_SSP(%rbx) /*************************************************************************/ /************************** Convenience macros ***************************/ /*************************************************************************/ /** * READ{8,16,32}: Read a value from memory. The value read is returned * zero-extended in %eax; the address parameter is destroyed. %rdx may not * be used as a parameter. */ .macro READ8 address and $0x00FFFFFF, \address mov Q68State_readb_func(%rbx), %rdx CALL1 *%rdx, \address movzx %al, %eax .endm .macro READ16 address and $0x00FFFFFF, \address mov Q68State_readw_func(%rbx), %rdx CALL1 *%rdx, \address movzx %ax, %eax .endm .macro READ32 address and $0x00FFFFFF, \address mov Q68State_readw_func(%rbx), %rdx #ifdef CPU_X64 push %rdi mov \address, %rdi call *%rdx push %rax add $2, %rdi and $0x00FFFFFF, %rdi mov Q68State_readw_func(%rbx), %rdx call *%rdx pop %rcx pop %rdi #else push \address call *%rdx xchg %rax, (%rsp) addl $2, %rax push %rax mov Q68State_readw_func(%rbx), %rdx call *%rdx pop %rcx pop %rcx #endif shl $16, %ecx or %ecx, %eax .endm /*-----------------------------------------------------------------------*/ /** * WRITE_CHECK_JIT: Check whether a write of size \nbytes (*bytes*, not * bits) to \address would clobber a page containing already-translated * blocks, and clear those translations if so. The value of \address is * preserved, but all other caller-saved registers are destroyed. * * Note that this macro uses local label 4. */ .macro WRITE_CHECK_JIT address, nbytes push \address mov \address, %rdx shr $Q68_JIT_PAGE_BITS+3, %rdx mov \address, %rcx shr $Q68_JIT_PAGE_BITS, %rcx and $7, %cl mov $1, %al shl %cl, %al test %al, Q68State_jit_pages(%rbx,%rdx,1) jz 4f /* Have to use an indirect call because the offset for the call * instruction will change based on where this code is copied */ mov (%rsp), \address #ifdef CPU_X64 mov $q68_jit_clear_write, %r8 mov $\nbytes, %edx CALL2 *%r8, %rbx, \address #else mov $q68_jit_clear_write, %edx pushl $\nbytes CALL2 *%edx, %ebx, \address pop %ecx #endif 4: pop \address .endm /*-----------------------------------------------------------------------*/ /** * WRITE{8,16,32}: Write a value to memory. %rdx may not be used as a * parameter; the address parameter is destroyed. */ .macro WRITE8 address, value and $0x00FFFFFF, \address push \value WRITE_CHECK_JIT \address, 1 pop \value mov Q68State_writeb_func(%rbx), %rdx CALL2 *%rdx, \address, \value .endm .macro WRITE16 address, value and $0x00FFFFFF, \address push \value WRITE_CHECK_JIT \address, 2 pop \value mov Q68State_writew_func(%rbx), %rdx CALL2 *%rdx, \address, \value .endm .macro WRITE32 address, value push \value push \address shr $16, \value WRITE16 \address, \value pop \address pop \value add $2, \address WRITE16 \address, \value .endm /*-----------------------------------------------------------------------*/ /** * {PUSH,POP}{16,32}: Push or pop values onto or off of the stack. For * POP, the value popped is zero-extended and returned in %rax; for PUSH, * register %rax is destroyed before storing. %rdx may not be used as a * parameter. */ .macro PUSH16 value mov A7, %eax sub $2, %eax mov %eax, A7 WRITE16 %rax, \value .endm .macro PUSH32 value mov A7, %eax sub $4, %eax mov %eax, A7 WRITE32 %rax, \value .endm .macro POP16 mov A7, %eax add $2, A7 READ16 %rax .endm .macro POP32 mov A7, %eax add $4, A7 READ32 %rax .endm /*************************************************************************/ /** * LDC_FROM_X: Set the x86 carry flag (CF) based on the value of the * 68000 extend flag (X). The byte register passed in \temp is destroyed. */ .macro LDC_FROM_X temp mov SR, \temp shr $5, \temp .endm /*************************************************************************/ /** * SETCC_NZ: Set the N and Z condition codes according to the x86 flag bits. */ .macro SETCC_NZ sets %cl setz %dl andb $~(SR_N|SR_Z), SR shl $SR_N_SHIFT, %cl shl $SR_Z_SHIFT, %dl or %cl, %dl or %dl, SR .endm /*-----------------------------------------------------------------------*/ /** * SETCC_NZ00: Set the N and Z condition codes according to the x86 flag * bits, and clear the V and C condition codes. */ .macro SETCC_NZ00 sets %cl setz %dl andb $~(SR_N|SR_Z|SR_V|SR_C), SR shl $SR_N_SHIFT, %cl shl $SR_Z_SHIFT, %dl or %cl, %dl or %dl, SR .endm /*-----------------------------------------------------------------------*/ /** * SETCC_NZVC: Set the N, Z, V, and C condition codes according to the x86 * flag bits. */ .macro SETCC_NZVC sets %cl setz %dl seto %ch setc %dh andb $~(SR_N|SR_Z|SR_V|SR_C), SR shl $SR_N_SHIFT, %cl shl $SR_Z_SHIFT, %dl shl $SR_V_SHIFT, %ch //shl $SR_C_SHIFT, %dh // SR_C_SHIFT is zero, so skip the shift or %ch, %cl or %dh, %dl or %cl, %dl or %dl, SR .endm /*-----------------------------------------------------------------------*/ /** * SETCC_XNZVC: Set the N, Z, V, and C condition codes according to the * x86 flag bits, and sets the X condition code equal to C. */ .macro SETCC_XNZVC sets %cl setz %dl seto %ch setc %dh andb $~(SR_X|SR_N|SR_Z|SR_V|SR_C), SR shl $SR_N_SHIFT, %cl shl $SR_Z_SHIFT, %dl shl $SR_V_SHIFT, %ch //shl $SR_C_SHIFT, %dh // SR_C_SHIFT is zero, so skip the shift or %ch, %cl or %dh, %dl shl $(SR_X_SHIFT - SR_C_SHIFT), %dh or %dh, %dl or %cl, %dl or %dl, SR .endm /*-----------------------------------------------------------------------*/ /** * SETCC_XNVC_Z: Set the N, V, and C condition codes according to the x86 * flag bits; clears the Z condition code if the x86 Z flag is clear, and * sets the X condition code equal to C. */ .macro SETCC_XNVC_Z sets %cl setnz %dl seto %ch setc %dh andb $~(SR_X|SR_N|SR_V|SR_C), SR shl $SR_N_SHIFT, %cl shl $SR_Z_SHIFT, %dl shl $SR_V_SHIFT, %ch //shl $SR_C_SHIFT, %dh // SR_C_SHIFT is zero, so skip the shift or %ch, %cl or %dh, %cl shl $(SR_X_SHIFT - SR_C_SHIFT), %dh or %dh, %cl or %cl, SR not %dl and %dl, SR .endm /*-----------------------------------------------------------------------*/ /** * UPDATE_SR: Set the status register and condition codes according to * the value in %ax. * * Note that this macro uses local labels 0, 1, 2, and 3. */ .macro UPDATE_SR value movzwl %ax, %ecx xor SR, %eax mov %ecx, SR test $SR_S, %eax // Change in S bit? jz 1f test $SR_S, %ecx // Which way did it change? jz 0f mov A7, %eax // Into supervisor mode mov %eax, USP mov SSP, %eax mov %eax, A7 jmp 1f 0: mov A7, %eax // Out of supervisor mode mov %eax, SSP mov USP, %eax mov %eax, A7 1: mov Q68State_irq(%rbx), %al and $7, %al cmp $7, %al je 2f and $7, %ch cmp %al, %ch jae 3f 2: movzx %al, %eax add $EX_LEVEL_1_INTERRUPT-1, %eax mov %eax, Q68State_exception(%rbx) movl $0, Q68State_irq(%rbx) TERMINATE 3: .endm /*************************************************************************/ /** * SETUP: Perform setup required before executing translated code. */ .macro SETUP push %rsi xor %esi, %esi .endm /*-----------------------------------------------------------------------*/ /** * TERMINATE: Terminate execution of the current block. The emulator will * resume execution at the address in state->PC. */ .macro TERMINATE pop %rax xor %rdi, %rdi ret .endm /*************************************************************************/ /**************************** Meta-operations ****************************/ /*************************************************************************/ /** * PROLOGUE: Any prologue necessary at the beginning of the code stream. */ DEFLABEL(PROLOGUE) SETUP DEFSIZE(PROLOGUE) /*-----------------------------------------------------------------------*/ /** * EPILOGUE: Any epilogue necessary at the end of the code stream. */ DEFLABEL(EPILOGUE) TERMINATE DEFSIZE(EPILOGUE) /*************************************************************************/ /** * TRACE: Trace the current instruction. */ DEFLABEL(TRACE) mov Q68State_cycles(%rbx), %eax push %rax add %esi, %eax mov %eax, Q68State_cycles(%rbx) #ifdef CPU_X64 push %rsi push %rdi #endif mov $q68_trace, %rdx call *%rdx #ifdef CPU_X64 pop %rdi pop %rsi #endif pop %rax mov %eax, Q68State_cycles(%rbx) DEFSIZE(TRACE) /*************************************************************************/ /** * ADD_CYCLES: Add the specified number of clock cycles to the cycle count. * * [Parameters] * cycles: Number of clock cycles to add */ DEFLABEL(ADD_CYCLES) add $0x12345678, %esi 9: DEFSIZE(ADD_CYCLES) DEFPARAM(ADD_CYCLES, cycles, 9b, -4) /*-----------------------------------------------------------------------*/ /** * CHECK_CYCLES: Check whether the clock cycle limit has been reached, and * interrupt execution if so. */ DEFLABEL(CHECK_CYCLES) cmp %esi, (%rsp) ja 4f pop %rax call 1f 0: jmp 2f 1: mov (%rsp), %rdi ret 2: add $3f-0b, %rdi ret 3: SETUP 4: DEFSIZE(CHECK_CYCLES) /*************************************************************************/ /** * ADVANCE_PC: Add the specified value to the current program counter. * * [Parameters] * value: Amount to add */ DEFLABEL(ADVANCE_PC) addl $0x12345678, PC 9: DEFSIZE(ADVANCE_PC) DEFPARAM(ADVANCE_PC, value, 9b, -4) /*-----------------------------------------------------------------------*/ /** * ADVANCE_PC_CHECK_ABORT: Add the specified value to the current program * counter, then check the jit_abort flag and abort if necessary. * * [Parameters] * value: Amount to add */ DEFLABEL(ADVANCE_PC_CHECK_ABORT) addl $0x12345678, PC 9: testb $1, Q68State_jit_abort(%rbx) jz 0f TERMINATE 0: DEFSIZE(ADVANCE_PC_CHECK_ABORT) DEFPARAM(ADVANCE_PC_CHECK_ABORT, value, 9b, -4) /*-----------------------------------------------------------------------*/ /** * CHECK_ABORT: Check the jit_abort flag and abort if necessary. */ DEFLABEL(CHECK_ABORT) testb $1, Q68State_jit_abort(%rbx) jz 0f TERMINATE 0: DEFSIZE(CHECK_ABORT) /*************************************************************************/ /** * EXCEPTION: Raise the specified exception. * * [Parameters] * num: Exception number */ DEFLABEL(EXCEPTION) movl $0x12345678, Q68State_exception(%rbx) 9: TERMINATE DEFSIZE(EXCEPTION) DEFPARAM(EXCEPTION, num, 9b, -4) /*-----------------------------------------------------------------------*/ /** * CHECK_ALIGNED_EA: Check whether the previously resolved effective * address is word-aligned (bit 0 is clear), and raise an address error * exception if not. * * [Parameters] * opcode: Instruction opcode * status: Status word for address error exception */ DEFLABEL(CHECK_ALIGNED_EA) testl $1, Q68State_ea_addr(%rbx) jz 0f movl $EX_ADDRESS_ERROR, Q68State_exception(%rbx) mov Q68State_ea_addr(%rbx), %eax mov %eax, Q68State_fault_addr(%rbx) movw $0x1234, Q68State_fault_opcode(%rbx) 8: movw $0x1234, Q68State_fault_status(%rbx) 9: TERMINATE 0: DEFSIZE(CHECK_ALIGNED_EA) DEFPARAM(CHECK_ALIGNED_EA, opcode, 8b, -2) DEFPARAM(CHECK_ALIGNED_EA, status, 9b, -2) /*-----------------------------------------------------------------------*/ /** * CHECK_ALIGNED_SP: Check whether the current stack pointer (register A7) * is word-aligned (bit 0 is clear), and raise an address error exception * if not. * * [Parameters] * opcode: Instruction opcode * status: Status word for address error exception */ DEFLABEL(CHECK_ALIGNED_SP) testl $1, A7 jz 0f movl $EX_ADDRESS_ERROR, Q68State_exception(%rbx) mov A7, %eax mov %eax, Q68State_fault_addr(%rbx) movw $0x1234, Q68State_fault_opcode(%rbx) 8: movw $0x1234, Q68State_fault_status(%rbx) 9: TERMINATE 0: DEFSIZE(CHECK_ALIGNED_SP) DEFPARAM(CHECK_ALIGNED_SP, opcode, 8b, -2) DEFPARAM(CHECK_ALIGNED_SP, status, 9b, -2) /*-----------------------------------------------------------------------*/ /** * CHECK_SUPER: Check whether the processor is in supervisor mode, and * raise a privilege violation exception if not. */ DEFLABEL(CHECK_SUPER) testl $SR_S, SR jnz 0f movl $EX_PRIVILEGE_VIOLATION, Q68State_exception(%rbx) TERMINATE 0: DEFSIZE(CHECK_SUPER) /*************************************************************************/ /********************* Effective address resolution **********************/ /*************************************************************************/ /** * RESOLVE_INDIRECT: Resolve an address register indirect reference. * * [Parameters] * reg4: (8+n)*4 for register An */ DEFLABEL(RESOLVE_INDIRECT) mov 1(%rbx), %eax 9: mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_INDIRECT) DEFPARAM(RESOLVE_INDIRECT, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * RESOLVE_POSTINC: Resolve an address register postincrement reference. * * [Parameters] * reg4: (8+n)*4 for register An * size: Size in bytes of the reference */ DEFLABEL(RESOLVE_POSTINC) lea 1(%rbx), %rcx 8: mov (%rcx), %eax add $1, (%rcx) 9: mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_POSTINC) DEFPARAM(RESOLVE_POSTINC, reg4, 8b, -1) DEFPARAM(RESOLVE_POSTINC, size, 9b, -1) /* For byte-sized (A7)+, make sure A7 stays even */ DEFLABEL(RESOLVE_POSTINC_A7_B) mov A7, %ecx lea 1(%ecx), %eax add $2, A7 mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_POSTINC_A7_B) /*-----------------------------------------------------------------------*/ /** * RESOLVE_PREDEC: Resolve an address register predecrement reference. * * [Parameters] * reg4: (8+n)*4 for register An * size: Size in bytes of the reference */ DEFLABEL(RESOLVE_PREDEC) lea 1(%rbx), %rcx 8: sub $1, (%rcx) 9: mov (%rcx), %eax mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_PREDEC) DEFPARAM(RESOLVE_PREDEC, reg4, 8b, -1) DEFPARAM(RESOLVE_PREDEC, size, 9b, -1) /* For byte-sized -(A7), make sure A7 stays even */ DEFLABEL(RESOLVE_PREDEC_A7_B) mov A7, %ecx lea -1(%ecx), %eax sub $2, A7 mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_PREDEC_A7_B) /*-----------------------------------------------------------------------*/ /** * RESOLVE_DISP: Resolve an address register indirect with displacement * reference. * * [Parameters] * reg4: (8+n)*4 for register An * disp: Displacement */ DEFLABEL(RESOLVE_DISP) mov 1(%rbx), %eax 8: add $0x12345678, %eax 9: mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_DISP) DEFPARAM(RESOLVE_DISP, reg4, 8b, -1) DEFPARAM(RESOLVE_DISP, disp, 9b, -4) /*-----------------------------------------------------------------------*/ /** * RESOLVE_INDEX_[WL]: Resolve an address register indirect with index * reference. * * [Parameters] * reg4: (8+n)*4 for register An * ireg4: Index register number * 4 * disp: Displacement */ DEFLABEL(RESOLVE_INDEX_W) mov 1(%rbx), %eax 7: mov 1(%rbx), %ecx 8: movsx %cx, %ecx lea 1(%eax, %ecx), %eax 9: mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_INDEX_W) DEFPARAM(RESOLVE_INDEX_W, reg4, 7b, -1) DEFPARAM(RESOLVE_INDEX_W, ireg4, 8b, -1) DEFPARAM(RESOLVE_INDEX_W, disp, 9b, -1) DEFLABEL(RESOLVE_INDEX_L) mov 1(%rbx), %eax 7: mov 1(%rbx), %ecx 8: lea 1(%eax, %ecx), %eax 9: mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_INDEX_L) DEFPARAM(RESOLVE_INDEX_L, reg4, 7b, -1) DEFPARAM(RESOLVE_INDEX_L, ireg4, 8b, -1) DEFPARAM(RESOLVE_INDEX_L, disp, 9b, -1) /*-----------------------------------------------------------------------*/ /** * RESOLVE_ABSOLUTE: Resolve an absolute short, absolute long, or * PC-relative reference. * * [Parameters] * addr: Absolute address */ DEFLABEL(RESOLVE_ABSOLUTE) mov $0x12345678, %eax 9: mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_ABSOLUTE) DEFPARAM(RESOLVE_ABSOLUTE, addr, 9b, -4) /*-----------------------------------------------------------------------*/ /** * RESOLVE_ABS_INDEX_[WL]: Resolve a PC-relative with index reference. * * [Parameters] * addr: Absolute address * ireg4: Index register number * 4 */ DEFLABEL(RESOLVE_ABS_INDEX_W) mov $0x12345678, %eax 8: movswl 1(%rbx), %ecx 9: add %ecx, %eax mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_ABS_INDEX_W) DEFPARAM(RESOLVE_ABS_INDEX_W, addr, 8b, -4) DEFPARAM(RESOLVE_ABS_INDEX_W, ireg4, 9b, -1) DEFLABEL(RESOLVE_ABS_INDEX_L) mov $0x12345678, %eax 8: add 1(%rbx), %ecx 9: mov %eax, Q68State_ea_addr(%rbx) DEFSIZE(RESOLVE_ABS_INDEX_L) DEFPARAM(RESOLVE_ABS_INDEX_L, addr, 8b, -4) DEFPARAM(RESOLVE_ABS_INDEX_L, ireg4, 9b, -1) /*************************************************************************/ /*************************** Operand retrieval ***************************/ /*************************************************************************/ /** * GET_OP1_REGISTER: Get the current value of the given register as * operand 1. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(GET_OP1_REGISTER) mov 1(%rbx), %edi 9: DEFSIZE(GET_OP1_REGISTER) DEFPARAM(GET_OP1_REGISTER, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * GET_OP1_EA_[BWL]: Get the value pointed to by the previously resolved * effective address as operand 1. */ DEFLABEL(GET_OP1_EA_B) mov Q68State_ea_addr(%rbx), %eax READ8 %rax movzx %al, %edi DEFSIZE(GET_OP1_EA_B) DEFLABEL(GET_OP1_EA_W) mov Q68State_ea_addr(%rbx), %eax READ16 %rax movzx %ax, %edi DEFSIZE(GET_OP1_EA_W) DEFLABEL(GET_OP1_EA_L) mov Q68State_ea_addr(%rbx), %eax READ32 %rax mov %eax, %edi DEFSIZE(GET_OP1_EA_L) /*-----------------------------------------------------------------------*/ /** * GET_OP1_IMMEDIATE: Get an immediate value as operand 1. * * [Parameters] * value: Immediate value */ DEFLABEL(GET_OP1_IMMEDIATE) mov $0x12345678, %edi 9: DEFSIZE(GET_OP1_IMMEDIATE) DEFPARAM(GET_OP1_IMMEDIATE, value, 9b, -4) /*-----------------------------------------------------------------------*/ /** * GET_OP1_CCR: Get the current value of CCR as operand 1. */ DEFLABEL(GET_OP1_CCR) movzbl SR, %edi DEFSIZE(GET_OP1_CCR) /*-----------------------------------------------------------------------*/ /** * GET_OP1_SR: Get the current value of SR as operand 1. */ DEFLABEL(GET_OP1_SR) mov SR, %edi DEFSIZE(GET_OP1_SR) /*************************************************************************/ /** * GET_OP2_*: Get the same things as above as operand 2. */ DEFLABEL(GET_OP2_REGISTER) mov 1(%rbx), %edx 9: DEFSIZE(GET_OP2_REGISTER) DEFPARAM(GET_OP2_REGISTER, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ DEFLABEL(GET_OP2_EA_B) mov Q68State_ea_addr(%rbx), %eax READ8 %rax movzx %al, %edx DEFSIZE(GET_OP2_EA_B) DEFLABEL(GET_OP2_EA_W) mov Q68State_ea_addr(%rbx), %eax READ16 %rax movzx %ax, %edx DEFSIZE(GET_OP2_EA_W) DEFLABEL(GET_OP2_EA_L) mov Q68State_ea_addr(%rbx), %eax READ32 %rax mov %eax, %edx DEFSIZE(GET_OP2_EA_L) /*-----------------------------------------------------------------------*/ DEFLABEL(GET_OP2_IMMEDIATE) mov $0x12345678, %edx 9: DEFSIZE(GET_OP2_IMMEDIATE) DEFPARAM(GET_OP2_IMMEDIATE, value, 9b, -4) /*-----------------------------------------------------------------------*/ DEFLABEL(GET_OP2_CCR) movzbl SR, %edx DEFSIZE(GET_OP2_CCR) /*-----------------------------------------------------------------------*/ DEFLABEL(GET_OP2_SR) mov SR, %edx DEFSIZE(GET_OP2_SR) /*************************************************************************/ /**************************** Result storing *****************************/ /*************************************************************************/ /** * SET_REGISTER_[BWL]: Set the value of the given register to the result * value. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(SET_REGISTER_B) mov %al, 1(%rbx) 9: DEFSIZE(SET_REGISTER_B) DEFPARAM(SET_REGISTER_B, reg4, 9b, -1) DEFLABEL(SET_REGISTER_W) mov %ax, 1(%rbx) 9: DEFSIZE(SET_REGISTER_W) DEFPARAM(SET_REGISTER_W, reg4, 9b, -1) DEFLABEL(SET_REGISTER_L) mov %eax, 1(%rbx) 9: DEFSIZE(SET_REGISTER_L) DEFPARAM(SET_REGISTER_L, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * SET_AREG_W: Set the value of the given address register to the * sign-extended result value. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(SET_AREG_W) cwde mov %eax, 1(%rbx) 9: DEFSIZE(SET_AREG_W) DEFPARAM(SET_AREG_W, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * SET_EA_[BWL]: Set the value pointed to by the previously resolved * effective address to the result value. */ DEFLABEL(SET_EA_B) mov Q68State_ea_addr(%rbx), %ecx WRITE8 %rcx, %rax DEFSIZE(SET_EA_B) DEFLABEL(SET_EA_W) mov Q68State_ea_addr(%rbx), %ecx WRITE16 %rcx, %rax DEFSIZE(SET_EA_W) DEFLABEL(SET_EA_L) mov Q68State_ea_addr(%rbx), %ecx WRITE32 %rcx, %rax DEFSIZE(SET_EA_L) /*-----------------------------------------------------------------------*/ /** * SET_CCR: Set the condition codes from the result value. */ DEFLABEL(SET_CCR) mov %al, SR // Update low byte only DEFSIZE(SET_CCR) /*-----------------------------------------------------------------------*/ /** * SET_SR: Set the status register from the result value. */ DEFLABEL(SET_SR) UPDATE_SR DEFSIZE(SET_SR) /*************************************************************************/ /*************************** Stack operations ****************************/ /*************************************************************************/ /** * PUSH_L: Push the 32-bit value of operand 1 onto the stack. */ DEFLABEL(PUSH_L) PUSH32 %rdi DEFSIZE(PUSH_L) /*-----------------------------------------------------------------------*/ /** * POP_L: Pop a 32-bit value off the stack into the result register. */ DEFLABEL(POP_L) POP32 DEFSIZE(POP_L) /*************************************************************************/ /************************ Condition code setting *************************/ /*************************************************************************/ /** * SETCC_ADD_[BWL]: Set the condition codes for the result of an ADD * instruction stored in the result register. */ DEFLABEL(SETCC_ADD_B) SETCC_XNZVC DEFSIZE(SETCC_ADD_B) DEFLABEL(SETCC_ADD_W) SETCC_XNZVC DEFSIZE(SETCC_ADD_W) DEFLABEL(SETCC_ADD_L) SETCC_XNZVC DEFSIZE(SETCC_ADD_L) /*************************************************************************/ /** * SETCC_ADDX_[BWL]: Set the condition codes for the result of an ADDX * instruction stored in the result register. */ DEFLABEL(SETCC_ADDX_B) SETCC_XNVC_Z DEFSIZE(SETCC_ADDX_B) DEFLABEL(SETCC_ADDX_W) SETCC_XNVC_Z DEFSIZE(SETCC_ADDX_W) DEFLABEL(SETCC_ADDX_L) SETCC_XNVC_Z DEFSIZE(SETCC_ADDX_L) /*************************************************************************/ /** * SETCC_SUB_[BWL]: Set the condition codes for the result of a SUB * instruction stored in the result register. */ DEFLABEL(SETCC_SUB_B) SETCC_XNZVC DEFSIZE(SETCC_SUB_B) DEFLABEL(SETCC_SUB_W) SETCC_XNZVC DEFSIZE(SETCC_SUB_W) DEFLABEL(SETCC_SUB_L) SETCC_XNZVC DEFSIZE(SETCC_SUB_L) /*************************************************************************/ /** * SETCC_SUBX_[BWL]: Set the condition codes for the result of a SUBX * instruction stored in the result register. */ DEFLABEL(SETCC_SUBX_B) SETCC_XNVC_Z DEFSIZE(SETCC_SUBX_B) DEFLABEL(SETCC_SUBX_W) SETCC_XNVC_Z DEFSIZE(SETCC_SUBX_W) DEFLABEL(SETCC_SUBX_L) SETCC_XNVC_Z DEFSIZE(SETCC_SUBX_L) /*************************************************************************/ /** * SETCC_CMP_[BWL]: Set the condition codes for the result of a CMP * instruction stored in the result register. The X flag is unmodified. */ DEFLABEL(SETCC_CMP_B) SETCC_NZVC DEFSIZE(SETCC_CMP_B) DEFLABEL(SETCC_CMP_W) SETCC_NZVC DEFSIZE(SETCC_CMP_W) DEFLABEL(SETCC_CMP_L) SETCC_NZVC DEFSIZE(SETCC_CMP_L) /*************************************************************************/ /** * SETCC_LOGIC_[BWL]: Set the condition codes for the result of a logical * instruction (MOVE, AND, OR, EOR) stored in the result register. The X * flag is unmodified. */ DEFLABEL(SETCC_LOGIC_B) SETCC_NZ00 DEFSIZE(SETCC_LOGIC_B) DEFLABEL(SETCC_LOGIC_W) SETCC_NZ00 DEFSIZE(SETCC_LOGIC_W) DEFLABEL(SETCC_LOGIC_L) SETCC_NZ00 DEFSIZE(SETCC_LOGIC_L) /*************************************************************************/ /*************************** Condition testing ***************************/ /*************************************************************************/ /** * TEST_*: Check whether a condition is true (based on the current * condition codes) and set %al based on the result (nonzero = true). */ DEFLABEL(TEST_T) mov $1, %al DEFSIZE(TEST_T) DEFLABEL(TEST_F) mov $0, %al DEFSIZE(TEST_F) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_HI) mov SR, %al mov %al, %cl shr $SR_Z_SHIFT, %cl or %cl, %al and $1, %al xor $1, %al DEFSIZE(TEST_HI) DEFLABEL(TEST_LS) mov SR, %al mov %al, %cl shr $SR_Z_SHIFT, %cl or %cl, %al and $1, %al DEFSIZE(TEST_LS) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_CC) mov SR, %al and $1, %al xor $1, %al DEFSIZE(TEST_CC) DEFLABEL(TEST_CS) mov SR, %al and $1, %al DEFSIZE(TEST_CS) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_NE) mov SR, %al shr $SR_Z_SHIFT, %al and $1, %al xor $1, %al DEFSIZE(TEST_NE) DEFLABEL(TEST_EQ) mov SR, %al shr $SR_Z_SHIFT, %al and $1, %al DEFSIZE(TEST_EQ) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_VC) mov SR, %al shr $SR_V_SHIFT, %al and $1, %al xor $1, %al DEFSIZE(TEST_VC) DEFLABEL(TEST_VS) mov SR, %al shr $SR_V_SHIFT, %al and $1, %al DEFSIZE(TEST_VS) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_PL) mov SR, %al shr $SR_N_SHIFT, %al and $1, %al xor $1, %al DEFSIZE(TEST_PL) DEFLABEL(TEST_MI) mov SR, %al shr $SR_N_SHIFT, %al and $1, %al DEFSIZE(TEST_MI) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_GE) mov SR, %al mov %al, %cl shr $SR_N_SHIFT, %al shr $SR_V_SHIFT, %cl xor %cl, %al and $1, %al xor $1, %al DEFSIZE(TEST_GE) DEFLABEL(TEST_LT) mov SR, %al mov %al, %cl shr $SR_N_SHIFT, %al shr $SR_V_SHIFT, %cl xor %cl, %al and $1, %al DEFSIZE(TEST_LT) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_GT) mov SR, %al push %rax mov %al, %cl shr $SR_N_SHIFT, %al shr $SR_V_SHIFT, %cl xor %al, %cl pop %rax shr $SR_Z_SHIFT, %al or %cl, %al and $1, %al xor $1, %al DEFSIZE(TEST_GT) DEFLABEL(TEST_LE) mov SR, %al push %rax mov %al, %cl shr $SR_N_SHIFT, %al shr $SR_V_SHIFT, %cl xor %al, %cl pop %rax shr $SR_Z_SHIFT, %al or %cl, %al and $1, %al DEFSIZE(TEST_LE) /*************************************************************************/ /**************************** ALU operations *****************************/ /*************************************************************************/ /** * MOVE_[BWL]: Evaluate op1, setting the result value for the MOVE * instruction. */ DEFLABEL(MOVE_B) mov %edi, %eax test %al, %al DEFSIZE(MOVE_B) DEFLABEL(MOVE_W) mov %edi, %eax test %ax, %ax DEFSIZE(MOVE_W) DEFLABEL(MOVE_L) mov %edi, %eax test %eax, %eax DEFSIZE(MOVE_L) /*************************************************************************/ /** * ADD_[BWL]: Evaluate op2 + op1. */ DEFLABEL(ADD_B) mov %edi, %eax add %dl, %al DEFSIZE(ADD_B) DEFLABEL(ADD_W) mov %edi, %eax add %dx, %ax DEFSIZE(ADD_W) DEFLABEL(ADD_L) mov %edi, %eax add %edx, %eax DEFSIZE(ADD_L) /*-----------------------------------------------------------------------*/ /** * ADDA_W: Sign-extend op1 to 32 bits, then evaluate op2 + op1. */ DEFLABEL(ADDA_W) movsx %di, %edi mov %edi, %eax add %edx, %eax DEFSIZE(ADDA_W) /*-----------------------------------------------------------------------*/ /** * ADDX_[BWL]: Evaluate op2 + op1 + X. */ DEFLABEL(ADDX_B) LDC_FROM_X %al mov %edi, %eax adc %dl, %al DEFSIZE(ADDX_B) DEFLABEL(ADDX_W) LDC_FROM_X %al mov %edi, %eax adc %dx, %ax DEFSIZE(ADDX_W) DEFLABEL(ADDX_L) LDC_FROM_X %al mov %edi, %eax adc %edx, %eax DEFSIZE(ADDX_L) /*************************************************************************/ /** * SUB_[BWL]: Evaluate op2 - op1. */ DEFLABEL(SUB_B) mov %edi, %eax xchg %edx, %eax sub %dl, %al DEFSIZE(SUB_B) DEFLABEL(SUB_W) mov %edi, %eax xchg %edx, %eax sub %dx, %ax DEFSIZE(SUB_W) DEFLABEL(SUB_L) mov %edi, %eax xchg %edx, %eax sub %edx, %eax DEFSIZE(SUB_L) /*-----------------------------------------------------------------------*/ /** * SUBA_W: Sign-extend op1 to 32 bits, then evaluate op2 - op1. */ DEFLABEL(SUBA_W) movsx %di, %edi mov %edi, %eax xchg %edx, %eax sub %edx, %eax DEFSIZE(SUBA_W) /*-----------------------------------------------------------------------*/ /** * SUBX_[BWL]: Evaluate op2 - op1 - X. */ DEFLABEL(SUBX_B) LDC_FROM_X %al mov %edi, %eax xchg %edx, %eax sbb %dl, %al DEFSIZE(SUBX_B) DEFLABEL(SUBX_W) LDC_FROM_X %al mov %edi, %eax xchg %edx, %eax sbb %dx, %ax DEFSIZE(SUBX_W) DEFLABEL(SUBX_L) LDC_FROM_X %al mov %edi, %eax xchg %edx, %eax sbb %edx, %eax DEFSIZE(SUBX_L) /*************************************************************************/ /** * MUL[SU]_W: Evaluate op2 * op1 in signed or unsigned context. */ DEFLABEL(MULS_W) movsx %di, %eax movsx %dx, %edx imul %edx test %eax, %eax DEFSIZE(MULS_W) DEFLABEL(MULU_W) movzx %di, %eax movzx %dx, %edx mul %edx test %eax, %eax DEFSIZE(MULU_W) /*************************************************************************/ /** * DIV[SU]_W: Evaluate op2 / op1 in signed or unsigned context, setting * the condition codes appropriately. The quotient is stored in the low * 16 bits, the remainder in the high 16 bits of the result value. On * overflow, op2 is copied to the result. */ DEFLABEL(DIVS_W) andb $~SR_C, SR test %di, %di jnz 0f movl $EX_DIVIDE_BY_ZERO, Q68State_exception(%rbx) TERMINATE 0: push %rdx // Save op2 (register value) so we can restore it as the // result on overflow mov %edx, %eax cdq movsx %di, %edi idiv %edi lea 0x8000(%eax), %ecx test $0xFFFF0000, %ecx jz 1f pop %rax orb $SR_V, SR jmp 2f 1: pop %rcx shl $16, %edx or %edx, %eax test %ax, %ax SETCC_NZ andb $~SR_V, SR 2: DEFSIZE(DIVS_W) DEFLABEL(DIVU_W) andb $~SR_C, SR test %di, %di jnz 0f movl $EX_DIVIDE_BY_ZERO, Q68State_exception(%rbx) TERMINATE 0: push %rdx // Save op2 (register value) so we can restore it as the // result on overflow mov %edx, %eax xor %edx, %edx movsx %di, %edi div %edi test $0xFFFF0000, %eax jz 1f pop %rax orb $SR_V, SR jmp 2f 1: pop %rcx shl $16, %edx or %edx, %eax test %ax, %ax SETCC_NZ andb $~SR_V, SR 2: DEFSIZE(DIVU_W) /*************************************************************************/ /** * AND_[BWL]: Evaluate op2 & op1. */ DEFLABEL(AND_B) mov %edi, %eax and %dl, %al DEFSIZE(AND_B) DEFLABEL(AND_W) mov %edi, %eax and %dx, %ax DEFSIZE(AND_W) DEFLABEL(AND_L) mov %edi, %eax and %edx, %eax DEFSIZE(AND_L) /*************************************************************************/ /** * OR_[BWL]: Evaluate op2 | op1. */ DEFLABEL(OR_B) mov %edi, %eax or %dl, %al DEFSIZE(OR_B) DEFLABEL(OR_W) mov %edi, %eax or %dx, %ax DEFSIZE(OR_W) DEFLABEL(OR_L) mov %edi, %eax or %edx, %eax DEFSIZE(OR_L) /*************************************************************************/ /** * EOR_[BWL]: Evaluate op2 ^ op1. */ DEFLABEL(EOR_B) mov %edi, %eax xor %dl, %al DEFSIZE(EOR_B) DEFLABEL(EOR_W) mov %edi, %eax xor %dx, %ax DEFSIZE(EOR_W) DEFLABEL(EOR_L) mov %edi, %eax xor %edx, %eax DEFSIZE(EOR_L) /*************************************************************************/ /** * EXT_[WL]: Sign-extend op1 from 8 to 16 or from 16 to 32 bits. */ DEFLABEL(EXT_W) mov %edi, %eax movsx %al, %ax test %ax, %ax DEFSIZE(EXT_W) DEFLABEL(EXT_L) movsx %di, %eax test %eax, %eax DEFSIZE(EXT_L) /*************************************************************************/ /** * SWAP: Swap the upper and lower 16-bit halves of op1, placing the result * in the result register. */ DEFLABEL(SWAP) mov %edi, %eax rol $16, %eax test %eax, %eax DEFSIZE(SWAP) /*************************************************************************/ /**************************** BCD operations *****************************/ /*************************************************************************/ /** * ABCD: Evaluate op2 + op1 + X, treating the operands as binary-coded * decimal values. */ DEFLABEL(ABCD) mov %edi, %ecx mov %edx, %eax and $0x0F, %ecx and $0x0F, %eax add %ecx, %eax mov SR, %ecx shr $SR_X_SHIFT, %ecx and $1, %ecx add %ecx, %eax cmp $10, %eax jb 0f add $6, %eax 0: and $0xF0, %edi and $0xF0, %edx add %edi, %edx add %edx, %eax xor %ecx, %ecx cmp $10<<4, %eax jb 1f sub $10<<4, %eax mov $1, %cl 1: mov %cl, %dl //shl $SR_C_SHIFT, %cl // Shift count is 0, so omitted shl $SR_X_SHIFT, %dl or %cl, %dl andb $~(SR_X|SR_C), SR or %dl, SR test %al, %al setnz %cl shl $SR_Z_SHIFT, %cl not %cl and %cl, SR DEFSIZE(ABCD) /*************************************************************************/ /** * SBCD: Evaluate op2 - op1 - X, treating the operands as binary-coded * decimal values. */ DEFLABEL(SBCD) mov %edi, %ecx mov %edx, %eax and $0x0F, %ecx and $0x0F, %eax sub %ecx, %eax mov SR, %ecx shr $SR_X_SHIFT, %ecx and $1, %ecx sub %ecx, %eax xor %ecx, %ecx test %eax, %eax jns 0f add $10, %eax add $16, %ecx 0: and $0xF0, %edi and $0xF0, %edx sub %ecx, %edx xor %ecx, %ecx sub %edi, %edx jns 1f add $10<<4, %eax mov $1, %cl 1: add %edx, %eax jns 2f mov $1, %cl 2: mov %cl, %dl //shl $SR_C_SHIFT, %cl // Shift count is 0, so omitted shl $SR_X_SHIFT, %dl or %cl, %dl andb $~(SR_X|SR_C), SR or %dl, SR test %al, %al setnz %cl shl $SR_Z_SHIFT, %cl not %cl and %cl, SR DEFSIZE(SBCD) /*************************************************************************/ /*********************** Bit-twiddling operations ************************/ /*************************************************************************/ /** * BTST_[BL]: Evaluate op2 & (1 << op1). The value (1 << op1), where the * high bits of op1 have been masked to zero, is left in %edi for use by a * subsequent BCHG/BCLR/BSET operation. */ DEFLABEL(BTST_B) mov %edi, %ecx and $7, %ecx mov $1, %edi shl %cl, %edi test %edi, %edx setz %cl shl $SR_Z_SHIFT, %cl and $~SR_Z, SR or %cl, SR DEFSIZE(BTST_B) DEFLABEL(BTST_L) mov %edi, %ecx and $31, %ecx mov $1, %edi shl %cl, %edi test %edi, %edx setz %cl shl $SR_Z_SHIFT, %cl and $~SR_Z, SR or %cl, SR DEFSIZE(BTST_L) /*************************************************************************/ /** * BCHG: Evaluate op2 ^ (1 << op1), where (1 << op1) has already been * stored in %edi. */ DEFLABEL(BCHG) mov %edx, %eax xor %edi, %eax DEFSIZE(BCHG) /*-----------------------------------------------------------------------*/ /** * BCLR: Evaluate op2 & ~(1 << op1), where (1 << op1) has already been * stored in %edi. */ DEFLABEL(BCLR) mov %edx, %eax not %edi and %edi, %eax DEFSIZE(BCLR) /*-----------------------------------------------------------------------*/ /** * BSET: Evaluate op2 | (1 << op1), where (1 << op1) has already been * stored in %edi. */ DEFLABEL(BSET) mov %edx, %eax or %edi, %eax DEFSIZE(BSET) /*************************************************************************/ /************************ Shift/rotate operations ************************/ /*************************************************************************/ /** * SETCC_XC_SHIFT: Set the X and C flags for a shift or rotate instruction. * The value to set (0 or 1) is passed in %dl; %dh is destroyed. Assumes * that the X and C flags have already been cleared. */ .macro SETCC_XC_SHIFT mov %dl, %dh shl $SR_X_SHIFT, %dh //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted or %dh, %dl or %dl, SR .endm /*************************************************************************/ /** * ASL_[BWL]: Evaluate (signed) op2 << op1. */ .macro DEF_ASL nbits, reg mov %edi, %ecx mov %edx, %eax and $0x3F, %ecx add %ecx, %esi add %ecx, %esi andb $~(SR_V|SR_C), SR test %ecx, %ecx jz 1f andb $~SR_X, SR // Have to shift bit by bit to detect overflow 0: sal $1, \reg setc %dl seto %dh shl $SR_V_SHIFT, %dh or %dh, SR loop 0b SETCC_XC_SHIFT 1: test \reg, \reg SETCC_NZ .endm DEFLABEL(ASL_B) DEF_ASL 8, %al DEFSIZE(ASL_B) DEFLABEL(ASL_W) DEF_ASL 16, %ax DEFSIZE(ASL_W) DEFLABEL(ASL_L) DEF_ASL 32, %eax DEFSIZE(ASL_L) /*-----------------------------------------------------------------------*/ /** * ASR_[BWL]: Evaluate (signed) op2 >> op1. */ .macro DEF_ASR nbits, reg mov %edi, %ecx mov %edx, %eax and $0x3F, %ecx add %ecx, %esi add %ecx, %esi andb $~(SR_V|SR_C), SR test %ecx, %ecx jz 4f andb $~SR_X, SR cmp $\nbits, %ecx jb 2f 1: // count >= nbits sar $\nbits-1, \reg mov %al, %dl and $1, %dl jmp 3f 2: // 0 < count < nbits sar %cl, \reg setc %dl 3: // count != 0 SETCC_XC_SHIFT 4: // All cases test \reg, \reg SETCC_NZ .endm DEFLABEL(ASR_B) DEF_ASR 8, %al DEFSIZE(ASR_B) DEFLABEL(ASR_W) DEF_ASR 16, %ax DEFSIZE(ASR_W) DEFLABEL(ASR_L) DEF_ASR 32, %eax DEFSIZE(ASR_L) /*************************************************************************/ /** * LSL_[BWL]: Evaluate (unsigned) op2 << op1. */ .macro DEF_LSL nbits, reg mov %edi, %ecx mov %edx, %eax and $0x3F, %ecx add %ecx, %esi add %ecx, %esi andb $~(SR_V|SR_C), SR test %ecx, %ecx jz 4f andb $~SR_X, SR cmp $\nbits, %ecx jb 2f ja 1f 0: // count == nbits and $1, %al mov %al, %dl xor \reg, \reg jmp 3f 1: // count > nbits xor \reg, \reg jmp 4f 2: // 0 < count < nbits sal %cl, \reg setc %dl 3: // 0 < count <= nbits SETCC_XC_SHIFT 4: // All cases test \reg, \reg SETCC_NZ .endm DEFLABEL(LSL_B) DEF_LSL 8, %al DEFSIZE(LSL_B) DEFLABEL(LSL_W) DEF_LSL 16, %ax DEFSIZE(LSL_W) DEFLABEL(LSL_L) DEF_LSL 32, %eax DEFSIZE(LSL_L) /*-----------------------------------------------------------------------*/ /** * LSR_[BWL]: Evaluate (unsigned) op2 >> op1. */ .macro DEF_LSR nbits, reg mov %edi, %ecx mov %edx, %eax and $0x3F, %ecx add %ecx, %esi add %ecx, %esi andb $~(SR_V|SR_C), SR test %ecx, %ecx jz 4f andb $~SR_X, SR cmp $\nbits, %ecx jb 2f ja 1f 0: // count == nbits shl $1, \reg setc %dl xor \reg, \reg jmp 3f 1: // count > nbits xor \reg, \reg jmp 4f 2: // 0 < count < nbits shr %cl, \reg setc %dl 3: // 0 < count <= nbits SETCC_XC_SHIFT 4: // All cases test \reg, \reg SETCC_NZ .endm DEFLABEL(LSR_B) DEF_LSR 8, %al DEFSIZE(LSR_B) DEFLABEL(LSR_W) DEF_LSR 16, %ax DEFSIZE(LSR_W) DEFLABEL(LSR_L) DEF_LSR 32, %eax DEFSIZE(LSR_L) /*************************************************************************/ /** * ROXL_[BWL]: Evaluate op2 ROXL op1. */ .macro DEF_ROXL nbits, reg mov %edi, %ecx mov %edx, %eax and $0x3F, %ecx add %ecx, %esi add %ecx, %esi andb $~(SR_V|SR_C), SR test %ecx, %ecx jnz 0f mov SR, %dl shr $SR_X_SHIFT, %dl and $1, %dl jmp 2f 0: LDC_FROM_X %dl 1: rcl \reg loop 1b setc %dl andb $~SR_X, SR 2: SETCC_XC_SHIFT test \reg, \reg SETCC_NZ .endm DEFLABEL(ROXL_B) DEF_ROXL 8, %al DEFSIZE(ROXL_B) DEFLABEL(ROXL_W) DEF_ROXL 16, %ax DEFSIZE(ROXL_W) DEFLABEL(ROXL_L) DEF_ROXL 32, %eax DEFSIZE(ROXL_L) /*-----------------------------------------------------------------------*/ /** * ROXR_[BWL]: Evaluate op2 ROXR op1. */ .macro DEF_ROXR nbits, reg mov %edi, %ecx mov %edx, %eax and $0x3F, %ecx add %ecx, %esi add %ecx, %esi andb $~(SR_V|SR_C), SR test %ecx, %ecx jnz 0f mov SR, %dl shr $SR_X_SHIFT, %dl and $1, %dl jmp 2f 0: LDC_FROM_X %dl 1: rcr \reg loop 1b setc %dl andb $~SR_X, SR 2: SETCC_XC_SHIFT test \reg, \reg SETCC_NZ .endm DEFLABEL(ROXR_B) DEF_ROXR 8, %al DEFSIZE(ROXR_B) DEFLABEL(ROXR_W) DEF_ROXR 16, %ax DEFSIZE(ROXR_W) DEFLABEL(ROXR_L) DEF_ROXR 32, %eax DEFSIZE(ROXR_L) /*************************************************************************/ /** * ROL_[BWL]: Evaluate op2 ROL op1. */ .macro DEF_ROL nbits, reg mov %edi, %ecx mov %edx, %eax and $0x3F, %ecx add %ecx, %esi add %ecx, %esi andb $~(SR_V|SR_C), SR test %ecx, %ecx jz 3f and $\nbits-1, %ecx jnz 2f 1: // count != 0 && count % nbits == 0 mov %al, %dl and $1, %dl //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted or %dl, SR jmp 3f 2: // count % nbits != 0 rol %cl, \reg setc %dl //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted or %dl, SR 3: // All cases test \reg, \reg SETCC_NZ .endm DEFLABEL(ROL_B) DEF_ROL 8, %al DEFSIZE(ROL_B) DEFLABEL(ROL_W) DEF_ROL 16, %ax DEFSIZE(ROL_W) DEFLABEL(ROL_L) DEF_ROL 32, %eax DEFSIZE(ROL_L) /*-----------------------------------------------------------------------*/ /** * ROR_[BWL]: Evaluate op2 ROR op1. */ .macro DEF_ROR nbits, reg mov %edi, %ecx mov %edx, %eax and $0x3F, %ecx add %ecx, %esi add %ecx, %esi andb $~(SR_V|SR_C), SR test %ecx, %ecx jz 3f and $\nbits-1, %ecx jnz 2f 1: // count != 0 && count % nbits == 0 mov %eax, %edx shr $\nbits-1, %edx and $1, %dl //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted or %dl, SR jmp 3f 2: // count % nbits != 0 ror %cl, \reg setc %dl //shl $SR_C_SHIFT, %dl // Shift count is 0, so omitted or %dl, SR 3: // All cases test \reg, \reg SETCC_NZ .endm DEFLABEL(ROR_B) DEF_ROR 8, %al DEFSIZE(ROR_B) DEFLABEL(ROR_W) DEF_ROR 16, %ax DEFSIZE(ROR_W) DEFLABEL(ROR_L) DEF_ROR 32, %eax DEFSIZE(ROR_L) /*************************************************************************/ /******************* Conditional and branch operations *******************/ /*************************************************************************/ /** * Scc: Set the lower 8 bits of the result value to 0xFF if the condition * is true, 0x00 if false. */ DEFLABEL(Scc) neg %al DEFSIZE(Scc) /*-----------------------------------------------------------------------*/ /** * ADD_CYCLES_Scc_Dn: Add the appropriate number of clock cycles for an * Scc Dn instruction to the cycle count. */ DEFLABEL(ADD_CYCLES_Scc_Dn) movzx %al, %ecx and $2, %ecx add $4, %ecx add %ecx, %esi DEFSIZE(ADD_CYCLES_Scc_Dn) /*************************************************************************/ /** * DBcc: Jump to the specified target address unless the condition is true * or the lower 16 bits of the given data register, after being decremented, * are equal to -1. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7) * target: Target address */ DEFLABEL(DBcc) test %al, %al jz 0f add $12, %esi jmp 2f 0: lea 1(%rbx), %rcx 8: subw $1, (%rcx) mov (%rcx), %ax cmp $-1, %ax jne 1f add $14, %esi jmp 2f 1: add $10, %esi mov $0x12345678, %eax 9: mov %eax, PC TERMINATE 2: DEFSIZE(DBcc) DEFPARAM(DBcc, reg4, 8b, -1) DEFPARAM(DBcc, target, 9b, -4) /*-----------------------------------------------------------------------*/ /** * DBcc_native: Implement DBcc using a jump within the native code. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7) * target: Target 68000 address * native_disp: Target native displacement from end of this fragment */ DEFLABEL(DBcc_native) test %al, %al jz 0f add $12, %esi jmp 2f 0: lea 1(%rbx), %rcx 8: subw $1, (%rcx) mov (%rcx), %ax cmp $-1, %ax jne 1f add $14, %esi jmp 2f 1: add $10, %esi mov $0x12345678, %eax 9: mov %eax, PC jmp .+0x12345678 2: DEFSIZE(DBcc_native) DEFPARAM(DBcc_native, reg4, 8b, -1) DEFPARAM(DBcc_native, target, 9b, -4) DEFPARAM(DBcc_native, native_disp, 2b, -4) /*************************************************************************/ /** * Bcc: Jump to the specified target address if the condition is true. * * [Parameters] * target: Target address */ DEFLABEL(Bcc) test %al, %al jz 0f mov $0x12345678, %eax 9: mov %eax, PC add $10, %esi TERMINATE 0: DEFSIZE(Bcc) DEFPARAM(Bcc, target, 9b, -4) /*-----------------------------------------------------------------------*/ /** * Bcc_native: Implement Bcc using a jump within the native code. * * [Parameters] * target: Target 68000 address * native_disp: Target native displacement from end of this fragment */ DEFLABEL(Bcc_native) test %al, %al jz 0f mov $0x12345678, %eax 9: mov %eax, PC add $10, %esi jmp .+0x12345678 0: DEFSIZE(Bcc_native) DEFPARAM(Bcc_native, target, 9b, -4) DEFPARAM(Bcc_native, native_disp, 0b, -4) /*-----------------------------------------------------------------------*/ /** * BSR: Push the address of the next instruction onto the stack, then jump * to the specified target address. * * [Parameters] * return_addr: Return address to push onto the stack * target: Target address */ DEFLABEL(BSR) mov $0x12345678, %ecx 8: PUSH32 %rcx mov $0x12345678, %eax 9: mov %eax, PC TERMINATE DEFSIZE(BSR) DEFPARAM(BSR, return_addr, 8b, -4) DEFPARAM(BSR, target, 9b, -4) /*************************************************************************/ /** * JMP: Jump to the previously resolved effective address. */ DEFLABEL(JMP) mov Q68State_ea_addr(%rbx), %eax mov %eax, PC TERMINATE DEFSIZE(JMP) /*-----------------------------------------------------------------------*/ /** * JSR: Push the address of the next instruction onto the stack, then jump * to the previously resolved effective address. * * [Parameters] * return_addr: Return address to push onto the stack */ DEFLABEL(JSR) mov $0x12345678, %ecx 9: PUSH32 %rcx mov Q68State_ea_addr(%rbx), %eax mov %eax, PC TERMINATE DEFSIZE(JSR) DEFPARAM(JSR, return_addr, 9b, -4) /*************************************************************************/ /*********************** MOVEM-related operations ************************/ /*************************************************************************/ /** * STORE_DEC_[WL]: Decrement state->ea_addr, then store the specified * register to the resulting location. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(STORE_DEC_W) mov Q68State_ea_addr(%rbx), %ecx sub $2, %ecx mov %ecx, Q68State_ea_addr(%rbx) mov 1(%rbx), %eax 9: WRITE16 %rcx, %rax DEFSIZE(STORE_DEC_W) DEFPARAM(STORE_DEC_W, reg4, 9b, -1) DEFLABEL(STORE_DEC_L) mov Q68State_ea_addr(%rbx), %ecx sub $4, %ecx mov %ecx, Q68State_ea_addr(%rbx) mov 1(%rbx), %eax 9: WRITE32 %rcx, %rax DEFSIZE(STORE_DEC_L) DEFPARAM(STORE_DEC_L, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * STORE_INC_[WL]: Store the specified register to the location indicated * by state->ea_addr, then increment state->ea_addr. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(STORE_INC_W) mov Q68State_ea_addr(%rbx), %ecx mov 1(%rbx), %eax 9: WRITE16 %rcx, %rax add $2, Q68State_ea_addr(%rbx) DEFSIZE(STORE_INC_W) DEFPARAM(STORE_INC_W, reg4, 9b, -1) DEFLABEL(STORE_INC_L) mov Q68State_ea_addr(%rbx), %ecx mov 1(%rbx), %eax 9: WRITE32 %rcx, %rax add $4, Q68State_ea_addr(%rbx) DEFSIZE(STORE_INC_L) DEFPARAM(STORE_INC_L, reg4, 9b, -1) /*************************************************************************/ /** * LOAD_INC_[WL]: Load the specified register from the location indicated * by state->ea_addr, then increment state->ea_addr. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(LOAD_INC_W) mov Q68State_ea_addr(%rbx), %ecx READ16 %rcx mov %ax, 1(%rbx) 9: add $2, Q68State_ea_addr(%rbx) DEFSIZE(LOAD_INC_W) DEFPARAM(LOAD_INC_W, reg4, 9b, -1) DEFLABEL(LOAD_INC_L) mov Q68State_ea_addr(%rbx), %ecx READ32 %rcx mov %eax, 1(%rbx) 9: add $4, Q68State_ea_addr(%rbx) DEFSIZE(LOAD_INC_L) DEFPARAM(LOAD_INC_L, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * LOADA_INC_W: Load the specified address register from the location * indicated by state->ea_addr, sign-extending the 16-bit value to 32 bits, * then increment state->ea_addr. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(LOADA_INC_W) mov Q68State_ea_addr(%rbx), %ecx READ16 %rcx cwde mov %eax, 1(%rbx) 9: add $2, Q68State_ea_addr(%rbx) DEFSIZE(LOADA_INC_W) DEFPARAM(LOADA_INC_W, reg4, 9b, -1) /*************************************************************************/ /** * MOVEM_WRITEBACK: Store the address in state->ea_addr to the specified * address register. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(MOVEM_WRITEBACK) mov Q68State_ea_addr(%rbx), %ecx mov %ecx, 1(%rbx) 9: DEFSIZE(MOVEM_WRITEBACK) DEFPARAM(MOVEM_WRITEBACK, reg4, 9b, -1) /*************************************************************************/ /*********************** Miscellaneous operations ************************/ /*************************************************************************/ /** * CHK_W: Raise a CHK exception if op1 < 0 or op1 > op2, treating both * operands as signed 16-bit values. */ DEFLABEL(CHK_W) test %di, %di jns 0f orb $SR_N, SR jmp 1f 0: cmp %dx, %di jle 2f andb $~SR_N, SR 1: movl $EX_CHK, Q68State_exception(%rbx) TERMINATE 2: DEFSIZE(CHK_W) /*************************************************************************/ /** * LEA: Store the previously resolved effective address in the specified * address register. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(LEA) mov Q68State_ea_addr(%rbx), %eax mov %eax, 1(%rbx) 9: DEFSIZE(LEA) DEFPARAM(LEA, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * PEA: Push the previously resolved effective address onto the stack. */ DEFLABEL(PEA) mov Q68State_ea_addr(%rbx), %ecx PUSH32 %rcx DEFSIZE(PEA) /*************************************************************************/ /** * TAS: Test the 8-bit value of op1, setting the condition codes * appropriately, then calculate op1 | 0x80. */ DEFLABEL(TAS) mov %edi, %eax test %al, %al SETCC_NZ00 or $0x80, %al DEFSIZE(TAS) /*************************************************************************/ /** * MOVE_FROM_USP: Copy the user stack pointer to the specified register. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(MOVE_FROM_USP) mov USP, %eax mov %eax, 1(%rbx) 9: DEFSIZE(MOVE_FROM_USP) DEFPARAM(MOVE_FROM_USP, reg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * MOVE_TO_USP: Copy the specified register to the user stack pointer. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(MOVE_TO_USP) mov 1(%rbx), %eax 9: mov %eax, USP DEFSIZE(MOVE_TO_USP) DEFPARAM(MOVE_TO_USP, reg4, 9b, -1) /*************************************************************************/ /** * STOP: Halt the processor. * * [Parameters] * newSR: Value to load into SR */ DEFLABEL(STOP) movl $1, Q68State_halted(%rbx) mov $0x1234, %ax 9: UPDATE_SR DEFSIZE(STOP) DEFPARAM(STOP, newSR, 9b, -2) /*************************************************************************/ /** * TRAPV: Raise a TRAPV exception if the overflow flag is set. */ DEFLABEL(TRAPV) testb $SR_V, SR jz 0f movl $EX_TRAPV, Q68State_exception(%rbx) TERMINATE 0: DEFSIZE(TRAPV) /*************************************************************************/ /** * RTS: Pop the PC from the stack. */ DEFLABEL(RTS) POP32 mov %eax, PC TERMINATE DEFSIZE(RTS) /*-----------------------------------------------------------------------*/ /** * RTR: Pop the condition codes and PC from the stack. */ DEFLABEL(RTR) POP16 mov %al, SR // Update low byte only POP32 mov %eax, PC TERMINATE DEFSIZE(RTR) /*-----------------------------------------------------------------------*/ /** * RTE: Pop the status register and PC from the stack. */ DEFLABEL(RTE) POP16 push %rax POP32 mov %eax, PC pop %rax UPDATE_SR TERMINATE DEFSIZE(RTE) /*************************************************************************/ /** * MOVEP_READ_[WL]: Read a value from memory, skipping every other byte. * * [Parameters] * areg4: Register number * 4 of base address register (32-60 = A0-A7) * disp: Displacement from base address register * dreg4: Register number * 4 of data reg. to receive data (0-28 = D0-D7) */ DEFLABEL(MOVEP_READ_W) mov 1(%rbx), %ecx 7: add $0x12345678, %ecx 8: push %rcx READ8 %rcx // Byte 1 movzx %al, %edi shl $8, %edi pop %rcx add $2, %ecx READ8 %rcx // Byte 0 movzx %al, %eax or %eax, %edi mov %di, 1(%rbx) 9: DEFSIZE(MOVEP_READ_W) DEFPARAM(MOVEP_READ_W, areg4, 7b, -1) DEFPARAM(MOVEP_READ_W, disp, 8b, -4) DEFPARAM(MOVEP_READ_W, dreg4, 9b, -1) DEFLABEL(MOVEP_READ_L) mov 1(%rbx), %ecx 7: add $0x12345678, %ecx 8: push %rcx READ8 %rcx // Byte 3 movzx %al, %edi shl $24, %edi mov (%rsp), %ecx add $2, %ecx READ8 %rcx // Byte 2 movzx %al, %eax shl $16, %eax or %eax, %edi mov (%rsp), %ecx add $4, %ecx READ8 %rcx // Byte 1 movzx %al, %eax shl $8, %eax or %eax, %edi pop %rcx add $6, %ecx READ8 %rcx // Byte 0 movzx %al, %eax or %eax, %edi mov %di, 1(%rbx) 9: DEFSIZE(MOVEP_READ_L) DEFPARAM(MOVEP_READ_L, areg4, 7b, -1) DEFPARAM(MOVEP_READ_L, disp, 8b, -4) DEFPARAM(MOVEP_READ_L, dreg4, 9b, -1) /*-----------------------------------------------------------------------*/ /** * MOVEP_WRITE_[WL]: Write a value to memory, skipping every other byte. * * [Parameters] * areg4: Register number * 4 of base address register (32-60 = A0-A7) * disp: Displacement from base address register * dreg4: Register number * 4 of data reg. containing data (0-28 = D0-D7) */ DEFLABEL(MOVEP_WRITE_W) mov 1(%rbx), %ecx 7: add $0x12345678, %ecx 8: mov 1(%rbx), %eax 9: push %rcx push %rax shr $8, %eax WRITE8 %rcx, %rax // Byte 1 pop %rax pop %rcx add $2, %ecx WRITE8 %rcx, %rax // Byte 0 DEFSIZE(MOVEP_WRITE_W) DEFPARAM(MOVEP_WRITE_W, areg4, 7b, -1) DEFPARAM(MOVEP_WRITE_W, disp, 8b, -4) DEFPARAM(MOVEP_WRITE_W, dreg4, 9b, -1) DEFLABEL(MOVEP_WRITE_L) mov 1(%rbx), %ecx 7: add $0x12345678, %ecx 8: mov 1(%rbx), %eax 9: push %rcx push %rax shr $24, %eax WRITE8 %rcx, %rax // Byte 3 pop %rax mov (%rsp), %ecx add $2, %ecx push %rax shr $16, %eax WRITE8 %rcx, %rax // Byte 2 pop %rax mov (%rsp), %ecx add $4, %ecx push %rax shr $8, %eax WRITE8 %rcx, %rax // Byte 1 pop %rax pop %rcx add $6, %ecx WRITE8 %rcx, %rax // Byte 0 DEFSIZE(MOVEP_WRITE_L) DEFPARAM(MOVEP_WRITE_L, areg4, 7b, -1) DEFPARAM(MOVEP_WRITE_L, disp, 8b, -4) DEFPARAM(MOVEP_WRITE_L, dreg4, 9b, -1) /*************************************************************************/ /** * EXG: Exchange the values of two registers. * * [Parameters] * reg1_4: Register number * 4 of first register (0-60 = D0-A7) * reg2_4: Register number * 4 of second register (0-60 = D0-A7) */ DEFLABEL(EXG) lea 1(%rbx), %ecx 8: lea 1(%rbx), %edx 9: mov (%rcx), %eax mov (%rdx), %edi mov %eax, (%rdx) mov %edi, (%rcx) DEFSIZE(EXG) DEFPARAM(EXG, reg1_4, 8b, -1) DEFPARAM(EXG, reg2_4, 9b, -1) /*************************************************************************/ /*************************************************************************/ yabause-0.9.13.1/src/q68/q68-jit-psp.S000644 001750 001750 00000206724 12256006177 020762 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-jit-psp.S: PSP dynamic translation implementation for Q68 Copyright 2009-2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "q68-const.h" .set noreorder .set nomips16 #define s8 fp // since binutils doesn't seem to recognize $s8 on its own /*************************************************************************/ /* * Register and stack usage is as follows: * * $v0 -- result; temporary used by macros; (on return) address to resume * execution at, or NULL to terminate execution * $v1 -- temporary * $a0 -- temporary * $a1 -- temporary * $a2 -- temporary * $a3 -- temporary * $t0 -- temporary (not used by macros) * $t1 -- temporary (not used by macros) * $t2 -- temporary (not used by macros) * $t3 -- temporary (not used by macros) * $t4 -- temporary (not used by macros) * $t5 -- not used * $t6 -- not used * $t7 -- operand 2 * $t8 -- not used * $t9 -- indirect function call pointer * $s0 -- Q68State structure pointer * $s1 -- cycles to execute * $s2 -- cumulative cycle count * $s3 -- operand 1; persistent temporary * $s4 -- PC mirror register * $s5 -- SR mirror register * $s6 -- effective address (used instead of state->ea_addr); persistent * temporary * $s7 -- value of Q68State.jit_abort * $s8 -- not used (not saved by JIT_CALL()) * * 0($sp) -- temporary (used by READ/WRITE macros to hold addresses) * 4($sp) -- temporary (used by READ/WRITE macros to hold values) * 8($sp) -- unused * 12($sp) -- saved $ra */ /*************************************************************************/ /* Label/size/parameter definition macros */ #define DEFLABEL(name) .globl JIT_PSP_##name; \ .type JIT_PSP_##name, @function; \ JIT_PSP_##name: #define DEFSIZE(name) .globl JIT_PSPSIZE_##name; \ .type JIT_PSPSIZE_##name, @object; \ JIT_PSPSIZE_##name: .int . - JIT_PSP_##name #define DEFPARAM(name,param,label,offset) \ .globl JIT_PSPPARAM_##name##_##param; \ .type JIT_PSPPARAM_##name##_##param, @object; \ JIT_PSPPARAM_##name##_##param: .int label - JIT_PSP_##name + (offset) /* Q68State structure offsets */ Q68State_D = 0 Q68State_A = 32 Q68State_PC = 64 Q68State_SR = 68 Q68State_USP = 72 Q68State_SSP = 76 Q68State_current_PC = 80 Q68State_ea_addr = 84 Q68State_exception = 88 Q68State_fault_addr = 92 Q68State_fault_opcode = 96 Q68State_fault_status = 98 Q68State_halted = 100 Q68State_irq = 104 Q68State_cycles = 108 Q68State_malloc_func = 112 Q68State_realloc_func = 116 Q68State_free_func = 120 Q68State_readb_func = 124 Q68State_readw_func = 128 Q68State_writeb_func = 132 Q68State_writew_func = 136 Q68State_jit_flush = 140 Q68State_jit_running = 144 Q68State_jit_abort = 148 Q68State_jit_table = 152 Q68State_jit_hashchain = 156 Q68State_jit_total_data = 160 Q68State_jit_timestamp = 164 Q68State_jit_blacklist = 168 Q68State_jit_in_blist = Q68State_jit_blacklist + (12 * Q68_JIT_BLACKLIST_SIZE) Q68State_jit_blist_num = Q68State_jit_in_blist + 4 Q68State_jit_callstack_top = Q68State_jit_blist_num + 4 Q68State_jit_callstack = Q68State_jit_callstack_top + 4 Q68State_jit_pages = Q68State_jit_callstack + (12 * Q68_JIT_CALLSTACK_SIZE) /*************************************************************************/ /* Shorthand for referencing Q68State fields */ #define D0 Q68State_D+0*4($s0) #define D1 Q68State_D+1*4($s0) #define D2 Q68State_D+2*4($s0) #define D3 Q68State_D+3*4($s0) #define D4 Q68State_D+4*4($s0) #define D5 Q68State_D+5*4($s0) #define D6 Q68State_D+6*4($s0) #define D7 Q68State_D+7*4($s0) #define A0 Q68State_A+0*4($s0) #define A1 Q68State_A+1*4($s0) #define A2 Q68State_A+2*4($s0) #define A3 Q68State_A+3*4($s0) #define A4 Q68State_A+4*4($s0) #define A5 Q68State_A+5*4($s0) #define A6 Q68State_A+6*4($s0) #define A7 Q68State_A+7*4($s0) #define PC Q68State_PC($s0) #define SR Q68State_SR($s0) #define USP Q68State_USP($s0) #define SSP Q68State_SSP($s0) /*************************************************************************/ /* LOAD_DELAY_NOP is used to indicate where a pipeline stall will occur * due to a dependency on data loaded by a previous instruction. It * doesn't actually expand to anything, but can be used as a hint for * optimization. */ #define LOAD_DELAY_NOP /*nothing*/ /*************************************************************************/ /************************** Convenience macros ***************************/ /*************************************************************************/ /** * seqz: seqz rd,rs is a clearer substitute for sltiu rd,rs,1 and sets rd * to 1 if rs is zero, 0 otherwise. */ .macro seqz rd, rs sltiu \rd, \rs, 1 .endm /*-----------------------------------------------------------------------*/ /** * snez: sgtz rd,rs is a clearer substitute for sltu rd,zero,rs and sets * rd to 1 if rs is positive, 0 otherwise. */ .macro snez rd, rs sltu \rd, $zero, \rs .endm /*-----------------------------------------------------------------------*/ /** * sgtz: sgtz rd,rs is a clearer substitute for slt rd,zero,rs and sets * rd to 1 if rs is positive, 0 otherwise. */ .macro sgtz rd, rs slt \rd, $zero, \rs .endm /*-----------------------------------------------------------------------*/ /** * sltz: sltz rd,rs is a clearer substitute for slti rd,rs,0 and sets rd * to 1 if rs is negative, 0 otherwise. */ .macro sltz rd, rs slti \rd, \rs, 0 .endm /*************************************************************************/ /** * SETUP: Perform setup required before executing translated code. */ .macro SETUP addiu $sp, $sp, -16 lw $s5, Q68State_SR($s0) lw $s4, Q68State_PC($s0) sw $ra, 12($sp) move $s7, $zero .endm /*-----------------------------------------------------------------------*/ /** * TERMINATE: Terminate execution of the current block. The emulator will * resume execution at the address in state->PC. */ .macro TERMINATE lw $ra, 12($sp) sw $s4, Q68State_PC($s0) sw $s5, Q68State_SR($s0) move $v0, $zero jr $ra addiu $sp, $sp, 16 .endm /*-----------------------------------------------------------------------*/ /** * TERMINATE_CONTINUE: Terminate execution of the current block, returning * the address of the following native instruction. */ .macro TERMINATE_CONTINUE sw $s4, Q68State_PC($s0) jal TERMINATE_CONTINUE_get_pc sw $s5, Q68State_SR($s0) jr $ra addiu $sp, $sp, 16 SETUP .endm /* Helper subroutine to get the PC */ TERMINATE_CONTINUE_get_pc: addiu $v0, $ra, 8 // Size of "jr ra; addiu $sp, $sp, 16" jr $ra lw $ra, 12($sp) // Preload the saved $ra to avoid a load stall /*************************************************************************/ /** * READ{8,16,32}: Read a value from memory. The address to read from is * taken from $s6; the value read is returned zero-extended in \return. */ .macro READ8 return lw $t9, Q68State_readb_func($s0) ext $a0, $s6, 0, 24 // Mask off top 8 bits LOAD_DELAY_NOP jalr $t9 nop andi \return, $v0, 0xFF .endm .macro READ16 return lw $t9, Q68State_readw_func($s0) ext $a0, $s6, 0, 24 LOAD_DELAY_NOP jalr $t9 nop andi \return, $v0, 0xFFFF .endm .macro READ32 return lw $t9, Q68State_readw_func($s0) ext $a0, $s6, 0, 24 LOAD_DELAY_NOP jalr $t9 // Read high word sw $a0, 0($sp) lw $a0, 0($sp) sll $v0, $v0, 16 lw $t9, Q68State_readw_func($s0) sw $v0, 4($sp) addiu $a0, $a0, 2 jalr $t9 // Read low word ext $a0, $a0, 0, 24 // Just in case we're reading from $FFFFFE lw $v1, 4($sp) andi $v0, $v0, 0xFFFF LOAD_DELAY_NOP or \return, $v0, $v1 .endm /*-----------------------------------------------------------------------*/ /** * WRITE_CHECK_JIT: Check whether a write of size \nbytes (*bytes*, not * bits) to the address in $s6 would clobber a page containing already- * translated blocks, and clear those translations if so. All caller-saved * registers are destroyed. * * Note that this macro uses local label 4. */ .macro WRITE_CHECK_JIT nbytes srl $a0, $s6, Q68_JIT_PAGE_BITS+3 addu $a0, $a0, $s0 lbu $v0, Q68State_jit_pages($a0) ext $a1, $s6, Q68_JIT_PAGE_BITS, 3 li $v1, 1 beqz $v0, 4f sllv $v1, $v1, $a1 and $v0, $v0, $v1 beqz $v0, 4f move $a1, $s6 li $a2, \nbytes jal q68_jit_clear_write move $a0, $s0 lw $s7, Q68State_jit_abort($s0) 4: .endm /*-----------------------------------------------------------------------*/ /** * WRITE{8,16,32}: Write a value to memory. The address must be stored in * $s6, and the value to write in $v0. Note that a 32-bit write spanning * two JIT pages that clobbers the first word of a translated routine * beginning on the second page will not be detected. */ .macro WRITE8 sw $v0, 4($sp) WRITE_CHECK_JIT 1 lw $t9, Q68State_writeb_func($s0) lw $a1, 4($sp) LOAD_DELAY_NOP jalr $t9 ext $a0, $s6, 0, 24 .endm .macro WRITE16 sw $v0, 4($sp) WRITE_CHECK_JIT 2 lw $t9, Q68State_writew_func($s0) lw $a1, 4($sp) LOAD_DELAY_NOP jalr $t9 ext $a0, $s6, 0, 24 .endm .macro WRITE32 sw $v0, 4($sp) WRITE_CHECK_JIT 4 lw $t9, Q68State_writew_func($s0) lw $a1, 4($sp) ext $a0, $s6, 0, 24 jalr $t9 // Write high word srl $a1, $a1, 16 lw $t9, Q68State_writew_func($s0) lhu $a1, 4($sp) // Keep only the low 16 bits of the value addiu $a0, $s6, 2 jalr $t9 // Write low word ext $a0, $a0, 0, 24 .endm /*-----------------------------------------------------------------------*/ /** * {PUSH,POP}{16,32}: Push or pop values onto or off of the stack. For * POP, the value popped is zero-extended and returned in \return. * * For pushes, it is assumed that the push does not clobber any * JIT-translated code. */ .macro PUSH16 value lw $a0, A7 lw $t9, Q68State_writew_func($s0) move $a1, \value addiu $a0, $a0, -2 sw $a0, A7 jalr $t9 ext $a0, $a0, 0, 24 .endm .macro PUSH32 value lw $a0, A7 lw $t9, Q68State_writew_func($s0) sw \value, 4($sp) addiu $a0, $a0, -4 sw $a0, A7 ext $a0, $a0, 0, 24 jalr $t9 // Write high word srl $a1, \value, 16 lw $a0, A7 lw $t9, Q68State_writew_func($s0) lhu $a1, 4($sp) // Keep only the low 16 bits of \value addiu $a0, $a0, 2 jalr $t9 // Write low word ext $a0, $a0, 0, 24 .endm .macro POP16 return lw $a0, A7 lw $t9, Q68State_readw_func($s0) LOAD_DELAY_NOP addiu $v0, $a0, 2 sw $v0, A7 jalr $t9 ext $a0, $a0, 0, 24 andi \return, $v0, 0xFFFF .endm .macro POP32 return lw $a0, A7 lw $t9, Q68State_readw_func($s0) LOAD_DELAY_NOP addiu $v0, $a0, 4 sw $v0, A7 jalr $t9 // Read high word ext $a0, $a0, 0, 24 lw $a0, A7 sll $v0, $v0, 16 lw $t9, Q68State_readw_func($s0) sw $v0, 4($sp) addiu $a0, $a0, -2 // Since it was already incremented by 4 jalr $t9 // Read low word ext $a0, $a0, 0, 24 lw $v1, 4($sp) andi $v0, $v0, 0xFFFF LOAD_DELAY_NOP or \return, $v0, $v1 .endm /*************************************************************************/ /** * SETCC_NZ_[BWL]: Set the N and Z condition codes according to \value. */ .macro SETCC_NZ_B value ext $v1, \value, 7, 1 ins $s5, $v1, SR_N_SHIFT, 1 andi $v1, \value, 0xFF seqz $v1, $v1 ins $s5, $v1, SR_Z_SHIFT, 1 .endm .macro SETCC_NZ_W value ext $v1, \value, 15, 1 ins $s5, $v1, SR_N_SHIFT, 1 andi $v1, \value, 0xFFFF seqz $v1, $v1 ins $s5, $v1, SR_Z_SHIFT, 1 .endm .macro SETCC_NZ_L value ext $v1, \value, 31, 1 ins $s5, $v1, SR_N_SHIFT, 1 seqz $v1, \value ins $s5, $v1, SR_Z_SHIFT, 1 .endm /*-----------------------------------------------------------------------*/ /** * SETCC_NZ00_[BWL]: Set the N and Z condition codes according to \value, * and clear the V and C condition codes. */ .macro SETCC_NZ00_B value SETCC_NZ_B \value ins $s5, $zero, 0, 2 .endm .macro SETCC_NZ00_W value SETCC_NZ_W \value ins $s5, $zero, 0, 2 .endm .macro SETCC_NZ00_L value SETCC_NZ_L \value ins $s5, $zero, 0, 2 .endm /*-----------------------------------------------------------------------*/ /** * SETCC_XNZVC_ADD: Set the condition codes for an ADD operation based on * the values in the op1, op2, and result registers. */ .macro SETCC_XNZVC_ADD nbits .if \nbits == 8 SETCC_NZ_B $v0 .else .if \nbits == 16 SETCC_NZ_W $v0 .else SETCC_NZ_L $v0 .endif .endif xor $a0, $s3, $v0 xor $a1, $t7, $v0 and $v1, $a0, $a1 ext $v1, $v1, \nbits-1, 1 ins $s5, $v1, SR_V_SHIFT, 1 ext $a0, $s3, \nbits-1, 1 ext $a1, $t7, \nbits-1, 1 ext $v1, $v0, \nbits-1, 1 addu $a0, $a0, $a1 subu $v1, $a0, $v1 sgtz $v1, $v1 ins $s5, $v1, SR_C_SHIFT, 1 ins $s5, $v1, SR_X_SHIFT, 1 .endm /*-----------------------------------------------------------------------*/ /** * SETCC_XNZVC_ADDX: Set the condition codes for an ADDX operation based * on the values in the op1, op2, and result registers. */ .macro SETCC_XNZVC_ADDX nbits // Z is only cleared (never set) by ADDX etc., so we can't use SETNZ ext $v1, $v0, \nbits-1, 1 ins $s5, $v1, SR_N_SHIFT, 1 .if \nbits < 32 ext $v1, $v0, 0, \nbits snez $v1, $v1 .else snez $v1, $v0 .endif sll $v1, $v1, SR_Z_SHIFT not $v1, $v1 and $s5, $s5, $v1 xor $a0, $s3, $v0 xor $a1, $t7, $v0 and $v1, $a0, $a1 ext $v1, $v1, \nbits-1, 1 ins $s5, $v1, SR_V_SHIFT, 1 ext $a0, $s3, \nbits-1, 1 ext $a1, $t7, \nbits-1, 1 ext $v1, $v0, \nbits-1, 1 addu $a0, $a0, $a1 subu $v1, $a0, $v1 sgtz $v1, $v1 ins $s5, $v1, SR_C_SHIFT, 1 ins $s5, $v1, SR_X_SHIFT, 1 .endm /*-----------------------------------------------------------------------*/ /** * SETCC_NZVC_SUB, SETCC_XNZVC_SUB: Set the condition codes for a SUB * operation (excluding or including the X flag) based on the values in the * op1, op2, and result registers. */ .macro SETCC_NZVC_SUB nbits .if \nbits == 8 SETCC_NZ_B $v0 .else .if \nbits == 16 SETCC_NZ_W $v0 .else SETCC_NZ_L $v0 .endif .endif xor $a0, $s3, $t7 xor $a1, $v0, $t7 and $v1, $a0, $a1 ext $v1, $v1, \nbits-1, 1 ins $s5, $v1, SR_V_SHIFT, 1 ext $a0, $s3, \nbits-1, 1 ext $a1, $t7, \nbits-1, 1 ext $v1, $v0, \nbits-1, 1 subu $a0, $a0, $a1 addu $v1, $a0, $v1 sgtz $v1, $v1 ins $s5, $v1, SR_C_SHIFT, 1 .endm .macro SETCC_XNZVC_SUB nbits SETCC_NZVC_SUB \nbits ins $s5, $v1, SR_X_SHIFT, 1 .endm /*-----------------------------------------------------------------------*/ /** * SETCC_XNZVC_SUBX: Set the condition codes for a SUBX operation based on * the values in the op1, op2, and result registers. */ .macro SETCC_XNZVC_SUBX nbits ext $v1, $v0, \nbits-1, 1 ins $s5, $v1, SR_N_SHIFT, 1 .if \nbits < 32 ext $v1, $v0, 0, \nbits snez $v1, $v1 .else snez $v1, $v0 .endif sll $v1, $v1, SR_Z_SHIFT not $v1, $v1 and $s5, $s5, $v1 xor $a0, $s3, $t7 xor $a1, $v0, $t7 and $v1, $a0, $a1 ext $v1, $v1, \nbits-1, 1 ins $s5, $v1, SR_V_SHIFT, 1 ext $a0, $s3, \nbits-1, 1 ext $a1, $t7, \nbits-1, 1 ext $v1, $v0, \nbits-1, 1 subu $a0, $a0, $a1 addu $v1, $a0, $v1 sgtz $v1, $v1 ins $s5, $v1, SR_C_SHIFT, 1 ins $s5, $v1, SR_X_SHIFT, 1 .endm /*************************************************************************/ /*************************** Local subroutines ***************************/ /*************************************************************************/ /** * UPDATE_SR: Set the status register according to the value in $v0. */ UPDATE_SR: lw $a0, Q68State_irq($s0) // Load early for IRQ check below xor $v1, $s5, $v0 andi $v1, $v1, SR_S // Change in S bit? beqz $v1, 1f andi $s5, $v0, 0xFFFF // Zero-extend value into SR mirror register andi $v1, $v0, SR_S // Which way did it change? beqz $v1, 0f lw $a0, A7 lw $a1, SSP // Into supervisor mode LOAD_DELAY_NOP sw $a0, USP j 1f sw $a1, A7 0: lw $a1, USP // Out of supervisor mode sw $a0, SSP LOAD_DELAY_NOP sw $a1, A7 1: andi $v1, $a0, 7 slti $a1, $v1, 7 // Check for a pending NMI beqz $a1, 2f ext $v0, $v0, SR_I0_SHIFT, 3 slt $v0, $v0, $v1 // Check for a pending unmasked interrupt beqz $v0, 3f 2: addiu $a0, $v1, EX_LEVEL_1_INTERRUPT-1 // In a delay slot, but okay sw $a0, Q68State_exception($s0) sw $zero, Q68State_irq($s0) TERMINATE 3: jr $ra nop /*************************************************************************/ /**************************** Meta-operations ****************************/ /*************************************************************************/ /** * PROLOGUE: Any prologue necessary at the beginning of the code stream. */ DEFLABEL(PROLOGUE) _PROLOGUE_TOP: /* Include various exceptional termination code here, so we can * conditionally branch to it (meaning we can skip the branch in * 1-2 cycles) instead of having to inverse-branch around an * unconditional jump (costing 3-4 cycles). */ b 0f nop _PROLOGUE_TERMINATE: // Generic termination (used for CHECK_ABORT) 1: TERMINATE _PROLOGUE_EXCEPTION: // Exception raised (exception number in $a0) b 1b sw $a0, Q68State_exception($s0) _PROLOGUE_ADDRESS_ERROR_EA: // EA address error (opcode in $a2, status in $a3) li $v1, EX_ADDRESS_ERROR sw $v1, Q68State_exception($s0) sw $s6, Q68State_fault_addr($s0) sh $a2, Q68State_fault_opcode($s0) b 1b sh $a3, Q68State_fault_status($s0) _PROLOGUE_ADDRESS_ERROR_SP: // Stack address error (address in $a1, // opcode in $a2, status in $a3) li $a0, EX_ADDRESS_ERROR sw $a0, Q68State_exception($s0) sw $v1, Q68State_fault_addr($s0) sh $a2, Q68State_fault_opcode($s0) b 1b sh $a3, Q68State_fault_status($s0) 0: // Actual setup starts here SETUP DEFSIZE(PROLOGUE) /* Offset definitions used for branching to termination code; "branchofs" * is the offset in _instructions_ (not bytes) from the beginning of the * named fragment */ #define DEFOFS(name,branchofs) \ .globl JIT_PSPOFS_##name; \ .type JIT_PSPOFS_##name, @object; \ JIT_PSPOFS_##name: .int _PROLOGUE_##name - _PROLOGUE_TOP - 4*branchofs DEFOFS(TERMINATE, 0) DEFOFS(EXCEPTION, 0) DEFOFS(ADDRESS_ERROR_EA, 2) DEFOFS(ADDRESS_ERROR_SP, 4) /*-----------------------------------------------------------------------*/ /** * EPILOGUE: Any epilogue necessary at the end of the code stream. */ DEFLABEL(EPILOGUE) TERMINATE DEFSIZE(EPILOGUE) /*************************************************************************/ /** * TRACE: Trace the current instruction. */ DEFLABEL(TRACE) lw $s3, Q68State_cycles($s0) addu $t0, $s3, $s2 sw $t0, Q68State_cycles($s0) sw $s4, Q68State_PC($s0) jal q68_trace sw $s5, Q68State_SR($s0) sw $s3, Q68State_cycles($s0) DEFSIZE(TRACE) /*************************************************************************/ /** * ADD_CYCLES: Add the specified number of clock cycles to the cycle count. * * [Parameters] * cycles: Number of clock cycles to add */ DEFLABEL(ADD_CYCLES) addiu $s2, $s2, 1 9: DEFSIZE(ADD_CYCLES) DEFPARAM(ADD_CYCLES, cycles, 9b, -4) /*-----------------------------------------------------------------------*/ /** * CHECK_CYCLES: Check whether the clock cycle limit has been reached, and * interrupt execution if so. */ DEFLABEL(CHECK_CYCLES) slt $v0, $s2, $s1 bnez $v0, 2f sw $s4, Q68State_PC($s0) // Can't have JAL in a delay slot jal CHECK_CYCLES_get_pc // (got to keep up their reputation) sw $s5, Q68State_SR($s0) 0: jr $ra addiu $sp, $sp, 16 1: SETUP 2: DEFSIZE(CHECK_CYCLES) /* Helper subroutine to get the PC (as with TERMINATE_CONTINUE) */ CHECK_CYCLES_get_pc: addiu $v0, $ra, 1b-0b jr $ra lw $ra, 12($sp) // Preload the saved $ra to avoid a load stall /*************************************************************************/ /** * ADVANCE_PC: Add the specified value to the current program counter. * * [Parameters] * value: Amount to add */ DEFLABEL(ADVANCE_PC) addiu $s4, $s4, 1 9: DEFSIZE(ADVANCE_PC) DEFPARAM(ADVANCE_PC, value, 9b, -4) /*-----------------------------------------------------------------------*/ /** * ADVANCE_PC_CHECK_ABORT: Add the specified value to the current program * counter, then check the jit_abort flag and abort if necessary. * * [Parameters] * value: Amount to add * disp_4: (JIT_PSPOFS_TERMINATE - native offset of this fragment) / 4 */ DEFLABEL(ADVANCE_PC_CHECK_ABORT) bnez $s7, .+0x1234 8: addiu $s4, $s4, 1 9: DEFSIZE(ADVANCE_PC_CHECK_ABORT) DEFPARAM(ADVANCE_PC_CHECK_ABORT, value, 9b, -4) DEFPARAM(ADVANCE_PC_CHECK_ABORT, disp_4, 8b, -4) /*-----------------------------------------------------------------------*/ /** * CHECK_ABORT: Check the jit_abort flag and abort if necessary. * * [Parameters] * disp_4: (JIT_PSPOFS_TERMINATE - native offset of this fragment) / 4 */ DEFLABEL(CHECK_ABORT) bnez $s7, .+0x1234 9: nop DEFSIZE(CHECK_ABORT) DEFPARAM(CHECK_ABORT, disp_4, 9b, -4) /*************************************************************************/ /** * EXCEPTION: Raise the specified exception. * * [Parameters] * num: Exception number * disp_4: (JIT_PSPOFS_EXCEPTION - native offset of this fragment) / 4 */ DEFLABEL(EXCEPTION) b .+0x1234 8: addiu $a0, $zero, 1 9: DEFSIZE(EXCEPTION) DEFPARAM(EXCEPTION, num, 9b, -4) DEFPARAM(EXCEPTION, disp_4, 8b, -4) /*-----------------------------------------------------------------------*/ /** * CHECK_ALIGNED_EA: Check whether the previously resolved effective * address is word-aligned (bit 0 is clear), and raise an address error * exception if not. * * [Parameters] * opcode: Instruction opcode * status: Status word for address error exception * disp_4: (JIT_PSPOFS_ADDRESS_ERROR_EA * - native offset of this fragment) / 4 */ DEFLABEL(CHECK_ALIGNED_EA) andi $v1, $s6, 1 ori $a2, $zero, 0x1234 7: bnezl $v1, .+0x1234 8: ori $a3, $zero, 0x1234 9: DEFSIZE(CHECK_ALIGNED_EA) DEFPARAM(CHECK_ALIGNED_EA, opcode, 7b, -4) DEFPARAM(CHECK_ALIGNED_EA, status, 9b, -4) DEFPARAM(CHECK_ALIGNED_EA, disp_4, 8b, -4) /*-----------------------------------------------------------------------*/ /** * CHECK_ALIGNED_SP: Check whether the current stack pointer (register A7) * is word-aligned (bit 0 is clear), and raise an address error exception * if not. Destroys $v1 and $a0-$a3. * * [Parameters] * opcode: Instruction opcode * status: Status word for address error exception * disp_4: (JIT_PSPOFS_ADDRESS_ERROR_SP * - native offset of this fragment) / 4 */ DEFLABEL(CHECK_ALIGNED_SP) lw $a1, A7 ori $a2, $zero, 0x1234 7: LOAD_DELAY_NOP andi $v1, $a1, 1 bnez $v1, .+0x1234 8: ori $a3, $zero, 0x1234 9: DEFSIZE(CHECK_ALIGNED_SP) DEFPARAM(CHECK_ALIGNED_SP, opcode, 7b, -4) DEFPARAM(CHECK_ALIGNED_SP, status, 9b, -4) DEFPARAM(CHECK_ALIGNED_SP, disp_4, 8b, -4) /*-----------------------------------------------------------------------*/ /** * CHECK_SUPER: Check whether the processor is in supervisor mode, and * raise a privilege violation exception if not. * * [Parameters] * disp_4: (JIT_PSPOFS_EXCEPTION - native offset of this fragment) / 4 */ DEFLABEL(CHECK_SUPER) andi $v0, $s5, SR_S beqzl $v0, .+0x1234 9: li $a0, EX_PRIVILEGE_VIOLATION DEFSIZE(CHECK_SUPER) DEFPARAM(CHECK_SUPER, disp_4, 9b, -4) /*************************************************************************/ /********************* Effective address resolution **********************/ /*************************************************************************/ /** * RESOLVE_INDIRECT: Resolve an address register indirect reference. * * [Parameters] * reg4: (8+n)*4 for register An */ DEFLABEL(RESOLVE_INDIRECT) lw $s6, 1($s0) 9: DEFSIZE(RESOLVE_INDIRECT) DEFPARAM(RESOLVE_INDIRECT, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * RESOLVE_POSTINC: Resolve an address register postincrement reference. * * [Parameters] * reg4: (8+n)*4 for register An * size: Size in bytes of the reference */ DEFLABEL(RESOLVE_POSTINC) lw $s6, 1($s0) 7: LOAD_DELAY_NOP LOAD_DELAY_NOP addiu $v0, $s6, 1 8: sw $v0, 1($s0) 9: DEFSIZE(RESOLVE_POSTINC) DEFPARAM(RESOLVE_POSTINC, reg4, 7b, -4) DEFPARAM(RESOLVE_POSTINC, size, 8b, -4) DEFPARAM(RESOLVE_POSTINC, reg4_b, 9b, -4) // same as reg4 /* For byte-sized (A7)+, make sure A7 stays even */ DEFLABEL(RESOLVE_POSTINC_A7_B) lw $s6, A7 LOAD_DELAY_NOP LOAD_DELAY_NOP addiu $s6, $s6, 1 addiu $v0, $s6, 1 sw $v0, A7 DEFSIZE(RESOLVE_POSTINC_A7_B) /*-----------------------------------------------------------------------*/ /** * RESOLVE_PREDEC: Resolve an address register predecrement reference. * * [Parameters] * reg4: (8+n)*4 for register An * nsize: Size in bytes of the reference, negated */ DEFLABEL(RESOLVE_PREDEC) lw $s6, 1($s0) 7: LOAD_DELAY_NOP LOAD_DELAY_NOP addiu $s6, $s6, -1 8: sw $s6, 1($s0) 9: DEFSIZE(RESOLVE_PREDEC) DEFPARAM(RESOLVE_PREDEC, reg4, 7b, -4) DEFPARAM(RESOLVE_PREDEC, nsize, 8b, -4) DEFPARAM(RESOLVE_PREDEC, reg4_b, 9b, -4) // same as reg4 /* For byte-sized -(A7), make sure A7 stays even */ DEFLABEL(RESOLVE_PREDEC_A7_B) lw $s6, 1($s0) LOAD_DELAY_NOP LOAD_DELAY_NOP addiu $s6, $s6, -1 addiu $v0, $s6, -1 sw $v0, A7 DEFSIZE(RESOLVE_PREDEC_A7_B) /*-----------------------------------------------------------------------*/ /** * RESOLVE_DISP: Resolve an address register indirect with displacement * reference. * * [Parameters] * reg4: (8+n)*4 for register An * disp: Displacement */ DEFLABEL(RESOLVE_DISP) lw $s6, 1($s0) 8: LOAD_DELAY_NOP LOAD_DELAY_NOP addiu $s6, $s6, 1 9: DEFSIZE(RESOLVE_DISP) DEFPARAM(RESOLVE_DISP, reg4, 8b, -4) DEFPARAM(RESOLVE_DISP, disp, 9b, -4) /*-----------------------------------------------------------------------*/ /** * RESOLVE_INDEX_[WL]: Resolve an address register indirect with index * reference. * * [Parameters] * reg4: (8+n)*4 for register An * ireg4: Index register number * 4 * disp: Displacement */ DEFLABEL(RESOLVE_INDEX_W) lw $s6, 1($s0) 7: lh $v0, 1($s0) 8: LOAD_DELAY_NOP addi $s6, $s6, 1 9: add $s6, $s6, $v0 DEFSIZE(RESOLVE_INDEX_W) DEFPARAM(RESOLVE_INDEX_W, reg4, 7b, -4) DEFPARAM(RESOLVE_INDEX_W, ireg4, 8b, -4) DEFPARAM(RESOLVE_INDEX_W, disp, 9b, -4) DEFLABEL(RESOLVE_INDEX_L) lw $s6, 1($s0) 7: lw $v0, 1($s0) 8: LOAD_DELAY_NOP addi $s6, $s6, 1 9: add $s6, $s6, $v0 DEFSIZE(RESOLVE_INDEX_L) DEFPARAM(RESOLVE_INDEX_L, reg4, 7b, -4) DEFPARAM(RESOLVE_INDEX_L, ireg4, 8b, -4) DEFPARAM(RESOLVE_INDEX_L, disp, 9b, -4) /*-----------------------------------------------------------------------*/ /** * RESOLVE_ABSOLUTE: Resolve an absolute short, absolute long, or * PC-relative reference. * * [Parameters] * addr_hi: Absolute address, high 16 bits * addr_lo: Absolute address, low 16 bits */ DEFLABEL(RESOLVE_ABSOLUTE) lui $s6, 0x1234 8: ori $s6, $s6, 0x5678 9: DEFSIZE(RESOLVE_ABSOLUTE) DEFPARAM(RESOLVE_ABSOLUTE, addr_hi, 8b, -4) DEFPARAM(RESOLVE_ABSOLUTE, addr_lo, 9b, -4) /*-----------------------------------------------------------------------*/ /** * RESOLVE_ABS_INDEX_[WL]: Resolve a PC-relative with index reference. * * [Parameters] * ireg4: Index register number * 4 * addr_hi: Absolute address, high 16 bits * addr_lo: Absolute address, low 16 bits */ DEFLABEL(RESOLVE_ABS_INDEX_W) lh $v0, 1($s0) 7: lui $s6, 0x1234 8: ori $s6, $s6, 0x5678 9: addu $s6, $s6, $v0 DEFSIZE(RESOLVE_ABS_INDEX_W) DEFPARAM(RESOLVE_ABS_INDEX_W, ireg4, 7b, -4) DEFPARAM(RESOLVE_ABS_INDEX_W, addr_hi, 8b, -4) DEFPARAM(RESOLVE_ABS_INDEX_W, addr_lo, 9b, -4) DEFLABEL(RESOLVE_ABS_INDEX_L) lh $v0, 1($s0) 7: lui $s6, 0x1234 8: ori $s6, $s6, 0x5678 9: addu $s6, $s6, $v0 DEFSIZE(RESOLVE_ABS_INDEX_L) DEFPARAM(RESOLVE_ABS_INDEX_L, ireg4, 7b, -4) DEFPARAM(RESOLVE_ABS_INDEX_L, addr_hi, 8b, -4) DEFPARAM(RESOLVE_ABS_INDEX_L, addr_lo, 9b, -4) /*************************************************************************/ /*************************** Operand retrieval ***************************/ /*************************************************************************/ /** * GET_OP1_REGISTER: Get the current value of the given register as * operand 1. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(GET_OP1_REGISTER) lw $s3, 1($s0) 9: DEFSIZE(GET_OP1_REGISTER) DEFPARAM(GET_OP1_REGISTER, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * GET_OP1_EA_[BWL]: Get the value pointed to by the previously resolved * effective address as operand 1. */ DEFLABEL(GET_OP1_EA_B) READ8 $s3 DEFSIZE(GET_OP1_EA_B) DEFLABEL(GET_OP1_EA_W) READ16 $s3 DEFSIZE(GET_OP1_EA_W) DEFLABEL(GET_OP1_EA_L) READ32 $s3 DEFSIZE(GET_OP1_EA_L) /*-----------------------------------------------------------------------*/ /** * GET_OP1_IMMED_{16S,16U,16HI,32}: Get an immediate value as operand 1. * * [Parameters] * value_hi: High 16 bits of immediate value * value_lo: Low 16 bits of immediate value (signed for 16S, else unsigned) */ DEFLABEL(GET_OP1_IMMED_16S) addiu $s3, $zero, 1 9: DEFSIZE(GET_OP1_IMMED_16S) DEFPARAM(GET_OP1_IMMED_16S, value_lo, 9b, -4) DEFLABEL(GET_OP1_IMMED_16U) ori $s3, $zero, 1 9: DEFSIZE(GET_OP1_IMMED_16U) DEFPARAM(GET_OP1_IMMED_16U, value_lo, 9b, -4) DEFLABEL(GET_OP1_IMMED_16HI) lui $s3, 1 9: DEFSIZE(GET_OP1_IMMED_16HI) DEFPARAM(GET_OP1_IMMED_16HI, value_hi, 9b, -4) DEFLABEL(GET_OP1_IMMED_32) lui $s3, 1 8: ori $s3, $s3, 1 9: DEFSIZE(GET_OP1_IMMED_32) DEFPARAM(GET_OP1_IMMED_32, value_hi, 8b, -4) DEFPARAM(GET_OP1_IMMED_32, value_lo, 9b, -4) /*-----------------------------------------------------------------------*/ /** * GET_OP1_CCR: Get the current value of CCR as operand 1. */ DEFLABEL(GET_OP1_CCR) andi $s3, $s5, 0xFF DEFSIZE(GET_OP1_CCR) /*-----------------------------------------------------------------------*/ /** * GET_OP1_SR: Get the current value of SR as operand 1. */ DEFLABEL(GET_OP1_SR) move $s3, $s5 DEFSIZE(GET_OP1_SR) /*************************************************************************/ /** * GET_OP2_*: Get the same things as above as operand 2. */ DEFLABEL(GET_OP2_REGISTER) lw $t7, 1($s0) 9: DEFSIZE(GET_OP2_REGISTER) DEFPARAM(GET_OP2_REGISTER, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ DEFLABEL(GET_OP2_EA_B) READ8 $t7 DEFSIZE(GET_OP2_EA_B) DEFLABEL(GET_OP2_EA_W) READ16 $t7 DEFSIZE(GET_OP2_EA_W) DEFLABEL(GET_OP2_EA_L) READ32 $t7 DEFSIZE(GET_OP2_EA_L) /*-----------------------------------------------------------------------*/ DEFLABEL(GET_OP2_IMMED_16S) addiu $t7, $zero, 1 9: DEFSIZE(GET_OP2_IMMED_16S) DEFPARAM(GET_OP2_IMMED_16S, value_lo, 9b, -4) DEFLABEL(GET_OP2_IMMED_16U) ori $t7, $zero, 1 9: DEFSIZE(GET_OP2_IMMED_16U) DEFPARAM(GET_OP2_IMMED_16U, value_lo, 9b, -4) DEFLABEL(GET_OP2_IMMED_16HI) lui $t7, 1 9: DEFSIZE(GET_OP2_IMMED_16HI) DEFPARAM(GET_OP2_IMMED_16HI, value_hi, 9b, -4) DEFLABEL(GET_OP2_IMMED_32) lui $t7, 1 8: ori $t7, $t7, 1 9: DEFSIZE(GET_OP2_IMMED_32) DEFPARAM(GET_OP2_IMMED_32, value_hi, 8b, -4) DEFPARAM(GET_OP2_IMMED_32, value_lo, 9b, -4) /*-----------------------------------------------------------------------*/ DEFLABEL(GET_OP2_CCR) andi $t7, $s5, 0xFF DEFSIZE(GET_OP2_CCR) /*-----------------------------------------------------------------------*/ DEFLABEL(GET_OP2_SR) move $t7, $s5 DEFSIZE(GET_OP2_SR) /*************************************************************************/ /**************************** Result storing *****************************/ /*************************************************************************/ /** * SET_REGISTER_[BWL]: Set the value of the given register to the result * value. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(SET_REGISTER_B) sb $v0, 1($s0) 9: DEFSIZE(SET_REGISTER_B) DEFPARAM(SET_REGISTER_B, reg4, 9b, -4) DEFLABEL(SET_REGISTER_W) sh $v0, 1($s0) 9: DEFSIZE(SET_REGISTER_W) DEFPARAM(SET_REGISTER_W, reg4, 9b, -4) DEFLABEL(SET_REGISTER_L) sw $v0, 1($s0) 9: DEFSIZE(SET_REGISTER_L) DEFPARAM(SET_REGISTER_L, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * SET_AREG_W: Set the value of the given address register to the * sign-extended result value. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(SET_AREG_W) seh $v1, $v0 sw $v1, 1($s0) 9: DEFSIZE(SET_AREG_W) DEFPARAM(SET_AREG_W, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * SET_EA_[BWL]: Set the value pointed to by the previously resolved * effective address to the result value. */ DEFLABEL(SET_EA_B) WRITE8 DEFSIZE(SET_EA_B) DEFLABEL(SET_EA_W) WRITE16 DEFSIZE(SET_EA_W) DEFLABEL(SET_EA_L) WRITE32 DEFSIZE(SET_EA_L) /*-----------------------------------------------------------------------*/ /** * SET_CCR: Set the condition codes from the result value. */ DEFLABEL(SET_CCR) ins $s5, $v0, 0, 8 DEFSIZE(SET_CCR) /*-----------------------------------------------------------------------*/ /** * SET_SR: Set the status register from the result value. */ DEFLABEL(SET_SR) jal UPDATE_SR nop DEFSIZE(SET_SR) /*************************************************************************/ /*************************** Stack operations ****************************/ /*************************************************************************/ /** * PUSH_L: Push the 32-bit value of operand 1 onto the stack. */ DEFLABEL(PUSH_L) PUSH32 $s3 DEFSIZE(PUSH_L) /*-----------------------------------------------------------------------*/ /** * POP_L: Pop a 32-bit value off the stack into the result register. */ DEFLABEL(POP_L) POP32 $v0 DEFSIZE(POP_L) /*************************************************************************/ /************************ Condition code setting *************************/ /*************************************************************************/ /** * SETCC_ADD_[BWL]: Set the condition codes for the result of an ADD * instruction stored in the result register. */ DEFLABEL(SETCC_ADD_B) SETCC_XNZVC_ADD 8 DEFSIZE(SETCC_ADD_B) DEFLABEL(SETCC_ADD_W) SETCC_XNZVC_ADD 16 DEFSIZE(SETCC_ADD_W) DEFLABEL(SETCC_ADD_L) SETCC_XNZVC_ADD 32 DEFSIZE(SETCC_ADD_L) /*************************************************************************/ /** * SETCC_ADDX_[BWL]: Set the condition codes for the result of an ADDX * instruction stored in the result register. */ DEFLABEL(SETCC_ADDX_B) SETCC_XNZVC_ADDX 8 DEFSIZE(SETCC_ADDX_B) DEFLABEL(SETCC_ADDX_W) SETCC_XNZVC_ADDX 16 DEFSIZE(SETCC_ADDX_W) DEFLABEL(SETCC_ADDX_L) SETCC_XNZVC_ADDX 32 DEFSIZE(SETCC_ADDX_L) /*************************************************************************/ /** * SETCC_SUB_[BWL]: Set the condition codes for the result of a SUB * instruction stored in the result register. */ DEFLABEL(SETCC_SUB_B) SETCC_XNZVC_SUB 8 DEFSIZE(SETCC_SUB_B) DEFLABEL(SETCC_SUB_W) SETCC_XNZVC_SUB 16 DEFSIZE(SETCC_SUB_W) DEFLABEL(SETCC_SUB_L) SETCC_XNZVC_SUB 32 DEFSIZE(SETCC_SUB_L) /*************************************************************************/ /** * SETCC_SUBX_[BWL]: Set the condition codes for the result of a SUBX * instruction stored in the result register. */ DEFLABEL(SETCC_SUBX_B) SETCC_XNZVC_SUBX 8 DEFSIZE(SETCC_SUBX_B) DEFLABEL(SETCC_SUBX_W) SETCC_XNZVC_SUBX 16 DEFSIZE(SETCC_SUBX_W) DEFLABEL(SETCC_SUBX_L) SETCC_XNZVC_SUBX 32 DEFSIZE(SETCC_SUBX_L) /*************************************************************************/ /** * SETCC_CMP_[BWL]: Set the condition codes for the result of a CMP * instruction stored in the result register. The X flag is unmodified. */ DEFLABEL(SETCC_CMP_B) SETCC_NZVC_SUB 8 DEFSIZE(SETCC_CMP_B) DEFLABEL(SETCC_CMP_W) SETCC_NZVC_SUB 16 DEFSIZE(SETCC_CMP_W) DEFLABEL(SETCC_CMP_L) SETCC_NZVC_SUB 32 DEFSIZE(SETCC_CMP_L) /*************************************************************************/ /** * SETCC_LOGIC_[BWL]: Set the condition codes for the result of a logical * instruction (MOVE, AND, OR, EOR) stored in the result register. The X * flag is unmodified. */ DEFLABEL(SETCC_LOGIC_B) SETCC_NZ00_B $v0 DEFSIZE(SETCC_LOGIC_B) DEFLABEL(SETCC_LOGIC_W) SETCC_NZ00_W $v0 DEFSIZE(SETCC_LOGIC_W) DEFLABEL(SETCC_LOGIC_L) SETCC_NZ00_L $v0 DEFSIZE(SETCC_LOGIC_L) /*************************************************************************/ /*************************** Condition testing ***************************/ /*************************************************************************/ /** * TEST_*: Check whether a condition is true (based on the current * condition codes) and set $v1 based on the result (nonzero = true). */ DEFLABEL(TEST_T) li $v1, 1 DEFSIZE(TEST_T) DEFLABEL(TEST_F) li $v1, 0 DEFSIZE(TEST_F) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_HI) ext $v1, $s5, SR_Z_SHIFT, 1 ext $a0, $s5, SR_C_SHIFT, 1 or $v1, $v1, $a0 xori $v1, $v1, 1 DEFSIZE(TEST_HI) DEFLABEL(TEST_LS) ext $v1, $s5, SR_Z_SHIFT, 1 ext $a0, $s5, SR_C_SHIFT, 1 or $v1, $v1, $a0 DEFSIZE(TEST_LS) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_CC) ext $v1, $s5, SR_C_SHIFT, 1 xori $v1, $v1, 1 DEFSIZE(TEST_CC) DEFLABEL(TEST_CS) ext $v1, $s5, SR_C_SHIFT, 1 DEFSIZE(TEST_CS) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_NE) ext $v1, $s5, SR_Z_SHIFT, 1 xori $v1, $v1, 1 DEFSIZE(TEST_NE) DEFLABEL(TEST_EQ) ext $v1, $s5, SR_Z_SHIFT, 1 DEFSIZE(TEST_EQ) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_VC) ext $v1, $s5, SR_V_SHIFT, 1 xori $v1, $v1, 1 DEFSIZE(TEST_VC) DEFLABEL(TEST_VS) ext $v1, $s5, SR_V_SHIFT, 1 DEFSIZE(TEST_VS) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_PL) ext $v1, $s5, SR_N_SHIFT, 1 xori $v1, $v1, 1 DEFSIZE(TEST_PL) DEFLABEL(TEST_MI) ext $v1, $s5, SR_N_SHIFT, 1 DEFSIZE(TEST_MI) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_GE) ext $v1, $s5, SR_N_SHIFT, 1 ext $a0, $s5, SR_V_SHIFT, 1 xor $v1, $v1, $a0 xori $v1, $v1, 1 DEFSIZE(TEST_GE) DEFLABEL(TEST_LT) ext $v1, $s5, SR_N_SHIFT, 1 ext $a0, $s5, SR_V_SHIFT, 1 xor $v1, $v1, $a0 DEFSIZE(TEST_LT) /*-----------------------------------------------------------------------*/ DEFLABEL(TEST_GT) ext $v1, $s5, SR_N_SHIFT, 1 ext $a0, $s5, SR_V_SHIFT, 1 ext $a1, $s5, SR_Z_SHIFT, 1 xor $v1, $v1, $a0 or $v1, $v1, $a1 xori $v1, $v1, 1 DEFSIZE(TEST_GT) DEFLABEL(TEST_LE) ext $v1, $s5, SR_N_SHIFT, 1 ext $a0, $s5, SR_V_SHIFT, 1 ext $a1, $s5, SR_Z_SHIFT, 1 xor $v1, $v1, $a0 or $v1, $v1, $a1 DEFSIZE(TEST_LE) /*************************************************************************/ /**************************** ALU operations *****************************/ /*************************************************************************/ /** * MOVE_[BWL]: Evaluate op1, setting the result value for the MOVE * instruction. */ DEFLABEL(MOVE_B) move $v0, $s3 DEFSIZE(MOVE_B) DEFLABEL(MOVE_W) move $v0, $s3 DEFSIZE(MOVE_W) DEFLABEL(MOVE_L) move $v0, $s3 DEFSIZE(MOVE_L) /*************************************************************************/ /** * ADD_[BWL]: Evaluate op2 + op1. */ DEFLABEL(ADD_B) addu $v0, $t7, $s3 DEFSIZE(ADD_B) DEFLABEL(ADD_W) addu $v0, $t7, $s3 DEFSIZE(ADD_W) DEFLABEL(ADD_L) addu $v0, $t7, $s3 DEFSIZE(ADD_L) /*-----------------------------------------------------------------------*/ /** * ADDA_W: Sign-extend op1 to 32 bits, then evaluate op2 + op1. */ DEFLABEL(ADDA_W) seh $v1, $s3 addu $v0, $t7, $v1 DEFSIZE(ADDA_W) /*-----------------------------------------------------------------------*/ /** * ADDX_[BWL]: Evaluate op2 + op1 + X. */ DEFLABEL(ADDX_B) ext $v1, $s5, SR_X_SHIFT, 1 addu $v0, $t7, $s3 addu $v0, $v0, $v1 DEFSIZE(ADDX_B) DEFLABEL(ADDX_W) ext $v1, $s5, SR_X_SHIFT, 1 addu $v0, $t7, $s3 addu $v0, $v0, $v1 DEFSIZE(ADDX_W) DEFLABEL(ADDX_L) ext $v1, $s5, SR_X_SHIFT, 1 addu $v0, $t7, $s3 addu $v0, $v0, $v1 DEFSIZE(ADDX_L) /*************************************************************************/ /** * SUB_[BWL]: Evaluate op2 - op1. */ DEFLABEL(SUB_B) subu $v0, $t7, $s3 DEFSIZE(SUB_B) DEFLABEL(SUB_W) subu $v0, $t7, $s3 DEFSIZE(SUB_W) DEFLABEL(SUB_L) subu $v0, $t7, $s3 DEFSIZE(SUB_L) /*-----------------------------------------------------------------------*/ /** * SUBA_W: Sign-extend op1 to 32 bits, then evaluate op2 - op1. */ DEFLABEL(SUBA_W) seh $v1, $s3 subu $v0, $t7, $v1 DEFSIZE(SUBA_W) /*-----------------------------------------------------------------------*/ /** * SUBX_[BWL]: Evaluate op2 - op1 - X. */ DEFLABEL(SUBX_B) ext $v1, $s5, SR_X_SHIFT, 1 subu $v0, $t7, $s3 subu $v0, $v0, $v1 DEFSIZE(SUBX_B) DEFLABEL(SUBX_W) ext $v1, $s5, SR_X_SHIFT, 1 subu $v0, $t7, $s3 subu $v0, $v0, $v1 DEFSIZE(SUBX_W) DEFLABEL(SUBX_L) ext $v1, $s5, SR_X_SHIFT, 1 subu $v0, $t7, $s3 subu $v0, $v0, $v1 DEFSIZE(SUBX_L) /*************************************************************************/ /** * MUL[SU]_W: Evaluate op2 * op1 in signed or unsigned context. */ DEFLABEL(MULS_W) seh $v0, $t7 seh $v1, $s3 mul $v0, $v1 mflo $v0 DEFSIZE(MULS_W) DEFLABEL(MULU_W) andi $v0, $t7, 0xFFFF andi $v1, $s3, 0xFFFF multu $v0, $v1 mflo $v0 DEFSIZE(MULU_W) /*************************************************************************/ /** * DIV[SU]_W: Evaluate op2 / op1 in signed or unsigned context, setting * the condition codes appropriately. The quotient is stored in the low * 16 bits, the remainder in the high 16 bits of the result value. On * overflow, op2 is copied to the result. */ DEFLABEL(DIVS_W) seh $v1, $s3 /* MIPS doesn't raise an exception on divide-by-zero, so let the * divider unit do its thing while we check for a zero divisor */ div $t7, $v1 bnez $v1, 0f // The C flag is always cleared, so do it here in the delay slot ins $s5, $zero, SR_C_SHIFT, 1 li $a0, EX_DIVIDE_BY_ZERO sw $a0, Q68State_exception($s0) TERMINATE 0: mflo $v0 mfhi $a3 li $a0, 0x8000 // Overflow check addu $a0, $v0, $a0 srl $a0, $a0, 16 beqz $a0, 1f sll $a3, $a3, 16 li $a0, 1 ins $s5, $a0, SR_V_SHIFT, 1 j 2f move $v0, $t7 1: andi $v0, $v0, 0xFFFF // Need to clear upper bits in case it's negative SETCC_NZ_W $v0 ins $s5, $zero, SR_V_SHIFT, 1 or $v0, $v0, $a3 2: DEFSIZE(DIVS_W) DEFLABEL(DIVU_W) andi $v1, $s3, 0xFFFF divu $t7, $v1 bnez $v1, 0f ins $s5, $zero, SR_C_SHIFT, 1 li $a0, EX_DIVIDE_BY_ZERO sw $a0, Q68State_exception($s0) TERMINATE 0: mflo $v0 mfhi $a3 srl $a0, $v0, 16 // Overflow check (easier for unsigned quotients) beqz $a0, 1f sll $a3, $a3, 16 li $a0, 1 ins $s5, $a0, SR_V_SHIFT, 1 j 2f move $v0, $t7 1: SETCC_NZ_W $v0 ins $s5, $zero, SR_V_SHIFT, 1 or $v0, $v0, $a3 2: DEFSIZE(DIVU_W) /*************************************************************************/ /** * AND_[BWL]: Evaluate op2 & op1. */ DEFLABEL(AND_B) and $v0, $t7, $s3 DEFSIZE(AND_B) DEFLABEL(AND_W) and $v0, $t7, $s3 DEFSIZE(AND_W) DEFLABEL(AND_L) and $v0, $t7, $s3 DEFSIZE(AND_L) /*************************************************************************/ /** * OR_[BWL]: Evaluate op2 | op1. */ DEFLABEL(OR_B) or $v0, $t7, $s3 DEFSIZE(OR_B) DEFLABEL(OR_W) or $v0, $t7, $s3 DEFSIZE(OR_W) DEFLABEL(OR_L) or $v0, $t7, $s3 DEFSIZE(OR_L) /*************************************************************************/ /** * EOR_[BWL]: Evaluate op2 ^ op1. */ DEFLABEL(EOR_B) xor $v0, $t7, $s3 DEFSIZE(EOR_B) DEFLABEL(EOR_W) xor $v0, $t7, $s3 DEFSIZE(EOR_W) DEFLABEL(EOR_L) xor $v0, $t7, $s3 DEFSIZE(EOR_L) /*************************************************************************/ /** * EXT_[WL]: Sign-extend op1 from 8 to 16 or from 16 to 32 bits. */ DEFLABEL(EXT_W) seb $v0, $s3 DEFSIZE(EXT_W) DEFLABEL(EXT_L) seh $v0, $s3 DEFSIZE(EXT_L) /*************************************************************************/ /** * SWAP: Swap the upper and lower 16-bit halves of op1, placing the result * in the result register. */ DEFLABEL(SWAP) srl $v0, $s3, 16 ins $v0, $s3, 16, 16 DEFSIZE(SWAP) /*************************************************************************/ /**************************** BCD operations *****************************/ /*************************************************************************/ /** * ABCD: Evaluate op2 + op1 + X, treating the operands as binary-coded * decimal values. */ DEFLABEL(ABCD) ext $t2, $s5, SR_X_SHIFT, 1 andi $t0, $t7, 0xF andi $t1, $s3, 0xF addu $t3, $t0, $t1 addu $t3, $t3, $t2 sltiu $v1, $t3, 10 beqzl $v1, 0f addiu $t3, $t3, 6 // Skipped if no carry from the units place 0: andi $t0, $t7, 0xF0 andi $t1, $s3, 0xF0 addu $t2, $t0, $t1 addu $v0, $t2, $t3 sltiu $v1, $v0, 10<<4 bnezl $v1, 1f move $a3, $zero // Executed if no carry from the tens place addiu $v0, $v0, -(10<<4) li $a3, 1 1: ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 andi $v1, $v0, 0xFF snez $v1, $v1 sll $v1, $v1, SR_Z_SHIFT not $v1, $v1 and $s5, $s5, $v1 DEFSIZE(ABCD) /*************************************************************************/ /** * SBCD: Evaluate op2 - op1 - X, treating the operands as binary-coded * decimal values. */ DEFLABEL(SBCD) ext $t2, $s5, SR_X_SHIFT, 1 andi $t0, $t7, 0xF andi $t1, $s3, 0xF subu $t3, $t0, $t1 subu $t3, $t3, $t2 sltz $v1, $t3 bnezl $v1, 0f move $t2, $zero // Executed if no borrow from the units place addiu $t3, $t3, 10 li $t2, 1<<4 0: andi $t0, $t7, 0xF0 andi $t1, $s3, 0xF0 subu $t4, $t0, $t1 subu $t4, $t4, $t2 sltz $v1, $t4 bnezl $v1, 1f move $a3, $zero // Executed if no carry from the tens place addiu $v0, $v0, 10<<4 li $a3, 1 1: addu $v0, $t3, $t4 sltz $v1, $v0 or $a3, $a3, $v1 ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 andi $v1, $v0, 0xFF snez $v1, $v1 sll $v1, $v1, SR_Z_SHIFT not $v1, $v1 and $s5, $s5, $v1 DEFSIZE(SBCD) /*************************************************************************/ /*********************** Bit-twiddling operations ************************/ /*************************************************************************/ /** * BTST_[BL]: Evaluate op2 & (1 << op1). The value (1 << op1), where the * high bits of op1 have been masked to zero, is left in $t0 for use by a * subsequent BCHG/BCLR/BSET operation. */ DEFLABEL(BTST_B) andi $a0, $s3, 7 li $t0, 1 sllv $t0, $t0, $a0 and $v1, $t7, $t0 seqz $v1, $v1 ins $s5, $v1, SR_Z_SHIFT, 1 DEFSIZE(BTST_B) DEFLABEL(BTST_L) andi $a0, $s3, 31 li $t0, 1 sllv $t0, $t0, $a0 and $v1, $t7, $t0 seqz $v1, $v1 ins $s5, $v1, SR_Z_SHIFT, 1 DEFSIZE(BTST_L) /*************************************************************************/ /** * BCHG: Evaluate op2 ^ (1 << op1), where (1 << op1) has already been * stored in $t0. */ DEFLABEL(BCHG) xor $v0, $t7, $t0 DEFSIZE(BCHG) /*-----------------------------------------------------------------------*/ /** * BCLR: Evaluate op2 & ~(1 << op1), where (1 << op1) has already been * stored in $t0. */ DEFLABEL(BCLR) not $v1, $t0 and $v0, $t7, $v1 DEFSIZE(BCLR) /*-----------------------------------------------------------------------*/ /** * BSET: Evaluate op2 | (1 << op1), where (1 << op1) has already been * stored in $t0. */ DEFLABEL(BSET) or $v0, $t7, $t0 DEFSIZE(BSET) /*************************************************************************/ /************************ Shift/rotate operations ************************/ /*************************************************************************/ /** * ASL_[BWL]: Evaluate (signed) op2 << op1. */ .macro DEF_ASL nbits andi $s3, $s3, 0x3F sll $v1, $s3, 1 // Add 2 clock cycles per shift add $s2, $s2, $v1 ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C beqz $s3, 1f move $v0, $t7 // Have to shift bit by bit to detect overflow 0: ext $a3, $v0, \nbits-1, 1 ext $v1, $v0, \nbits-2, 1 sll $v0, $v0, 1 addiu $s3, $s3, -1 xor $v1, $v1, $a3 sll $v1, $v1, SR_V_SHIFT bnez $s3, 0b or $s5, $s5, $v1 ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 1: .endm DEFLABEL(ASL_B) DEF_ASL 8 SETCC_NZ_B $v0 DEFSIZE(ASL_B) DEFLABEL(ASL_W) DEF_ASL 16 SETCC_NZ_W $v0 DEFSIZE(ASL_W) DEFLABEL(ASL_L) DEF_ASL 32 SETCC_NZ_L $v0 DEFSIZE(ASL_L) /*-----------------------------------------------------------------------*/ /** * ASR_[BWL]: Evaluate (signed) op2 >> op1. */ .macro DEF_ASR nbits andi $s3, $s3, 0x3F sll $v1, $s3, 1 add $s2, $s2, $v1 ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C beqz $s3, 3f move $v0, $t7 sltiu $v1, $s3, \nbits bnez $v1, 2f nop 1: // count >= nbits sra $v0, $t7, \nbits-1 andi $a3, $v0, 1 ins $s5, $a3, SR_C_SHIFT, 1 j 3f ins $s5, $a3, SR_X_SHIFT, 1 2: // count != 0 && count < nbits addiu $v1, $s3, -1 srav $v0, $t7, $v1 andi $a3, $v0, 1 ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 sra $v0, $v0, 1 3: // All cases .endm DEFLABEL(ASR_B) seb $t7 DEF_ASR 8 SETCC_NZ_B $v0 DEFSIZE(ASR_B) DEFLABEL(ASR_W) seh $t7 DEF_ASR 16 SETCC_NZ_W $v0 DEFSIZE(ASR_W) DEFLABEL(ASR_L) DEF_ASR 32 SETCC_NZ_L $v0 DEFSIZE(ASR_L) /*************************************************************************/ /** * LSL_[BWL]: Evaluate (unsigned) op2 << op1. */ .macro DEF_LSL nbits andi $s3, $s3, 0x3F sll $v1, $s3, 1 add $s2, $s2, $v1 ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C beqz $s3, 3f move $v0, $t7 li $a0, \nbits sltu $v1, $s3, $a0 bnez $v1, 2f sltu $v1, $a0, $s3 bnez $v1, 1f nop 0: // count == nbits ins $s5, $t7, SR_C_SHIFT, 1 ins $s5, $t7, SR_X_SHIFT, 1 j 3f move $v0, $zero 1: // count > nbits ins $s5, $zero, SR_X_SHIFT, 1 j 3f move $v0, $zero 2: // count != 0 && count < nbits addiu $v1, $s3, -1 sllv $v0, $t7, $v1 ext $a3, $v0, \nbits-1, 1 ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 sll $v0, $v0, 1 3: // All cases .endm DEFLABEL(LSL_B) DEF_LSL 8 SETCC_NZ_B $v0 DEFSIZE(LSL_B) DEFLABEL(LSL_W) DEF_LSL 16 SETCC_NZ_W $v0 DEFSIZE(LSL_W) DEFLABEL(LSL_L) DEF_LSL 32 SETCC_NZ_L $v0 DEFSIZE(LSL_L) /*-----------------------------------------------------------------------*/ /** * LSR_[BWL]: Evaluate (unsigned) op2 >> op1. */ .macro DEF_LSR nbits andi $s3, $s3, 0x3F sll $v1, $s3, 1 add $s2, $s2, $v1 ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C beqz $s3, 3f move $v0, $t7 li $a0, \nbits sltu $v1, $s3, $a0 bnez $v1, 2f sltu $v1, $a0, $s3 bnez $v1, 1f nop 0: // count == nbits ext $a3, $t7, \nbits-1, 1 ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 j 3f move $v0, $zero 1: // count > nbits ins $s5, $zero, SR_X_SHIFT, 1 j 3f move $v0, $zero 2: // count != 0 && count < nbits addiu $v1, $s3, -1 srlv $v0, $t7, $v1 andi $a3, $v0, 1 ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 srl $v0, $v0, 1 3: // All cases .endm DEFLABEL(LSR_B) andi $t7, $t7, 0xFF DEF_LSR 8 SETCC_NZ_B $v0 DEFSIZE(LSR_B) DEFLABEL(LSR_W) andi $t7, $t7, 0xFFFF DEF_LSR 16 SETCC_NZ_W $v0 DEFSIZE(LSR_W) DEFLABEL(LSR_L) DEF_LSR 32 SETCC_NZ_L $v0 DEFSIZE(LSR_L) /*************************************************************************/ /** * ROXL_[BWL]: Evaluate op2 ROXL op1. */ .macro DEF_ROXL nbits andi $s3, $s3, 0x3F sll $v1, $s3, 1 add $s2, $s2, $v1 ext $a3, $s5, SR_X_SHIFT, 1 ins $s5, $a3, SR_C_SHIFT, 2 // Clear V while setting C beqz $s3, 1f move $v0, $t7 0: sll $v1, $v0, 1 ext $a0, $v0, \nbits-1, 1 or $v0, $v1, $a3 addiu $s3, $s3, -1 bnez $s3, 0b move $a3, $a0 ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 1: .endm DEFLABEL(ROXL_B) DEF_ROXL 8 SETCC_NZ_B $v0 DEFSIZE(ROXL_B) DEFLABEL(ROXL_W) DEF_ROXL 16 SETCC_NZ_W $v0 DEFSIZE(ROXL_W) DEFLABEL(ROXL_L) DEF_ROXL 32 SETCC_NZ_L $v0 DEFSIZE(ROXL_L) /*-----------------------------------------------------------------------*/ /** * ROXR_[BWL]: Evaluate op2 ROXR op1. */ .macro DEF_ROXR nbits andi $s3, $s3, 0x3F sll $v1, $s3, 1 add $s2, $s2, $v1 ext $a3, $s5, SR_X_SHIFT, 1 ins $s5, $a3, SR_C_SHIFT, 2 // Clear V while setting C beqz $s3, 1f move $v0, $t7 0: srl $v1, $v0, 1 ins $v1, $a3, \nbits-1, 1 andi $a3, $v0, 1 addiu $s3, $s3, -1 bnez $s3, 0b move $v0, $v1 ins $s5, $a3, SR_C_SHIFT, 1 ins $s5, $a3, SR_X_SHIFT, 1 1: .endm DEFLABEL(ROXR_B) DEF_ROXR 8 SETCC_NZ_B $v0 DEFSIZE(ROXR_B) DEFLABEL(ROXR_W) DEF_ROXR 16 SETCC_NZ_W $v0 DEFSIZE(ROXR_W) DEFLABEL(ROXR_L) DEF_ROXR 32 SETCC_NZ_L $v0 DEFSIZE(ROXR_L) /*************************************************************************/ /** * ROL_[BWL]: Evaluate op2 ROL op1. */ .macro DEF_ROL nbits andi $s3, $s3, 0x3F sll $v1, $s3, 1 add $s2, $s2, $v1 ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C beqz $s3, 3f move $v0, $t7 andi $s3, $s3, \nbits-1 bnez $s3, 2f 1: // count != 0 && count % nbits == 0 andi $a3, $t7, 1 // Branch delay slot from above (this is safe) j 3f ins $s5, $a3, SR_C_SHIFT, 1 2: // count % nbits != 0 li $v1, \nbits sub $v1, $v1, $s3 sllv $a0, $t7, $s3 srlv $t0, $t7, $v1 or $v0, $a0, $t0 andi $a3, $t7, 1 ins $s5, $a3, SR_C_SHIFT, 1 3: // All cases .endm DEFLABEL(ROL_B) andi $t7, $t7, 0xFF DEF_ROL 8 SETCC_NZ_B $v0 DEFSIZE(ROL_B) DEFLABEL(ROL_W) andi $t7, $t7, 0xFFFF DEF_ROL 16 SETCC_NZ_W $v0 DEFSIZE(ROL_W) DEFLABEL(ROL_L) DEF_ROL 32 SETCC_NZ_L $v0 DEFSIZE(ROL_L) /*-----------------------------------------------------------------------*/ /** * ROR_[BWL]: Evaluate op2 ROR op1. */ .macro DEF_ROR nbits andi $s3, $s3, 0x3F sll $v1, $s3, 1 add $s2, $s2, $v1 ins $s5, $zero, SR_C_SHIFT, 2 // Clear V and C beqz $s3, 3f move $v0, $t7 andi $s3, $s3, \nbits-1 bnez $s3, 2f 1: // count != 0 && count % nbits == 0 ext $a3, $t7, \nbits-1, 1 // Branch delay slot from above (safe) j 3f ins $s5, $a3, SR_C_SHIFT, 1 2: // count % nbits != 0 li $v1, \nbits sub $v1, $v1, $s3 srlv $a0, $t7, $s3 sllv $t0, $t7, $v1 or $v0, $a0, $t0 ext $a3, $t7, \nbits-1, 1 ins $s5, $a3, SR_C_SHIFT, 1 3: // All cases .endm DEFLABEL(ROR_B) andi $t7, $t7, 0xFF DEF_ROR 8 SETCC_NZ_B $v0 DEFSIZE(ROR_B) DEFLABEL(ROR_W) andi $t7, $t7, 0xFFFF DEF_ROR 16 SETCC_NZ_W $v0 DEFSIZE(ROR_W) DEFLABEL(ROR_L) DEF_ROR 32 SETCC_NZ_L $v0 DEFSIZE(ROR_L) /*************************************************************************/ /******************* Conditional and branch operations *******************/ /*************************************************************************/ /** * Scc: Set the lower 8 bits of the result value to 0xFF if the condition * is true, 0x00 if false. */ DEFLABEL(Scc) negu $v0, $v1 DEFSIZE(Scc) /*-----------------------------------------------------------------------*/ /** * ADD_CYCLES_Scc_Dn: Add the appropriate number of clock cycles for an * Scc Dn instruction to the cycle count. */ DEFLABEL(ADD_CYCLES_Scc_Dn) andi $v1, $v0, 2 addiu $v1, $v1, 4 addu $s2, $s2, $v1 DEFSIZE(ADD_CYCLES_Scc_Dn) /*************************************************************************/ /** * DBcc: Jump to the specified target address unless the condition is true * or the lower 16 bits of the given data register, after being decremented, * are equal to -1. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7) * target_hi: High 16 bits of target address * target_lo: Low 16 bits of target address */ DEFLABEL(DBcc) lhu $v0, 1($s0) 6: bnezl $v1, 0f addiu $s2, $s2, 12 addiu $v1, $v0, -1 sh $v1, 1($s0) 7: beqzl $v0, 0f addiu $s2, $s2, 14 addiu $s2, $s2, 10 lui $v0, 0x1234 8: ori $s4, $v0, 0x5678 9: TERMINATE 0: DEFSIZE(DBcc) DEFPARAM(DBcc, reg4, 6b, -4) DEFPARAM(DBcc, reg4_b, 7b, -4) // same as reg4 DEFPARAM(DBcc, target_hi, 8b, -4) DEFPARAM(DBcc, target_lo, 9b, -4) /*-----------------------------------------------------------------------*/ /** * DBcc_native: Implement DBcc using a jump within the native code. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7) * target_hi: High 16 bits of target 68000 address * target_lo: Low 16 bits of target 68000 address * native_disp_4_4: (Native displacement from end of this fragment + 4) / 4 */ DEFLABEL(DBcc_native) lhu $v0, 1($s0) 6: bnezl $v1, 0f addiu $s2, $s2, 12 addiu $v1, $v0, -1 sh $v1, 1($s0) 7: beqzl $v0, 0f addiu $s2, $s2, 14 addiu $s2, $s2, 10 lui $v0, 0x1234 8: ori $s4, $v0, 0x5678 9: b .+0x1234 5: nop 0: DEFSIZE(DBcc_native) DEFPARAM(DBcc_native, reg4, 6b, -4) DEFPARAM(DBcc_native, reg4_b, 7b, -4) // same as reg4 DEFPARAM(DBcc_native, target_hi, 8b, -4) DEFPARAM(DBcc_native, target_lo, 9b, -4) DEFPARAM(DBcc_native, native_disp_4_4, 5b, -4) /*************************************************************************/ /** * Bcc_common: Jump to the specified target address if the condition is * true. Used for both interpreted jumps (by branching to TERMINATE) and * native jumps. * * [Parameters] * target_hi: High 16 bits of target 68000 address * target_lo: Low 16 bits of target 68000 address * disp_4: Native displacement / 4 (either JIT_PSPOFS_TERMINATE or * target address minus address of "nop" following branch) */ DEFLABEL(Bcc_common) beqz $v1, 0f lui $v0, 0x1234 // In the delay slot, but not a problem 8: ori $s4, $v0, 0x5678 9: addiu $s2, $s2, 10 b .+0x1234 5: nop 0: DEFSIZE(Bcc_common) DEFPARAM(Bcc_common, target_hi, 8b, -4) DEFPARAM(Bcc_common, target_lo, 9b, -4) DEFPARAM(Bcc_common, disp_4, 5b, -4) /*-----------------------------------------------------------------------*/ /** * BSR: Push the address of the next instruction onto the stack, then jump * to the specified target address. * * [Parameters] * return_addr_hi: High 16 bits of return address to push onto the stack * return_addr_lo: High 16 bits of return address to push onto the stack * target_hi: High 16 bits of target address * target_lo: Low 16 bits of target address */ DEFLABEL(BSR) lui $v0, 0x1234 6: ori $v0, $v0, 0x5678 7: PUSH32 $v0 lui $v0, 0x1234 8: ori $s4, $v0, 0x5678 9: ori $s2, $s2, 0x8000 // Indicate that this is a BSR/JSR termination TERMINATE_CONTINUE DEFSIZE(BSR) DEFPARAM(BSR, return_addr_hi, 6b, -4) DEFPARAM(BSR, return_addr_lo, 7b, -4) DEFPARAM(BSR, target_hi, 8b, -4) DEFPARAM(BSR, target_lo, 9b, -4) /*************************************************************************/ /** * JMP: Jump to the previously resolved effective address. */ DEFLABEL(JMP) move $s4, $s6 TERMINATE DEFSIZE(JMP) /*-----------------------------------------------------------------------*/ /** * JSR: Push the address of the next instruction onto the stack, then jump * to the previously resolved effective address. * * [Parameters] * return_addr_hi: High 16 bits of return address to push onto the stack * return_addr_lo: High 16 bits of return address to push onto the stack */ DEFLABEL(JSR) lui $v0, 0x1234 8: ori $v0, $v0, 0x5678 9: PUSH32 $v0 move $s4, $s6 ori $s2, $s2, 0x8000 // Indicate that this is a BSR/JSR termination TERMINATE_CONTINUE DEFSIZE(JSR) DEFPARAM(JSR, return_addr_hi, 8b, -4) DEFPARAM(JSR, return_addr_lo, 9b, -4) /*************************************************************************/ /*********************** MOVEM-related operations ************************/ /*************************************************************************/ /** * STORE_DEC_[WL]: Decrement state->ea_addr, then store the specified * register to the resulting location. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(STORE_DEC_W) lw $v0, 1($s0) 9: addiu $s6, $s6, -2 LOAD_DELAY_NOP WRITE16 DEFSIZE(STORE_DEC_W) DEFPARAM(STORE_DEC_W, reg4, 9b, -4) DEFLABEL(STORE_DEC_L) lw $v0, 1($s0) 9: addiu $s6, $s6, -4 LOAD_DELAY_NOP WRITE32 DEFSIZE(STORE_DEC_L) DEFPARAM(STORE_DEC_L, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * STORE_INC_[WL]: Store the specified register to the location indicated * by state->ea_addr, then increment state->ea_addr. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(STORE_INC_W) lw $v0, 1($s0) 9: LOAD_DELAY_NOP LOAD_DELAY_NOP WRITE16 addiu $s6, $s6, 2 DEFSIZE(STORE_INC_W) DEFPARAM(STORE_INC_W, reg4, 9b, -4) DEFLABEL(STORE_INC_L) lw $v0, 1($s0) 9: LOAD_DELAY_NOP LOAD_DELAY_NOP WRITE32 addiu $s6, $s6, 4 DEFSIZE(STORE_INC_L) DEFPARAM(STORE_INC_L, reg4, 9b, -4) /*************************************************************************/ /** * LOAD_INC_[WL]: Load the specified register from the location indicated * by state->ea_addr, then increment state->ea_addr. * * [Parameters] * reg4: Register number * 4 (0-28: D0-D7, 32-60: A0-A7) */ DEFLABEL(LOAD_INC_W) READ16 $v0 sh $v0, 1($s0) 9: addiu $s6, $s6, 2 DEFSIZE(LOAD_INC_W) DEFPARAM(LOAD_INC_W, reg4, 9b, -4) DEFLABEL(LOAD_INC_L) READ32 $v0 sw $v0, 1($s0) 9: addiu $s6, $s6, 4 DEFSIZE(LOAD_INC_L) DEFPARAM(LOAD_INC_L, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * LOADA_INC_W: Load the specified address register from the location * indicated by state->ea_addr, sign-extending the 16-bit value to 32 bits, * then increment state->ea_addr. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(LOADA_INC_W) READ16 $v0 seh $v0, $v0 sh $v0, 1($s0) 9: addiu $s6, $s6, 2 DEFSIZE(LOADA_INC_W) DEFPARAM(LOADA_INC_W, reg4, 9b, -4) /*************************************************************************/ /** * MOVEM_WRITEBACK: Store the address in state->ea_addr to the specified * address register. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(MOVEM_WRITEBACK) sw $s6, 1($s0) 9: DEFSIZE(MOVEM_WRITEBACK) DEFPARAM(MOVEM_WRITEBACK, reg4, 9b, -4) /*************************************************************************/ /*********************** Miscellaneous operations ************************/ /*************************************************************************/ /** * CHK_W: Raise a CHK exception if op1 < 0 or op1 > op2, treating both * operands as signed 16-bit values. */ DEFLABEL(CHK_W) seh $s3, $s3 seh $t7, $t7 bltzl $s3, 0f ori $s5, $s5, SR_N slt $v1, $t7, $s3 beqz $v1, 1f nop ins $s5, $zero, SR_N_SHIFT, 1 0: li $a0, EX_CHK sw $a0, Q68State_exception($s0) TERMINATE 1: DEFSIZE(CHK_W) /*************************************************************************/ /** * LEA: Store the previously resolved effective address in the specified * address register. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(LEA) sw $s6, 1($s0) 9: DEFSIZE(LEA) DEFPARAM(LEA, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * PEA: Push the previously resolved effective address onto the stack. */ DEFLABEL(PEA) PUSH32 $s6 DEFSIZE(PEA) /*************************************************************************/ /** * TAS: Test the 8-bit value of op1, setting the condition codes * appropriately, then calculate op1 | 0x80. */ DEFLABEL(TAS) SETCC_NZ00_B $s3 ori $v0, $s3, 0x80 DEFSIZE(TAS) /*************************************************************************/ /** * MOVE_FROM_USP: Copy the user stack pointer to the specified register. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(MOVE_FROM_USP) lw $v0, USP LOAD_DELAY_NOP LOAD_DELAY_NOP sw $v0, 1($s0) 9: DEFSIZE(MOVE_FROM_USP) DEFPARAM(MOVE_FROM_USP, reg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * MOVE_TO_USP: Copy the specified register to the user stack pointer. * * [Parameters] * reg4: Register number * 4 (32-60: A0-A7) */ DEFLABEL(MOVE_TO_USP) lw $v0, 1($s0) 9: LOAD_DELAY_NOP LOAD_DELAY_NOP sw $v0, USP DEFSIZE(MOVE_TO_USP) DEFPARAM(MOVE_TO_USP, reg4, 9b, -4) /*************************************************************************/ /** * STOP: Halt the processor. * * [Parameters] * newSR: Value to load into SR */ DEFLABEL(STOP) li $v0, 1 sw $v0, Q68State_halted($s0) jal UPDATE_SR ori $v0, $zero, 0x1234 9: DEFSIZE(STOP) DEFPARAM(STOP, newSR, 9b, -4) /*************************************************************************/ /** * TRAPV: Raise a TRAPV exception if the overflow flag is set. */ DEFLABEL(TRAPV) ext $v1, $s5, SR_V_SHIFT, 1 beqz $v1, 0f li $a0, EX_TRAPV // In the delay slot, but that's okay sw $a0, Q68State_exception($s0) TERMINATE 0: DEFSIZE(TRAPV) /*************************************************************************/ /** * RTS: Pop the PC from the stack. */ DEFLABEL(RTS) POP32 $s4 ori $s2, $s2, 0xC000 // Indicate that this is an RTS/RTR termination TERMINATE DEFSIZE(RTS) /*-----------------------------------------------------------------------*/ /** * RTR: Pop the condition codes and PC from the stack. */ DEFLABEL(RTR) POP16 $v0 ins $s5, $v0, 0, 8 POP32 $s4 ori $s2, $s2, 0xC000 // Indicate that this is an RTS/RTR termination TERMINATE DEFSIZE(RTR) /*-----------------------------------------------------------------------*/ /** * RTE: Pop the status register and PC from the stack. */ DEFLABEL(RTE) POP16 $s3 // Borrow op1, since POP32 will destroy temporary registers POP32 $s4 jal UPDATE_SR move $v0, $s3 TERMINATE DEFSIZE(RTE) /*************************************************************************/ /** * MOVEP_READ_[WL]: Read a value from memory, skipping every other byte. * * [Parameters] * areg4: Register number * 4 of base address register (32-60 = A0-A7) * disp: Displacement from base address register * dreg4: Register number * 4 of data reg. to receive data (0-28 = D0-D7) */ DEFLABEL(MOVEP_READ_W) lw $s6, 1($s0) 7: LOAD_DELAY_NOP LOAD_DELAY_NOP addiu $s6, $s6, 1 8: READ8 $v0 // Byte 1 addiu $s6, $s6, 2 ins $s3, $v0, 8, 8 READ8 $v0 // Byte 0 ins $s3, $v0, 0, 8 sh $s3, 1($s0) 9: DEFSIZE(MOVEP_READ_W) DEFPARAM(MOVEP_READ_W, areg4, 7b, -4) DEFPARAM(MOVEP_READ_W, disp, 8b, -4) DEFPARAM(MOVEP_READ_W, dreg4, 9b, -4) DEFLABEL(MOVEP_READ_L) lw $s6, 1($s0) 7: LOAD_DELAY_NOP LOAD_DELAY_NOP addiu $s6, $s6, 1 8: READ8 $v0 // Byte 3 addiu $s6, $s6, 2 ins $s3, $v0, 24, 8 READ8 $v0 // Byte 2 addiu $s6, $s6, 2 ins $s3, $v0, 16, 8 READ8 $v0 // Byte 1 addiu $s6, $s6, 2 ins $s3, $v0, 8, 8 READ8 $v0 // Byte 0 ins $s3, $v0, 0, 8 sh $s3, 1($s0) 9: DEFSIZE(MOVEP_READ_L) DEFPARAM(MOVEP_READ_L, areg4, 7b, -4) DEFPARAM(MOVEP_READ_L, disp, 8b, -4) DEFPARAM(MOVEP_READ_L, dreg4, 9b, -4) /*-----------------------------------------------------------------------*/ /** * MOVEP_WRITE_[WL]: Write a value to memory, skipping every other byte. * * [Parameters] * areg4: Register number * 4 of base address register (32-60 = A0-A7) * disp: Displacement from base address register * dreg4: Register number * 4 of data reg. containing data (0-28 = D0-D7) */ DEFLABEL(MOVEP_WRITE_W) lw $s6, 1($s0) 7: lw $s3, 1($s0) 9: LOAD_DELAY_NOP addiu $s6, $s6, 1 8: ext $v0, $s3, 8, 8 WRITE8 // Byte 1 addiu $s6, $s6, 2 ext $v0, $s3, 0, 8 WRITE8 // Byte 0 DEFSIZE(MOVEP_WRITE_W) DEFPARAM(MOVEP_WRITE_W, areg4, 7b, -4) DEFPARAM(MOVEP_WRITE_W, disp, 8b, -4) DEFPARAM(MOVEP_WRITE_W, dreg4, 9b, -4) DEFLABEL(MOVEP_WRITE_L) lw $s6, 1($s0) 7: lw $s3, 1($s0) 9: LOAD_DELAY_NOP addiu $s6, $s6, 1 8: ext $v0, $s3, 24, 8 WRITE8 // Byte 3 addiu $s6, $s6, 2 ext $v0, $s3, 16, 8 WRITE8 // Byte 2 addiu $s6, $s6, 2 ext $v0, $s3, 8, 8 WRITE8 // Byte 1 addiu $s6, $s6, 2 ext $v0, $s3, 0, 8 WRITE8 // Byte 0 DEFSIZE(MOVEP_WRITE_L) DEFPARAM(MOVEP_WRITE_L, areg4, 7b, -4) DEFPARAM(MOVEP_WRITE_L, disp, 8b, -4) DEFPARAM(MOVEP_WRITE_L, dreg4, 9b, -4) /*************************************************************************/ /** * EXG: Exchange the values of two registers. * * [Parameters] * reg1_4: Register number * 4 of first register (0-60 = D0-A7) * reg2_4: Register number * 4 of second register (0-60 = D0-A7) */ DEFLABEL(EXG) addiu $a0, $s0, 1 8: lw $v0, 0($a0) addiu $a1, $s0, 1 9: lw $v1, 0($a1) sw $v0, 0($a1) LOAD_DELAY_NOP sw $v1, 0($a0) DEFSIZE(EXG) DEFPARAM(EXG, reg1_4, 8b, -1) DEFPARAM(EXG, reg2_4, 9b, -1) /*************************************************************************/ /*************************************************************************/ yabause-0.9.13.1/src/q68/q68-jit.c000644 001750 001750 00000375026 12256006177 020204 0ustar00guillaumeguillaume000000 000000 /* src/q68/q68-jit.c: Dynamic translation support for Q68 Copyright 2009-2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "q68.h" #include "q68-const.h" #include "q68-internal.h" #include "q68-jit.h" /*************************************************************************/ /* * Dynamic translation of 68000 instructions into native code is performed * as follows: * * 1) The emulation core (q68-core.c) calls q68_jit_find() on the current * PC to check whether there is a translated block starting at that * address. * * 2) If no translated block exists, the emulation core calls * q68_jit_translate() to translate a block beginning at the current * PC. q68_jit_translate() continues translating through the end of * the block of code (determined heuristically) and returns the * translated block to be passed to q68_jit_run(). * * 3) If a translated block exists or was just created, the emulation core * calls q68_jit_run() to begin execution of the translated code. * * 4) The translated code returns either when it reaches the end of the * block, or when the number of cycles executed exceeds the requested * cycle count. (For efficiency, cycle count checks are only performed * before branching instructions: BRA, BSR, Bcc, DBcc, JMP, JSR, RTS, * RTE, RTR, TRAP, STOP.) * * 5) If the translated code returns before the end of the block, the core * continues to call q68_jit_run() until the end of the block is * reached. * * 6) If a write is made to a region of memory containing one or more * translated blocks, the core deletes the translations by calling * q68_jit_clear_page() so the modified code can be retranslated. * Writes originating in the translated code itself (i.e. self-modifying * code) are handled by the native code calling q68_jit_clear_write() * when detecting a write to a page containing translations. * * The amount of translated code which can be stored at one time is * dependent upon two factors: the size of the hash table (set by the * Q68_JIT_TABLE_SIZE define in q68-const.h) and the maximum translated * data size (set by the Q68_JIT_DATA_LIMIT define). If either the hash * table becomes full or the data size limit is reached, any subsequent * translation will cause the oldest existing translation to be deleted, * where "oldest" is defined as "last executed the greatest number of * q68_jit_run() calls ago". Note that the data size limit is checked only * when beginning a translation, so the total data size at the end of a * translation may slightly exceed the specified limit. * * ======================================================================== * * Native code is generated through calls to JIT_EMIT_*() routines. * Typically, these routines will copy a pre-assembled code fragment to the * native code buffer, then patch that code fragment with values specific * to the instruction being translated (such as register numbers or branch * targets). The general sequence of operations emitted is as follows: * * JIT_EMIT_RESOLVE_* -- resolves an effective address to a 68000 * memory location * JIT_EMIT_GET_OP1_* -- loads the first operand for an instruction * JIT_EMIT_GET_OP2_* -- loads the second operand for an instruction * JIT_EMIT_TEST_* -- tests the state of condition codes * JIT_EMIT_(insn) -- executes the instruction itself * JIT_EMIT_SETCC_* -- updates the 68000 condition codes * JIT_EMIT_SET_* -- stores the result of the instruction * JIT_EMIT_ADD_CYCLES -- increments the count of clock cycles executed(*) * JIT_EMIT_ADVANCE_PC -- increments the program counter register(*) * * (*) In some cases, particularly when the instruction may cause execution * of the block to terminate, these operations will occur earlier in * the sequence. The code generator takes responsibility for ensuring * that such updates happen in the proper order. * * The code generator guarantees that the following invariants hold: * * - No operations which could result in a 68000 memory access will be * generated between loading of the second operand (JIT_EMIT_GET_OP2_*) * and execution of the instruction. * * - A JIT_EMIT_TEST_* operation will always be followed immediately by * the instruction which uses the operation (such as JIT_EMIT_Scc). * * - A JIT_EMIT_SETCC_* operation will always immediately follow the * operation which produced the result it is testing. * * The machine-dependent implementations must obey the following rules: * * - Each write to memory must be preceded by a check for translated code * at the target address (by checking state->jit_pages[]) and a call to * q68_jit_clear_write() if any translations are found. * (Implementations may violate this rule in circumstances considered * unlikely, at the risk of incorrect behavior if such circumstances * actually occur; for example, the PSP implementation does not check * for a longword write overlapping the end of a JIT page, and does not * check writes from the MOVEM instruction.) * * - Any instruction which modifies the program counter (Bcc, etc.) must * terminate execution of the native code block, _except for_ the * following instructions: * JMP, RTE, RTR, RTS * * - Any instruction which raises an exception must do so by storing the * appropriate exception number in state->exception and terminating * execution of the native code block. * * - It is the responsibility of the native code to check for a pending * unmasked interrupt when modifying the status register. * * The BSR/JSR and RTS/RTR instructions may make use of a call stack * provided by the JIT core to allow native code to quickly return to the * point at which a subroutine call took place. The BSR and JSR * implementations should, when they terminate execution, return a pointer * to the following native code (as for termination in CHECK_CYCLES), and * should set bit 15 of the cycle count returned from JIT_CALL(); * q68_jit_run() will detect this as a subroutine call, and save the native * code pointer returned before switching to the subroutine block. The RTS * and RTR implementations should set bits 15 and 14 of the cycle count * they return, which will cause q68_jit_run() to search the call stack * from top to bottom for an entry matching the new 68000 PC; if one is * found, the corresponding code will be immediately executed, bypassing * the ordinary block search and execution process (steps 1 through 3 * above). * * See the q68-jit-*.[hS] files for implementation details. * * ======================================================================== * * The JIT code generator includes a primitive optimization step (if the * Q68_JIT_OPTIMIZE_FLAGS preprocessor symbol is defined) which omits the * generation of code to set the 68000 condition flags (X, N, Z, V, and C) * when unnecessary for correct execution. Specifically, the translator * checks both the current and the following instruction to determine if * there are any condition flags which are: * - set by the current instruction, AND * - used as input by the following instruction OR * - NOT set by the following instruction * If there are no such flags, then it is impossible for the flag values * set by the current instruction to have any effect on program flow, so * the native code to set the condition codes can be safely omitted. * * The above logic is contained in the cc_needed() routine (and its helper * routine cc_info()). For each instruction that can set the condition * flags, the translation routine first calls cc_needed() to determine * whether the condition flags need to be set or not. If cc_needed() * returns zero, then there are no flags whose output is required, and the * relevant JIT_EMIT_SETCC_* operation will be skipped. * * In the interests of speed and code clarity, the helper routine cc_info() * does not check the validity of the opcode passed to it; as a result, it * may return invalid flag information for some invalid opcodes. If such * an invalid opcode actually occurs in the instruction stream, the CCR * register may therefore contain an incorrect value when control is * transferred to the illegal instruction exception handler. In situations * where this can cause undesired behavior, this optimization should be * disabled. * * When Q68_JIT_OPTIMIZE_FLAGS is not defined, the definitions of * cc_needed() and cc_info() are omitted, and cc_needed() is instead * defined at the preprocessor level to return 1; this has the effect of * always emitting code to set the condition flags. */ /*************************************************************************/ /* For the PSP, we need to avoid local data here sharing a cache line with * data in other files due to the lack of SC/ME cache coherency */ #ifdef PSP static __attribute__((aligned(64),used)) int dummy_top; #endif /*----------------------------------*/ /* Entry into which translated code is currently being stored (set by * q68_jit_translate(), used by opcode translation functions) */ static Q68JitEntry *current_entry; /* Address from which data is being read */ static uint32_t jit_PC; /* Flag indicating whether the PC was updated by an instruction (e.g. jumps) */ static int PC_updated; /* Branch target lookup table (indicates where in the native code each * address is located) */ static struct { uint32_t m68k_address; // Address of 68000 instruction uint32_t native_offset; // Byte offset into current_entry->native_code } btcache[Q68_JIT_BTCACHE_SIZE]; static unsigned int btcache_index; // Where to store the next instruction /* Unresolved branch list (saves locations and targets of forward branches) */ static struct { uint32_t m68k_target; // Branch target (68000 address) uint32_t native_offset; // Offset of native branch instruction to update } unres_branches[Q68_JIT_UNRES_BRANCH_SIZE]; /*----------------------------------*/ #ifdef PSP // As above static __attribute__((aligned(64),used)) int dummy_bottom; #endif /*-----------------------------------------------------------------------*/ /* Redefine IFETCH to reference jit_PC */ static inline uint32_t jit_IFETCH(Q68State *state) { uint32_t data = READU16(state, jit_PC); jit_PC += 2; return data; } #define IFETCH jit_IFETCH /*************************************************************************/ /* * Forward declarations for helper functions and instruction implementations. * These are set up identically to q68-core.c so that bugfixes or other * changes to one can be easily ported to the other. * * Note that the return value of OpcodeFunc is taken to be the end-of-block * flag as returned from q68_jit_translate(), not the number of clock cycles * taken by the instruction. */ static int translate_insn(Q68State *state, Q68JitEntry *entry); static void clear_entry(Q68State *state, Q68JitEntry *entry); static void clear_oldest_entry(Q68State *state); static int expand_buffer(Q68JitEntry *entry); static int32_t btcache_lookup(uint32_t address); static void record_unresolved_branch(uint32_t m68k_target, uint32_t native_offset); static inline void JIT_EMIT_TEST_cc(int cond, Q68JitEntry *entry); static void advance_PC(Q68State *state); static int raise_exception(Q68State *state, uint8_t num); static inline int op_ill(Q68State *state, uint32_t opcode); #ifdef Q68_JIT_OPTIMIZE_FLAGS static unsigned int cc_needed(Q68State *state, uint16_t opcode); # ifdef __GNUC__ __attribute__((const)) # endif static unsigned int cc_info(uint16_t opcode); #else # define cc_needed(state,opcode) 1 #endif static int ea_resolve(Q68State *state, uint32_t opcode, int size, int access_type); static void ea_get(Q68State *state, uint32_t opcode, int size, int is_rmw, int *cycles_ret, int op_num); static void ea_set(Q68State *state, uint32_t opcode, int size); static int op_imm(Q68State *state, uint32_t opcode); static int op_bit(Q68State *state, uint32_t opcode); static int opMOVE(Q68State *state, uint32_t opcode); static int op4xxx(Q68State *state, uint32_t opcode); static int op_CHK(Q68State *state, uint32_t opcode); static int op_LEA(Q68State *state, uint32_t opcode); static int opADSQ(Q68State *state, uint32_t opcode); static int op_Scc(Q68State *state, uint32_t opcode); static int opDBcc(Q68State *state, uint32_t opcode); static int op_Bcc(Q68State *state, uint32_t opcode); static int opMOVQ(Q68State *state, uint32_t opcode); static int op_alu(Q68State *state, uint32_t opcode); static int op_DIV(Q68State *state, uint32_t opcode); static int opAxxx(Q68State *state, uint32_t opcode); static int op_MUL(Q68State *state, uint32_t opcode); static int opshft(Q68State *state, uint32_t opcode); static int opFxxx(Q68State *state, uint32_t opcode); static int op4alu(Q68State *state, uint32_t opcode); static int opMVSR(Q68State *state, uint32_t opcode); static int opNBCD(Q68State *state, uint32_t opcode); static int op_PEA(Q68State *state, uint32_t opcode); static int opSWAP(Q68State *state, uint32_t opcode); static int op_TAS(Q68State *state, uint32_t opcode); static int op_EXT(Q68State *state, uint32_t opcode); static int op_STM(Q68State *state, uint32_t opcode); static int op_LDM(Q68State *state, uint32_t opcode); static int opmisc(Q68State *state, uint32_t opcode); static int opTRAP(Q68State *state, uint32_t opcode); static int opLINK(Q68State *state, uint32_t opcode); static int opUNLK(Q68State *state, uint32_t opcode); static int opMUSP(Q68State *state, uint32_t opcode); static int op4E7x(Q68State *state, uint32_t opcode); static int opjump(Q68State *state, uint32_t opcode); static int opMOVP(Q68State *state, uint32_t opcode); static int opADSX(Q68State *state, uint32_t opcode); static int op_BCD(Q68State *state, uint32_t opcode); static int opCMPM(Q68State *state, uint32_t opcode); static int op_EXG(Q68State *state, uint32_t opcode); /*-----------------------------------------------------------------------*/ /* Main table of instruction implemenation functions; table index is bits * 15-12 and 8-6 of the opcode (ABCD ...E FG.. .... -> 0ABC DEFG). */ static OpcodeFunc * const opcode_table[128] = { op_imm, op_imm, op_imm, op_imm, op_bit, op_bit, op_bit, op_bit, // 00 opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 10 opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 20 opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, opMOVE, // 30 op4xxx, op4xxx, op4xxx, op4xxx, op_ill, op_ill, op_CHK, op_LEA, // 40 opADSQ, opADSQ, opADSQ, op_Scc, opADSQ, opADSQ, opADSQ, op_Scc, // 50 op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, op_Bcc, // 60 opMOVQ, opMOVQ, opMOVQ, opMOVQ, op_ill, op_ill, op_ill, op_ill, // 70 op_alu, op_alu, op_alu, op_DIV, op_alu, op_alu, op_alu, op_DIV, // 80 op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // 90 opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, opAxxx, // A0 op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // B0 op_alu, op_alu, op_alu, op_MUL, op_alu, op_alu, op_alu, op_MUL, // C0 op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, op_alu, // D0 opshft, opshft, opshft, opshft, opshft, opshft, opshft, opshft, // E0 opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, opFxxx, // F0 }; /* Subtable for instructions in the $4xxx (miscellaneous) group; table index * is bits 11-9 and 7-6 of the opcode (1000 ABC0 DE.. .... -> 000A BCDE). */ static OpcodeFunc * const opcode_4xxx_table[32] = { op4alu, op4alu, op4alu, opMVSR, // 40xx op4alu, op4alu, op4alu, op_ill, // 42xx op4alu, op4alu, op4alu, opMVSR, // 44xx op4alu, op4alu, op4alu, opMVSR, // 46xx opNBCD, op_PEA, op_STM, op_STM, // 48xx op4alu, op4alu, op4alu, op_TAS, // 4Axx op_ill, op_ill, op_LDM, op_LDM, // 4Cxx op_ill, opmisc, opjump, opjump, // 4Exx }; /* Sub-subtable for instructions in the $4E40-$4E7F range, used by opmisc(); * index is bits 5-3 of the opcode. */ static OpcodeFunc * const opcode_4E4x_table[8] = { opTRAP, opTRAP, opLINK, opUNLK, opMUSP, opMUSP, op4E7x, op_ill, }; /*************************************************************************/ /* Include the header appropriate to the platform (make sure to do this * after the local function declarations) */ #if defined(CPU_X86) || defined(CPU_X64) # include "q68-jit-x86.h" #elif defined(CPU_PSP) # include "q68-jit-psp.h" #else # error Dynamic translation is not supported on this platform #endif /*************************************************************************/ /********************** External interface routines **********************/ /*************************************************************************/ /** * q68_jit_init: Allocate memory for JIT data. Must be called before any * other JIT function. * * [Parameters] * state: Processor state block * [Return value] * Nonzero on success, zero on error */ int q68_jit_init(Q68State *state) { state->jit_table = state->malloc_func(sizeof(*state->jit_table) * Q68_JIT_TABLE_SIZE); if (!state->jit_table) { DMSG("No memory for JIT table"); goto error_return; } state->jit_hashchain = state->malloc_func(sizeof(*state->jit_hashchain) * Q68_JIT_TABLE_SIZE); if (!state->jit_hashchain) { DMSG("No memory for JIT hash chain table"); goto error_free_jit_table; } /* Make sure all entries are marked as unused (so we don't try to free * invalid pointers in q68_jit_reset()) */ int i; for (i = 0; i < Q68_JIT_TABLE_SIZE; i++) { state->jit_table[i].m68k_start = 0; } /* Make sure page table is clear (so writes before processor reset * don't trigger JIT clearing */ memset(state->jit_pages, 0, sizeof(state->jit_pages)); /* Default to no cache flush function */ state->jit_flush = NULL; #ifdef Q68_DISABLE_ADDRESS_ERROR /* Hack to avoid compiler warnings about unused functions */ if (0) { JIT_EMIT_CHECK_ALIGNED_EA(&state->jit_table[0], 0, 0); JIT_EMIT_CHECK_ALIGNED_SP(&state->jit_table[0], 0, 0); } #endif return 1; error_free_jit_table: state->free_func(state->jit_table); state->jit_table = NULL; error_return: return 0; } /*-----------------------------------------------------------------------*/ /** * q68_jit_reset: Reset the dynamic translation state, clearing out all * previously stored data. * * [Parameters] * state: Processor state block * [Return value] * None */ void q68_jit_reset(Q68State *state) { int index; state->jit_abort = 0; for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { if (state->jit_table[index].m68k_start) { clear_entry(state, &state->jit_table[index]); } } for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { state->jit_hashchain[index] = NULL; } state->jit_total_data = 0; state->jit_timestamp = 0; for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { state->jit_blacklist[index].m68k_start = 0; state->jit_blacklist[index].m68k_end = 0; } state->jit_in_blist = 0; state->jit_blist_num = 0; state->jit_callstack_top = 0; memset(state->jit_pages, 0, sizeof(state->jit_pages)); } /*-----------------------------------------------------------------------*/ /** * q68_jit_cleanup: Destroy all JIT-related data. * * [Parameters] * state: Processor state block * [Return value] * None */ void q68_jit_cleanup(Q68State *state) { q68_jit_reset(state); state->free_func(state->jit_hashchain); state->jit_hashchain = NULL; state->free_func(state->jit_table); state->jit_table = NULL; } /*************************************************************************/ /** * q68_jit_translate: Dynamically translate a block of instructions * starting at the given address. If a translation already exists for the * given address, it is cleared. * * [Parameters] * state: Processor state block * address: Start address in 68000 address space * [Return value] * Translated block to be passed to q68_jit_run(), or NULL on error */ Q68JitEntry *q68_jit_translate(Q68State *state, uint32_t address) { int index; if (address == 0) { /* We use address 0 to indicate an unused entry, so we can't * translate from address 0. But this should never happen except * in pathological cases (or unhandled exceptions), so just punt * and let the interpreter handle it */ return NULL; } if (address & 1) { /* Odd addresses are invalid, so we can't translate them in the * first place */ return NULL; } address &= 0xFFFFFF; /* Check whether we're trying to translate a blacklisted address. */ if (state->jit_in_blist) { index = state->jit_blist_num; /* See whether we've exited the blacklisted block */ if (address >= state->jit_blacklist[index].m68k_start && address <= state->jit_blacklist[index].m68k_end ) { return NULL; } state->jit_in_blist = 0; } // We might have entered another blacklisted block, so no "else" here if (!state->jit_in_blist) { /* See if we're in a blacklisted block, and skip its translation * if so */ for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { if (address >= state->jit_blacklist[index].m68k_start && address <= state->jit_blacklist[index].m68k_end ) { uint32_t dt = state->jit_timestamp - state->jit_blacklist[index].timestamp; if (dt < Q68_JIT_BLACKLIST_TIMEOUT) { state->jit_in_blist = 1; state->jit_blist_num = index; return NULL; } else { /* Entry expired, so clear it */ state->jit_blacklist[index].m68k_start = 0; state->jit_blacklist[index].m68k_end = 0; } } } } /* Clear out any existing translation, then search for an empty slot in * the hash table. If we've reached the data size limit, first evict * old entries until we're back under the limit. */ q68_jit_clear(state, address); while (state->jit_total_data >= Q68_JIT_DATA_LIMIT) { clear_oldest_entry(state); } const int hashval = JIT_HASH(address); index = hashval; int oldest = index; while (state->jit_table[index].m68k_start != 0) { if (TIMESTAMP_COMPARE(state->jit_timestamp, state->jit_table[index].timestamp, state->jit_table[oldest].timestamp) < 0) { oldest = index; } index++; /* Using an if here is faster than taking the remainder with % */ if (UNLIKELY(index >= Q68_JIT_TABLE_SIZE)) { index = 0; } if (UNLIKELY(index == hashval)) { /* Out of entries, so clear the oldest one and use it */ #ifdef Q68_JIT_VERBOSE DMSG("No free slots for code at $%06X, clearing oldest ($%06X)", (int)address, (int)state->jit_table[oldest].m68k_start); #endif clear_entry(state, &state->jit_table[oldest]); index = oldest; } } current_entry = &state->jit_table[index]; /* Initialize the new entry */ current_entry->native_code = state->malloc_func(Q68_JIT_BLOCK_EXPAND_SIZE); if (!current_entry->native_code) { DMSG("No memory for code at $%06X", address); current_entry = NULL; return NULL; } current_entry->next = state->jit_hashchain[hashval]; if (state->jit_hashchain[hashval]) { state->jit_hashchain[hashval]->prev = current_entry; } state->jit_hashchain[hashval] = current_entry; current_entry->prev = NULL; current_entry->state = state; current_entry->m68k_start = address; current_entry->native_size = Q68_JIT_BLOCK_EXPAND_SIZE; current_entry->native_length = 0; current_entry->exec_address = NULL; current_entry->timestamp = state->jit_timestamp; current_entry->must_clear = 0; JIT_EMIT_PROLOGUE(current_entry); /* Clear out the branch target cache and unresolved branch list */ for (index = 0; index < lenof(btcache); index++) { btcache[index].m68k_address = 0; } btcache_index = 0; for (index = 0; index < lenof(unres_branches); index++) { unres_branches[index].m68k_target = 0; } /* Translate a block of 68000 code */ jit_PC = address; const uint32_t limit = address + Q68_JIT_MAX_BLOCK_SIZE; int done = 0; while (!done && jit_PC < limit) { /* Make sure we haven't entered a blacklisted block */ for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { if (UNLIKELY(address >= state->jit_blacklist[index].m68k_start && address <= state->jit_blacklist[index].m68k_end) ) { const uint32_t age = state->jit_timestamp - state->jit_blacklist[index].timestamp; if (age < Q68_JIT_BLACKLIST_TIMEOUT) { done = 1; break; } else { /* Entry expired, so clear it */ state->jit_blacklist[index].m68k_start = 0; state->jit_blacklist[index].m68k_end = 0; } } } if (LIKELY(!done)) { done = translate_insn(state, current_entry); } } /* Close out the translated block */ JIT_EMIT_EPILOGUE(current_entry); current_entry->m68k_end = jit_PC - 1; for (index = current_entry->m68k_start >> Q68_JIT_PAGE_BITS; index <= current_entry->m68k_end >> Q68_JIT_PAGE_BITS; index++ ) { JIT_PAGE_SET(state, index); } void *newptr = state->realloc_func(current_entry->native_code, current_entry->native_length); if (newptr) { current_entry->native_code = newptr; current_entry->native_size = current_entry->native_length; } state->jit_total_data += current_entry->native_size; /* Prepare the block for execution so it can be immediately passed to * q68_jit_run() (see q68_jit_find() for why we do it here) */ current_entry->exec_address = current_entry->native_code; Q68JitEntry *retval = current_entry; current_entry = NULL; if (state->jit_flush) { state->jit_flush(); } return retval; } /*************************************************************************/ /** * q68_jit_find: Find the translated block for a given address, if any. * * [Parameters] * state: Processor state block * address: Start address in 68000 address space * [Return value] * Translated block to be passed to q68_jit_run(), or NULL if no such * block exists */ Q68JitEntry *q68_jit_find(Q68State *state, uint32_t address) { const int hashval = JIT_HASH(address); Q68JitEntry *entry = state->jit_hashchain[hashval]; while (entry) { if (entry->m68k_start == address) { /* Prepare the block for execution. We set exec_address here * both to avoid overhead in q68_jit_run(), and because * exec_address could be non-NULL if (for example) the native * code stopped due to reaching the cycle limit and the core * then serviced an interrupt. */ entry->exec_address = entry->native_code; return entry; } entry = entry->next; } return NULL; } /*-----------------------------------------------------------------------*/ /** * q68_jit_run: Run translated 68000 code. * * [Parameters] * state: Processor state block * cycle_limit: Clock cycle limit on execution (code will stop when * state->cycles >= cycles) * address_ptr: Pointer to translated block to execute; will be cleared * to NULL on return if the end of the block was reached * [Return value] * None */ void q68_jit_run(Q68State *state, uint32_t cycle_limit, Q68JitEntry **entry_ptr) { Q68JitEntry *entry = *entry_ptr; again: entry->timestamp = state->jit_timestamp; state->jit_timestamp++; entry->running = 1; int cycles = JIT_CALL(state, cycle_limit - state->cycles, &entry->exec_address); entry->running = 0; state->jit_abort = 0; state->cycles += cycles & 0x3FFF; if (UNLIKELY(entry->must_clear)) { clear_entry(state, entry); entry = NULL; } else if (cycles & 0x8000) { // BSR/JSR/RTS/RTR if (cycles & 0x4000) { // RTS/RTR entry = NULL; unsigned int top = state->jit_callstack_top; unsigned int i; for (i = Q68_JIT_CALLSTACK_SIZE; i > 0; i--) { top = (top + Q68_JIT_CALLSTACK_SIZE-1) % Q68_JIT_CALLSTACK_SIZE; if (state->jit_callstack[top].return_PC == state->PC) { entry = state->jit_callstack[top].return_entry; entry->exec_address = state->jit_callstack[top].return_native; state->jit_callstack_top = top; if (state->cycles < cycle_limit) { goto again; } else { break; } } } } else { // BSR/JSR const unsigned int top = state->jit_callstack_top; const uint32_t return_PC = READU32(state, state->A[7]); state->jit_callstack[top].return_PC = return_PC; state->jit_callstack[top].return_entry = entry; state->jit_callstack[top].return_native = entry->exec_address; state->jit_callstack_top = (top+1) % Q68_JIT_CALLSTACK_SIZE; entry = NULL; } } else if (!entry->exec_address) { entry = NULL; } /* If we finished a block, we still have cycles to go, there's no * exception pending, and there's already a translated block at the * next PC, jump right to it so we don't incur the extra overhead of * returning to the caller */ if (!entry && state->cycles < cycle_limit && !state->exception) { entry = q68_jit_find(state, state->PC); if (entry) { goto again; } } *entry_ptr = entry; } /*-----------------------------------------------------------------------*/ /** * q68_jit_clear: Clear any translation beginning at the given address. * * [Parameters] * state: Processor state block * address: Start address in 68000 address space * [Return value] * None */ void q68_jit_clear(Q68State *state, uint32_t address) { const int hashval = JIT_HASH(address); Q68JitEntry *entry = state->jit_hashchain[hashval]; while (entry) { if (entry->m68k_start == address) { clear_entry(state, entry); return; } entry = entry->next; } } /*-----------------------------------------------------------------------*/ /** * q68_jit_clear_page: Clear any translation which occurs in the JIT page * containing the given address. Intended for use on writes from external * sources. * * [Parameters] * state: Processor state block * address: Address to which data was written * [Return value] * None */ void q68_jit_clear_page(Q68State *state, uint32_t address) { const uint32_t page = address >> Q68_JIT_PAGE_BITS; #ifdef Q68_JIT_VERBOSE DMSG("WARNING: jit_clear_page($%06X)", page << Q68_JIT_PAGE_BITS); #endif int index; for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { if (state->jit_table[index].m68k_start != 0 && state->jit_table[index].m68k_start >> Q68_JIT_PAGE_BITS <= page && state->jit_table[index].m68k_end >> Q68_JIT_PAGE_BITS >= page ) { if (UNLIKELY(state->jit_table[index].running)) { state->jit_table[index].must_clear = 1; state->jit_abort = 1; } else { clear_entry(state, &state->jit_table[index]); } } } JIT_PAGE_CLEAR(state, page); } /*-----------------------------------------------------------------------*/ /** * q68_jit_clear_write: Clear any translation which includes the given * address, and (if there is at least one such translation) blacklist the * address from being translated. * * [Parameters] * state: Processor state block * address: Address to which data was written * size: Size of data written * [Return value] * None */ void q68_jit_clear_write(Q68State *state, uint32_t address, uint32_t size) { int index; /* If the address is in a blacklisted block, we don't need to do * anything (but update the timestamp to extend its timeout) */ for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { if (address >= state->jit_blacklist[index].m68k_start && address <= state->jit_blacklist[index].m68k_end ) { state->jit_blacklist[index].timestamp = state->jit_timestamp; return; } } /* Clear the translations-exist flag now; we'll set it later if we * find a translation on the page that we don't clear */ const uint32_t page = address >> Q68_JIT_PAGE_BITS; const uint32_t page_start = page << Q68_JIT_PAGE_BITS; const uint32_t page_end = ((page+1) << Q68_JIT_PAGE_BITS) - 1; JIT_PAGE_CLEAR(state, page); /* Clear any translations affected by the address, and determine * the range to be blacklisted. We default to assuming the address is * the last byte/word of the longest possible instruction (10 bytes: * MOVE.L #$12345678, ($12345678).l), but do not extend backwards past * the beginning of a block. */ int found = 0; uint32_t start = address + size, end = address + (size-1); #ifdef Q68_JIT_VERBOSE DMSG("WARNING: jit_clear_write($%06X,%d)", (int)address, (int)size); #endif for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { if (state->jit_table[index].m68k_start == 0) { continue; } if (state->jit_table[index].m68k_start < address + size && state->jit_table[index].m68k_end >= address) { found = 1; if (start > state->jit_table[index].m68k_start) { /* Use the earliest start address of those we see */ start = state->jit_table[index].m68k_start; } if (UNLIKELY(state->jit_table[index].running)) { state->jit_table[index].must_clear = 1; state->jit_abort = 1; } else { clear_entry(state, &state->jit_table[index]); } } else if (state->jit_table[index].m68k_start <= page_start && state->jit_table[index].m68k_end >= page_end) { /* No need to clear this one, so set the page bit again */ JIT_PAGE_SET(state, page); } } if (!found || start < (address & ~1) - 8) { start = (address & ~1) - 8; } /* Add the blacklist entry */ #ifdef Q68_JIT_VERBOSE DMSG("Blacklisting $%06X...$%06X", (int)start, (int)end); #endif /* First see if this overlaps with another entry */ found = 0; for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { if (state->jit_blacklist[index].m68k_start <= end && state->jit_blacklist[index].m68k_end >= start) { #ifdef Q68_JIT_VERBOSE DMSG("(Merging with $%06X...%06X)", (int)state->jit_blacklist[index].m68k_start, (int)state->jit_blacklist[index].m68k_end); #endif if (start > state->jit_blacklist[index].m68k_start) { start = state->jit_blacklist[index].m68k_start; } if (end < state->jit_blacklist[index].m68k_end) { end = state->jit_blacklist[index].m68k_end; } found = 1; break; } } /* Otherwise, add this as a new entry; if there are no free slots, * evict the oldest entry */ if (!found) { int oldest = 0; for (index = 0; index < Q68_JIT_BLACKLIST_SIZE; index++) { if (state->jit_blacklist[index].m68k_start == 0) { found = 1; break; } else if (TIMESTAMP_COMPARE(state->jit_timestamp, state->jit_blacklist[index].timestamp, state->jit_blacklist[oldest].timestamp) < 0) { oldest = index; } } if (!found) { index = oldest; } } state->jit_blacklist[index].m68k_start = start; state->jit_blacklist[index].m68k_end = end; state->jit_blacklist[index].timestamp = state->jit_timestamp; } /*************************************************************************/ /************************ Local helper functions *************************/ /*************************************************************************/ /** * translate_insn: Translate a single 68000 instruction. * * [Parameters] * state: Processor state block * entry: Q68JitEntry structure pointer * [Return value] * Nonzero if the instruction marks the end of the block, else zero */ static int translate_insn(Q68State *state, Q68JitEntry *entry) { /* See if there are any branches to this address we can update */ int i; for (i = 0; i < lenof(unres_branches); i++) { if (unres_branches[i].m68k_target == jit_PC) { JIT_FIXUP_BRANCH(entry, unres_branches[i].native_offset, current_entry->native_length); unres_branches[i].m68k_target = 0; } } /* Update the branch target cache with this address */ btcache[btcache_index].m68k_address = jit_PC; btcache[btcache_index].native_offset = current_entry->native_length; btcache_index = (btcache_index + 1) % lenof(btcache); /* Fetch the next instruction */ const unsigned int opcode = IFETCH(state); state->current_PC = jit_PC; /* Emit a cycle count check if appropriate */ #ifdef Q68_JIT_LOOSE_TIMING if ((opcode & 0xFF00) == 0x6100 // Bcc || (opcode & 0xF0F8) == 0x50C8 // DBcc || (opcode & 0xFFF0) == 0x4E40 // TRAP || (opcode & 0xFF80) == 0x4E80 // JSR/JMP || opcode == 0x4E72 // STOP || opcode == 0x4E73 // RTE || opcode == 0x4E75 // RTS || opcode == 0x4E77 // RTR # ifdef Q68_M68K_TESTER // Define when linking with m68k-tester || opcode == 0x7100 // m68k-tester abort opcode # endif ) { #endif JIT_EMIT_CHECK_CYCLES(current_entry); #ifdef Q68_JIT_LOOSE_TIMING } #endif /* Add a trace call if we're tracing */ #ifdef Q68_TRACE JIT_EMIT_TRACE(current_entry); #endif /* Translate the instruction itself and update the 68000 PC */ PC_updated = 0; const unsigned int index = (opcode>>9 & 0x78) | (opcode>>6 & 0x07); int done = (*opcode_table[index])(state, opcode); /* Only update the PC if the function didn't do so itself (see e.g. * op_imm() to SR), but check the jit_abort flag unless we're * terminating anyway */ if (!done) { if (!PC_updated) { const int32_t advance = jit_PC - (state->current_PC - 2); JIT_EMIT_ADVANCE_PC_CHECK_ABORT(current_entry, advance); } else { JIT_EMIT_CHECK_ABORT(current_entry); } } else { if (!PC_updated) { advance_PC(state); } } return done; } /*************************************************************************/ /** * clear_entry: Clear a specific entry from the JIT table, freeing the * native code buffer and unlinking the entry from its references. * * [Parameters] * state: Processor state block * entry: Q68JitEntry structure pointer * [Return value] * None */ static void clear_entry(Q68State *state, Q68JitEntry *entry) { /* Clear the entry out of the call stack first */ int i; for (i = 0; i < Q68_JIT_CALLSTACK_SIZE; i++) { if (state->jit_callstack[i].return_entry == entry) { state->jit_callstack[i].return_PC = 0; } } /* Free the native code */ state->jit_total_data -= entry->native_size; state->free_func(entry->native_code); entry->native_code = NULL; /* Clear the entry from the table and hash chain */ if (entry->next) { entry->next->prev = entry->prev; } if (entry->prev) { entry->prev->next = entry->next; } else { state->jit_hashchain[JIT_HASH(entry->m68k_start)] = entry->next; } /* Mark the entry as free */ entry->m68k_start = 0; } /*-----------------------------------------------------------------------*/ /** * clear_oldest_entry: Clear the oldest entry from the JIT table. * * [Parameters] * state: Processor state block * [Return value] * None */ static void clear_oldest_entry(Q68State *state) { int oldest = -1; int index; for (index = 0; index < Q68_JIT_TABLE_SIZE; index++) { if (state->jit_table[index].m68k_start == 0) { continue; } if (oldest < 0 || TIMESTAMP_COMPARE(state->jit_timestamp, state->jit_table[index].timestamp, state->jit_table[oldest].timestamp) < 0) { oldest = index; } } if (LIKELY(oldest >= 0)) { clear_entry(state, &state->jit_table[oldest]); } else { DMSG("Tried to clear oldest entry from an empty table!"); /* Set the total size to zero, just in case something weird happened */ state->jit_total_data = 0; } } /*************************************************************************/ /** * expand_buffer: Expands the native code buffer in the given JIT entry by * Q68_JIT_BLOCK_EXPAND_SIZE. * * [Parameters] * entry: Q68JitEntry structure pointer * [Return value] * Nonzero on success, zero on failure (out of memory) */ static int expand_buffer(Q68JitEntry *entry) { const uint32_t newsize = entry->native_size + Q68_JIT_BLOCK_EXPAND_SIZE; void *newptr = entry->state->realloc_func(entry->native_code, newsize); if (!newptr) { DMSG("Out of memory"); return 0; } entry->native_code = newptr; entry->native_size = newsize; return 1; } /*************************************************************************/ /** * btcache_lookup: Search the branch target cache for the given 68000 * address. * * [Parameters] * address: 68000 address to search for * [Return value] * Corresponding byte offset into current_entry->native_code, or -1 if * the address could not be found */ static int32_t btcache_lookup(uint32_t address) { /* Search backwards from the current instruction so we can handle short * loops quickly; note that btcache_index is now pointing to where the * _next_ instruction will go */ const int current = (btcache_index + (lenof(btcache)-1)) % lenof(btcache); int index = current; do { if (btcache[index].m68k_address == address) { return btcache[index].native_offset; } index = (index + (lenof(btcache)-1)) % lenof(btcache); } while (index != current); return -1; } /*-----------------------------------------------------------------------*/ /** * record_unresolved_branch: Record the given branch target and native * offset in an empty slot in the unresolved branch table. If there are * no empty slots, purge the oldest (lowest native offset) entry. * * [Parameters] * m68k_target: Branch target address in 68000 address space * native_offset: Offset of branch to update in native code * [Return value] * None */ static void record_unresolved_branch(uint32_t m68k_target, uint32_t native_offset) { int oldest = 0; int i; for (i = 0; i < lenof(unres_branches); i++) { if (unres_branches[i].m68k_target == 0) { oldest = i; break; } else if (unres_branches[i].native_offset < unres_branches[oldest].native_offset) { oldest = i; } } unres_branches[oldest].m68k_target = m68k_target; unres_branches[oldest].native_offset = native_offset; } /*************************************************************************/ /** * JIT_EMIT_TEST_cc: Emit the appropriate TEST_* operation depending on * the specified condition. * * [Parameters] * cond: Condition code * entry: Q68JitEntry structure pointer */ static inline void JIT_EMIT_TEST_cc(int cond, Q68JitEntry *entry) { switch ((cond)) { case COND_T: JIT_EMIT_TEST_T (entry); break; case COND_F: JIT_EMIT_TEST_F (entry); break; case COND_HI: JIT_EMIT_TEST_HI(entry); break; case COND_LS: JIT_EMIT_TEST_LS(entry); break; case COND_CC: JIT_EMIT_TEST_CC(entry); break; case COND_CS: JIT_EMIT_TEST_CS(entry); break; case COND_NE: JIT_EMIT_TEST_NE(entry); break; case COND_EQ: JIT_EMIT_TEST_EQ(entry); break; case COND_VC: JIT_EMIT_TEST_VC(entry); break; case COND_VS: JIT_EMIT_TEST_VS(entry); break; case COND_PL: JIT_EMIT_TEST_PL(entry); break; case COND_MI: JIT_EMIT_TEST_MI(entry); break; case COND_GE: JIT_EMIT_TEST_GE(entry); break; case COND_LT: JIT_EMIT_TEST_LT(entry); break; case COND_GT: JIT_EMIT_TEST_GT(entry); break; case COND_LE: JIT_EMIT_TEST_LE(entry); break; } } /*************************************************************************/ /** * advance_PC: Emit JIT code to advance the PC to the location indicated * by jit_PC, and set the PC_updated flag so that the PC is not advanced * again after the current instruction has been processed. * * [Parameters] * state: Processor state block * [Return value] * None */ static void advance_PC(Q68State *state) { JIT_EMIT_ADVANCE_PC(current_entry, jit_PC - (state->current_PC - 2)); PC_updated = 1; } /*************************************************************************/ /** * raise_exception: Emit JIT code to raise an exception. * * [Parameters] * state: Processor state block * num: Exception number * [Return value] * Nonzero (end translated block) */ static int raise_exception(Q68State *state, uint8_t num) { JIT_EMIT_EXCEPTION(current_entry, num); return 1; } /*************************************************************************/ /** * op_ill: Emit JIT code to handle a generic illegal opcode. * * [Parameters] * state: Processor state block * opcode: Instruction opcode * [Return value] * Nonzero (end translated block) */ static inline int op_ill(Q68State *state, uint32_t opcode) { return raise_exception(state, EX_ILLEGAL_INSTRUCTION); } /*************************************************************************/ /*************************************************************************/ #ifdef Q68_JIT_OPTIMIZE_FLAGS /** * cc_needed: Return whether any condition code outputs are required from * the current instruction, based on the instruction located at jit_PC. * * This routine assumes that the current and following instructions are * valid ones; certain illegal forms of instructions may be incorrectly * treated as valid and thus cause an incorrect result. * * [Parameters] * state: Processor state block * opcode: Opcode of the instruction currently being processed * [Return value] * Nonzero if condition code outputs are required, zero otherwise */ static unsigned int cc_needed(Q68State *state, uint16_t opcode) { const uint16_t next_opcode = READU16(state, jit_PC); const unsigned int this_output = cc_info(opcode) & 0x1F; const unsigned int next_input = (cc_info(next_opcode) >> 8) & 0x1F; const unsigned int next_output = cc_info(next_opcode) & 0x1F; /* A condition code output from this instruction is known to be * unneeded if the following instruction (1) does not use that * condition code as input and (2) also outputs to the same condition * code. We want to know whether there are any condition codes in the * current instruction's output set for which these conditions are * _not_ fulfilled. */ return this_output & ~(~next_input & next_output); } /*************************************************************************/ /** * cc_info: Return a bitmask of which condition codes are used and which * are modified by the given opcode. * * [Parameters] * opcode: Opcode to check * [Return value] * Bits 12-8: Which of XNZVC are used as input by the given opcode * Bits 4-0: Which of XNZVC are modified by the given opcode */ static unsigned int cc_info(uint16_t opcode) { const unsigned int INPUT_XNZVC = 0x1F00; const unsigned int INPUT_XZ = 0x1400; const unsigned int INPUT_X = 0x1000; const unsigned int INPUT_N = 0x0800; const unsigned int INPUT_V = 0x0200; const unsigned int INPUT_NONE = 0x0000; const unsigned int OUTPUT_XNZVC = 0x001F; const unsigned int OUTPUT_XZC = 0x0015; const unsigned int OUTPUT_NZVC = 0x000F; const unsigned int OUTPUT_N = 0x0008; const unsigned int OUTPUT_Z = 0x0004; const unsigned int OUTPUT_NONE = 0x0000; static const unsigned int cond_inputs[] = { [COND_T ] = 0x0000, [COND_F ] = 0x0000, [COND_HI] = 0x0500, [COND_LS] = 0x0500, [COND_CC] = 0x0100, [COND_CS] = 0x0100, [COND_NE] = 0x0400, [COND_EQ] = 0x0400, [COND_VC] = 0x0200, [COND_VS] = 0x0200, [COND_PL] = 0x0800, [COND_MI] = 0x0800, [COND_GE] = 0x0A00, [COND_LT] = 0x0A00, [COND_GT] = 0x0E00, [COND_LE] = 0x0E00, }; switch (opcode>>12) { case 0x0: if (opcode & 0x100) { if ((opcode>>3 & 7) == 1) { // MOVEP return INPUT_NONE | OUTPUT_NONE; } else { // BTST, etc. (dynamic) return INPUT_NONE | OUTPUT_Z; } } else if ((opcode>>6 & 3) == 3) { // Illegal (size==3) return 0; } else { switch (opcode>>9 & 7) { case 0: // ORI if ((opcode & 0xBF) == 0x3C) { // ORI to CCR/SR return INPUT_XNZVC | OUTPUT_XNZVC; } else { return INPUT_NONE | OUTPUT_NZVC; } case 1: // ANDI if ((opcode & 0xBF) == 0x3C) { // ANDI to CCR/SR return INPUT_XNZVC | OUTPUT_XNZVC; } else { return INPUT_NONE | OUTPUT_NZVC; } case 2: // SUBI return INPUT_NONE | OUTPUT_XNZVC; case 3: // ADDI return INPUT_NONE | OUTPUT_XNZVC; case 4: // BTST, etc. (static) return INPUT_NONE | OUTPUT_Z; case 5: // EORI if ((opcode & 0xBF) == 0x3C) { // EORI to CCR/SR return INPUT_XNZVC | OUTPUT_XNZVC; } else { return INPUT_NONE | OUTPUT_NZVC; } case 6: // CMPI return INPUT_NONE | OUTPUT_NZVC; case 7: // Illegal return 0; } } case 0x1: case 0x2: case 0x3: if ((opcode>>6 & 7) == 1) { // MOVEA.[LW] return INPUT_NONE | OUTPUT_NONE; } else { // MOVE.[BLW] return INPUT_NONE | OUTPUT_NZVC; } case 0x4: if (opcode & 0x0100) { switch (opcode>>6 & 3) { case 0: // Illegal case 1: // Illegal return 0; case 2: // CHK /* N is unmodified if no exception occurs, so treat as input */ return INPUT_N | OUTPUT_N; case 3: // LEA return INPUT_NONE | OUTPUT_NONE; } } else { switch (opcode & 0x0EC0) { case 0x0000: // NEGX.B case 0x0040: // NEGX.W case 0x0080: // NEGX.L return INPUT_XZ | OUTPUT_XNZVC; case 0x00C0: // MOVE from SR return INPUT_XNZVC | OUTPUT_NONE; case 0x0200: // CLR.B case 0x0240: // CLR.W case 0x0280: // CLR.L return INPUT_NONE | OUTPUT_NZVC; case 0x02C0: // Illegal return 0; case 0x0400: // NEG.B case 0x0440: // NEG.W case 0x0480: // NEG.L return INPUT_NONE | OUTPUT_XNZVC; case 0x04C0: // MOVE to CCR return INPUT_NONE | OUTPUT_XNZVC; case 0x0600: // NOT.B case 0x0640: // NOT.W case 0x0680: // NOT.L return INPUT_NONE | OUTPUT_NZVC; case 0x06C0: // MOVE to SR return INPUT_NONE | OUTPUT_XNZVC; case 0x0800: // NBCD return INPUT_XZ | OUTPUT_XZC; case 0x0840: // PEA if ((opcode>>3 & 7) == 0) { // SWAP.L return INPUT_NONE | OUTPUT_NZVC; } else { return INPUT_NONE | OUTPUT_NONE; } case 0x0880: // MOVEM.W reglist, case 0x08C0: // MOVEM.L reglist, if ((opcode>>3 & 7) == 0) { // EXT.* return INPUT_NONE | OUTPUT_NZVC; } else { return INPUT_NONE | OUTPUT_NONE; } case 0x0A00: // TST.B case 0x0A40: // TST.W case 0x0A80: // TST.L case 0x0AC0: // TAS return INPUT_NONE | OUTPUT_NZVC; case 0x0C00: // TST.B return 0; case 0x0C40: // Miscellaneous switch (opcode>>3 & 7) { case 0: // TRAP #0-7 case 1: // TRAP #8-15 case 2: // LINK case 3: // UNLK case 4: // MOVE from USP case 5: // MOVE to USP return INPUT_NONE | OUTPUT_NONE; case 6: // Miscellaneous switch (opcode & 7) { case 0: // RESET case 1: // NOP return INPUT_NONE | OUTPUT_NONE; case 2: // STOP case 3: // RTE return INPUT_NONE | OUTPUT_XNZVC; case 4: // Illegal return 0; case 5: // RTS return INPUT_NONE | OUTPUT_NONE; case 6: // TRAPV return INPUT_V | OUTPUT_NONE; case 7: // RTR return INPUT_NONE | OUTPUT_XNZVC; } case 7: // Illegal return 0; } case 0x0C80: // MOVEM.W ,reglist case 0x0CC0: // MOVEM.L ,reglist return INPUT_NONE | OUTPUT_NONE; case 0x0E00: // Illegal case 0x0E40: // Illegal return 0; case 0x0E80: // JSR case 0x0EC0: // JMP return INPUT_NONE | OUTPUT_NONE; } } case 0x5: if ((opcode>>6 & 3) == 3) { // Scc/DBcc return cond_inputs[opcode>>8 & 0xF] | OUTPUT_NONE; } else { // ADDQ/SUBQ if ((opcode>>3 & 7) == 1) { // Address register target return INPUT_NONE | OUTPUT_NONE; } else { // Other target return INPUT_NONE | OUTPUT_XNZVC; } } case 0x6: /* Bcc/BSR */ return cond_inputs[opcode>>8 & 0xF] | OUTPUT_NONE; case 0x7: if (opcode & 0x0100) { // Illegal return 0; } else { // MOVEQ return INPUT_NONE | OUTPUT_NZVC; } case 0x8: if ((opcode>>6 & 3) == 3) { // MULS/MULU return INPUT_NONE | OUTPUT_NZVC; } else if ((opcode & 0x01F0) == 0x0100) { // SBCD return INPUT_XZ | OUTPUT_XZC; } else { // OR return INPUT_NONE | OUTPUT_NZVC; } case 0x9: if ((opcode>>6 & 3) == 3) { // SUBA return INPUT_NONE | OUTPUT_NONE; } else if ((opcode & 0x0130) == 0x0100) { // SUBX return INPUT_XZ | OUTPUT_XNZVC; } else { // SUB return INPUT_NONE | OUTPUT_XNZVC; } case 0xA: /* Nothing here */ return 0; case 0xB: /* CMP/CMPA/CMPM/EOR */ return INPUT_NONE | OUTPUT_NZVC; case 0xC: if ((opcode>>6 & 3) == 3) { // DIVS/DIVD return INPUT_NONE | OUTPUT_NZVC; } else if ((opcode & 0x01F0) == 0x0100) { // ABCD return INPUT_XZ | OUTPUT_XZC; } else if ((opcode & 0x0130) == 0x0100) { // EXG return INPUT_NONE | OUTPUT_NONE; } else { // AND return INPUT_NONE | OUTPUT_NZVC; } case 0xD: if ((opcode>>6 & 3) == 3) { // ADDA return INPUT_NONE | OUTPUT_NONE; } else if ((opcode & 0x0130) == 0x0100) { // ADDX return INPUT_XZ | OUTPUT_XNZVC; } else { // ADD return INPUT_NONE | OUTPUT_XNZVC; } case 0xE: /* Shift/rotate */ return INPUT_X | OUTPUT_XNZVC; case 0xF: /* Nothing here */ return 0; } // switch (opcode>>12) return 0; // Should be unreachable, but just for safety } #endif // Q68_JIT_OPTIMIZE_FLAGS /*************************************************************************/ /*************************************************************************/ /** * ea_resolve: Emit JIT code to resolve the address for the * memory-reference EA indicated by opcode[5:0] and store it in * state->ea_addr. Behavior is undefined if the EA is a direct register * reference. * * [Parameters] * state: Processor state block * opcode: Instruction opcode * size: Access size (SIZE_*) * access_type: Access type (ACCESS_*) * [Return value] * Clock cycles used (negative indicates an illegal EA) */ static int ea_resolve(Q68State *state, uint32_t opcode, int size, int access_type) { const unsigned int mode = EA_MODE(opcode); const unsigned int reg = EA_REG(opcode); const unsigned int bytes = SIZE_TO_BYTES(size); static const int base_cycles[8] = {0, 0, 4, 4, 6, 8, 10, 0}; int cycles = base_cycles[mode] + (size==SIZE_L ? 4 : 0); switch (mode) { case EA_INDIRECT: JIT_EMIT_RESOLVE_INDIRECT(current_entry, (8+reg)*4); break; case EA_POSTINCREMENT: if (bytes == 1 && reg == 7) { // A7 must stay even JIT_EMIT_RESOLVE_POSTINC_A7_B(current_entry); } else { JIT_EMIT_RESOLVE_POSTINC(current_entry, (8+reg)*4, bytes); } break; case EA_PREDECREMENT: if (access_type == ACCESS_WRITE) { /* 2-cycle penalty not applied to write-only accesses * (MOVE and MOVEM) */ cycles -= 2; } if (bytes == 1 && reg == 7) { // A7 must stay even JIT_EMIT_RESOLVE_PREDEC_A7_B(current_entry); } else { JIT_EMIT_RESOLVE_PREDEC(current_entry, (8+reg)*4, bytes); } break; case EA_DISPLACEMENT: JIT_EMIT_RESOLVE_DISP(current_entry, (8+reg)*4, (int16_t)IFETCH(state)); break; case EA_INDEX: { const uint16_t ext = IFETCH(state); const unsigned int ireg = ext >> 12; // 0..15 const int8_t disp = (int8_t)ext; if (ext & 0x0800) { JIT_EMIT_RESOLVE_INDEX_L(current_entry, (8+reg)*4, ireg*4, disp); } else { JIT_EMIT_RESOLVE_INDEX_W(current_entry, (8+reg)*4, ireg*4, disp); } break; } default: /* case EA_MISC */ switch (reg) { case EA_MISC_ABSOLUTE_W: cycles += 8; JIT_EMIT_RESOLVE_ABSOLUTE(current_entry, (int16_t)IFETCH(state)); break; case EA_MISC_ABSOLUTE_L: { cycles += 12; uint32_t addr = IFETCH(state) << 16; addr |= (uint16_t)IFETCH(state); JIT_EMIT_RESOLVE_ABSOLUTE(current_entry, addr); break; } case EA_MISC_PCREL: if (access_type != ACCESS_READ) { return -1; } else { cycles += 8; JIT_EMIT_RESOLVE_ABSOLUTE( current_entry, state->current_PC + (int16_t)IFETCH(state) ); } break; case EA_MISC_PCREL_INDEX: if (access_type != ACCESS_READ) { return -1; } else { cycles += 10; const uint16_t ext = IFETCH(state); const unsigned int ireg = ext >> 12; // 0..15 const int32_t disp = (int32_t)((int8_t)ext); if (ext & 0x0800) { JIT_EMIT_RESOLVE_ABS_INDEX_L( current_entry, state->current_PC + disp, ireg*4 ); } else { JIT_EMIT_RESOLVE_ABS_INDEX_W( current_entry, state->current_PC + disp, ireg*4 ); } } break; default: return -1; } } return cycles; } /*-----------------------------------------------------------------------*/ /** * ea_get: Emit JIT code to read an unsigned value from the EA indicated * by opcode[5:0] and use it as either the first or the second operand to * an operation, as specified by the op_num parameter. * * If the EA selector is invalid for the access size and mode, an illegal * instruction exception is raised, and the error is indicated by a * negative value returned in *cycles_ret. * * [Parameters] * state: Processor state block * opcode: Instruction opcode * size: Access size (SIZE_*) * is_rmw: Nonzero if the operand will be modified and written back * cycles_ret: Pointer to variable to receive clock cycles used * (negative indicates an illegal EA) * op_num: Which operand to read the value into (1 or 2) * [Return value] * None */ static void ea_get(Q68State *state, uint32_t opcode, int size, int is_rmw, int *cycles_ret, int op_num) { switch (EA_MODE(opcode)) { case EA_DATA_REG: *cycles_ret = 0; if (op_num == 1) { JIT_EMIT_GET_OP1_REGISTER(current_entry, EA_REG(opcode) * 4); } else { JIT_EMIT_GET_OP2_REGISTER(current_entry, EA_REG(opcode) * 4); } break; case EA_ADDRESS_REG: *cycles_ret = 0; if (size == SIZE_B) { /* An.b not permitted */ raise_exception(state, EX_ILLEGAL_INSTRUCTION); *cycles_ret = -1; return; } else { if (op_num == 1) { JIT_EMIT_GET_OP1_REGISTER(current_entry, (8 + EA_REG(opcode)) * 4); } else { JIT_EMIT_GET_OP2_REGISTER(current_entry, (8 + EA_REG(opcode)) * 4); } } break; case EA_MISC: if (EA_REG(opcode) == EA_MISC_IMMEDIATE) { if (is_rmw) { raise_exception(state, EX_ILLEGAL_INSTRUCTION); *cycles_ret = -1; return; } else { *cycles_ret = (size==SIZE_L ? 8 : 4); uint32_t val; val = IFETCH(state); if (size == SIZE_B) { val &= 0xFF; } else if (size == SIZE_L) { val <<= 16; val |= (uint16_t)IFETCH(state); } if (op_num == 1) { JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, val); } else { JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, val); } } break; } /* else fall through */ default: *cycles_ret = ea_resolve(state, opcode, size, is_rmw ? ACCESS_MODIFY : ACCESS_READ); if (*cycles_ret < 0) { raise_exception(state, EX_ILLEGAL_INSTRUCTION); return; } if (size == SIZE_B) { if (op_num == 1) { JIT_EMIT_GET_OP1_EA_B(current_entry); } else { JIT_EMIT_GET_OP2_EA_B(current_entry); } } else if (size == SIZE_W) { #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_EA( current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ ); #endif if (op_num == 1) { JIT_EMIT_GET_OP1_EA_W(current_entry); } else { JIT_EMIT_GET_OP2_EA_W(current_entry); } } else { // size == SIZE_L #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_EA( current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ ); #endif if (op_num == 1) { JIT_EMIT_GET_OP1_EA_L(current_entry); } else { JIT_EMIT_GET_OP2_EA_L(current_entry); } } break; } // switch (EA_MODE(opcode)) } /*-----------------------------------------------------------------------*/ /** * ea_set: Emit JIT code to update a value at the EA indicated by * opcode[5:0]. If the EA is a memory reference, uses the previously * resolved address in state->ea_addr rather than resolving the address * again. Behavior is undefined if the previous ea_resolve() or ea_get() * failed (or if no previous call was made). * * [Parameters] * state: Processor state block * opcode: Instruction opcode * size: Access size (SIZE_*) * [Return value] * None */ static void ea_set(Q68State *state, uint32_t opcode, int size) { switch (EA_MODE(opcode)) { case EA_DATA_REG: if (size == SIZE_B) { JIT_EMIT_SET_REGISTER_B(current_entry, EA_REG(opcode) * 4); } else if (size == SIZE_W) { JIT_EMIT_SET_REGISTER_W(current_entry, EA_REG(opcode) * 4); } else { // size == SIZE_L JIT_EMIT_SET_REGISTER_L(current_entry, EA_REG(opcode) * 4); } return; case EA_ADDRESS_REG: if (size == SIZE_W) { JIT_EMIT_SET_AREG_W(current_entry, (8 + EA_REG(opcode)) * 4); } else { // size == SIZE_L JIT_EMIT_SET_REGISTER_L(current_entry, (8 + EA_REG(opcode)) * 4); } return; default: { if (size == SIZE_B) { JIT_EMIT_SET_EA_B(current_entry); } else if (size == SIZE_W) { #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_EA( current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE ); #endif JIT_EMIT_SET_EA_W(current_entry); } else { // size == SIZE_L #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_EA( current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE ); #endif JIT_EMIT_SET_EA_L(current_entry); } return; } } } /*************************************************************************/ /*********************** Major instruction groups ************************/ /*************************************************************************/ /** * op_imm: Immediate instructions (format 0000 xxx0 xxxx xxxx). */ static int op_imm(Q68State *state, uint32_t opcode) { /* Check for bit-twiddling and illegal opcodes first */ enum {OR = 0, AND, SUB, ADD, _BIT, EOR, CMP, _ILL} aluop; aluop = opcode>>9 & 7; if (aluop == _BIT) { return op_bit(state, opcode); } else if (aluop == _ILL) { return op_ill(state, opcode); } /* Get the instruction size */ INSN_GET_SIZE; if (size == 3) { return op_ill(state, opcode); } /* Fetch the immediate value */ int cycles_dummy; ea_get(state, EA_MISC<<3 | EA_MISC_IMMEDIATE, size, 0, &cycles_dummy, 1); /* Fetch the EA operand (which may be SR or CCR) */ int use_SR; int cycles; if ((aluop==OR || aluop==AND || aluop==EOR) && (opcode & 0x3F) == 0x3C) { /* xxxI #imm,SR (or CCR) use the otherwise-invalid form of an * immediate value destination */ use_SR = 1; cycles = 8; // Total instruction time is 20 cycles switch (size) { case SIZE_B: JIT_EMIT_GET_OP2_CCR(current_entry); break; case SIZE_W: JIT_EMIT_CHECK_SUPER(current_entry); JIT_EMIT_GET_OP2_SR(current_entry); break; default: return op_ill(state, opcode); } } else { use_SR = 0; ea_get(state, opcode, size, 1, &cycles, 2); if (cycles < 0) { return 1; } } /* Check whether we need to output condition codes */ const int do_cc = cc_needed(state, opcode); /* Perform the operation */ switch (aluop) { case OR: if (size == SIZE_B) { JIT_EMIT_OR_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_OR_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_OR_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; case AND: if (size == SIZE_B) { JIT_EMIT_AND_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_AND_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_AND_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; case EOR: if (size == SIZE_B) { JIT_EMIT_EOR_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_EOR_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_EOR_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; case CMP: if (size == SIZE_L) { // CMPI takes less time in most cases if (EA_MODE(opcode) != EA_DATA_REG) { cycles -= 8; } else { cycles -= 2; } } else { if (EA_MODE(opcode) != EA_DATA_REG) { cycles -= 4; } } if (size == SIZE_B) { JIT_EMIT_SUB_B(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUB_W(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUB_L(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_L(current_entry); } break; case SUB: if (size == SIZE_B) { JIT_EMIT_SUB_B(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUB_W(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUB_L(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_L(current_entry); } break; default: // case ADD if (size == SIZE_B) { JIT_EMIT_ADD_B(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ADD_W(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ADD_L(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_L(current_entry); } break; } /* Update the cycle counter (and PC) before writing the result, in case * a change to SR triggers an interrupt */ cycles += (size==SIZE_L ? 16 : 8); cycles += (EA_MODE(opcode) == EA_DATA_REG ? 0 : 4); JIT_EMIT_ADD_CYCLES(current_entry, cycles); advance_PC(state); /* Update the EA operand (if not CMPI) */ if (aluop != CMP) { if (use_SR) { if (size == SIZE_B) { JIT_EMIT_SET_CCR(current_entry); } else { JIT_EMIT_SET_SR(current_entry); } } else { ea_set(state, opcode, size); } } /* All done */ return 0; } /*************************************************************************/ /** * op_bit: Bit-twiddling instructions (formats 0000 rrr1 xxxx xxxx and * 0000 1000 xxxx xxxx). */ static int op_bit(Q68State *state, uint32_t opcode) { /* Check early for MOVEP (coded as BTST/BCHG/BCLR/BSET Dn,An) */ if (EA_MODE(opcode) == EA_ADDRESS_REG) { if (opcode & 0x0100) { return opMOVP(state, opcode); } else { return op_ill(state, opcode); } } enum {BTST = 0, BCHG = 1, BCLR = 2, BSET = 3} op = opcode>>6 & 3; int cycles; /* Get the bit number to operate on */ if (opcode & 0x0100) { /* Bit number in register */ INSN_GET_REG; JIT_EMIT_GET_OP1_REGISTER(current_entry, reg*4); cycles = 0; } else { unsigned int bitnum = IFETCH(state); JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, bitnum); cycles = 4; } /* EA operand is 32 bits when coming from a register, 8 when from memory */ int size = (EA_MODE(opcode)==EA_DATA_REG ? SIZE_L : SIZE_B); int cycles_tmp; ea_get(state, opcode, size, 1, &cycles_tmp, 2); if (cycles_tmp < 0) { return 1; } cycles += cycles_tmp; if (size == SIZE_L && (op == BCLR || op == BTST)) { cycles += 2; } /* Perform the operation: first test the bit, then (for non-BTST cases) * twiddle it as appropriate. All size-related checking is performed * in BTST, so the remaining operations are unsized. */ if (size == SIZE_B) { JIT_EMIT_BTST_B(current_entry); } else { // size == SIZE_L JIT_EMIT_BTST_L(current_entry); } switch (op) { default: break; // case BTST: nothing to do case BCHG: JIT_EMIT_BCHG(current_entry); break; case BCLR: JIT_EMIT_BCLR(current_entry); break; case BSET: JIT_EMIT_BSET(current_entry); break; } /* Update EA operand (but not for BTST) */ if (op != BTST) { ea_set(state, opcode, size); } /* Update cycle counter; note that the times for BCHG.L, BCLR.L, and * BSET.L are maximums (though how they vary is undocumented) */ JIT_EMIT_ADD_CYCLES(current_entry, (op==BTST ? 4 : 8) + cycles); return 0; } /*************************************************************************/ /** * opMOVE: MOVE.[bwl] instruction (format {01,10,11}xx xxxx xxxx xxxx). */ static int opMOVE(Q68State *state, uint32_t opcode) { const int size = (opcode>>12==1 ? SIZE_B : opcode>>12==2 ? SIZE_L : SIZE_W); int cycles_src; ea_get(state, opcode, size, 0, &cycles_src, 1); if (cycles_src < 0) { return 1; } /* Rearrange the opcode bits so we can pass the destination EA to * ea_resolve() */ const uint32_t dummy_opcode = (opcode>>9 & 7) | (opcode>>3 & 0x38); int cycles_dest; if (EA_MODE(dummy_opcode) <= EA_ADDRESS_REG) { cycles_dest = 0; } else { cycles_dest = ea_resolve(state, dummy_opcode, size, ACCESS_WRITE); if (cycles_dest < 0) { return op_ill(state, opcode); } } /* Copy the operand to the result and set flags (if needed) */ const int do_cc = cc_needed(state, opcode); if (EA_MODE(dummy_opcode) == EA_ADDRESS_REG) { if (size == SIZE_W) { JIT_EMIT_EXT_L(current_entry); } else { // size == SIZE_L JIT_EMIT_MOVE_L(current_entry); } } else { if (size == SIZE_B) { JIT_EMIT_MOVE_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_MOVE_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_MOVE_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } } /* Update the destination EA and cycle count */ ea_set(state, dummy_opcode, size); JIT_EMIT_ADD_CYCLES(current_entry, 4 + cycles_src + cycles_dest); return 0; } /*************************************************************************/ /** * op4xxx: Miscellaneous instructions (format 0100 xxx0 xxxx xxxx). */ static int op4xxx(Q68State *state, uint32_t opcode) { const unsigned int index = (opcode>>7 & 0x1C) | (opcode>>6 & 3); return (*opcode_4xxx_table[index])(state, opcode); } /*************************************************************************/ /** * op_CHK: CHK instruction (format 0100 rrr1 10xx xxxx). */ static int op_CHK(Q68State *state, uint32_t opcode) { INSN_GET_REG; JIT_EMIT_GET_OP1_REGISTER(current_entry, reg*4); int cycles; if (EA_MODE(opcode) == EA_ADDRESS_REG) { return op_ill(state, opcode); } ea_get(state, opcode, SIZE_W, 0, &cycles, 2); if (cycles < 0) { return 1; } JIT_EMIT_ADD_CYCLES(current_entry, 10 + cycles); /* The JIT code takes care of adding the extra 34 cycles of exception * processing if necessary */ JIT_EMIT_CHK_W(current_entry); return 0; } /*************************************************************************/ /** * op_LEA: LEA instruction (format 0100 rrr1 11xx xxxx). */ static int op_LEA(Q68State *state, uint32_t opcode) { INSN_GET_REG; /* Register, predecrement, postincrement, immediate modes are illegal */ if (EA_MODE(opcode) == EA_DATA_REG || EA_MODE(opcode) == EA_ADDRESS_REG || EA_MODE(opcode) == EA_POSTINCREMENT || EA_MODE(opcode) == EA_PREDECREMENT || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE) ) { return op_ill(state, opcode); } int cycles = ea_resolve(state, opcode, SIZE_W, ACCESS_READ); if (cycles < 0) { return op_ill(state, opcode); } if (cycles % 4 == 2) { // d(An,ix) and d(PC,ix) take 2 extra cycles cycles += 2; } JIT_EMIT_LEA(current_entry, (8+reg)*4); JIT_EMIT_ADD_CYCLES(current_entry, cycles); return 0; } /*************************************************************************/ /** * opADSQ: ADDQ and SUBQ instructions (format 0101 iiix xxxx xxxx). */ static int opADSQ(Q68State *state, uint32_t opcode) { const int is_sub = opcode & 0x0100; INSN_GET_COUNT; INSN_GET_SIZE; if (EA_MODE(opcode) == EA_ADDRESS_REG && size == 1) { size = 2; // ADDQ.W #imm,An is equivalent to ADDQ.L #imm,An } JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, count); int cycles; ea_get(state, opcode, size, 1, &cycles, 2); if (cycles < 0) { return 1; } const int do_cc = cc_needed(state, opcode); if (is_sub) { if (EA_MODE(opcode) == EA_ADDRESS_REG) { JIT_EMIT_SUB_L(current_entry); } else { if (size == SIZE_B) { JIT_EMIT_SUB_B(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUB_W(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUB_L(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_L(current_entry); } } } else { if (EA_MODE(opcode) == EA_ADDRESS_REG) { JIT_EMIT_ADD_L(current_entry); } else { if (size == SIZE_B) { JIT_EMIT_ADD_B(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ADD_W(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ADD_L(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_L(current_entry); } } } ea_set(state, opcode, size); cycles += (size==SIZE_L || EA_MODE(opcode) == EA_ADDRESS_REG ? 8 : 4); cycles += (EA_MODE(opcode) >= EA_INDIRECT ? 4 : 0); JIT_EMIT_ADD_CYCLES(current_entry, cycles); return 0; } /*************************************************************************/ /** * op_Scc: Scc instruction (format 0101 cccc 11xx xxxx). */ static int op_Scc(Q68State *state, uint32_t opcode) { if (EA_MODE(opcode) == EA_ADDRESS_REG) { /* DBcc Dn,disp is coded as Scc An with an extension word */ return opDBcc(state, opcode); } INSN_GET_COND; /* From the cycle counts, it looks like this is a standard read/write * access rather than a write-only access */ int cycles; if (EA_MODE(opcode) == EA_DATA_REG) { cycles = 0; } else { cycles = ea_resolve(state, opcode, SIZE_B, ACCESS_MODIFY); if (cycles < 0) { return op_ill(state, opcode); } } JIT_EMIT_TEST_cc(cond, current_entry); JIT_EMIT_Scc(current_entry); if (EA_MODE(opcode) == EA_DATA_REG) { /* Scc Dn is a special case */ JIT_EMIT_ADD_CYCLES_Scc_Dn(current_entry); } else { JIT_EMIT_ADD_CYCLES(current_entry, 8 + cycles); } ea_set(state, opcode, SIZE_B); return 0; } /*-----------------------------------------------------------------------*/ /** * op_DBcc: DBcc instruction (format 0101 cccc 1100 1xxx). */ static int opDBcc(Q68State *state, uint32_t opcode) { INSN_GET_COND; INSN_GET_REG0; INSN_GET_IMM16; uint32_t target = state->current_PC + imm16; int32_t offset = btcache_lookup(target); JIT_EMIT_TEST_cc(cond, current_entry); if (offset >= 0) { JIT_EMIT_DBcc_native(current_entry, reg0*4, target, offset); } else { JIT_EMIT_DBcc(current_entry, reg0*4, target); } return 0; } /*************************************************************************/ /** * op_Bcc: Conditional branch instructions (format 0110 cccc dddd dddd). */ static int op_Bcc(Q68State *state, uint32_t opcode) { INSN_GET_COND; INSN_GET_DISP8; int cycles = 0; if (disp == 0) { disp = (int16_t)IFETCH(state); cycles = 4; } uint32_t target = state->current_PC + disp; if (cond == COND_F) { /* BF is really BSR */ #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); #endif JIT_EMIT_ADD_CYCLES(current_entry, 18); advance_PC(state); JIT_EMIT_BSR(current_entry, jit_PC, target); return 0; } else { int32_t offset; #ifdef Q68_OPTIMIZE_IDLE /* FIXME: Temporary hack to improve PSP performance */ if (target == 0x1066 && ((cond == COND_EQ && state->current_PC - 2 == 0x001092) || (cond == COND_PL && state->current_PC - 2 == 0x0010B4)) ) { /* BIOS intro animation */ JIT_EMIT_ADD_CYCLES(current_entry, 468); // Length of one loop when idle } else if (target == 0x10BC && ((cond == COND_PL && state->current_PC - 2 == 0x001122) || (cond == COND_T && state->current_PC - 2 == 0x00116A)) ) { /* Azel: Panzer Dragoon RPG (JP) */ JIT_EMIT_ADD_CYCLES(current_entry, 178*4); // Assuming a cycle_limit of 768 } #endif if (target < state->current_PC) { offset = btcache_lookup(target); } else { offset = -1; // Forward jumps can't be in the cache } JIT_EMIT_TEST_cc(cond, current_entry); if (offset >= 0) { JIT_EMIT_Bcc_native(current_entry, target, offset); } else { int32_t branch_offset; JIT_EMIT_Bcc(current_entry, target, &branch_offset); if (target >= state->current_PC && branch_offset >= 0) { record_unresolved_branch(target, branch_offset); } } JIT_EMIT_ADD_CYCLES(current_entry, 8 + cycles); return 0; } } /*************************************************************************/ /** * opMOVQ: MOVEQ instruction (format 0111 rrr0 iiii iiii). */ static int opMOVQ(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_IMM8; JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, imm8); const int do_cc = cc_needed(state, opcode); JIT_EMIT_MOVE_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); JIT_EMIT_SET_REGISTER_L(current_entry, reg*4); JIT_EMIT_ADD_CYCLES(current_entry, 4); return 0; } /*************************************************************************/ /** * op_alu: Non-immediate ALU instructions (format 1ooo rrrx xxxx xxxx for * ooo = 000, 001, 011, 100, 101). */ static int op_alu(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_SIZE; /* Pass off special and invalid instructions early */ if (size != 3) { if ((opcode & 0xB130) == 0x9100) { /* ADDX/SUBX are coded as ADD/SUB.* Dn, */ return opADSX(state, opcode); } if ((opcode & 0xB1F0) == 0x8100) { /* ABCD/SBCD are coded as AND/OR.b Dn, */ return op_BCD(state, opcode); } if ((opcode & 0xF130) == 0xC100) { /* EXG is coded as AND.[wl] Dn, */ return op_EXG(state, opcode); } if ((opcode & 0xF130) == 0x8100) { /* OR.[wl] Dn, is invalid on the 68000 (later PACK/UNPK) */ return op_ill(state, opcode); } if ((opcode & 0xF138) == 0xB108 && (opcode>>6 & 3) != 3) { /* CMPM is coded as EOR.* Dn, */ return opCMPM(state, opcode); } } int ea_dest = opcode & 0x100; int areg_dest = 0; // For ADDA/SUBA/CMPA enum {OR, AND, EOR, CMP, SUB, ADD} aluop; /* Find the instruction for the opcode group */ switch (opcode>>12) { case 0x8: aluop = OR; break; case 0x9: aluop = SUB; break; case 0xB: aluop = (((opcode>>6)+1) & 7) <= 4 ? CMP : EOR; break; case 0xC: aluop = AND; break; default: aluop = ADD; break; // case 0xD } /* Handle the special formats of ADDA/SUBA/CMPA */ if ((aluop == ADD || aluop == SUB || aluop == CMP) && size == 3) { size = ea_dest ? SIZE_L : SIZE_W; ea_dest = 0; areg_dest = 1; } /* Retrieve the register and EA values; make sure to load operand 1 * first, since operand 2 may be destroyed by memory operations */ int cycles; if (ea_dest) { JIT_EMIT_GET_OP1_REGISTER(current_entry, reg*4); ea_get(state, opcode, size, ea_dest, &cycles, 2); } else { ea_get(state, opcode, size, ea_dest, &cycles, 1); if (areg_dest) { JIT_EMIT_GET_OP2_REGISTER(current_entry, (8+reg)*4); } else { JIT_EMIT_GET_OP2_REGISTER(current_entry, reg*4); } } if (cycles < 0) { return 1; } if (size == SIZE_L || areg_dest) { cycles += 4; } if (ea_dest) { cycles += 4; } else if ((aluop == CMP && areg_dest) || (size == SIZE_L && (EA_MODE(opcode) <= EA_ADDRESS_REG || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE)))) { cycles -= 2; } /* Perform the actual computation */ const int do_cc = cc_needed(state, opcode); switch (aluop) { case OR: if (size == SIZE_B) { JIT_EMIT_OR_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_OR_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_OR_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; case AND: if (size == SIZE_B) { JIT_EMIT_AND_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_AND_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_AND_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; case EOR: if (size == SIZE_B) { JIT_EMIT_EOR_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_EOR_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_EOR_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; case CMP: if (areg_dest && size == SIZE_W) { JIT_EMIT_SUBA_W(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_W(current_entry); } else if (size == SIZE_B) { JIT_EMIT_SUB_B(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUB_W(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUB_L(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_L(current_entry); } break; case SUB: if (areg_dest && size == SIZE_W) { JIT_EMIT_SUBA_W(current_entry); } else if (areg_dest && size == SIZE_L) { JIT_EMIT_SUB_L(current_entry); } else if (size == SIZE_B) { JIT_EMIT_SUB_B(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUB_W(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUB_L(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_L(current_entry); } break; default: // case ADD if (areg_dest && size == SIZE_W) { JIT_EMIT_ADDA_W(current_entry); } else if (areg_dest && size == SIZE_L) { JIT_EMIT_ADD_L(current_entry); } else if (size == SIZE_B) { JIT_EMIT_ADD_B(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ADD_W(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ADD_L(current_entry); if (do_cc) JIT_EMIT_SETCC_ADD_L(current_entry); } break; } // switch (aluop) /* Store the result in the proper place (if the instruction is not CMP) */ if (aluop != CMP) { if (ea_dest) { ea_set(state, opcode, size); } else if (areg_dest) { JIT_EMIT_SET_REGISTER_L(current_entry, (8+reg)*4); } else if (size == SIZE_B) { JIT_EMIT_SET_REGISTER_B(current_entry, reg*4); } else if (size == SIZE_W) { JIT_EMIT_SET_REGISTER_W(current_entry, reg*4); } else { // size == SIZE_L JIT_EMIT_SET_REGISTER_L(current_entry, reg*4); } } JIT_EMIT_ADD_CYCLES(current_entry, 4 + cycles); return 0; } /*************************************************************************/ /** * op_DIV: DIVU and DIVS instructions (format 1000 rrrx 11xx xxxx). */ static int op_DIV(Q68State *state, uint32_t opcode) { INSN_GET_REG; const int sign = opcode & (1<<8); int cycles; ea_get(state, opcode, SIZE_W, 0, &cycles, 1); if (cycles < 0) { return 1; } JIT_EMIT_GET_OP2_REGISTER(current_entry, reg*4); /* Add the EA cycles now, in case a divide-by-zero exception occurs */ JIT_EMIT_ADD_CYCLES(current_entry, cycles); if (sign) { JIT_EMIT_DIVS_W(current_entry); } else { JIT_EMIT_DIVU_W(current_entry); } JIT_EMIT_SET_REGISTER_L(current_entry, reg*4); /* The 68000 docs say that the timing difference between best and * worst cases is less than 10%, so we just return the worst case */ JIT_EMIT_ADD_CYCLES(current_entry, sign ? 158 : 140); return 0; } /*************************************************************************/ /** * opAxxx: $Axxx illegal instruction set (format 1010 xxxx xxxx xxxx). */ static int opAxxx(Q68State *state, uint32_t opcode) { return raise_exception(state, EX_LINE_1010); } /*************************************************************************/ /** * op_MUL: MULU and MULS instructions (format 1100 rrrx 11xx xxxx). */ static int op_MUL(Q68State *state, uint32_t opcode) { INSN_GET_REG; const int sign = opcode & (1<<8); int cycles; ea_get(state, opcode, SIZE_W, 0, &cycles, 1); if (cycles < 0) { return 1; } JIT_EMIT_GET_OP2_REGISTER(current_entry, reg*4); const int do_cc = cc_needed(state, opcode); if (sign) { JIT_EMIT_MULS_W(current_entry); } else { JIT_EMIT_MULU_W(current_entry); } /* 16*16 -> 32 multiplication can't produce carry or overflow, so we * can treat it like a logical operation for setting condition codes */ if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); JIT_EMIT_SET_REGISTER_L(current_entry, reg*4); JIT_EMIT_ADD_CYCLES(current_entry, 54 + cycles); return 0; } /*************************************************************************/ /** * opshft: Shift and rotate instructions (format 1110 xxxx xxxx xxxx). */ static int opshft(Q68State *state, uint32_t opcode) { const int is_left = opcode & 0x0100; INSN_GET_SIZE; INSN_GET_COUNT; INSN_GET_REG0; int is_memory; int type; // Shift/rotate type (0=ASL/ASR, 1=LSL/LSR, ...) int cycles; if (size == 3) { /* Memory shift/rotate */ is_memory = 1; if ((opcode & 0x0800) || EA_MODE(opcode) <= EA_ADDRESS_REG) { return op_ill(state, opcode); } size = SIZE_W; type = opcode>>9 & 3; JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, 1); ea_get(state, opcode, size, 1, &cycles, 2); if (cycles < 0) { return 1; } } else { /* Register shift/rotate */ is_memory = 0; type = opcode>>3 & 3; if (opcode & 0x0020) { INSN_GET_REG; JIT_EMIT_GET_OP1_REGISTER(current_entry, reg*4); } else { JIT_EMIT_GET_OP1_IMMEDIATE(current_entry, count); } JIT_EMIT_GET_OP2_REGISTER(current_entry, reg0*4); cycles = 0; } switch (type) { case 0: // ASL/ASR if (is_left) { if (size == SIZE_B) { JIT_EMIT_ASL_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ASL_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ASL_L(current_entry); } } else { if (size == SIZE_B) { JIT_EMIT_ASR_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ASR_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ASR_L(current_entry); } } break; case 1: // LSL/LSR if (is_left) { if (size == SIZE_B) { JIT_EMIT_LSL_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_LSL_W(current_entry); } else { // size == SIZE_L JIT_EMIT_LSL_L(current_entry); } } else { if (size == SIZE_B) { JIT_EMIT_LSR_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_LSR_W(current_entry); } else { // size == SIZE_L JIT_EMIT_LSR_L(current_entry); } } break; case 2: // ROXL/ROXR if (is_left) { if (size == SIZE_B) { JIT_EMIT_ROXL_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ROXL_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ROXL_L(current_entry); } } else { if (size == SIZE_B) { JIT_EMIT_ROXR_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ROXR_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ROXR_L(current_entry); } } break; case 3: // ROL/ROR if (is_left) { if (size == SIZE_B) { JIT_EMIT_ROL_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ROL_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ROL_L(current_entry); } } else { if (size == SIZE_B) { JIT_EMIT_ROR_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ROR_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ROR_L(current_entry); } } break; } // switch (type) if (is_memory) { ea_set(state, opcode, size); } else if (size == SIZE_B) { JIT_EMIT_SET_REGISTER_B(current_entry, reg0*4); } else if (size == SIZE_W) { JIT_EMIT_SET_REGISTER_W(current_entry, reg0*4); } else { // size == SIZE_L JIT_EMIT_SET_REGISTER_L(current_entry, reg0*4); } /* Cycles based on count are added in the shift/rotate processing */ JIT_EMIT_ADD_CYCLES(current_entry, (size==SIZE_L ? 8 : 6) + cycles); return 0; } /*************************************************************************/ /** * opFxxx: $Fxxx illegal instruction set (format 1111 xxxx xxxx xxxx). */ static int opFxxx(Q68State *state, uint32_t opcode) { return raise_exception(state, EX_LINE_1111); } /*************************************************************************/ /*********************** $4xxx group instructions ************************/ /*************************************************************************/ /** * op4alu: Single-operand ALU instructions in the $4xxx opcode range * (format 0100 ooo0 ssxx xxxx for ooo = 000, 001, 010, 011, 101). */ static int op4alu(Q68State *state, uint32_t opcode) { INSN_GET_SIZE; enum {NEGX = 0, CLR = 1, NEG = 2, NOT = 3, TST = 5} aluop; aluop = opcode>>9 & 7; if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed return op_ill(state, opcode); } /* Retrieve the EA value */ int cycles; ea_get(state, opcode, size, 1, &cycles, 1); if (cycles < 0) { return 1; } if (aluop != TST) { if (EA_MODE(opcode) == EA_DATA_REG) { if (size == SIZE_L) { cycles += 2; } } else { cycles += (size == SIZE_L) ? 8 : 4; } } /* Perform the actual computation */ /* For simplicity, use the 2-argument operations with 0 or ~0 as the * second operand: * -n = 0 - n * 0 = 0 & n * ~n = ~0 ^ n * n = 0 | n */ JIT_EMIT_GET_OP2_IMMEDIATE(current_entry, aluop==NOT ? ~(uint32_t)0 : 0); const int do_cc = cc_needed(state, opcode); switch (aluop) { case NEGX:if (size == SIZE_B) { JIT_EMIT_SUBX_B(current_entry); if (do_cc) JIT_EMIT_SETCC_SUBX_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUBX_W(current_entry); if (do_cc) JIT_EMIT_SETCC_SUBX_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUBX_L(current_entry); if (do_cc) JIT_EMIT_SETCC_SUBX_L(current_entry); } break; case NEG: if (size == SIZE_B) { JIT_EMIT_SUB_B(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUB_W(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUB_L(current_entry); if (do_cc) JIT_EMIT_SETCC_SUB_L(current_entry); } break; case CLR: if (size == SIZE_B) { JIT_EMIT_AND_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_AND_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_AND_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; case NOT: if (size == SIZE_B) { JIT_EMIT_EOR_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_EOR_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_EOR_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; default: // case TST if (size == SIZE_B) { JIT_EMIT_OR_B(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_OR_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); } else { // size == SIZE_L JIT_EMIT_OR_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); } break; } // switch (aluop) /* Store the result in the proper place (if the instruction is not TST) */ if (aluop != TST) { ea_set(state, opcode, size); } JIT_EMIT_ADD_CYCLES(current_entry, 4 + cycles); return 0; } /*************************************************************************/ /** * opMVSR: MOVE to/from SR/CCR instructions (format 0100 0xx0 11xx xxxx). */ static int opMVSR(Q68State *state, uint32_t opcode) { int is_CCR; int ea_dest; int cycles; switch (opcode>>9 & 3) { case 0: // MOVE SR, is_CCR = 0; ea_dest = 1; cycles = (EA_MODE(opcode) == EA_DATA_REG) ? 6 : 8; break; case 1: // Undefined (MOVE CCR, on 68010) return op_ill(state, opcode); case 2: // MOVE ,CCR is_CCR = 1; ea_dest = 0; cycles = 12; break; default: // MOVE ,SR (case 3) JIT_EMIT_CHECK_SUPER(current_entry); is_CCR = 0; ea_dest = 0; cycles = 12; break; } if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed return op_ill(state, opcode); } /* Motorola docs say the address is read before being written, even * for the SR, format; also, the access size is a word even for * CCR operations. */ int cycles_tmp; ea_get(state, opcode, SIZE_W, ea_dest, &cycles_tmp, 1); if (cycles_tmp < 0) { return 1; } cycles += cycles_tmp; /* Update the cycle counter (and PC) before writing the result, in case * a change to SR triggers an interrupt */ JIT_EMIT_ADD_CYCLES(current_entry, cycles); advance_PC(state); if (ea_dest) { if (is_CCR) { JIT_EMIT_GET_OP1_CCR(current_entry); } else { JIT_EMIT_GET_OP1_SR(current_entry); } JIT_EMIT_MOVE_W(current_entry); ea_set(state, opcode, SIZE_W); } else { JIT_EMIT_MOVE_W(current_entry); /* No need to set condition codes--we're about to overwrite them */ if (is_CCR) { JIT_EMIT_SET_CCR(current_entry); } else { JIT_EMIT_SET_SR(current_entry); } } return 0; } /*************************************************************************/ /** * opNBCD: NBCD instruction (format 0100 1000 00xx xxxx). */ static int opNBCD(Q68State *state, uint32_t opcode) { if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed return op_ill(state, opcode); } int cycles; ea_get(state, opcode, SIZE_B, 1, &cycles, 1); if (cycles < 0) { return 1; } /* Treat it as something like SBCD ,#0 for simplicity */ JIT_EMIT_GET_OP2_IMMEDIATE(current_entry, 0); JIT_EMIT_SBCD(current_entry); ea_set(state, opcode, SIZE_B); JIT_EMIT_ADD_CYCLES(current_entry, (EA_MODE(opcode) == EA_DATA_REG ? 6 : 8) + cycles); return 0; } /*************************************************************************/ /** * op_PEA: PEA instruction (format 0100 1000 01xx xxxx). */ static int op_PEA(Q68State *state, uint32_t opcode) { /* SWAP is coded as PEA Dn */ if (EA_MODE(opcode) == EA_DATA_REG) { return opSWAP(state, opcode); } if (EA_MODE(opcode) == EA_DATA_REG || EA_MODE(opcode) == EA_ADDRESS_REG || EA_MODE(opcode) == EA_POSTINCREMENT || EA_MODE(opcode) == EA_PREDECREMENT || (EA_MODE(opcode) == EA_MISC && EA_REG(opcode) == EA_MISC_IMMEDIATE) ) { return op_ill(state, opcode); } int cycles = ea_resolve(state, opcode, SIZE_W, ACCESS_READ); if (cycles < 0) { return op_ill(state, opcode); } if (cycles % 4 == 2) { // d(An,ix) and d(PC,ix) take 2 extra cycles cycles += 2; } #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); #endif JIT_EMIT_PEA(current_entry); JIT_EMIT_ADD_CYCLES(current_entry, 8 + cycles); return 0; } /*************************************************************************/ /** * opSWAP: SWAP instruction (format 0100 1000 0100 0rrr). */ static int opSWAP(Q68State *state, uint32_t opcode) { INSN_GET_REG0; JIT_EMIT_GET_OP1_REGISTER(current_entry, reg0*4); const int do_cc = cc_needed(state, opcode); JIT_EMIT_SWAP(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); JIT_EMIT_SET_REGISTER_L(current_entry, reg0*4); JIT_EMIT_ADD_CYCLES(current_entry, 4); return 0; } /*************************************************************************/ /** * op_TAS: TAS instruction (format 0100 1010 11xx xxxx). Also covers the * ILLEGAL instruction (format 0100 1010 1111 1100). */ static int op_TAS(Q68State *state, uint32_t opcode) { if (EA_MODE(opcode) == EA_ADDRESS_REG) { // Address registers not allowed return op_ill(state, opcode); } int cycles; ea_get(state, opcode, SIZE_B, 1, &cycles, 1); if (cycles < 0) { /* Note that the ILLEGAL instruction is coded as TAS #imm, so it * will be rejected as unwriteable by ea_get() */ return 1; } JIT_EMIT_TAS(current_entry); ea_set(state, opcode, SIZE_B); JIT_EMIT_ADD_CYCLES(current_entry, (EA_MODE(opcode) == EA_DATA_REG ? 4 : 10) + cycles); return 0; } /*************************************************************************/ /** * op_EXT: EXT instruction (format 0100 1000 1s00 0rrr). */ static int op_EXT(Q68State *state, uint32_t opcode) { INSN_GET_REG0; JIT_EMIT_GET_OP1_REGISTER(current_entry, reg0*4); const int do_cc = cc_needed(state, opcode); if (opcode & 0x0040) { JIT_EMIT_EXT_L(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_L(current_entry); JIT_EMIT_SET_REGISTER_L(current_entry, reg0*4); } else { JIT_EMIT_EXT_W(current_entry); if (do_cc) JIT_EMIT_SETCC_LOGIC_W(current_entry); JIT_EMIT_SET_REGISTER_W(current_entry, reg0*4); } JIT_EMIT_ADD_CYCLES(current_entry, 4); return 0; } /*************************************************************************/ /** * op_STM: MOVEM reglist, (i.e. STore Multiple) instruction (format * 0100 1000 1sxx xxxx). */ static int op_STM(Q68State *state, uint32_t opcode) { /* EXT.* is coded as MOVEM.* reglist,Dn */ if (EA_MODE(opcode) == EA_DATA_REG) { return op_EXT(state, opcode); } unsigned int regmask = IFETCH(state); int size = (opcode & 0x0040) ? SIZE_L : SIZE_W; if (EA_MODE(opcode) <= EA_ADDRESS_REG || EA_MODE(opcode) == EA_POSTINCREMENT // Not allowed for store ) { return op_ill(state, opcode); } /* Avoid modifying the register during address resolution */ uint16_t safe_ea; if (EA_MODE(opcode) == EA_PREDECREMENT) { safe_ea = EA_INDIRECT<<3 | EA_REG(opcode); } else { safe_ea = opcode; } int cycles = ea_resolve(state, safe_ea, SIZE_W, ACCESS_WRITE); if (cycles < 0) { return op_ill(state, opcode); } if (regmask != 0) { // FIXME: does a real 68000 choke even if regmask==0? #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_EA(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); #endif } if (EA_MODE(opcode) == EA_PREDECREMENT) { /* Register order is reversed in predecrement mode */ int reg; for (reg = 15; reg >= 0; reg--, regmask >>= 1) { if (regmask & 1) { if (size == SIZE_W) { JIT_EMIT_STORE_DEC_W(current_entry, reg*4); cycles += 4; } else { JIT_EMIT_STORE_DEC_L(current_entry, reg*4); cycles += 8; } } } JIT_EMIT_MOVEM_WRITEBACK(current_entry, (8 + EA_REG(opcode)) * 4); } else { int reg; for (reg = 0; reg < 16; reg++, regmask >>= 1) { if (regmask & 1) { if (size == SIZE_W) { JIT_EMIT_STORE_INC_W(current_entry, reg*4); cycles += 4; } else { JIT_EMIT_STORE_INC_L(current_entry, reg*4); cycles += 8; } } } } JIT_EMIT_ADD_CYCLES(current_entry, 4 + cycles); return 0; } /*-----------------------------------------------------------------------*/ /** * op_LDM: MOVEM ,reglist (i.e. LoaD Multiple) instruction (format * 0100 1100 1sxx xxxx). */ static int op_LDM(Q68State *state, uint32_t opcode) { unsigned int regmask = IFETCH(state); int size = (opcode & 0x0040) ? SIZE_L : SIZE_W; if (EA_MODE(opcode) <= EA_ADDRESS_REG || EA_MODE(opcode) == EA_PREDECREMENT // Not allowed for load ) { return op_ill(state, opcode); } /* Avoid modifying the register during address resolution */ uint16_t safe_ea; if (EA_MODE(opcode) == EA_POSTINCREMENT) { safe_ea = EA_INDIRECT<<3 | EA_REG(opcode); } else { safe_ea = opcode; } int cycles = ea_resolve(state, safe_ea, SIZE_W, ACCESS_READ); if (cycles < 0) { return op_ill(state, opcode); } if (regmask != 0) { // FIXME: does a real 68000 choke even if regmask==0? #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_EA(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); #endif } int reg; for (reg = 0; reg < 16; reg++, regmask >>= 1) { if (regmask & 1) { if (size == SIZE_W) { if (reg < 8) { JIT_EMIT_LOAD_INC_W(current_entry, reg*4); } else { JIT_EMIT_LOADA_INC_W(current_entry, reg*4); } cycles += 4; } else { JIT_EMIT_LOAD_INC_L(current_entry, reg*4); cycles += 8; } } } if (EA_MODE(opcode) == EA_POSTINCREMENT) { JIT_EMIT_MOVEM_WRITEBACK(current_entry, (8 + EA_REG(opcode)) * 4); } JIT_EMIT_ADD_CYCLES(current_entry, 8 + cycles); return 0; } /*************************************************************************/ /** * opmisc: $4xxx-group misc. instructions (format 0100 1110 01xx xxxx). */ static int opmisc(Q68State *state, uint32_t opcode) { const unsigned int index = (opcode>>3 & 7); return (*opcode_4E4x_table[index])(state, opcode); } /*-----------------------------------------------------------------------*/ /** * opTRAP: TRAP #n instruction (format 0100 1110 0100 nnnn). */ static int opTRAP(Q68State *state, uint32_t opcode) { return raise_exception(state, EX_TRAP + (opcode & 0x000F)); } /*-----------------------------------------------------------------------*/ /** * opLINK: LINK instruction (format 0100 1110 0101 0rrr). */ static int opLINK(Q68State *state, uint32_t opcode) { INSN_GET_REG0; int16_t disp = IFETCH(state); #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); #endif JIT_EMIT_GET_OP1_REGISTER(current_entry, (8+reg0)*4); JIT_EMIT_PUSH_L(current_entry); JIT_EMIT_GET_OP1_REGISTER(current_entry, (8+7)*4); JIT_EMIT_MOVE_L(current_entry); JIT_EMIT_SET_REGISTER_L(current_entry, (8+reg0)*4); JIT_EMIT_GET_OP2_IMMEDIATE(current_entry, disp); JIT_EMIT_ADD_L(current_entry); JIT_EMIT_SET_REGISTER_L(current_entry, (8+7)*4); JIT_EMIT_ADD_CYCLES(current_entry, 16); return 0; } /*-----------------------------------------------------------------------*/ /** * opUNLK: UNLK instruction (format 0100 1110 0101 1rrr). */ static int opUNLK(Q68State *state, uint32_t opcode) { INSN_GET_REG0; JIT_EMIT_GET_OP1_REGISTER(current_entry, (8+reg0)*4); JIT_EMIT_MOVE_L(current_entry); JIT_EMIT_SET_REGISTER_L(current_entry, (8+7)*4); #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); #endif JIT_EMIT_POP_L(current_entry); JIT_EMIT_SET_REGISTER_L(current_entry, (8+reg0)*4); JIT_EMIT_ADD_CYCLES(current_entry, 12); return 0; } /*-----------------------------------------------------------------------*/ /** * opMUSP: MOVE An,USP and MOVE USP,An instructions (format * 0100 1110 0110 xrrr). */ static int opMUSP(Q68State *state, uint32_t opcode) { JIT_EMIT_CHECK_SUPER(current_entry); INSN_GET_REG0; if (opcode & 0x0008) { JIT_EMIT_MOVE_TO_USP(current_entry, reg0*4); } else { JIT_EMIT_MOVE_FROM_USP(current_entry, reg0*4); } JIT_EMIT_ADD_CYCLES(current_entry, 4); return 0; } /*-----------------------------------------------------------------------*/ /** * op4E7x: Instructions with opcodes $4E70-$4E77 that don't fit anywhere * else. */ static int op4E7x(Q68State *state, uint32_t opcode) { switch (opcode & 7) { case 0: // $4E70 RESET JIT_EMIT_CHECK_SUPER(current_entry); JIT_EMIT_ADD_CYCLES(current_entry, 132); return 0; case 1: // $4E71 NOP JIT_EMIT_ADD_CYCLES(current_entry, 4); return 0; case 2: // $4E72 STOP JIT_EMIT_CHECK_SUPER(current_entry); JIT_EMIT_ADD_CYCLES(current_entry, 4); advance_PC(state); JIT_EMIT_STOP(current_entry, IFETCH(state)); return 1; case 3: { // $4E73 RTE JIT_EMIT_CHECK_SUPER(current_entry); #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); #endif JIT_EMIT_ADD_CYCLES(current_entry, 20); JIT_EMIT_RTE(current_entry); PC_updated = 1; return 1; } case 5: // $4E75 RTS #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); #endif JIT_EMIT_ADD_CYCLES(current_entry, 16); JIT_EMIT_RTS(current_entry); PC_updated = 1; return 1; case 6: // $4E76 TRAPV JIT_EMIT_TRAPV(current_entry); JIT_EMIT_ADD_CYCLES(current_entry, 4); return 0; case 7: { // $4E77 RTR #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_READ); #endif JIT_EMIT_ADD_CYCLES(current_entry, 20); JIT_EMIT_RTR(current_entry); PC_updated = 1; return 1; } default: // $4E74 RTD is 68010 only return op_ill(state, opcode); } } /*************************************************************************/ /** * opjump: JSR and JMP instructions (format 0100 1110 1xxx xxxx). */ static int opjump(Q68State *state, uint32_t opcode) { int is_jsr = ~opcode & 0x0040; /* JMP is essentially identical to LEA PC, and has the same * constraints. JSR is equivalent to MOVE.L PC,-(A7) followed by a * JMP to the address. Both use a separate timing table, however. */ int cycles; switch (EA_MODE(opcode)) { case EA_INDIRECT: cycles = 8; break; case EA_DISPLACEMENT: cycles = 10; break; case EA_INDEX: cycles = 14; break; case EA_MISC: switch (EA_REG(opcode)) { case EA_MISC_ABSOLUTE_W: cycles = 10; break; case EA_MISC_ABSOLUTE_L: cycles = 12; break; case EA_MISC_PCREL: cycles = 10; break; case EA_MISC_PCREL_INDEX: cycles = 14; break; default: return op_ill(state, opcode); } break; default: return op_ill(state, opcode); } if (is_jsr) { cycles += 8; } JIT_EMIT_ADD_CYCLES(current_entry, cycles); advance_PC(state); ea_resolve(state, opcode, SIZE_W, ACCESS_READ); // cannot fail if (is_jsr) { #ifndef Q68_DISABLE_ADDRESS_ERROR JIT_EMIT_CHECK_ALIGNED_SP(current_entry, opcode, FAULT_STATUS_IN_DATA | FAULT_STATUS_RW_WRITE); #endif JIT_EMIT_JSR(current_entry, jit_PC); return 0; } else { JIT_EMIT_JMP(current_entry); return 1; } } /*************************************************************************/ /******************* Other miscellaneous instructions ********************/ /*************************************************************************/ /** * opMOVP: MOVEP instruction (0000 rrr1 xx00 1rrr). */ static int opMOVP(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_REG0; int to_memory = opcode & 0x0080; int is_long = opcode & 0x0040; int16_t disp = IFETCH(state); if (to_memory) { if (is_long) { JIT_EMIT_MOVEP_WRITE_L(current_entry, reg0*4, disp, reg*4); } else { JIT_EMIT_MOVEP_WRITE_W(current_entry, reg0*4, disp, reg*4); } } else { if (is_long) { JIT_EMIT_MOVEP_READ_L(current_entry, reg0*4, disp, reg*4); } else { JIT_EMIT_MOVEP_READ_W(current_entry, reg0*4, disp, reg*4); } } JIT_EMIT_ADD_CYCLES(current_entry, is_long ? 24 : 16); return 0; } /*************************************************************************/ /** * opADSX: ADDX/SUBX instructions (1x01 rrr1 ss00 xrrr). */ static int opADSX(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_SIZE; INSN_GET_REG0; const int is_add = opcode & 0x4000; const int is_memory = opcode & 0x0008; const uint16_t src_ea = (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg0; const uint16_t dest_ea = (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg; int dummy; ea_get(state, src_ea, size, 0, &dummy, 1); ea_get(state, dest_ea, size, 1, &dummy, 2); const int do_cc = cc_needed(state, opcode); if (is_add) { if (size == SIZE_B) { JIT_EMIT_ADDX_B(current_entry); if (do_cc) JIT_EMIT_SETCC_ADDX_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_ADDX_W(current_entry); if (do_cc) JIT_EMIT_SETCC_ADDX_W(current_entry); } else { // size == SIZE_L JIT_EMIT_ADDX_L(current_entry); if (do_cc) JIT_EMIT_SETCC_ADDX_L(current_entry); } } else { if (size == SIZE_B) { JIT_EMIT_SUBX_B(current_entry); if (do_cc) JIT_EMIT_SETCC_SUBX_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUBX_W(current_entry); if (do_cc) JIT_EMIT_SETCC_SUBX_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUBX_L(current_entry); if (do_cc) JIT_EMIT_SETCC_SUBX_L(current_entry); } } ea_set(state, dest_ea, size); JIT_EMIT_ADD_CYCLES(current_entry, (is_memory ? (size==SIZE_L ? 30 : 18) : (size==SIZE_L ? 8 : 4))); return 0; } /*-----------------------------------------------------------------------*/ /** * op_BCD: ABCD/SBCD instructions (1x00 rrr1 0000 xrrr). */ static int op_BCD(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_REG0; const int is_add = opcode & 0x4000; const int is_memory = opcode & 0x0008; const uint16_t src_ea = (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg0; const uint16_t dest_ea = (is_memory ? EA_PREDECREMENT : EA_DATA_REG) << 3 | reg; int dummy; ea_get(state, src_ea, SIZE_B, 0, &dummy, 1); ea_get(state, dest_ea, SIZE_B, 1, &dummy, 2); if (is_add) { JIT_EMIT_ABCD(current_entry); } else { JIT_EMIT_SBCD(current_entry); } ea_set(state, dest_ea, SIZE_B); JIT_EMIT_ADD_CYCLES(current_entry, is_memory ? 18 : 6); return 0; } /*-----------------------------------------------------------------------*/ /** * opCMPM: CMPM instructions (1011 rrr1 ss00 1rrr). */ static int opCMPM(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_SIZE; INSN_GET_REG0; const uint16_t src_ea = EA_POSTINCREMENT<<3 | reg0; const uint16_t dest_ea = EA_POSTINCREMENT<<3 | reg; int dummy; ea_get(state, src_ea, size, 0, &dummy, 1); ea_get(state, dest_ea, size, 0, &dummy, 2); const int do_cc = cc_needed(state, opcode); // Just for consistency if (size == SIZE_B) { JIT_EMIT_SUB_B(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_B(current_entry); } else if (size == SIZE_W) { JIT_EMIT_SUB_W(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_W(current_entry); } else { // size == SIZE_L JIT_EMIT_SUB_L(current_entry); if (do_cc) JIT_EMIT_SETCC_CMP_L(current_entry); } JIT_EMIT_ADD_CYCLES(current_entry, SIZE_L ? 20 : 12); return 0; } /*************************************************************************/ /** * op_EXG: EXG instruction (1100 rrr1 xx00 1rrr). */ static int op_EXG(Q68State *state, uint32_t opcode) { INSN_GET_REG; INSN_GET_REG0; const int mode = opcode & 0xF8; if (mode == 0x40) { JIT_EMIT_EXG(current_entry, reg*4, reg0*4); } else if (mode == 0x48) { JIT_EMIT_EXG(current_entry, (8+reg)*4, (8+reg0)*4); } else if (mode == 0x88) { JIT_EMIT_EXG(current_entry, reg*4, (8+reg0)*4); } else { return op_ill(state, opcode); } JIT_EMIT_ADD_CYCLES(current_entry, 6); return 0; } /*************************************************************************/ /*************************************************************************/ /* * Local variables: * c-file-style: "stroustrup" * c-file-offsets: ((case-label . *) (statement-case-intro . *)) * indent-tabs-mode: nil * End: * * vim: expandtab shiftwidth=4: */ yabause-0.9.13.1/src/sh2idle.c000644 001750 001750 00000043610 12256006174 017702 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Fabien Coulon This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "sh2core.h" #include "sh2idle.h" #include "sh2int.h" #include "sh2d.h" #include "memory.h" #define MAX_CYCLE_CHECK 14 // idle loops greater than MAX_CYCLE_CHECK instructions will not be detected. /* Detection of idle loops: ie loops in which no write to memory is done, ending with a conditional jump, at the point of which all registers are "deterministic" in the sense they don't depend on the number of executed loops */ /* bDet : Bitwise register markers. 1: register is deterministic bChg : Bitwise register markers. 1: register has been changed, not in a deterministic way */ u32 bDet, bChg; /* Macro : makes changes resulting from the execution of an instruction in which the content of register only depends on register(s) (and potentially constant values, including memory content which is constant in an idle loop) */ #define delayCheck(PC) SH2idleCheckIterate( fetchlist[((PC) >> 20) & 0x0FF](PC), PC ) #define implies(dest,src) if ( src ) bDet |= dest; else bChg |= dest; #define implies2(dest,dest2,src) if ( src ) bDet |= dest|dest2; else bChg |= dest|dest2; #define implies3(dest,dest2,dest3,src) if ( src ) bDet |= dest|dest2|dest3; else bChg |= dest|dest2|dest3; #define destRB (1< // return 0 : cannot continue idle check, probably because of a memory write switch (INSTRUCTION_A(instruction)) { case 0: switch (INSTRUCTION_D(instruction)) { case 2: switch (INSTRUCTION_C(instruction)) { case 0: implies(destRB, srcSR); //stcsr break; case 1: implies(destRB, srcGBR); //stcgbr; break; case 2: implies(destRB, srcVBR); //stcvbr; break; } break; case 3: switch (INSTRUCTION_C(instruction)) { case 0: implies( destPR, 1 ); //bsrf; case 2: isConst( srcRB ); return delayCheck(PC+2); //braf; } break; case 4: //movbs0; case 5: //movws0; case 6: return 0; //movls0; case 7: implies(destMACL, srcRC && srcRB ); // mull break; case 8: switch (INSTRUCTION_C(instruction)) { case 0: //clrt; case 1: implies( destSRT, 1 ); //sett; break; case 2: implies( destMAC, 1 ); //clrmac; break; } break; case 9: switch (INSTRUCTION_C(instruction)) { case 0: //nop; break; case 1: implies3( destSRM, destSRQ, destSRT, 1 ); //div0u; break; case 2: implies(destRB, srcSRT ); //movt; break; } break; case 10: switch (INSTRUCTION_C(instruction)) { case 0: implies(destRB, srcMACH); //stsmach; break; case 1: implies(destRB, srcMACL); //stsmacl; break; case 2: implies(destRB, srcPR); //stspr; break; } break; case 11: switch (INSTRUCTION_C(instruction)) { case 1: //sleep; break; case 0: isConst(srcPR); //rts; return delayCheck(PC+2); case 2: isConst(srcR15); implies(destSR, srcR15); return delayCheck(PC+2); //rte; } break; case 12: //movbl0; case 13: //movwl0; case 14: implies(destRB, srcRC && srcR0); //movll0; break; case 15: implies(destMAC, srcRC && srcRB && srcSRS && srcMACL && srcMACH); //macl break; } break; case 1: return 0; //movls4; case 2: switch (INSTRUCTION_D(instruction)) { case 0: //movbs; case 1: //movws; case 2: return 0; //movls; case 4: //movbm; case 5: //movwm; case 6: return 0;//movlm; case 7: implies(destSR, srcRC && srcRB ); //div0s break; case 8: implies(destSRT, srcRC && srcRB ); //tst break; case 9: //y_and; case 10: //y_xor; case 11: implies( destRB, srcRB && srcRC ); //y_or break; case 12: implies( destSRT, srcRC && srcRB ); //cmpstr; break; case 13: implies( destRB, srcRB && srcRC ); //xtrct break; case 14: implies( destMAC, srcRB && srcRC ); //mulu break; case 15: implies( destMACL, srcRB && srcRC ); //muls break; } break; case 3: switch(INSTRUCTION_D(instruction)) { case 0: //cmpeq; case 2: //cmphs; case 6: //cmphi; case 7: //cmpgt; case 3: implies( destSRT, srcRB && srcRC ); break; case 4: implies3( destSRQ, destSRT, destRB, srcRB && srcRC && srcSRQ && srcSRM ); //div1; /* CHECK ME */ break; case 13: //dmuls; case 5: implies( destMAC, srcRB && srcRC ); // dmulu break; case 8: implies( destRB, srcRB && srcRC ); //sub break; case 15: //addv; case 11: implies( destSRT, srcRB && srcRC ); //subv break; case 12: implies( destRB, srcRB && srcRC ); //add break; case 10: //subc; case 14: implies2( destRB, destSRT, srcRB && srcRC && srcSRT ); //addc break; } break; case 4: switch(INSTRUCTION_D(instruction)) { case 0: switch(INSTRUCTION_C(instruction)) { case 1: //dt; case 0: //shll; case 2: implies2( destSRT, destRB, srcRB ); //shal break; } break; case 1: switch(INSTRUCTION_C(instruction)) { case 2: case 0: implies2( destSRT, destRB, srcRB ); //shlr; break; case 1: implies( destSRT, srcRB ); //cmppz break; } break; case 2: switch(INSTRUCTION_C(instruction)) { case 0: //stsmmach; case 1: //stsmmacl; case 2: return 0;//stsmpr; } break; case 3: switch(INSTRUCTION_C(instruction)) { case 0: //stcmsr; case 1: //stcmgbr; case 2: return 0; //stcmvbr; } break; case 4: switch(INSTRUCTION_C(instruction)) { case 0: //rotl; case 2: implies2( destRB, destSRT, srcRB ); //rotcl } break; case 5: switch(INSTRUCTION_C(instruction)) { case 0: implies2( destRB, destSRT, srcRB ); //rotr; break; case 1: implies( destSRT, srcRB ); //cmppl; break; case 2: implies2( destSRT, destRB, srcSRT && srcRB ); //rotcr break; } break; case 6: switch(INSTRUCTION_C(instruction)) { case 0: implies( destMACH, srcRB ); //ldsmmach break; case 1: implies( destMACL, srcRB ); //lsdmmacl break; case 2: implies( destPR, srcRB ); //ldsmpr break; } break; case 7: switch(INSTRUCTION_C(instruction)) { case 0: implies( destSR, srcRB ); //ldcmsr break; case 1: implies( destGBR, srcRB ); //ldcmgbr break; case 2: implies( destVBR, srcRB ); //lscmvbr break; } break; case 8: switch(INSTRUCTION_C(instruction)) { case 0: //shll2; case 1: //shll8; case 2: implies( destRB, srcRB ); //shll16 } break; case 9: switch(INSTRUCTION_C(instruction)) { case 0: //shlr2; case 1: //shlr8; case 2: implies( destRB, srcRB ); //shlr16 } break; case 10: switch(INSTRUCTION_C(instruction)) { case 0: implies( destMACH, srcRB ); //ldsmach break; case 1: implies( destMACL, srcRB ); //ldsmacl break; case 2: implies( destPR, srcRB ); //ldspr break; } break; case 11: switch(INSTRUCTION_C(instruction)) { case 0: isConst( srcRB ); implies( destPR, 1 ); break; //jsr case 1: return 0; //tas; case 2: isConst( srcRB ); return delayCheck(PC+2); //jmp } break; case 14: switch(INSTRUCTION_C(instruction)) { case 0: implies( destSR, srcRB ); //ldcsr break; case 1: implies( destGBR, srcRB ); //ldcgbr break; case 2: implies( destVBR, srcRB ); //ldcvrb break; } break; case 15: implies( destMAC, srcRB && srcRC && srcMAC ); //macw break; } case 5: implies( destRB, srcRC ); //movll4 break; case 6: switch (INSTRUCTION_D(instruction)) { case 6: //movlp; case 5: //movwp; case 4: return 0; //movbp; case 0: //movbl; case 1: //movwl; case 2: //movll; case 3: //mov; case 7: //y_not; case 8: //swapb; case 11: //neg; case 12: //extub; case 13: //extuw; case 14: //extsb; case 15: //extsw; case 9: implies( destRB, srcRC ); //swapw; break; case 10: implies2( destRB, destSRT, srcRB && srcSRT ); //negc break; } break; case 7: implies( destRB, srcRB ); //addi; break; case 8: switch (INSTRUCTION_B(instruction)) { case 0: //movbs4; case 1: return 0; //movws4; case 4: //movbl4; case 5: implies( destR0, srcRC ); //movwl4; break; case 8: implies( destSRT, srcR0 ); //cmpim; break; case 9: //bt; case 11: //bf; case 13: //bts; case 15: return 0; //bfs; } break; case 9: implies( destRB, 1 ); //movwi; break; case 10: return delayCheck(PC+2); //bra; case 11: implies( destPR, 1 ); return delayCheck(PC+2); //bsr; case 12: switch(INSTRUCTION_B(instruction)) { case 0: //movbsg; case 1: //movwsg; case 2: //movlsg; case 3: return 0; //trapa; case 4: //movblg; case 5: //movwlg; case 6: implies( destR0, srcGBR ); //movllg break; case 7: implies( destR0, 1 ); //mova; break; case 8: implies( destSRT, srcR0 ); //tsti; break; case 9: //andi; case 10: //xori; case 11: implies( destR0, srcR0 ); //ori; break; case 12: implies( destSRT, srcGBR && srcR0 ); //tstm; break; case 13: //andm; case 14: //xorm; case 15: return 0;//orm; } break; case 13: //movli; case 14: implies( destRB, 1 ); //movi; break; } return 1; } #ifdef IDLE_DETECT_VERBOSE static u32 idleCheckCount = 0; static u32 sh2cycleCount = 0; static u32 sh2oldCycleCount = 0; static u32 oldCheckCount = 0; #define DROP_IDLE {\ idleCheckCount += cycles - context->cycles; \ context->cycles = cycles;} #define IDLE_VERBOSE_SH2_COUNT {\ sh2cycleCount += cycles; \ if ( sh2cycleCount-sh2oldCycleCount > 0x4ffffff ) { \ fprintf( stderr, "%lu idle instructions dropped / %lu sh2 instructions parsed : %g %%\n", \ idleCheckCount-oldCheckCount, sh2cycleCount-sh2oldCycleCount, \ (float)(idleCheckCount-oldCheckCount)/(sh2cycleCount-sh2oldCycleCount)*100 ); \ oldCheckCount = idleCheckCount; \ sh2oldCycleCount = sh2cycleCount; \ }} #else #define DROP_IDLE context->cycles = cycles; #define IDLE_VERBOSE_SH2_COUNT #endif void FASTCALL SH2idleCheck(SH2_struct *context, u32 cycles) { // try to find an idle loop while interpreting u8 isDelayed = 0; u32 loopEnd; u32 loopBegin; s32 disp; u32 cyclesCheckEnd; u32 PC1, PC2, PC3; IDLE_VERBOSE_SH2_COUNT; // run until conditional branching - delayed instruction excluded for (;;) { // Fetch Instruction context->instruction = fetchlist[(context->regs.PC >> 20) & 0x0FF](context->regs.PC); if ( INSTRUCTION_A(context->instruction)==8 ) { switch( INSTRUCTION_B(context->instruction) ) { case 13: //SH2bts isDelayed = 1; case 9: //SH2bt if (context->regs.SR.part.T != 1) { context->regs.PC += 2; context->cycles++; return; } loopEnd = context->regs.PC; disp = (s32)(s8)context->instruction; loopBegin = context->regs.PC = context->regs.PC+(disp<<1)+4; context->cycles += 3; goto branching_reached; break; case 15: //SH2bfs isDelayed = 1; case 11: //SH2bf if (context->regs.SR.part.T == 1) { context->regs.PC += 2; context->cycles++; return; } loopEnd = context->regs.PC; disp = (s32)(s8)context->instruction; loopBegin = context->regs.PC = context->regs.PC+(disp<<1)+4; context->cycles += 3; goto branching_reached; break; default: opcodes[context->instruction](context); } } else opcodes[context->instruction](context); if ( context->cycles >= cycles ) return; } branching_reached: // if branching, execute (delayed included) until getting back to the conditional instruction bDet = bChg = 0; // initialize markers cyclesCheckEnd = context->cycles + MAX_CYCLE_CHECK; if ( isDelayed ) { context->instruction = fetchlist[((loopEnd+2) >> 20) & 0x0FF](loopEnd+2); opcodes[context->instruction](context); context->regs.PC -= 2; if ( !SH2idleCheckIterate(context->instruction,0) ) return; } // First pass while ( context->regs.PC != loopEnd ) { PC1 = context->regs.PC; context->instruction = fetchlist[(PC1 >> 20) & 0x0FF](PC1); if ( !SH2idleCheckIterate(context->instruction,PC1) ) return; opcodes[context->instruction](context); if ( context->cycles >= cyclesCheckEnd ) return; } // conditional jump PC2 = context->regs.PC; context->instruction = fetchlist[(PC2 >> 20) & 0x0FF](PC2); opcodes[context->instruction](context); if ( context->regs.PC != loopBegin ) return; // We are not in a single loop... forget it // Mark unchanged registers as deterministic registers bDet = ~bChg; bDet |= destCONST; // some values need to be constant. From now, changing them is forbidden. // Second pass if ( isDelayed ) if ( !SH2idleCheckIterate(fetchlist[((loopEnd+2) >> 20) & 0x0FF](loopEnd+2),0) ) return; while ( context->regs.PC != loopEnd ) { PC3 = context->regs.PC; context->instruction = fetchlist[(PC3 >> 20) & 0x0FF](PC3); if ( !SH2idleCheckIterate(context->instruction,PC3) ) return; opcodes[context->instruction](context); } context->instruction = fetchlist[(PC2 >> 20) & 0x0FF](PC2); opcodes[context->instruction](context); if ( context->regs.PC != loopBegin ) return; #ifdef IDLE_DETECT_VERBOSE { static u32 oldLoopBegin[2][2] = {{0,0},{0,0}}; if (( !~bDet )&&(loopBegin!=oldLoopBegin[context==MSH2][0])&&(loopBegin!=oldLoopBegin[context==MSH2][1])) { char lineBuf[64]; u32 offset,end; printf( "New %s idle loop at %X -- %X\n", (context==MSH2)?"master":"slave", (int)loopBegin, (int)loopEnd ); if ( loopEnd > loopBegin ) { offset = loopBegin; end = loopEnd; } else { offset = loopEnd; end = loopBegin; } for ( ; offset <= end ; offset+=2 ) { SH2Disasm(offset, MappedMemoryReadWord(offset), 0, lineBuf); printf( "%s\n", lineBuf ); } oldLoopBegin[context==MSH2][1] = oldLoopBegin[context==MSH2][0]; oldLoopBegin[context==MSH2][0] = loopBegin; } } #endif if ( !~bDet ) { DROP_IDLE; context->isIdle = 1; } } void FASTCALL SH2idleParse( SH2_struct *context, u32 cycles ) { // called when is in idle state : check whether we are still idle IDLE_VERBOSE_SH2_COUNT; for(;;) { u32 PC = context->regs.PC; context->instruction = fetchlist[(PC >> 20) & 0x0FF](PC); if ( INSTRUCTION_A(context->instruction)==8 ) { switch( INSTRUCTION_B(context->instruction) ) { case 13: //SH2bts case 9: //SH2bt if ( !context->regs.SR.part.T ) context->isIdle = 0; else DROP_IDLE; opcodes[context->instruction](context); return; case 15: //SH2bfs case 11: //SH2bf if ( context->regs.SR.part.T ) context->isIdle = 0; else DROP_IDLE; opcodes[context->instruction](context); return; } } opcodes[context->instruction](context); } } /* ------------------------------------------------------ */ /* Code markers */ /* typedef struct { u32 begin, end; u16 instruction; } idlemarker; idlemarker idleMark[1024]; int topMark = 0; void addMarker( u32 begin, u32 end ) { if ( topMark < 1024 ) { idleMark[topMark].instruction = MappedMemoryReadWord(begin); if ( (idleMark[topMark].instruction & 0xff0000ff) == OPCODE_HC ) return; idleMark[topMark].begin = begin; idleMark[topMark].end = end; idleMark[topMark].instruction = MappedMemoryReadWord(begin); MappedMemoryWriteWord( begin, OPCODE_HC | (topMark<<8) ); topMark++; } else printf( stderr, "Code Marker overflow !\n" ); } void markerExec( SH2_struct *sh, u16 nMark ) { opcodes[sh->instruction = idleMark[instruction]](sh); // execute the hidden instruction for{;;} { u32 PC = sh->regs.PC; sh->instruction = fetchlist[(PC >> 20) & 0x0FF](PC); if ( INSTRUCTION_A(context->instruction)==8 ) { switch( INSTRUCTION_B(context->instruction) ) { case 13: //SH2bts case 9: //SH2bt if ( sh->regs.SR.T ) cycles = 0xffffffff; return; case 15: //SH2bfs case 11: //SH2bf if ( ! sh->regs.SR.T ) cycles = 0xffffffff; return; } opcodes[context->instruction](context); } } */ yabause-0.9.13.1/src/scr-x.c000644 001750 001750 00000006167 12256006124 017405 0ustar00guillaumeguillaume000000 000000 /* Copyright 2013 Theo Berkau Copyright 2013 Guillaume Duhamel This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "screen.h" #include #include #include typedef struct { Display *dpy; XRRScreenSize *xrrs; int num_sizes; int current_size; short * rates; int num_rates; int current_rate; } X11ResolutionList; static XRRScreenConfiguration *x11Conf; static short x11OriginalRate; static SizeID x11OriginalSizeId; static Rotation x11OriginalRotation; ResolutionList ScreenGetResolutions() { X11ResolutionList * list; list = malloc(sizeof(X11ResolutionList)); list->dpy = XOpenDisplay(NULL); list->xrrs = XRRSizes(list->dpy, 0, &list->num_sizes); list->rates = XRRRates(list->dpy, 0, 0, &list->num_rates); list->current_size = 0; list->current_rate = 0; return list; } int ScreenNextResolution(ResolutionList rl, supportedRes_struct * res) { X11ResolutionList * list = rl; if (list->current_rate < list->num_rates) { res->index = list->current_size; res->width = list->xrrs[list->current_size].width; res->height = list->xrrs[list->current_size].height; res->freq = list->rates[list->current_rate]; res->bpp = 0; list->current_rate++; return 0; } list->current_size++; if (list->current_size < list->num_sizes) { list->rates = XRRRates(list->dpy, 0, list->current_size, &list->num_rates); list->current_rate = 0; return ScreenNextResolution(list, res); } XCloseDisplay(list->dpy); free(list); return 1; } void ScreenChangeResolution(supportedRes_struct * res) { Display *dpy; Window root; // Open X11 connection dpy = XOpenDisplay(NULL); root = RootWindow(dpy, 0); // Save original settings x11Conf = XRRGetScreenInfo(dpy, root); x11OriginalRate = XRRConfigCurrentRate(x11Conf); x11OriginalSizeId = XRRConfigCurrentConfiguration(x11Conf, &x11OriginalRotation); // Change resolution XRRSetScreenConfigAndRate(dpy, x11Conf, root, res->index, RR_Rotate_0, res->freq, CurrentTime); // Close connection XCloseDisplay(dpy); } void ScreenRestoreResolution() { Display *dpy; Window root; // Open X11 connection dpy = XOpenDisplay(NULL); root = RootWindow(dpy, 0); XRRSetScreenConfigAndRate(dpy, x11Conf, root, x11OriginalSizeId, x11OriginalRotation, x11OriginalRate, CurrentTime); // Close connection XCloseDisplay(dpy); } yabause-0.9.13.1/src/scsp2.c000644 001750 001750 00000342441 12256006126 017403 0ustar00guillaumeguillaume000000 000000 /* src/scsp2.c: New, threadable SCSP implementation for Yabause Copyright 2004 Stephane Dallongeville Copyright 2004-2007 Theo Berkau Copyright 2006 Guillaume Duhamel Copyright 2010 Andrew Church This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "core.h" #include "debug.h" #include "error.h" #include "m68kcore.h" #include "memory.h" #include "scsp.h" #include "threads.h" #include "yabause.h" #include #include #undef round // In case math.h defines it #define round(x) ((int) (floor((x) + 0.5))) #undef ScspInit // Disable compatibility alias extern SoundInterface_struct *SNDCoreList[]; // Defined by each port /////////////////////////////////////////////////////////////////////////// // This SCSP implementation is designed to be runnable as an independent // thread, encompassing the SCSP emulation itself as well as the MC68EC000 // sound processor and the actual generation of PCM audio data. // // When running in multithreaded mode, the actual SCSP and M68K emulation // is performed via ScspThread(). This function is started as a subthread // (YAB_THREAD_SCSP), and loops over ScspDoExec() until scsp_thread_running // goes to zero, which is used as a signal for the thread to stop. // Synchronization is performed via the scsp_clock_target variable; the SCSP // thread sleeps until clock_target != clock, then calls ScspDoExec() for // (clock - clock_target) cycles. The main thread wakes up the subthread // both when clock_target is updated and when register writes, discussed // below, are submitted. // // Additionally, any register writes from outside the SCSP/M68K will be // processed synchronously in multithreaded mode, by passing the write // through a shared buffer (scsp_write_buffer_* variables). When the // thread loop detects scsp_write_buffer_size nonzero, it processes the // write before its next ScspDoExec() iteration and clears ..._size; the // main thread then waits for ..._size to return to zero before returning // from the write operation. (This will typically be no more expensive // than a pair of context switches, and it seems that SCSP register writes // from the SH-2 are uncommon.) // // The "PSP_*" macros scattered throughout the file are to support the // execution of the SCSP thread on the Media Engine CPU (ME) in the PSP. // The ME lacks cache coherence with the main CPU (SC), so special care // needs to be taken to avoid bugs arising from inconsistent cache states; // see the technical notes in README.PSP for details. These macros are all // no-ops on other platforms. //------------------------------------------------------------------------- // PSP cache management macros #ifdef PSP # include "psp/common.h" # include "psp/me.h" # include "psp/me-utility.h" # include "psp/misc.h" // psp_writeback_cache_for_scsp() declaration // Data section management (to avoid cache line collisions between CPUs) # define PSP_SECTION(name) \ __attribute__((section(".meshared.scsp." #name))) # define PSP_SECTION_START(name) \ __attribute__((section(".meshared.scsp." #name), aligned(64))) \ extern volatile char __scsp_sectstart_##name[64]; # define PSP_SECTION_END(name) \ __attribute__((section(".meshared.scsp." #name))) \ extern volatile char __scsp_sectend_##name; // Cache control # define PSP_WRITEBACK_CACHE(ptr,len) \ sceKernelDcacheWritebackRange((ptr), (len)) # define PSP_WRITEBACK_ALL() \ sceKernelDcacheWritebackAll() # define PSP_FLUSH_ALL() \ sceKernelDcacheWritebackInvalidateAll() // Uncached pointer access # define PSP_UCPTR(ptr) ((typeof(&*ptr))((uint32_t)(ptr) | 0x40000000)) // Uncached variable access (either read or write) # define PSP_UC(var) (*((typeof(&var))((uint32_t)(&var) | 0x40000000))) #else // !PSP # define PSP_SECTION(name) /*nothing*/ # define PSP_SECTION_START(name) /*nothing*/ # define PSP_SECTION_END(name) /*nothing*/ # define PSP_WRITEBACK_CACHE(ptr,len) /*nothing*/ # define PSP_WRITEBACK_ALL() /*nothing*/ # define PSP_FLUSH_ALL() /*nothing*/ # define PSP_UCPTR(ptr) ptr # define PSP_UC(var) var #endif //------------------------------------------------------------------------- // SCSP constants // SCSP hardware version (4 bits) #define SCSP_VERSION 0 // SCSP clock frequency (11.2896 MHz, or exactly 44100*256) #define SCSP_CLOCK_FREQ (44100 * 256) // SCSP output frequency #define SCSP_OUTPUT_FREQ (SCSP_CLOCK_FREQ / 256) // SCSP clock increment per 1/10 scanline #define SCSP_CLOCK_INC_NTSC (((u64)SCSP_CLOCK_FREQ<<20) * 1001 / 60000 / 263 / 10) #define SCSP_CLOCK_INC_PAL (((u64)SCSP_CLOCK_FREQ<<20) / 50 / 313 / 10) // Limit on execution time for a single thread loop (in SCSP clock cycles); // if the thread's delay exceeds this value, we stop in the main loop to // let the SCSP catch up #define SCSP_CLOCK_MAX_EXEC (SCSP_CLOCK_FREQ / 1000) // Sound RAM size #define SCSP_RAM_SIZE 0x80000 #define SCSP_RAM_MASK (SCSP_RAM_SIZE - 1) // Envelope phases #define SCSP_ENV_RELEASE 0 #define SCSP_ENV_SUSTAIN 1 #define SCSP_ENV_DECAY 2 #define SCSP_ENV_ATTACK 3 // LFO waveform types (equal to ALFOWS/PLFOWS values) #define SCSP_LFO_SAWTOOTH 0 #define SCSP_LFO_SQUARE 1 #define SCSP_LFO_TRIANGLE 2 #define SCSP_LFO_NOISE 3 // Bit sizes of fixed-point counters // Fractional part of frequency counter (determines accuracy of audio // playback frequency) #define SCSP_FREQ_LOW_BITS 10 // Integer part of envelope counter (determines resolution of attack/decay // envelope); also used to define envelope value range #define SCSP_ENV_HIGH_BITS 10 // Fractional part of envelope counter (determines accuracy of envelope timing) #define SCSP_ENV_LOW_BITS 10 // Integer part of LFO counter (determines resolution of LFO waveform); // also used to define LFO value range #define SCSP_LFO_HIGH_BITS 10 // Fractional part of LFO counter (determines accuracy of LFO frequency) #define SCSP_LFO_LOW_BITS 10 // Fractional part of TL attenuation lookup table (determines resolution of // per-voice volume control) #define SCSP_TL_BITS 10 // Envelope/waveform table data sizes and corresponding masks #define SCSP_ENV_LEN (1 << SCSP_ENV_HIGH_BITS) #define SCSP_ENV_MASK (SCSP_ENV_LEN - 1) #define SCSP_LFO_LEN (1 << SCSP_LFO_HIGH_BITS) #define SCSP_LFO_MASK (SCSP_LFO_LEN - 1) // Envelope attack/decay points (counter values) #define SCSP_ENV_ATTACK_START 0 #define SCSP_ENV_DECAY_START (SCSP_ENV_LEN << SCSP_ENV_LOW_BITS) #define SCSP_ENV_ATTACK_END (SCSP_ENV_DECAY_START - 1) #define SCSP_ENV_DECAY_END (((2 * SCSP_ENV_LEN) << SCSP_ENV_LOW_BITS) - 1) // Envelope attack/decay base times #define SCSP_ATTACK_TIME ((u32) (8 * SCSP_OUTPUT_FREQ)) #define SCSP_DECAY_TIME ((u32) (12 * SCSP_ATTACK_TIME)) // Interrupt bit numbers #define SCSP_INTERRUPT_MIDI_IN 3 // Data available in MIDI input buffer #define SCSP_INTERRUPT_DMA 4 // DMA complete #define SCSP_INTERRUPT_MANUAL 5 // 1 written to bit 5 of [MS]CIPD #define SCSP_INTERRUPT_TIMER_A 6 // Timer A reached 0xFF #define SCSP_INTERRUPT_TIMER_B 7 // Timer B reached 0xFF #define SCSP_INTERRUPT_TIMER_C 8 // Timer C reached 0xFF #define SCSP_INTERRUPT_MIDI_OUT 9 // MIDI output buffer became empty #define SCSP_INTERRUPT_SAMPLE 10 // Raised once per output sample // Interrupt target flags #define SCSP_INTTARGET_MAIN (1 << 0) // Interrupt to main CPU (SCU) #define SCSP_INTTARGET_SOUND (1 << 1) // Interrupt to sound CPU #define SCSP_INTTARGET_BOTH (SCSP_INTTARGET_MAIN | SCSP_INTTARGET_SOUND) // PCM output buffer size parameters #define SCSP_SOUND_LEN_NTSC (SCSP_OUTPUT_FREQ / 60) // Samples per frame #define SCSP_SOUND_LEN_PAL (SCSP_OUTPUT_FREQ / 50) // Reserve 10x the maximum samples per frame #define SCSP_SOUND_BUFSIZE (10 * SCSP_SOUND_LEN_PAL) // CDDA data buffer size in sectors (must be at least 3) #define CDDA_NUM_BUFFERS 3 // CDDA playback start delay in samples (see cdda_delay comments) #define CDDA_DELAY_SAMPLES 100 //------------------------------------------------------------------------- // Internal state data structures // Per-slot data structure typedef struct SlotState_struct { //////////// // Register fields // ISR $00 // [12] Write 1 to execute KEY state change u8 key; // [11] KEY state (on/off) u8 sbctl; // [10:9] Source bit control u8 ssctl; // [8:7] Sound source control u8 lpctl; // [6:5] Loop control u8 pcm8b; // [4] PCM sound format // [3:0] Start address (in bytes), high bits (19:16) // ISR $02 u32 sa; // [15:0] Start address (in bytes), low bits (15:0) // ISR $04 u16 lsa; // [15:0] Loop start address (in samples) // ISR $06 u16 lea; // [15:0] Loop end address (in samples) // ISR $08 u8 sr; // [15:11] Sustain rate u8 dr; // [10:6] Decay rate u8 eghold; // [5] Envelope hold (attack rate 0) flag u8 ar; // [4:0] Attack rate // ISR $0A u8 lpslnk; // [14] Loop start link (start decay on reaching LSA) u8 krs; // [13:10] Key rate scale u8 sl; // [9:5] Sustain level u8 rr; // [4:0] Release rate // ISR $0C u8 stwinh; // [9] Stack write inhibit flag u8 sdir; // [8] Sound direct output flag u8 tl; // [7:0] Total level // ISR $0E u8 mdl; // [15:12] Modulation level u8 mdx; // [11:6] Modulation source X u8 mdy; // [5:0] Modulation source Y // ISR $10 u8 oct; // [14:11] Octave (treated as signed -8..7) u16 fns; // [9:0] Frequency number switch // ISR $12 u8 lfore; // [15] LFO reset flag (1 = reset, 0 = count) u8 lfof; // [14:10] LFO frequency index u8 plfows; // [9:8] Pitch LFO waveform select u8 plfos; // [7:5] Pitch LFO sensitivity u8 alfows; // [4:3] Amplitude LFO waveform select u8 alfos; // [2:0] Amplitude LFO sensitivity // ISR $14 u8 isel; // [6:3] Input selector u8 imxl; // [2:0] Input mix level // ISR $16 u8 disdl; // [15:13] Direct data send level u8 dipan; // [12:8] Direct data pan position u8 efsdl; // [7:5] Effect data send level u8 efpan; // [2:0] Effect data pan position //////////// // Internal state // Audio generation routine (selected based on slot parameters) void (* FASTCALL audiogen)(struct SlotState_struct *slot, u32 len); const void *buf; // Pointer to sample data in sound RAM u32 addr_counter; // Address (playback) counter u32 addr_step; // Address counter increment u8 octave_shift; // Octave shift amount (0..15) u32 lsa_shifted; // lsa << SCSP_FREQ_LOW_BITS (for addr_counter) u32 lea_shifted; // lea << SCSP_FREQ_LOW_BITS (for addr_counter) u32 looplen_shifted;// (lea - lsa + 1) << SCSP_FREQ_LOW_BITS u32 env_phase; // Current envelope phase (attack/decay/...) s32 env_counter; // Envelope counter s32 env_step; // Envelope counter increment for current phase s32 env_target; // Envelope target value for advancing to next phase s32 env_step_a; // Envelope counter increment for attack phase s32 env_step_d; // Envelope counter increment for decay phase s32 env_step_s; // Envelope counter increment for sustain phase s32 env_step_r; // Envelope counter increment for release phase s32 last_env; // Last calculated envelope multiplier u8 krs_shift; // Shift count corresponding to KRS s32 sl_target; // Compare value corresponding to SL s32 tl_mult; // Envelope volume multiplier corresponding to TL u32 lfo_counter; // LFO counter (fixed point index into LFO waveforms) s32 lfo_step; // LFO counter increment, or -1 if in reset mode s32 *lfo_fm_wave; // LFO frequency modulation waveform pointer s32 *lfo_am_wave; // LFO amplitude modulation waveform pointer s8 lfo_fm_shift; // LFO frequency modulation strength, -1 if disabled s8 lfo_am_shift; // LFO amplitude modulation strength, -1 if disabled u8 outshift_l; // Output shift for left channel (down to 16 bits) u8 outshift_r; // Output shift for right channel (down to 16 bits) u8 imxl_shift; // Shift count for IMXL } SlotState; //------------------------------------ // Overall SCSP data structure typedef struct ScspState_struct { //////////// // Register fields // $400 u8 mem4mb; // [9] Sound RAM memory size flag (4Mbit vs. 2Mbit) u8 dac18b; // [8] DAC 18-bit output flag (ignored) u8 ver; // [7:4] Hardware version (fixed at 0) u8 mvol; // [3:0] Master volume // $402 u8 rbl; // [8:7] Ring buffer length (8192<Exec or M68KExecBP static s32 m68k_saved_cycles; // Requested minus actual cycles executed static M68KBreakpointInfo m68k_breakpoint[M68K_MAX_BREAKPOINTS]; static int m68k_num_breakpoints; static void (*M68KBreakpointCallback)(u32); static u8 m68k_in_breakpoint; //------------------------------------------------------------------------- // Local function declarations static void ScspThread(void *arg); static void ScspDoExec(u32 cycles); static u32 ScspTimerCyclesLeft(u16 timer, u8 timer_scale); static void ScspUpdateTimer(u32 samples, u16 *timer_ptr, u8 timer_scale, int interrupt); static void ScspGenerateAudio(s32 *bufL, s32 *bufR, u32 samples); static void ScspGenerateAudioForSlot(SlotState *slot, u32 samples); static void ScspGenerateAudioForCDDA(s32 *bufL, s32 *bufR, u32 samples); static u8 FASTCALL ScspReadByteDirect(u32 address); static u16 FASTCALL ScspReadWordDirect(u32 address); static void FASTCALL ScspWriteByteDirect(u32 address, u8 data); static void FASTCALL ScspWriteWordDirect(u32 address, u16 data); static u16 ScspReadMonitor(void); static void ScspDoKeyOnOff(void); static void ScspKeyOn(SlotState *slot); static void ScspKeyOff(SlotState *slot); static void ScspUpdateSlotAddress(SlotState *slot); static void ScspUpdateSlotEnv(SlotState *slot); static void ScspUpdateSlotFunc(SlotState *slot); static u16 ScspMidiIn(void); static void ScspMidiOut(u8 data); static void ScspDoDMA(void); static void ScspSyncThread(void); static void ScspRaiseInterrupt(int which, int target); static void ScspCheckInterrupts(u16 mask, int target); static void ScspClearInterrupts(u16 mask, int target); static void ScspRunM68K(u32 cycles); static s32 FASTCALL M68KExecBP(s32 cycles); static int scsp_mute_flags = 0; static int scsp_volume = 100; /////////////////////////////////////////////////////////////////////////// // Single-slot audio generation routines and corresponding table. The // table is indexed by: // scsp_audiogen_func_table[F][A][S][L][R] // ^ ^ ^ ^ ^-- Right channel on/off (on = 1) // | | | `-- Left channel on/off (on = 1) // | | `-- Sample size 16/8 bit (16 bit = 1) // | `-- Amplitude modulation on/off (on = 1) // `-- Frequency modulation on/off (on = 1) /////////////////////////////////////////////////////////////////////////// // For convenience, we use a single, parameterized macro to define every // function, with 0 or 1 in each of the F/A/S/L/R parameters; the compiler // will optimize out the disabled branches. // A couple of handy sub-macros: #define ADDRESS (addr_counter >> SCSP_FREQ_LOW_BITS) #ifdef WORDS_BIGENDIAN #define ADDRESS_8BIT (ADDRESS) #else #define ADDRESS_8BIT (ADDRESS ^ 1) #endif #define ENV_POS (env_counter >> SCSP_ENV_LOW_BITS) #define LFO_POS ((slot->lfo_counter >> SCSP_LFO_LOW_BITS) & SCSP_LFO_MASK) #define DEFINE_AUDIOGEN(tag,F,A,S,L,R) \ static void FASTCALL audiogen_##tag(SlotState *slot, u32 len) \ { \ /* Load these first to avoid having to reload them every iteration */ \ u32 addr_counter = slot->addr_counter; \ const u32 addr_step = slot->addr_step; \ u32 env_counter = slot->env_counter; \ u32 env_step = slot->env_step; \ \ u32 pos; \ for (pos = 0; pos < len; pos++) \ { \ if (L || R) /* Don't bother with calculations if it's all silent */ \ { \ /* Compute envelope/TL multiplier for waveform data */ \ s32 env = scsp_env_table[ENV_POS] * slot->tl_mult >> SCSP_TL_BITS; \ if (A) \ env -= slot->lfo_am_wave[LFO_POS] >> slot->lfo_am_shift; \ slot->last_env = env; \ \ /* Apply envelope / channel volume to waveform data and output */ \ if (LIKELY(env > 0)) \ { \ s32 out; \ if (S) \ out = (s32) ((const s16 *)slot->buf)[ADDRESS]; \ else \ out = (s32) ((const s8 *)slot->buf)[ADDRESS_8BIT] << 8; \ out *= env; \ if (L) \ scsp_bufL[pos] += out >> slot->outshift_l; \ if (R) \ scsp_bufR[pos] += out >> slot->outshift_r; \ } \ } \ \ /* Update address counter, exiting if we reach the end of the data */ \ if (F) \ { \ /* FIXME: need to handle the case where LFO data range != 1<lfo_fm_wave[LFO_POS] \ << slot->lfo_fm_shift \ >> slot->octave_shift; \ } \ addr_counter += addr_step; \ if (UNLIKELY(addr_counter > slot->lea_shifted)) \ { \ /* FIXME: reverse/alternating loops not implemented */ \ if (slot->lpctl) \ { \ addr_counter = slot->lsa_shifted \ + ((addr_counter - slot->lsa_shifted) \ % slot->looplen_shifted); \ } \ else \ { \ env_counter = SCSP_ENV_DECAY_END; \ goto done; \ } \ } \ \ /* Update envelope counter, advancing the envelope phase as needed */ \ env_counter += env_step; \ if (UNLIKELY(env_counter >= slot->env_target)) \ { \ switch (slot->env_phase) \ { \ case SCSP_ENV_ATTACK: \ env_counter = SCSP_ENV_DECAY_START; \ env_step = slot->env_step = slot->env_step_d; \ slot->env_target = slot->sl_target; \ slot->env_phase = SCSP_ENV_DECAY; \ break; \ case SCSP_ENV_DECAY: \ env_counter = slot->sl_target; \ env_step = slot->env_step = slot->env_step_s; \ slot->env_target = SCSP_ENV_DECAY_END; \ slot->env_phase = SCSP_ENV_SUSTAIN; \ break; \ default: \ env_counter = SCSP_ENV_DECAY_END; \ env_step = slot->env_step = 0; \ slot->env_target = SCSP_ENV_DECAY_END + 1; \ goto done; \ } \ } \ \ /* Update the LFO counter if either LFO waveform is in use \ * (technically, we should update whenever slot->lfore == 0, but \ * we skip the update on non-modulated channels to save time) */ \ if (F || A) \ slot->lfo_counter += slot->lfo_step; \ } \ \ done: \ slot->addr_counter = addr_counter; \ slot->env_counter = env_counter; \ } //------------------------------------------------------------------------- // Define the actual audio generation functions. For simplicity, we name // each function using the state of its parameter flags, with uppercase for // an enabled flag and lowercase for a disabled flag. We also use the null // output function for all cases where L and R are zero, to avoid // unnecessary code bloat. DEFINE_AUDIOGEN(null, 0,0,0,0,0) DEFINE_AUDIOGEN(faslR, 0,0,0,0,1) DEFINE_AUDIOGEN(fasLr, 0,0,0,1,0) DEFINE_AUDIOGEN(fasLR, 0,0,0,1,1) DEFINE_AUDIOGEN(faSlR, 0,0,1,0,1) DEFINE_AUDIOGEN(faSLr, 0,0,1,1,0) DEFINE_AUDIOGEN(faSLR, 0,0,1,1,1) DEFINE_AUDIOGEN(fAslR, 0,1,0,0,1) DEFINE_AUDIOGEN(fAsLr, 0,1,0,1,0) DEFINE_AUDIOGEN(fAsLR, 0,1,0,1,1) DEFINE_AUDIOGEN(fASlR, 0,1,1,0,1) DEFINE_AUDIOGEN(fASLr, 0,1,1,1,0) DEFINE_AUDIOGEN(fASLR, 0,1,1,1,1) DEFINE_AUDIOGEN(FaslR, 1,0,0,0,1) DEFINE_AUDIOGEN(FasLr, 1,0,0,1,0) DEFINE_AUDIOGEN(FasLR, 1,0,0,1,1) DEFINE_AUDIOGEN(FaSlR, 1,0,1,0,1) DEFINE_AUDIOGEN(FaSLr, 1,0,1,1,0) DEFINE_AUDIOGEN(FaSLR, 1,0,1,1,1) DEFINE_AUDIOGEN(FAslR, 1,1,0,0,1) DEFINE_AUDIOGEN(FAsLr, 1,1,0,1,0) DEFINE_AUDIOGEN(FAsLR, 1,1,0,1,1) DEFINE_AUDIOGEN(FASlR, 1,1,1,0,1) DEFINE_AUDIOGEN(FASLr, 1,1,1,1,0) DEFINE_AUDIOGEN(FASLR, 1,1,1,1,1) // We don't need these anymore, so get rid of them #undef ADDRESS #undef ADDRESS_8BIT #undef ENV_POS #undef LFO_POS #undef DEFINE_AUDIOGEN //------------------------------------------------------------------------- // Define the function lookup table. static void (* FASTCALL scsp_audiogen_func_table[2][2][2][2][2])(SlotState *slot, u32 len) = { { // F==0 { // A==0 {{audiogen_null, audiogen_faslR}, {audiogen_fasLr, audiogen_fasLR}}, {{audiogen_null, audiogen_faSlR}, {audiogen_faSLr, audiogen_faSLR}} }, { // A==1 {{audiogen_null, audiogen_fAslR}, {audiogen_fAsLr, audiogen_fAsLR}}, {{audiogen_null, audiogen_fASlR}, {audiogen_fASLr, audiogen_fASLR}} } }, { // F==1 { // A==0 {{audiogen_null, audiogen_FaslR}, {audiogen_FasLr, audiogen_FasLR}}, {{audiogen_null, audiogen_FaSlR}, {audiogen_FaSLr, audiogen_FaSLR}} }, { // A==1 {{audiogen_null, audiogen_FAslR}, {audiogen_FAsLr, audiogen_FAsLR}}, {{audiogen_null, audiogen_FASlR}, {audiogen_FASLr, audiogen_FASLR}} } } }; /////////////////////////////////////////////////////////////////////////// // Initialization, configuration, and cleanup routines /////////////////////////////////////////////////////////////////////////// // ScspInit: Initialize the SCSP emulation. interrupt_handler should // specify a function to handle interrupts delivered to the SCU. // Must be called after M68KInit(); returns 0 on success, -1 on failure. int ScspInit(int coreid, void (*interrupt_handler)(void)) { int i, j; double x; if ((SoundRam = T2MemoryInit(0x80000)) == NULL) return -1; // Fill in lookup tables for (i = 0; i < SCSP_ENV_LEN; i++) { // Attack Curve (x^4 ?) x = pow(((double) (SCSP_ENV_MASK - i) / SCSP_ENV_LEN), 4); x *= (double) SCSP_ENV_LEN; scsp_env_table[i] = SCSP_ENV_MASK - (s32) floor(x); // Decay curve (x = linear) scsp_env_table[i + SCSP_ENV_LEN] = SCSP_ENV_MASK - i; } for (i = 0, j = 0; i < 32; i++) { double lfo_frequency, lfo_step; // Frequency divider follows the pattern 1,2,3,4, 6,8,10,12, 16,... j += 1 << (i >> 2); // Base LFO frequency is 44100/256 or ~172.3 Hz lfo_frequency = (44100.0 / 256.0) / j; // Calculate LFO address step per output sample; we use a literal // 44100 above but OUTPUT_FREQ here in case anyone wants to try // upsampling the audio output someday lfo_step = (lfo_frequency / SCSP_OUTPUT_FREQ) * SCSP_LFO_LEN; scsp_lfo_step[31 - i] = round(lfo_step * (1 << SCSP_LFO_LOW_BITS)); } for (i = 0; i < SCSP_LFO_LEN; i++) { // Amplitude modulation uses unsigned values which are subtracted // from the base envelope value scsp_lfo_wave_amp[SCSP_LFO_SAWTOOTH][i] = i; if (i < SCSP_LFO_LEN / 2) scsp_lfo_wave_amp[SCSP_LFO_SQUARE][i] = 0; else scsp_lfo_wave_amp[SCSP_LFO_SQUARE][i] = SCSP_LFO_MASK; if (i < SCSP_LFO_LEN / 2) scsp_lfo_wave_amp[SCSP_LFO_TRIANGLE][i] = i*2; else scsp_lfo_wave_amp[SCSP_LFO_TRIANGLE][i] = SCSP_LFO_MASK - ((i - SCSP_LFO_LEN/2) * 2); scsp_lfo_wave_amp[SCSP_LFO_NOISE][i] = rand() & SCSP_LFO_MASK; // FIXME: note that the noise generator output should be independent // of LFORE/LFOF // Frequency modulation uses signed values which are added to the // address counter if (i < SCSP_LFO_LEN / 2) scsp_lfo_wave_freq[SCSP_LFO_SAWTOOTH][i] = i; else scsp_lfo_wave_freq[SCSP_LFO_SAWTOOTH][i] = i - SCSP_LFO_LEN; if (i < SCSP_LFO_LEN / 2) scsp_lfo_wave_freq[SCSP_LFO_SQUARE][i] = SCSP_LFO_MASK - SCSP_LFO_LEN/2; else scsp_lfo_wave_freq[SCSP_LFO_SQUARE][i] = 0 - SCSP_LFO_LEN/2; if (i < SCSP_LFO_LEN / 4) scsp_lfo_wave_freq[SCSP_LFO_TRIANGLE][i] = i*2; else if (i < SCSP_LFO_LEN * 3 / 4) scsp_lfo_wave_freq[SCSP_LFO_TRIANGLE][i] = SCSP_LFO_MASK - i*2; else scsp_lfo_wave_freq[SCSP_LFO_TRIANGLE][i] = i*2 - SCSP_LFO_LEN*2; scsp_lfo_wave_freq[SCSP_LFO_NOISE][i] = scsp_lfo_wave_amp[SCSP_LFO_NOISE][i] - SCSP_LFO_LEN/2; } for (i = 0; i < 4; i++) { scsp_attack_rate[i] = 0; scsp_decay_rate[i] = 0; } for (i = 0; i < 60; i++) { x = 1.0 + ((i & 3) * 0.25); // Bits 0-1: x1.00, x1.25, x1.50, x1.75 x *= 1 << (i >> 2); // Bits 2-5: shift bits (x2^0 - x2^15) x *= SCSP_ENV_LEN << SCSP_ENV_LOW_BITS; // Adjust for envelope table size scsp_attack_rate[i + 4] = round(x / SCSP_ATTACK_TIME); if (scsp_attack_rate[i + 4] == 0) scsp_attack_rate[i + 4] = 1; scsp_decay_rate[i + 4] = round(x / SCSP_DECAY_TIME); if (scsp_decay_rate[i + 4] == 0) scsp_decay_rate[i + 4] = 1; } scsp_attack_rate[63] = SCSP_ENV_ATTACK_END; scsp_decay_rate[61] = scsp_decay_rate[60]; scsp_decay_rate[62] = scsp_decay_rate[60]; scsp_decay_rate[63] = scsp_decay_rate[60]; for (i = 64; i < 78; i++) { scsp_attack_rate[i] = scsp_attack_rate[63]; scsp_decay_rate[i] = scsp_decay_rate[63]; } for (i = 0; i < 256; i++) scsp_tl_table[i] = round(pow(2.0, -(i/16.0)) * (1 << SCSP_TL_BITS)); // Initialize the SCSP state scsp_interrupt_handler = interrupt_handler; scsp_clock_inc = yabsys.IsPal ? SCSP_CLOCK_INC_PAL : SCSP_CLOCK_INC_NTSC; ScspReset(); // Note that we NEVER reset the clock counter after initialization, // because in multithreaded mode, that would cause a race condition in // which the SCSP thread runs between the two assignments and detects // clock != clock_target, causing it to execute a huge number of cycles. // (We do, however, reset the accumulated fraction of a cycle inside // ScspReset().) scsp_clock = 0; scsp_clock_target = 0; // Initialize the M68K state if (M68K->Init() != 0) return -1; M68K->SetReadB(M68KReadByte); M68K->SetReadW(M68KReadWord); M68K->SetWriteB(M68KWriteByte); M68K->SetWriteW(M68KWriteWord); M68K->SetFetch(0x000000, 0x040000, (pointer)SoundRam); M68K->SetFetch(0x040000, 0x080000, (pointer)SoundRam); M68K->SetFetch(0x080000, 0x0C0000, (pointer)SoundRam); M68K->SetFetch(0x0C0000, 0x100000, (pointer)SoundRam); m68k_running = 0; m68k_execf = M68K->Exec; m68k_saved_cycles = 0; for (i = 0; i < MAX_BREAKPOINTS; i++) m68k_breakpoint[i].addr = 0xFFFFFFFF; m68k_num_breakpoints = 0; M68KBreakpointCallback = NULL; m68k_in_breakpoint = 0; // Set up sound output scsp_sound_genpos = 0; scsp_sound_left = 0; if (ScspChangeSoundCore(coreid) < 0) return -1; // Start a subthread if requested scsp_thread_running = 0; if (yabsys.UseThreads) { scsp_thread_running = 1; // Set now so the thread doesn't quit instantly PSP_FLUSH_ALL(); if (YabThreadStart(YAB_THREAD_SCSP, ScspThread, NULL) < 0) { SCSPLOG("Failed to start SCSP thread\n"); scsp_thread_running = 0; } } // Successfully initialized! return 0; } //------------------------------------------------------------------------- // ScspReset: Reset the SCSP to its power-on state, also stopping the M68K // processor. void ScspReset(void) { int slotnum; if (scsp_thread_running) ScspSyncThread(); scsp.mem4mb = 0; scsp.dac18b = 0; scsp.ver = 0; scsp.mvol = 0; scsp.rbl = 0; scsp.rbp = 0; scsp.mofull = 0; scsp.moemp = 1; scsp.miovf = 0; scsp.mifull = 0; scsp.miemp = 1; scsp.mibuf = 0; scsp.mobuf = 0; scsp.mslc = 0; scsp.ca = 0; scsp.dmea = 0; scsp.drga = 0; scsp.dgate = 0; scsp.ddir = 0; scsp.dexe = 0; scsp.dtlg = 0; scsp.tactl = 0; scsp.tima = 0xFF00; scsp.tbctl = 0; scsp.timb = 0xFF00; scsp.tcctl = 0; scsp.timc = 0xFF00; scsp.mcieb = 0; scsp.mcipd = 0; scsp.scilv0 = 0; scsp.scilv1 = 0; scsp.scilv2 = 0; scsp.scieb = 0; scsp.scipd = 0; memset(scsp_regcache, 0, sizeof(scsp_regcache)); scsp_regcache[0x400>>1] = SCSP_VERSION << 4; memset(scsp.stack, 0, sizeof(scsp.stack)); for (slotnum = 0; slotnum < 32; slotnum++) { memset(&scsp.slot[slotnum], 0, sizeof(scsp.slot[slotnum])); scsp.slot[slotnum].env_counter = SCSP_ENV_DECAY_END; // Slot off scsp.slot[slotnum].outshift_l = 31; // Output off scsp.slot[slotnum].outshift_r = 31; scsp.slot[slotnum].audiogen = audiogen_null; } scsp.sound_ram_mask = 0x3FFFF; scsp_clock_frac = 0; scsp.sample_timer = 0; scsp_main_interrupt_pending = 0; scsp_write_buffer_size = 0; cdda_next_in = 0; cdda_next_out = 0; cdda_delay = CDDA_DELAY_SAMPLES; m68k_running = 0; if (scsp_thread_running) PSP_FLUSH_ALL(); } //------------------------------------------------------------------------- // ScspChangeSoundCore: Change the module used for sound output. Returns // 0 on success, -1 on error. int ScspChangeSoundCore(int coreid) { int i; // Make sure the old core is freed if (SNDCore) SNDCore->DeInit(); // If the default was requested, use the first core in the list if (coreid == SNDCORE_DEFAULT) SNDCore = SNDCoreList[0]; else { // Otherwise, go through core list and find the id for (i = 0; SNDCoreList[i] != NULL; i++) { if (SNDCoreList[i]->id == coreid) { // Set to current core SNDCore = SNDCoreList[i]; break; } } } if (SNDCore == NULL) { SNDCore = &SNDDummy; return -1; } if (SNDCore->Init() == -1) { // Since it failed, instead of it being fatal, we'll just use the dummy // core instead // This might be helpful though. YabSetError(YAB_ERR_CANNOTINIT, (void *)SNDCore->Name); SNDCore = &SNDDummy; } if (SNDCore) { if (scsp_mute_flags) SNDCore->MuteAudio(); else SNDCore->UnMuteAudio(); SNDCore->SetVolume(scsp_volume); } return 0; } //------------------------------------------------------------------------- // ScspChangeVideoFormat: Update SCSP parameters for a change in video // format. type is nonzero for PAL (50Hz), zero for NTSC (59.94Hz) video. // Always returns 0 for success. int ScspChangeVideoFormat(int type) { scsp_clock_inc = yabsys.IsPal ? SCSP_CLOCK_INC_PAL : SCSP_CLOCK_INC_NTSC; SNDCore->ChangeVideoFormat(type ? 50 : 60); return 0; } //------------------------------------------------------------------------- // ScspSetFrameAccurate: Set whether sound should be generated with // frame-accurate timing. void ScspSetFrameAccurate(int on) { scsp_frame_accurate = (on != 0); } //------------------------------------------------------------------------- // ScspMuteAudio, ScspUnMuteAudio: Mute or unmute the sound output. Does // not affect actual SCSP processing. void ScspMuteAudio(int flags) { scsp_mute_flags |= flags; if (SNDCore && scsp_mute_flags) SNDCore->MuteAudio(); } void ScspUnMuteAudio(int flags) { scsp_mute_flags &= ~flags; if (SNDCore && (scsp_mute_flags == 0)) SNDCore->UnMuteAudio(); } //------------------------------------------------------------------------- // ScspSetVolume: Set the sound output volume. Does not affect actual // SCSP processing. void ScspSetVolume(int volume) { scsp_volume = volume; if (SNDCore) SNDCore->SetVolume(volume); } //------------------------------------------------------------------------- // ScspDeInit: Free all resources used by the SCSP emulation. void ScspDeInit(void) { if (scsp_thread_running) { scsp_thread_running = 0; // Tell the subthread to stop YabThreadWake(YAB_THREAD_SCSP); YabThreadWait(YAB_THREAD_SCSP); } if (SNDCore) SNDCore->DeInit(); SNDCore = NULL; if (SoundRam) T2MemoryDeInit(SoundRam); SoundRam = NULL; } /////////////////////////////////////////////////////////////////////////// // Main SCSP processing routine and internal helpers /////////////////////////////////////////////////////////////////////////// // ScspExec: Main SCSP processing routine. Executes (decilines/10.0) // scanlines worth of SCSP emulation; in multithreaded mode, bumps the // clock target by the same amount of time. void ScspExec(int decilines) { u32 new_target; scsp_clock_frac += scsp_clock_inc * decilines; new_target = scsp_clock_target + (scsp_clock_frac >> 20); scsp_clock_target = new_target; scsp_clock_frac &= 0xFFFFF; if (scsp_thread_running) { #ifdef PSP if (!psp_writeback_cache_for_scsp()) PSP_UC(scsp_clock_target) = new_target; // Push just this one through #endif while (new_target - PSP_UC(scsp_clock) > SCSP_CLOCK_MAX_EXEC) { YabThreadWake(YAB_THREAD_SCSP); YabThreadYield(); } if (PSP_UC(scsp_main_interrupt_pending)) { (*scsp_interrupt_handler)(); PSP_UC(scsp_main_interrupt_pending) = 0; } } else ScspDoExec(new_target - scsp_clock); } /////////////////////////////////////////////////////////////////////////// // ScspThread: Control routine for SCSP thread. Loops over ScspDoExec() // and SCSP write buffer processing until told to stop. static void ScspThread(void *arg) { while (PSP_UC(scsp_thread_running)) { const u8 write_size = PSP_UC(scsp_write_buffer_size); u32 clock_cycles; if (write_size != 0) { const u32 address = PSP_UC(scsp_write_buffer_address); const u32 data = PSP_UC(scsp_write_buffer_data); if (write_size == 1) ScspWriteByteDirect(address, data); else if (write_size == 2) ScspWriteWordDirect(address, data); else { ScspWriteWordDirect(address, data >> 16); ScspWriteWordDirect(address+2, data & 0xFFFF); } PSP_UC(scsp_write_buffer_size) = 0; } clock_cycles = PSP_UC(scsp_clock_target) - scsp_clock; if (clock_cycles > SCSP_CLOCK_MAX_EXEC) clock_cycles = SCSP_CLOCK_MAX_EXEC; if (clock_cycles > 0) { ScspDoExec(clock_cycles); YabThreadYield(); } else YabThreadSleep(); } } /////////////////////////////////////////////////////////////////////////// // ScspDoExec: Main SCSP processing routine implementation. Runs M68K // code, updates timers, and generates samples for the given number of // SCSP clock cycles. static void ScspDoExec(u32 cycles) { #if 0 s16 stereodata16[(44100 / 60) * 16]; //11760 #endif u32 cycles_left; u32 sample_count; u32 audio_free; // If any of the timer interrupts are enabled, give the M68K a chance // to respond to them immediately, so that music doesn't slow down if // the SCSP thread gets behind and executes a lot of cycles at once. sample_count = 0; cycles_left = cycles; while (cycles_left > 0) { u32 this_samples = 0; u32 this_cycles = cycles_left; if (scsp.scieb & (1 << SCSP_INTERRUPT_TIMER_A)) this_cycles = MIN(this_cycles, ScspTimerCyclesLeft(scsp.tima, scsp.tactl)); if (scsp.scieb & (1 << SCSP_INTERRUPT_TIMER_B)) this_cycles = MIN(this_cycles, ScspTimerCyclesLeft(scsp.timb, scsp.tbctl)); if (scsp.scieb & (1 << SCSP_INTERRUPT_TIMER_C)) this_cycles = MIN(this_cycles, ScspTimerCyclesLeft(scsp.timc, scsp.tcctl)); scsp.sample_timer += this_cycles; this_samples = scsp.sample_timer >> 8; scsp.sample_timer &= 0xFF; cycles_left -= this_cycles; sample_count += this_samples; ScspRunM68K(this_cycles); ScspUpdateTimer(this_samples, &scsp.tima, scsp.tactl, SCSP_INTERRUPT_TIMER_A); ScspUpdateTimer(this_samples, &scsp.timb, scsp.tbctl, SCSP_INTERRUPT_TIMER_B); ScspUpdateTimer(this_samples, &scsp.timc, scsp.tcctl, SCSP_INTERRUPT_TIMER_C); } if (scsp_frame_accurate) { s32 *bufL, *bufR; // Update sound buffers if (scsp_sound_left + sample_count > SCSP_SOUND_BUFSIZE) { u32 overrun = (scsp_sound_left + sample_count) - SCSP_SOUND_BUFSIZE; SCSPLOG("WARNING: Sound buffer overrun, %u samples\n", (int)overrun); scsp_sound_left -= overrun; } while (sample_count > 0) { u32 this_count = sample_count; if (scsp_sound_genpos >= SCSP_SOUND_BUFSIZE) scsp_sound_genpos = 0; if (this_count > SCSP_SOUND_BUFSIZE - scsp_sound_genpos) this_count = SCSP_SOUND_BUFSIZE - scsp_sound_genpos; bufL = &scsp_buffer_L[scsp_sound_genpos]; bufR = &scsp_buffer_R[scsp_sound_genpos]; ScspGenerateAudio(bufL, bufR, this_count); scsp_sound_genpos += this_count; scsp_sound_left += this_count; sample_count -= this_count; } // Send audio to the output device if possible while (scsp_sound_left > 0 && (audio_free = SNDCore->GetAudioSpace()) > 0) { s32 out_start = (s32)scsp_sound_genpos - (s32)scsp_sound_left; if (out_start < 0) out_start += SCSP_SOUND_BUFSIZE; if (audio_free > scsp_sound_left) audio_free = scsp_sound_left; if (audio_free > SCSP_SOUND_BUFSIZE - out_start) audio_free = SCSP_SOUND_BUFSIZE - out_start; SNDCore->UpdateAudio((u32 *)&scsp_buffer_L[out_start], (u32 *)&scsp_buffer_R[out_start], audio_free); scsp_sound_left -= audio_free; #if 0 ScspConvert32uto16s(&scsp_buffer_L[out_start], &scsp_buffer_R[out_start], (s16 *)stereodata16, audio_free); DRV_AviSoundUpdate(stereodata16, audio_free); #endif } } else // !scsp_frame_accurate { if ((audio_free = SNDCore->GetAudioSpace())) { if (audio_free > SCSP_SOUND_BUFSIZE) audio_free = SCSP_SOUND_BUFSIZE; ScspGenerateAudio(scsp_buffer_L, scsp_buffer_R, audio_free); SNDCore->UpdateAudio((u32 *)scsp_buffer_L, (u32 *)scsp_buffer_R, audio_free); #if 0 ScspConvert32uto16s((s32 *)scsp_buffer_L, (s32 *)scsp_buffer_R, (s16 *)stereodata16, audio_free); DRV_AviSoundUpdate(stereodata16, audio_free); #endif } } // if (scsp_frame_accurate) // Update scsp_clock last, so the main thread can use it as a signal // that we've finished processing to this point scsp_clock += cycles; } //------------------------------------------------------------------------- // ScspTimerCyclesLeft: Return the approximate number of SCSP clock cycles // before an SCSP timer (A, B, or C) triggers an interrupt. static u32 ScspTimerCyclesLeft(u16 timer, u8 timer_scale) { return (0xFF00 - timer) << timer_scale; } //----------------------------------// // ScspUpdateTimer: Update an SCSP timer (A, B, or C) by the given number // of output samples, and raise an interrupt if the timer reaches 0xFF. static void ScspUpdateTimer(u32 samples, u16 *timer_ptr, u8 timer_scale, int interrupt) { u32 timer_new = *timer_ptr + (samples << (8 - timer_scale)); if (UNLIKELY(timer_new >= 0xFF00)) { ScspRaiseInterrupt(interrupt, SCSP_INTTARGET_BOTH); timer_new -= 0xFF00; // We won't pass 0xFF00 multiple times at once } *timer_ptr = timer_new; } //------------------------------------------------------------------------- // ScspGenerateAudio: Generate the given number of audio samples based on // the current SCSP state, and update the sound slot counters. static void ScspGenerateAudio(s32 *bufL, s32 *bufR, u32 samples) { int slotnum; u32 i; for (i = 0; i < samples; i++) bufL[i] = bufR[i] = 0; scsp_bufL = bufL; scsp_bufR = bufR; for (slotnum = 0; slotnum < 32; slotnum++) ScspGenerateAudioForSlot(&scsp.slot[slotnum], samples); if (cdda_next_out != PSP_UC(cdda_next_in) * 2352) { if (cdda_delay > 0) { if (samples > cdda_delay) { samples -= cdda_delay; cdda_delay = 0; } else { cdda_delay -= samples; samples = 0; } } if (cdda_delay == 0) ScspGenerateAudioForCDDA(bufL, bufR, samples); } if (cdda_next_out == PSP_UC(cdda_next_in) * 2352) cdda_delay = CDDA_DELAY_SAMPLES; // No data buffered, so reset delay } //----------------------------------// // ScspGenerateAudioForSlot: Generate audio samples and update counters for // a single slot. scsp_bufL and scsp_bufR are assumed to be set properly. static void ScspGenerateAudioForSlot(SlotState *slot, u32 samples) { if (slot->env_counter >= SCSP_ENV_DECAY_END) return; // No sound is currently playing (*slot->audiogen)(slot, samples); } //----------------------------------// // ScspGenerateAudioForCDDA: Generate audio samples for buffered CDDA data. static void ScspGenerateAudioForCDDA(s32 *bufL, s32 *bufR, u32 samples) { // May need to wrap around the buffer, so use nested loops while (samples > 0) { const u32 next_out = cdda_next_out; // Save volatile value locally const s32 temp = (PSP_UC(cdda_next_in) * 2352) - next_out; const u32 out_left = (temp < 0) ? sizeof(cdda_buf) - next_out : temp; const u32 this_len = (samples > out_left/4) ? out_left/4 : samples; const u8 *buf = &cdda_buf.data[next_out]; const u8 *top = buf + this_len*4; if (this_len == 0) break; // We ran out of buffered data for (; buf < top; buf += 4, bufL++, bufR++) { *bufL += (s32)(s16)((buf[1] << 8) | buf[0]); *bufR += (s32)(s16)((buf[3] << 8) | buf[2]); } if (next_out + this_len*4 >= sizeof(cdda_buf)) cdda_next_out = 0; else cdda_next_out = next_out + this_len*4; samples -= this_len; } } /////////////////////////////////////////////////////////////////////////// // SCSP register/memory access and I/O interface routines /////////////////////////////////////////////////////////////////////////// // SoundRam{Read,Write}{Byte,Word,Long}: Read or write sound RAM. // Intended for calling from external sources. u8 FASTCALL SoundRamReadByte(u32 address) { address &= scsp.sound_ram_mask; return T2ReadByte(SoundRam, address); } u16 FASTCALL SoundRamReadWord(u32 address) { address &= scsp.sound_ram_mask; return T2ReadWord(SoundRam, address); } u32 FASTCALL SoundRamReadLong(u32 address) { address &= scsp.sound_ram_mask; return T2ReadLong(SoundRam, address); } //----------------------------------// void FASTCALL SoundRamWriteByte(u32 address, u8 data) { address &= scsp.sound_ram_mask; T2WriteByte(SoundRam, address, data); M68K->WriteNotify(address, 1); } void FASTCALL SoundRamWriteWord(u32 address, u16 data) { address &= scsp.sound_ram_mask; T2WriteWord(SoundRam, address, data); M68K->WriteNotify(address, 2); } void FASTCALL SoundRamWriteLong(u32 address, u32 data) { address &= scsp.sound_ram_mask; T2WriteLong(SoundRam, address, data); M68K->WriteNotify(address, 4); } //------------------------------------------------------------------------- // Scsp{Read,Write}{Byte,Word,Long}: Read or write SCSP registers. // Intended for calling from external sources. u8 FASTCALL ScspReadByte(u32 address) { const u16 data = ScspReadWord(address & ~1); if (address & 1) return data & 0xFF; else return data >> 8; } u16 FASTCALL ScspReadWord(u32 address) { #ifdef PSP // Special handling for PSP cache management switch (address) { case 0x404: // MIDI in return 0xFF; // Not even supported, so don't bother trying case 0x408: // CA/SGC/EG return ScspReadMonitor(); default: return PSP_UC(scsp_regcache[address >> 1]); } #else return ScspReadWordDirect(address & 0xFFF); #endif } u32 FASTCALL ScspReadLong(u32 address) { return (u32)ScspReadWord(address) << 16 | ScspReadWord(address+2); } //----------------------------------// void FASTCALL ScspWriteByte(u32 address, u8 data) { if (scsp_thread_running) { PSP_UC(scsp_write_buffer_address) = address & 0xFFF; PSP_UC(scsp_write_buffer_data) = data; PSP_UC(scsp_write_buffer_size) = 1; while (PSP_UC(scsp_write_buffer_size) != 0) { YabThreadWake(YAB_THREAD_SCSP); YabThreadYield(); } return; } ScspWriteByteDirect(address & 0xFFF, data); } void FASTCALL ScspWriteWord(u32 address, u16 data) { if (scsp_thread_running) { PSP_UC(scsp_write_buffer_address) = address & 0xFFF; PSP_UC(scsp_write_buffer_data) = data; PSP_UC(scsp_write_buffer_size) = 2; while (PSP_UC(scsp_write_buffer_size) != 0) { YabThreadWake(YAB_THREAD_SCSP); YabThreadYield(); } return; } ScspWriteWordDirect(address & 0xFFF, data); } void FASTCALL ScspWriteLong(u32 address, u32 data) { if (scsp_thread_running) { PSP_UC(scsp_write_buffer_address) = address & 0xFFF; PSP_UC(scsp_write_buffer_data) = data; PSP_UC(scsp_write_buffer_size) = 4; while (PSP_UC(scsp_write_buffer_size) != 0) { YabThreadWake(YAB_THREAD_SCSP); YabThreadYield(); } return; } ScspWriteWordDirect(address & 0xFFF, data >> 16); ScspWriteWordDirect((address+2) & 0xFFF, data & 0xFFFF); } //------------------------------------------------------------------------- // ScspReceiveCDDA: Receive and buffer a sector (2352 bytes) of CDDA audio // data. Intended to be called by the CD driver when an audio sector has // been read in for playback. void ScspReceiveCDDA(const u8 *sector) { const u32 next_in = cdda_next_in; // Save volatile value locally const u32 next_next_in = (next_in + 1) % (sizeof(cdda_buf.sectors) / sizeof(cdda_buf.sectors[0])); // Make sure we have room for the new sector first const u32 next_out = PSP_UC(cdda_next_out); if (next_out > next_in * 2352 && next_out <= (next_in+1) * 2352) { SCSPLOG("WARNING: CDDA buffer overflow, discarding sector\n"); return; } memcpy(cdda_buf.sectors[next_in], sector, 2352); PSP_WRITEBACK_CACHE(cdda_buf.sectors[next_in], 2352); cdda_next_in = next_next_in; } /////////////////////////////////////////////////////////////////////////// // Miscellaneous SCSP interface routines /////////////////////////////////////////////////////////////////////////// // SoundSaveState: Save the current SCSP state to the given file. int SoundSaveState(FILE *fp) { int i; u32 temp; u8 temp8; int offset; IOCheck_struct check; if (scsp_thread_running) ScspSyncThread(); offset = StateWriteHeader(fp, "SCSP", 2); // Save 68k registers first ywrite(&check, (void *)&m68k_running, 1, 1, fp); for (i = 0; i < 8; i++) { temp = M68K->GetDReg(i); ywrite(&check, (void *)&temp, 4, 1, fp); } for (i = 0; i < 8; i++) { temp = M68K->GetAReg(i); ywrite(&check, (void *)&temp, 4, 1, fp); } temp = M68K->GetSR(); ywrite(&check, (void *)&temp, 4, 1, fp); temp = M68K->GetPC(); ywrite(&check, (void *)&temp, 4, 1, fp); // Now for the SCSP registers ywrite(&check, (void *)scsp_regcache, 0x1000, 1, fp); // Sound RAM is important ywrite(&check, (void *)SoundRam, 0x80000, 1, fp); // Write slot internal variables for (i = 0; i < 32; i++) { ywrite(&check, (void *)&scsp.slot[i].key, 1, 1, fp); ywrite(&check, (void *)&scsp.slot[i].addr_counter, 4, 1, fp); ywrite(&check, (void *)&scsp.slot[i].env_counter, 4, 1, fp); ywrite(&check, (void *)&scsp.slot[i].env_step, 4, 1, fp); ywrite(&check, (void *)&scsp.slot[i].env_target, 4, 1, fp); ywrite(&check, (void *)&scsp.slot[i].env_phase, 4, 1, fp); // Was enxt in scsp1; we don't use it, so just derive the proper // value from env_phase if (scsp.slot[i].env_phase == SCSP_ENV_RELEASE) temp8 = 1; else if (scsp.slot[i].env_phase == SCSP_ENV_SUSTAIN) temp8 = 2; else if (scsp.slot[i].env_phase == SCSP_ENV_DECAY) temp8 = 3; else if (scsp.slot[i].env_phase == SCSP_ENV_ATTACK) temp8 = 4; else // impossible, but avoid "undefined value" warnings temp8 = 0; ywrite(&check, (void *)&temp8, 1, 1, fp); ywrite(&check, (void *)&scsp.slot[i].lfo_counter, 4, 1, fp); ywrite(&check, (void *)&scsp.slot[i].lfo_step, 4, 1, fp); } // Write main internal variables // FIXME/SCSP1: need to write a lot of these from temporary variables // to maintain save state compatibility temp = scsp.mem4mb; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.mvol; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.rbl; ywrite(&check, (void *)&temp, 4, 1, fp); ywrite(&check, (void *)&scsp.rbp, 4, 1, fp); temp = scsp.mslc; ywrite(&check, (void *)&temp, 4, 1, fp); ywrite(&check, (void *)&scsp.dmea, 4, 1, fp); temp = scsp.drga; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.dgate<<6 | scsp.ddir<<5 | scsp.dexe<<4; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.dtlg; ywrite(&check, (void *)&temp, 4, 1, fp); ywrite(&check, (void *)scsp.midi_in_buf, 1, 4, fp); ywrite(&check, (void *)scsp.midi_out_buf, 1, 4, fp); ywrite(&check, (void *)&scsp.midi_in_cnt, 1, 1, fp); ywrite(&check, (void *)&scsp.midi_out_cnt, 1, 1, fp); temp8 = scsp.mofull<<4 | scsp.moemp<<3 | scsp.miovf<<2 | scsp.mifull<<1 | scsp.miemp<<0; ywrite(&check, (void *)&temp8, 1, 1, fp); temp = scsp.tima; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.tactl; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.timb; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.tbctl; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.timc; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.tcctl; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.scieb; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.scipd; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.scilv0; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.scilv1; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.scilv2; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.mcieb; ywrite(&check, (void *)&temp, 4, 1, fp); temp = scsp.mcipd; ywrite(&check, (void *)&temp, 4, 1, fp); ywrite(&check, (void *)scsp.stack, 4, 32 * 2, fp); return StateFinishHeader(fp, offset); } //------------------------------------------------------------------------- // SoundLoadState: Load the current SCSP state from the given file. int SoundLoadState(FILE *fp, int version, int size) { int i, i2; u32 temp; u8 temp8; IOCheck_struct check; if (scsp_thread_running) ScspSyncThread(); // Read 68k registers first yread(&check, (void *)&m68k_running, 1, 1, fp); for (i = 0; i < 8; i++) { yread(&check, (void *)&temp, 4, 1, fp); M68K->SetDReg(i, temp); } for (i = 0; i < 8; i++) { yread(&check, (void *)&temp, 4, 1, fp); M68K->SetAReg(i, temp); } yread(&check, (void *)&temp, 4, 1, fp); M68K->SetSR(temp); yread(&check, (void *)&temp, 4, 1, fp); M68K->SetPC(temp); // Now for the SCSP registers yread(&check, (void *)scsp_regcache, 0x1000, 1, fp); // And sound RAM yread(&check, (void *)SoundRam, 0x80000, 1, fp); // Break out slot registers into their respective fields for (i = 0; i < 32; i++) { for (i2 = 0; i2 < 0x18; i2 += 2) ScspWriteWordDirect(i<<5 | i2, scsp_regcache[(i<<5 | i2) >> 1]); // These are also called during writes, so they're not technically // necessary, but call them again anyway just to ensure everything's // up to date ScspUpdateSlotAddress(&scsp.slot[i]); ScspUpdateSlotFunc(&scsp.slot[i]); } if (version > 1) { // Read slot internal variables for (i = 0; i < 32; i++) { yread(&check, (void *)&scsp.slot[i].key, 1, 1, fp); yread(&check, (void *)&scsp.slot[i].addr_counter, 4, 1, fp); yread(&check, (void *)&scsp.slot[i].env_counter, 4, 1, fp); yread(&check, (void *)&scsp.slot[i].env_step, 4, 1, fp); yread(&check, (void *)&scsp.slot[i].env_target, 4, 1, fp); yread(&check, (void *)&scsp.slot[i].env_phase, 4, 1, fp); // Was enxt in scsp1; we don't use it, so just read and ignore yread(&check, (void *)&temp8, 1, 1, fp); yread(&check, (void *)&scsp.slot[i].lfo_counter, 4, 1, fp); yread(&check, (void *)&scsp.slot[i].lfo_step, 4, 1, fp); } // Read main internal variables yread(&check, (void *)&temp, 4, 1, fp); scsp.mem4mb = temp; // This one isn't saved in the state file (though it's not used anyway) scsp.dac18b = (scsp_regcache[0x400>>1] >> 8) & 1; yread(&check, (void *)&temp, 4, 1, fp); scsp.mvol = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.rbl = temp; yread(&check, (void *)&scsp.rbp, 4, 1, fp); yread(&check, (void *)&temp, 4, 1, fp); scsp.mslc = temp; yread(&check, (void *)&scsp.dmea, 4, 1, fp); yread(&check, (void *)&temp, 4, 1, fp); scsp.drga = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.dgate = temp>>6 & 1; scsp.ddir = temp>>5 & 1; scsp.dexe = temp>>4 & 1; yread(&check, (void *)&temp, 4, 1, fp); scsp.dtlg = temp; yread(&check, (void *)scsp.midi_in_buf, 1, 4, fp); yread(&check, (void *)scsp.midi_out_buf, 1, 4, fp); yread(&check, (void *)&scsp.midi_in_cnt, 1, 1, fp); yread(&check, (void *)&scsp.midi_out_cnt, 1, 1, fp); yread(&check, (void *)&temp8, 1, 1, fp); scsp.mofull = temp8>>4 & 1; scsp.moemp = temp8>>3 & 1; scsp.miovf = temp8>>2 & 1; scsp.mifull = temp8>>1 & 1; scsp.miemp = temp8>>0 & 1; yread(&check, (void *)&temp, 4, 1, fp); scsp.tima = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.tactl = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.timb = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.tbctl = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.timc = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.tcctl = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.scieb = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.scipd = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.scilv0 = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.scilv1 = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.scilv2 = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.mcieb = temp; yread(&check, (void *)&temp, 4, 1, fp); scsp.mcipd = temp; yread(&check, (void *)scsp.stack, 4, 32 * 2, fp); } if (scsp_thread_running) PSP_FLUSH_ALL(); return size; } //------------------------------------------------------------------------- // ScspSlotDebugStats: Generate a string describing the given slot's state // and store it in the passed-in buffer (which is assumed to be large enough // to hold the result). // Helper functions (defined below) static char *AddSoundLFO(char *outstring, const char *string, u16 level, u16 waveform); static char *AddSoundPan(char *outstring, u16 pan); static char *AddSoundLevel(char *outstring, u16 level); void ScspSlotDebugStats(u8 slotnum, char *outstring) { AddString(outstring, "Sound Source = "); switch (scsp.slot[slotnum].ssctl) { case 0: { AddString(outstring, "External DRAM data\r\n"); break; } case 1: { AddString(outstring, "Internal(Noise)\r\n"); break; } case 2: { AddString(outstring, "Internal(0's)\r\n"); break; } default: { AddString(outstring, "Invalid setting\r\n"); break; } } AddString(outstring, "Source bit = "); switch (scsp.slot[slotnum].sbctl) { case 0: { AddString(outstring, "No bit reversal\r\n"); break; } case 1: { AddString(outstring, "Reverse other bits\r\n"); break; } case 2: { AddString(outstring, "Reverse sign bit\r\n"); break; } case 3: { AddString(outstring, "Reverse sign and other bits\r\n"); break; } } // Loop Control AddString(outstring, "Loop Mode = "); switch (scsp.slot[slotnum].lpctl) { case 0: { AddString(outstring, "Off\r\n"); break; } case 1: { AddString(outstring, "Normal\r\n"); break; } case 2: { AddString(outstring, "Reverse\r\n"); break; } case 3: { AddString(outstring, "Alternating\r\n"); break; } } // PCM8B if (scsp.slot[slotnum].pcm8b) { AddString(outstring, "8-bit samples\r\n"); } else { AddString(outstring, "16-bit samples\r\n"); } AddString(outstring, "Start Address = %05lX\r\n", (unsigned long)scsp.slot[slotnum].sa); AddString(outstring, "Loop Start Address = %04X\r\n", scsp.slot[slotnum].lsa); AddString(outstring, "Loop End Address = %04X\r\n", scsp.slot[slotnum].lea); AddString(outstring, "Decay 1 Rate = %d\r\n", scsp.slot[slotnum].dr); AddString(outstring, "Decay 2 Rate = %d\r\n", scsp.slot[slotnum].sr); if (scsp.slot[slotnum].eghold) { AddString(outstring, "EG Hold Enabled\r\n"); } AddString(outstring, "Attack Rate = %d\r\n", scsp.slot[slotnum].ar); if (scsp.slot[slotnum].lpslnk) { AddString(outstring, "Loop Start Link Enabled\r\n"); } if (scsp.slot[slotnum].krs != 0) { AddString(outstring, "Key rate scaling = %d\r\n", scsp.slot[slotnum].krs); } AddString(outstring, "Decay Level = %d\r\n", scsp.slot[slotnum].sl); AddString(outstring, "Release Rate = %d\r\n", scsp.slot[slotnum].rr); if (scsp.slot[slotnum].stwinh) { AddString(outstring, "Stack Write Inhibited\r\n"); } if (scsp.slot[slotnum].sdir) { AddString(outstring, "Sound Direct Enabled\r\n"); } AddString(outstring, "Total Level = %d\r\n", scsp.slot[slotnum].tl); AddString(outstring, "Modulation Level = %d\r\n", scsp.slot[slotnum].mdl); AddString(outstring, "Modulation Input X = %d\r\n", scsp.slot[slotnum].mdx); AddString(outstring, "Modulation Input Y = %d\r\n", scsp.slot[slotnum].mdy); AddString(outstring, "Octave = %d\r\n", scsp.slot[slotnum].oct); AddString(outstring, "Frequency Number Switch = %d\r\n", scsp.slot[slotnum].fns); AddString(outstring, "LFO Reset = %s\r\n", scsp.slot[slotnum].lfore ? "TRUE" : "FALSE"); AddString(outstring, "LFO Frequency = %d\r\n", scsp.slot[slotnum].lfof); outstring = AddSoundLFO(outstring, "LFO Frequency modulation waveform =", scsp.slot[slotnum].plfos, scsp.slot[slotnum].plfows); AddString(outstring, "LFO Frequency modulation level = %d\r\n", scsp.slot[slotnum].plfos); outstring = AddSoundLFO(outstring, "LFO Amplitude modulation waveform =", scsp.slot[slotnum].alfos, scsp.slot[slotnum].alfows); AddString(outstring, "LFO Amplitude modulation level = %d\r\n", scsp.slot[slotnum].alfos); AddString(outstring, "Input mix level = "); outstring = AddSoundLevel(outstring, scsp.slot[slotnum].imxl); AddString(outstring, "Input Select = %d\r\n", scsp.slot[slotnum].isel); AddString(outstring, "Direct data send level = "); outstring = AddSoundLevel(outstring, scsp.slot[slotnum].disdl); AddString(outstring, "Direct data panpot = "); outstring = AddSoundPan(outstring, scsp.slot[slotnum].dipan); AddString(outstring, "Effect data send level = "); outstring = AddSoundLevel(outstring, scsp.slot[slotnum].efsdl); AddString(outstring, "Effect data panpot = "); outstring = AddSoundPan(outstring, scsp.slot[slotnum].efpan); } //----------------------------------// static char *AddSoundLFO(char *outstring, const char *string, u16 level, u16 waveform) { if (level > 0) { switch (waveform) { case 0: AddString(outstring, "%s Sawtooth\r\n", string); break; case 1: AddString(outstring, "%s Square\r\n", string); break; case 2: AddString(outstring, "%s Triangle\r\n", string); break; case 3: AddString(outstring, "%s Noise\r\n", string); break; } } return outstring; } //----------------------------------// static char *AddSoundPan(char *outstring, u16 pan) { if (pan == 0x0F) { AddString(outstring, "Left = -MAX dB, Right = -0 dB\r\n"); } else if (pan == 0x1F) { AddString(outstring, "Left = -0 dB, Right = -MAX dB\r\n"); } else { AddString(outstring, "Left = -%d dB, Right = -%d dB\r\n", (pan & 0xF) * 3, (pan >> 4) * 3); } return outstring; } //----------------------------------// static char *AddSoundLevel(char *outstring, u16 level) { if (level == 0) { AddString(outstring, "-MAX dB\r\n"); } else { AddString(outstring, "-%d dB\r\n", (7-level) * 6); } return outstring; } //------------------------------------------------------------------------- // ScspCommonControlRegisterDebugStats: Generate a string describing the // SCSP common state registers and store it in the passed-in buffer (which // is assumed to be large enough to hold the result). void ScspCommonControlRegisterDebugStats(char *outstring) { AddString(outstring, "Memory: %s\r\n", scsp.mem4mb ? "4 Mbit" : "2 Mbit"); AddString(outstring, "Master volume: %d\r\n", scsp.mvol); AddString(outstring, "Ring buffer length: %d\r\n", scsp.rbl); AddString(outstring, "Ring buffer address: %08lX\r\n", (unsigned long)scsp.rbp); AddString(outstring, "\r\n"); AddString(outstring, "Slot Status Registers\r\n"); AddString(outstring, "-----------------\r\n"); AddString(outstring, "Monitor slot: %d\r\n", scsp.mslc); AddString(outstring, "Call address: %d\r\n", (ScspReadWordDirect(0x408) >> 7) & 0xF); AddString(outstring, "\r\n"); AddString(outstring, "DMA Registers\r\n"); AddString(outstring, "-----------------\r\n"); AddString(outstring, "DMA memory address start: %08lX\r\n", (unsigned long)scsp.dmea); AddString(outstring, "DMA register address start: %03X\r\n", scsp.drga); AddString(outstring, "DMA Flags: %02X (%cDGATE %cDDIR %cDEXE)\r\n", scsp.dgate<<6 | scsp.ddir<<5 | scsp.dexe<<4, scsp.dgate ? '+' : '-', scsp.ddir ? '+' : '-', scsp.dexe ? '+' : '-'); AddString(outstring, "\r\n"); AddString(outstring, "Timer Registers\r\n"); AddString(outstring, "-----------------\r\n"); AddString(outstring, "Timer A counter: %02X\r\n", scsp.tima >> 8); AddString(outstring, "Timer A increment: Every %d sample(s)\r\n", 1 << scsp.tactl); AddString(outstring, "Timer B counter: %02X\r\n", scsp.timb >> 8); AddString(outstring, "Timer B increment: Every %d sample(s)\r\n", 1 << scsp.tbctl); AddString(outstring, "Timer C counter: %02X\r\n", scsp.timc >> 8); AddString(outstring, "Timer C increment: Every %d sample(s)\r\n", 1 << scsp.tcctl); AddString(outstring, "\r\n"); AddString(outstring, "Interrupt Registers\r\n"); AddString(outstring, "-----------------\r\n"); AddString(outstring, "Sound cpu interrupt pending: %04X\r\n", scsp.scipd); AddString(outstring, "Sound cpu interrupt enable: %04X\r\n", scsp.scieb); AddString(outstring, "Sound cpu interrupt level 0: %04X\r\n", scsp.scilv0); AddString(outstring, "Sound cpu interrupt level 1: %04X\r\n", scsp.scilv1); AddString(outstring, "Sound cpu interrupt level 2: %04X\r\n", scsp.scilv2); AddString(outstring, "Main cpu interrupt pending: %04X\r\n", scsp.mcipd); AddString(outstring, "Main cpu interrupt enable: %04X\r\n", scsp.mcieb); AddString(outstring, "\r\n"); } //------------------------------------------------------------------------- // ScspSlotDebugSaveRegisters: Write the values of a single slot's // registers to a file. int ScspSlotDebugSaveRegisters(u8 slotnum, const char *filename) { FILE *fp; int i; IOCheck_struct check; if ((fp = fopen(filename, "wb")) == NULL) return -1; for (i = (slotnum * 0x20); i < ((slotnum+1) * 0x20); i += 2) { #ifdef WORDS_BIGENDIAN ywrite(&check, (void *)&scsp_regcache[i], 1, 2, fp); #else ywrite(&check, (void *)&scsp_regcache[i+1], 1, 1, fp); ywrite(&check, (void *)&scsp_regcache[i], 1, 1, fp); #endif } fclose(fp); return 0; } //------------------------------------------------------------------------- // ScspSlotDebugAudioSaveWav: Generate audio for a single slot and save it // to a WAV file. // Helper function to generate audio (defined below) static u32 ScspSlotDebugAudio(SlotState *slot, s32 *workbuf, s16 *buf, u32 len); int ScspSlotDebugAudioSaveWav(u8 slotnum, const char *filename) { typedef struct { char id[4]; u32 size; } chunk_struct; typedef struct { chunk_struct riff; char rifftype[4]; } waveheader_struct; typedef struct { chunk_struct chunk; u16 compress; u16 numchan; u32 rate; u32 bytespersec; u16 blockalign; u16 bitspersample; } fmt_struct; s32 workbuf[512*2*2]; s16 buf[512*2]; SlotState slot; FILE *fp; u32 counter = 0; waveheader_struct waveheader; fmt_struct fmt; chunk_struct data; long length; IOCheck_struct check; if (scsp.slot[slotnum].lea == 0) return 0; if ((fp = fopen(filename, "wb")) == NULL) return -1; // Do wave header memcpy(waveheader.riff.id, "RIFF", 4); waveheader.riff.size = 0; // we'll fix this after the file is closed memcpy(waveheader.rifftype, "WAVE", 4); ywrite(&check, (void *)&waveheader, 1, sizeof(waveheader_struct), fp); // fmt chunk memcpy(fmt.chunk.id, "fmt ", 4); fmt.chunk.size = 16; // we'll fix this at the end fmt.compress = 1; // PCM fmt.numchan = 2; // Stereo fmt.rate = 44100; fmt.bitspersample = 16; fmt.blockalign = fmt.bitspersample / 8 * fmt.numchan; fmt.bytespersec = fmt.rate * fmt.blockalign; ywrite(&check, (void *)&fmt, 1, sizeof(fmt_struct), fp); // data chunk memcpy(data.id, "data", 4); data.size = 0; // we'll fix this at the end ywrite(&check, (void *)&data, 1, sizeof(chunk_struct), fp); memcpy(&slot, &scsp.slot[slotnum], sizeof(slot)); // Clear out the phase counter, etc. slot.addr_counter = 0; slot.env_counter = SCSP_ENV_ATTACK_START; slot.env_step = slot.env_step_a; slot.env_target = SCSP_ENV_ATTACK_END; slot.env_phase = SCSP_ENV_ATTACK; // Mix the audio, and then write it to the file for(;;) { if (ScspSlotDebugAudio(&slot, workbuf, buf, 512) == 0) break; counter += 512; ywrite(&check, (void *)buf, 2, 512 * 2, fp); if (slot.lpctl != 0 && counter >= (44100 * 2 * 5)) break; } length = ftell(fp); // Let's fix the riff chunk size and the data chunk size fseek(fp, sizeof(waveheader_struct)-0x8, SEEK_SET); length -= 0x4; ywrite(&check, (void *)&length, 1, 4, fp); fseek(fp, sizeof(waveheader_struct)+sizeof(fmt_struct)+0x4, SEEK_SET); length -= sizeof(waveheader_struct)+sizeof(fmt_struct); ywrite(&check, (void *)&length, 1, 4, fp); fclose(fp); return 0; } //----------------------------------// static u32 ScspSlotDebugAudio(SlotState *slot, s32 *workbuf, s16 *buf, u32 len) { s32 *bufL, *bufR; bufL = workbuf; bufR = workbuf+len; scsp_bufL = bufL; scsp_bufR = bufR; if (slot->env_counter >= SCSP_ENV_DECAY_END) return 0; // Not playing if (slot->ssctl) return 0; // not yet supported! memset(bufL, 0, sizeof(u32) * len); memset(bufR, 0, sizeof(u32) * len); ScspGenerateAudioForSlot(slot, len); ScspConvert32uto16s(bufL, bufR, buf, len); return len; } //------------------------------------------------------------------------- // ScspConvert32uto16s: Saturate two 32-bit input sample buffers to 16-bit // and interleave them into a single output buffer. void ScspConvert32uto16s(s32 *srcL, s32 *srcR, s16 *dest, u32 len) { u32 i; for (i = 0; i < len; i++, srcL++, srcR++, dest += 2) { // Left channel if (*srcL > 0x7FFF) dest[0] = 0x7FFF; else if (*srcL < -0x8000) dest[0] = -0x8000; else dest[0] = *srcL; // Right channel if (*srcR > 0x7FFF) dest[1] = 0x7FFF; else if (*srcR < -0x8000) dest[1] = -0x8000; else dest[1] = *srcR; } } /////////////////////////////////////////////////////////////////////////// // SCSP register read/write routines and internal helpers /////////////////////////////////////////////////////////////////////////// // Scsp{Read,Write}{Byte,Word}Direct: Perform an SCSP register read or // write. These are internal routines that implement the actual register // read/write logic. The address must be in the range [0,0xFFF]. static u8 FASTCALL ScspReadByteDirect(u32 address) { const u16 data = ScspReadWordDirect(address & ~1); if (address & 1) return data & 0xFF; else return data >> 8; } //----------------------------------// static u16 ScspReadMonitor(void) { u8 ca, sgc, eg; ca = (scsp.slot[scsp.mslc].addr_counter >> (SCSP_FREQ_LOW_BITS + 12)) & 0xF; switch (scsp.slot[scsp.mslc].env_phase) { case SCSP_ENV_ATTACK: sgc = 0; break; case SCSP_ENV_DECAY: sgc = 1; break; case SCSP_ENV_SUSTAIN: sgc = 2; break; case SCSP_ENV_RELEASE: sgc = 3; break; } eg = 0x1f - (scsp.slot[scsp.mslc].last_env >> 27); return (ca << 7) | (sgc << 5) | eg; } static u16 FASTCALL ScspReadWordDirect(u32 address) { switch (address) { case 0x404: // MIDI in return ScspMidiIn(); case 0x408: // CA/SGC/EG return ScspReadMonitor(); default: return PSP_UC(scsp_regcache[address >> 1]); } } //----------------------------------// static void FASTCALL ScspWriteByteDirect(u32 address, u8 data) { switch (address >> 8) { case 0x0: case 0x1: case 0x2: case 0x3: write_as_word: { // These can be treated as word writes, borrowing the missing // 8 bits from the register cache u16 word_data; if (address & 1) word_data = (PSP_UC(scsp_regcache[address >> 1]) & 0xFF00) | data; else word_data = (PSP_UC(scsp_regcache[address >> 1]) & 0x00FF) | (data << 8); ScspWriteWordDirect(address & ~1, word_data); return; } case 0x4: switch (address & 0xFF) { // FIXME: if interrupts are only triggered on 0->1 changes in // [SM]CIEB, we can skip 0x1E/0x1F/0x26/0x27 (see FIXME in // ScspWriteWordDirect()) case 0x1E: data &= 0x07; scsp.scieb = (data << 8) | (scsp.scieb & 0x00FF); ScspCheckInterrupts(0x700, SCSP_INTTARGET_SOUND); break; case 0x1F: scsp.scieb = (scsp.scieb & 0xFF00) | data; ScspCheckInterrupts(0x700, SCSP_INTTARGET_SOUND); break; case 0x20: return; // Not writable case 0x21: if (data & (1<<5)) ScspRaiseInterrupt(5, SCSP_INTTARGET_SOUND); return; // Not writable case 0x2A: data &= 0x07; scsp.mcieb = (data << 8) | (scsp.mcieb & 0x00FF); ScspCheckInterrupts(0x700, SCSP_INTTARGET_MAIN); break; case 0x2B: scsp.mcieb = (scsp.mcieb & 0xFF00) | data; ScspCheckInterrupts(0x700, SCSP_INTTARGET_MAIN); break; case 0x2C: return; // Not writable case 0x2D: if (data & (1<<5)) ScspRaiseInterrupt(5, SCSP_INTTARGET_MAIN); return; // Not writable case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: 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 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2E: case 0x2F: goto write_as_word; default: goto unhandled_write; } break; default: unhandled_write: SCSPLOG("ScspWriteByteDirect(): unhandled write %02X to 0x%03X\n", data, address); break; } if (address & 1) { PSP_UC(scsp_regcache[address >> 1]) &= 0xFF00; PSP_UC(scsp_regcache[address >> 1]) |= data; } else { PSP_UC(scsp_regcache[address >> 1]) &= 0x00FF; PSP_UC(scsp_regcache[address >> 1]) |= data << 8; } } //----------------------------------// static void FASTCALL ScspWriteWordDirect(u32 address, u16 data) { switch (address >> 8) { case 0x0: case 0x1: case 0x2: case 0x3: { const int slotnum = (address & 0x3E0) >> 5; SlotState *slot = &scsp.slot[slotnum]; switch (address & 0x1F) { case 0x00: slot->key = (data >> 11) & 0x1; slot->sbctl = (data >> 9) & 0x3; slot->ssctl = (data >> 7) & 0x3; slot->lpctl = (data >> 5) & 0x3; slot->pcm8b = (data >> 4) & 0x1; slot->sa =((data >> 0) & 0xF) << 16 | (slot->sa & 0xFFFF); ScspUpdateSlotFunc(slot); if (slot->env_counter < SCSP_ENV_DECAY_END) ScspUpdateSlotAddress(slot); if (data & (1<<12)) ScspDoKeyOnOff(); data &= 0x0FFF; // Don't save KYONEX break; case 0x02: slot->sa = (slot->sa & 0xF0000) | data; if (slot->env_counter < SCSP_ENV_DECAY_END) ScspUpdateSlotAddress(slot); break; case 0x04: slot->lsa = data; if (slot->env_counter < SCSP_ENV_DECAY_END) ScspUpdateSlotAddress(slot); break; case 0x06: slot->lea = data; if (slot->env_counter < SCSP_ENV_DECAY_END) ScspUpdateSlotAddress(slot); break; case 0x08: slot->sr = (data >> 11) & 0x1F; slot->dr = (data >> 6) & 0x1F; slot->eghold = (data >> 5) & 0x1; slot->ar = (data >> 0) & 0x1F; ScspUpdateSlotEnv(slot); break; case 0x0A: data &= 0x7FFF; slot->lpslnk = (data >> 14) & 0x1; slot->krs = (data >> 10) & 0xF; slot->sl = (data >> 5) & 0x1F; slot->rr = (data >> 0) & 0x1F; if (slot->krs == 0xF) slot->krs_shift = 4; else slot->krs_shift = slot->krs >> 2; ScspUpdateSlotEnv(slot); slot->sl_target = (slot->sl << (5 + SCSP_ENV_LOW_BITS)) + SCSP_ENV_DECAY_START; break; case 0x0C: data &= 0x03FF; slot->stwinh = (data >> 9) & 0x1; slot->sdir = (data >> 8) & 0x1; slot->tl = (data >> 0) & 0xFF; slot->tl_mult = scsp_tl_table[slot->tl]; break; case 0x0E: slot->mdl = (data >> 12) & 0xF; slot->mdx = (data >> 6) & 0x3F; slot->mdy = (data >> 0) & 0x3F; break; case 0x10: data &= 0x7BFF; slot->oct = (data >> 11) & 0xF; slot->fns = (data >> 0) & 0x3FF; if (slot->oct & 8) slot->octave_shift = 23 - slot->oct; else slot->octave_shift = 7 - slot->oct; slot->addr_step = ((0x400 + slot->fns) << 7) >> slot->octave_shift; ScspUpdateSlotEnv(slot); break; case 0x12: slot->lfore = (data >> 15) & 0x1; slot->lfof = (data >> 10) & 0x1F; slot->plfows = (data >> 8) & 0x3; slot->plfos = (data >> 5) & 0x7; slot->alfows = (data >> 3) & 0x3; slot->alfos = (data >> 0) & 0x7; if (slot->lfore) { slot->lfo_step = -1; slot->lfo_counter = 0; slot->lfo_fm_shift = -1; slot->lfo_am_shift = -1; } else { slot->lfo_step = scsp_lfo_step[slot->lfof]; if (slot->plfos) slot->lfo_fm_shift = slot->plfos - 1; else slot->lfo_fm_shift = -1; if (slot->alfos) slot->lfo_am_shift = 11 - slot->alfos; else slot->lfo_am_shift = -1; } slot->lfo_fm_wave = scsp_lfo_wave_freq[slot->plfows]; slot->lfo_am_wave = scsp_lfo_wave_amp[slot->alfows]; ScspUpdateSlotFunc(slot); break; case 0x14: data &= 0x007F; slot->isel = (data >> 3) & 0xF; slot->imxl = (data >> 0) & 0x7; if (slot->imxl) slot->imxl_shift = (7 - slot->imxl) + SCSP_ENV_HIGH_BITS; else slot->imxl_shift = 31; break; case 0x16: slot->disdl = (data >> 13) & 0x7; slot->dipan = (data >> 8) & 0x1F; slot->efsdl = (data >> 5) & 0x7; slot->efpan = (data >> 0) & 0x1F; // Compute the output shift counts for the left and right // channels. If the direct sound output is muted, we assume // the data is being passed through the DSP (which we don't // currently implement) and take the effect output level // instead. Note that we lose 1 bit of resolution from the // panning parameter because we adjust the output level by // shifting (powers of two), while DIPAN/EFPAN have a // resolution of sqrt(2). if (slot->disdl) { slot->outshift_l = slot->outshift_r = (7 - slot->disdl) + SCSP_ENV_HIGH_BITS; if (slot->dipan & 0x10) // Pan left { if (slot->dipan == 0x1F) slot->outshift_r = 31; else slot->outshift_r += (slot->dipan >> 1) & 7; } else // Pan right { if (slot->dipan == 0xF) slot->outshift_l = 31; else slot->outshift_l += (slot->dipan >> 1) & 7; } } else if (slot->efsdl) { slot->outshift_l = slot->outshift_r = (7 - slot->efsdl) + SCSP_ENV_HIGH_BITS; if (slot->efpan & 0x10) // Pan left { if (slot->efpan == 0x1F) slot->outshift_r = 31; else slot->outshift_r += (slot->efpan >> 1) & 7; } else // Pan right { if (slot->efpan == 0xF) slot->outshift_l = 31; else slot->outshift_l += (slot->efpan >> 1) & 7; } } else slot->outshift_l = slot->outshift_r = 31; // Muted ScspUpdateSlotFunc(slot); break; default: goto unhandled_write; } break; } case 0x4: switch (address & 0xFF) { case 0x00: data &= 0x030F; // VER is hardwired data |= SCSP_VERSION << 4; scsp.mem4mb = (data >> 9) & 0x1; scsp.dac18b = (data >> 8) & 0x1; scsp.mvol = (data >> 0) & 0xF; if (scsp.mem4mb) M68K->SetFetch(0x000000, 0x080000, (pointer)SoundRam); else { M68K->SetFetch(0x000000, 0x040000, (pointer)SoundRam); M68K->SetFetch(0x040000, 0x080000, (pointer)SoundRam); M68K->SetFetch(0x080000, 0x0C0000, (pointer)SoundRam); M68K->SetFetch(0x0C0000, 0x100000, (pointer)SoundRam); } scsp.sound_ram_mask = scsp.mem4mb ? 0x7FFFF : 0x3FFFF; break; case 0x02: data &= 0x01FF; scsp.rbl = (data >> 7) & 0x3; scsp.rbp =((data >> 0) & 0x7F) << 13; break; case 0x04: return; // Not writable case 0x06: data &= 0x00FF; ScspMidiOut(data); break; case 0x08: data &= 0x7800; // CA/SGC/EG are not writable scsp.mslc = (data >> 11) & 0x1F; break; case 0x12: data &= 0xFFFE; scsp.dmea = (scsp.dmea & 0xF0000) | data; break; case 0x14: data &= 0xFFFE; scsp.dmea =((data >> 12) & 0xF) << 16 | (scsp.dmea & 0xFFFF); scsp.drga = (data >> 0) & 0xFFF; break; case 0x16: data &= 0x7FFE; scsp.dgate = (data >> 14) & 0x1; scsp.ddir = (data >> 13) & 0x1; scsp.dexe |= (data >> 12) & 0x1; // Writing 0 not allowed scsp.dtlg = (data >> 0) & 0xFFF; if (data & (1<<12)) ScspDoDMA(); break; case 0x18: data &= 0x07FF; scsp.tactl = (data >> 8) & 0x7; scsp.tima =((data >> 0) & 0xFF) << 8; break; case 0x1A: data &= 0x07FF; scsp.tbctl = (data >> 8) & 0x7; scsp.timb =((data >> 0) & 0xFF) << 8; break; case 0x1C: data &= 0x07FF; scsp.tcctl = (data >> 8) & 0x7; scsp.timc =((data >> 0) & 0xFF) << 8; break; case 0x1E: data &= 0x07FF; scsp.scieb = data; // FIXME: If a bit is already 1 in both SCIEB and SCIPD, // does writing another 1 here (no change) trigger another // interrupt or not? ScspCheckInterrupts(0x7FF, SCSP_INTTARGET_SOUND); break; case 0x20: if (data & (1<<5)) ScspRaiseInterrupt(5, SCSP_INTTARGET_SOUND); return; // Not writable case 0x22: ScspClearInterrupts(data, SCSP_INTTARGET_SOUND); return; // Not writable case 0x24: data &= 0x00FF; scsp.scilv0 = data; break; case 0x26: data &= 0x00FF; scsp.scilv1 = data; break; case 0x28: data &= 0x00FF; scsp.scilv2 = data; break; case 0x2A: data &= 0x07FF; scsp.mcieb = data; // FIXME: as above (SCIEB) ScspCheckInterrupts(0x7FF, SCSP_INTTARGET_MAIN); break; case 0x2C: if (data & (1<<5)) ScspRaiseInterrupt(5, SCSP_INTTARGET_MAIN); return; // Not writable case 0x2E: ScspClearInterrupts(data, SCSP_INTTARGET_MAIN); return; // Not writable default: goto unhandled_write; } break; default: unhandled_write: SCSPLOG("ScspWriteWordDirect(): unhandled write %04X to 0x%03X\n", data, address); break; } PSP_UC(scsp_regcache[address >> 1]) = data; } //------------------------------------------------------------------------- // ScspDoKeyOnOff: Apply the key-on/key-off setting for all slots. // Implements the KYONEX trigger. static void ScspDoKeyOnOff(void) { int slotnum; for (slotnum = 0; slotnum < 32; slotnum++) { if (scsp.slot[slotnum].key) ScspKeyOn(&scsp.slot[slotnum]); else ScspKeyOff(&scsp.slot[slotnum]); } } //----------------------------------// // ScspKeyOn: Execute a key-on event for a single slot. static void ScspKeyOn(SlotState *slot) { if (slot->env_phase != SCSP_ENV_RELEASE) return; // Can't key a sound that's already playing ScspUpdateSlotAddress(slot); slot->addr_counter = 0; slot->env_phase = SCSP_ENV_ATTACK; slot->env_counter = SCSP_ENV_ATTACK_START; // FIXME: should this start at the current value if the old sound is still decaying? slot->env_step = slot->env_step_a; slot->env_target = SCSP_ENV_ATTACK_END; } //----------------------------------// // ScspKeyOff: Execute a key-off event for a single slot. static void ScspKeyOff(SlotState *slot) { if (slot->env_phase == SCSP_ENV_RELEASE) return; // Can't release a sound that's already released // If we still are in attack phase at release time, convert attack to decay if (slot->env_phase == SCSP_ENV_ATTACK) slot->env_counter = SCSP_ENV_DECAY_END - slot->env_counter; slot->env_phase = SCSP_ENV_RELEASE; slot->env_step = slot->env_step_r; slot->env_target = SCSP_ENV_DECAY_END; } //------------------------------------------------------------------------- // ScspUpdateSlotAddress: Update the sample data pointer for the given // slot, bound slot->lea to the end of sound RAM, and cache the shifted // values of slot->lsa and slot->lea in slot->{lea,lsa}_shifted. static void ScspUpdateSlotAddress(SlotState *slot) { u32 max_samples; if (slot->pcm8b) slot->sa &= ~1; slot->sa &= scsp.sound_ram_mask; slot->buf = &SoundRam[slot->sa]; max_samples = scsp.sound_ram_mask - slot->sa; if (slot->pcm8b) max_samples >>= 1; if (slot->lsa > max_samples) slot->lsa = max_samples; slot->lsa_shifted = slot->lsa << SCSP_FREQ_LOW_BITS; if (slot->lea > max_samples) slot->lea = max_samples; slot->lea_shifted = ((slot->lea + 1) << SCSP_FREQ_LOW_BITS) - 1; slot->looplen_shifted = slot->lea_shifted - slot->lsa_shifted + 1; } //----------------------------------// // ScspUpdateSlotEnv: Update the envelope step values from the SR/DR/AR/RR // slot registers. slot->krs_shift and slot->octave_shift are assumed to // be up to date with respect to the KRS and OCT registers. static void ScspUpdateSlotEnv(SlotState *slot) { if (slot->sr) { const s32 *rate_table = &scsp_decay_rate[slot->sr << 1]; slot->env_step_s = rate_table[(15 - slot->octave_shift) >> slot->krs_shift]; } else slot->env_step_s = 0; if (slot->dr) { const s32 *rate_table = &scsp_decay_rate[slot->dr << 1]; slot->env_step_d = rate_table[(15 - slot->octave_shift) >> slot->krs_shift]; } else slot->env_step_d = 0; if (slot->ar) { const s32 *rate_table = &scsp_attack_rate[slot->ar << 1]; slot->env_step_a = rate_table[(15 - slot->octave_shift) >> slot->krs_shift]; } else slot->env_step_a = 0; if (slot->rr) { const s32 *rate_table = &scsp_decay_rate[slot->rr << 1]; slot->env_step_r = rate_table[(15 - slot->octave_shift) >> slot->krs_shift]; } else slot->env_step_r = 0; } //----------------------------------// // ScspUpdateSlotFunc: Update the audio generation function for the given // slot based on the slot's parameters. static void ScspUpdateSlotFunc(SlotState *slot) { if (slot->ssctl) // FIXME: noise (ssctl==1) not implemented slot->audiogen = scsp_audiogen_func_table[0][0][0][0][0]; else slot->audiogen = scsp_audiogen_func_table[slot->lfo_fm_shift >= 0] [slot->lfo_am_shift >= 0] [slot->pcm8b == 0] [slot->outshift_l != 31] [slot->outshift_r != 31]; } //------------------------------------------------------------------------- // ScspMidiIn: Handle a read from the MIDI input register ($404). Since // there is no facility for sending MIDI data (the Saturn does not have a // MIDI I/O port), most of this is essentially a giant no-op, but the logic // is included for reference. static u16 ScspMidiIn(void) { scsp.miovf = 0; scsp.mifull = 0; if (scsp.midi_in_cnt > 0) { scsp.mibuf = scsp.midi_in_buf[0]; scsp.midi_in_buf[0] = scsp.midi_in_buf[1]; scsp.midi_in_buf[1] = scsp.midi_in_buf[2]; scsp.midi_in_buf[2] = scsp.midi_in_buf[3]; scsp.midi_in_cnt--; scsp.miemp = (scsp.midi_in_cnt == 0); if (!scsp.miemp) ScspRaiseInterrupt(SCSP_INTERRUPT_MIDI_IN, SCSP_INTTARGET_BOTH); } else // scsp.midi_in_cnt == 0 scsp.mibuf = 0xFF; return scsp.mofull << 12 | scsp.moemp << 11 | scsp.miovf << 10 | scsp.mifull << 9 | scsp.miemp << 8 | scsp.mibuf << 0; } //----------------------------------// // ScspMidiOut: Handle a write to the MIDI output register ($406). static void ScspMidiOut(u8 data) { scsp.moemp = 0; if (scsp.midi_out_cnt < 4) scsp.midi_out_buf[scsp.midi_out_cnt++] = data; scsp.mofull = (scsp.midi_out_cnt >= 4); } //------------------------------------------------------------------------- // ScspDoDMA: Handle a DMA request (when 1 is written to DEXE). DMA is // processed instantaneously regardless of transfer size. static void ScspDoDMA(void) { const u32 dmea = scsp.dmea & scsp.sound_ram_mask; if (scsp.ddir) // {RAM,zero} -> registers { SCSPLOG("DMA %s RAM[$%05X] -> registers[$%03X]\n", scsp.dgate ? "clear" : "copy", dmea, scsp.drga); if (scsp.dgate) { u32 i; for (i = 0; i < scsp.dtlg; i += 2) ScspWriteWordDirect(scsp.drga + i, 0); } else { u32 i; for (i = 0; i < scsp.dtlg; i += 2) ScspWriteWordDirect(scsp.drga + i, T2ReadWord(SoundRam, dmea + i)); } } else // !scsp.ddir, i.e. registers -> RAM { SCSPLOG("DMA %s registers[$%03X] -> RAM[$%05X]\n", scsp.dgate ? "clear" : "copy", scsp.drga, dmea); if (scsp.dgate) memset(&SoundRam[dmea], 0, scsp.dtlg); else { u32 i; for (i = 0; i < scsp.dtlg; i += 2) T2WriteWord(SoundRam, dmea + i, ScspReadWordDirect(scsp.drga + i)); } M68K->WriteNotify(dmea, scsp.dtlg); } scsp.dexe = 0; PSP_UC(scsp_regcache[0x416>>1]) &= ~(1<<12); ScspRaiseInterrupt(SCSP_INTERRUPT_DMA, SCSP_INTTARGET_BOTH); } /////////////////////////////////////////////////////////////////////////// // Other SCSP internal helper routines /////////////////////////////////////////////////////////////////////////// // ScspSyncThread: Wait for the SCSP subthread to finish executing all // pending cycles. Do not call if the subthread is not running. static void ScspSyncThread(void) { PSP_FLUSH_ALL(); while (PSP_UC(scsp_clock) != scsp_clock_target) { YabThreadWake(YAB_THREAD_SCSP); YabThreadYield(); } } //------------------------------------------------------------------------- // ScspRaiseInterrupt: Raise an interrupt for the main and/or sound CPU. static void ScspRaiseInterrupt(int which, int target) { if (target & SCSP_INTTARGET_MAIN) { scsp.mcipd |= 1 << which; PSP_UC(scsp_regcache[0x42C >> 1]) = scsp.mcipd; if (scsp.mcieb & (1 << which)) { if (scsp_thread_running) PSP_UC(scsp_main_interrupt_pending) = 1; else (*scsp_interrupt_handler)(); } } if (target & SCSP_INTTARGET_SOUND) { scsp.scipd |= 1 << which; PSP_UC(scsp_regcache[0x420 >> 1]) = scsp.scipd; if (scsp.scieb & (1 << which)) { const int level_shift = (which > 7) ? 7 : which; const int level = ((scsp.scilv0 >> level_shift) & 1) << 0 | ((scsp.scilv1 >> level_shift) & 1) << 1 | ((scsp.scilv2 >> level_shift) & 1) << 2; M68K->SetIRQ(level); } } } //----------------------------------// // ScspCheckInterrupts: Check pending interrupts for the main or sound CPU // against the interrupt enable flags, and raise any interrupts which are // both enabled and pending. The mask parameter indicates which interrupts // should be checked. Implements writes to SCIEB and MCIEB. static void ScspCheckInterrupts(u16 mask, int target) { int i; for (i = 0; i < 11; i++) { if ((1<> 1]) = scsp.mcipd; } if (target & SCSP_INTTARGET_SOUND) { scsp.scipd &= ~mask; PSP_UC(scsp_regcache[0x420 >> 1]) = scsp.scipd; } } //------------------------------------------------------------------------- // ScspRunM68K: Run the M68K for the given number of cycles. static void ScspRunM68K(u32 cycles) { if (LIKELY(m68k_running)) { s32 new_cycles = m68k_saved_cycles + cycles; if (LIKELY(new_cycles > 0)) new_cycles -= (*m68k_execf)(new_cycles); m68k_saved_cycles = new_cycles; } } //----------------------------------// // M68KExecBP: Wrapper for M68K->Exec() which checks for breakpoints and // calls the breakpoint callback when one is reached. This logic is // extracted from M68KExec() to avoid unnecessary register spillage on the // fast (no-breakpoint) path. static s32 FASTCALL M68KExecBP(s32 cycles) { s32 cycles_to_exec = cycles; s32 cycles_executed = 0; int i; while (cycles_executed < cycles_to_exec) { // Make sure it isn't one of our breakpoints for (i = 0; i < m68k_num_breakpoints; i++) { if ((M68K->GetPC() == m68k_breakpoint[i].addr) && !m68k_in_breakpoint) { m68k_in_breakpoint = 1; if (M68KBreakpointCallback) M68KBreakpointCallback(m68k_breakpoint[i].addr); m68k_in_breakpoint = 0; } } // Execute instructions individually cycles_executed += M68K->Exec(1); } return cycles_executed; } /////////////////////////////////////////////////////////////////////////// // M68K management routines /////////////////////////////////////////////////////////////////////////// // M68KStart: Start the M68K processor running. void M68KStart(void) { if (scsp_thread_running) ScspSyncThread(); M68K->Reset(); m68k_saved_cycles = 0; m68k_running = 1; if (scsp_thread_running) PSP_FLUSH_ALL(); } //------------------------------------------------------------------------- // M68KStop: Halt the M68K processor. void M68KStop(void) { if (scsp_thread_running) ScspSyncThread(); m68k_running = 0; if (scsp_thread_running) PSP_FLUSH_ALL(); } //------------------------------------------------------------------------- // M68KStep: Execute a single M68K instruction. void M68KStep(void) { M68K->Exec(1); } //------------------------------------------------------------------------- // M68KWriteNotify: Notify the M68K emulator that a region of sound RAM // has been written to by an external agent. void M68KWriteNotify(u32 address, u32 size) { M68K->WriteNotify(address, size); } //------------------------------------------------------------------------- // M68KGetRegisters, M68KSetRegisters: Get or set the current values of // the M68K registers. void M68KGetRegisters(M68KRegs *regs) { int i; if (regs != NULL) { for (i = 0; i < 8; i++) { regs->D[i] = M68K->GetDReg(i); regs->A[i] = M68K->GetAReg(i); } regs->SR = M68K->GetSR(); regs->PC = M68K->GetPC(); } } void M68KSetRegisters(const M68KRegs *regs) { int i; if (regs != NULL) { for (i = 0; i < 8; i++) { M68K->SetDReg(i, regs->D[i]); M68K->SetAReg(i, regs->A[i]); } M68K->SetSR(regs->SR); M68K->SetPC(regs->PC); } } //------------------------------------------------------------------------- // M68KSetBreakpointCallback: Set a function to be called whenever an M68K // breakpoint is reached. void M68KSetBreakpointCallBack(void (*func)(u32 address)) { M68KBreakpointCallback = func; } //------------------------------------------------------------------------- // M68KAddCodeBreakpoint: Add an M68K breakpoint on the given address. // Returns 0 on success, -1 if the breakpoint table is full or there is // already a breakpoint set on the address. int M68KAddCodeBreakpoint(u32 address) { int i; if (m68k_num_breakpoints >= MAX_BREAKPOINTS) return -1; // Make sure it isn't already on the list for (i = 0; i < m68k_num_breakpoints; i++) { if (m68k_breakpoint[i].addr == address) return -1; } m68k_breakpoint[m68k_num_breakpoints].addr = address; m68k_num_breakpoints++; // Switch to the slow exec routine so we can catch the breakpoint m68k_execf = M68KExecBP; return 0; } //------------------------------------------------------------------------- // M68KDelCodeBreakpoint: Delete an M68K breakpoint on the given address. // Returns 0 on success, -1 if there was no breakpoint set on the address. int M68KDelCodeBreakpoint(u32 address) { int i; if (m68k_num_breakpoints > 0) { for (i = 0; i < m68k_num_breakpoints; i++) { if (m68k_breakpoint[i].addr == address) { // Swap with the last breakpoint in the table, so there are // no holes in the breakpoint list m68k_breakpoint[i].addr = m68k_breakpoint[m68k_num_breakpoints-1].addr; m68k_breakpoint[m68k_num_breakpoints-1].addr = 0xFFFFFFFF; m68k_num_breakpoints--; if (m68k_num_breakpoints == 0) { // Last breakpoint deleted, so go back to the fast exec routine m68k_execf = M68K->Exec; } return 0; } } } return -1; } //------------------------------------------------------------------------- // M68KGetBreakpointList: Return the array of breakpoints currently set. // The array is M68K_MAX_BREAKPOINTS elements long, and an address of // 0xFFFFFFFF indicates that no breakpoint is set in that slot. const M68KBreakpointInfo *M68KGetBreakpointList(void) { return m68k_breakpoint; } //------------------------------------------------------------------------- // M68KClearCodeBreakpoints: Clear all M68K breakpoints. void M68KClearCodeBreakpoints(void) { int i; for (i = 0; i < MAX_BREAKPOINTS; i++) m68k_breakpoint[i].addr = 0xFFFFFFFF; m68k_num_breakpoints = 0; m68k_execf = M68K->Exec; } //------------------------------------------------------------------------- // M68K{Read,Write}{Byte,Word}: Memory access routines for the M68K // emulation. Exported for use in debugging. u32 FASTCALL M68KReadByte(u32 address) { if (address < 0x100000) return T2ReadByte(SoundRam, address & scsp.sound_ram_mask); else return ScspReadByteDirect(address & 0xFFF); } u32 FASTCALL M68KReadWord(u32 address) { if (address < 0x100000) return T2ReadWord(SoundRam, address & scsp.sound_ram_mask); else return ScspReadWordDirect(address & 0xFFF); } void FASTCALL M68KWriteByte(u32 address, u32 data) { if (address < 0x100000) T2WriteByte(SoundRam, address & scsp.sound_ram_mask, data); else ScspWriteByteDirect(address & 0xFFF, data); } void FASTCALL M68KWriteWord(u32 address, u32 data) { if (address < 0x100000) T2WriteWord(SoundRam, address & scsp.sound_ram_mask, data); else ScspWriteWordDirect(address & 0xFFF, data); } /////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/macjoy.c000644 001750 001750 00000036551 12256006161 017634 0ustar00guillaumeguillaume000000 000000 /* This file was imported form CrabEmu ( http://crabemu.sourceforge.net/ ) -- A Sega Master System emulator for Mac OS X (among other targets). The rest of the file is left intact from CrabEmu to make things easier if this were to be upgraded in the future. */ /* This file is part of CrabEmu. Copyright (C) 2008 Lawrence Sebald CrabEmu is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. CrabEmu 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 General Public License for more details. You should have received a copy of the GNU General Public License along with CrabEmu; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "macjoy.h" static int joy_count = 0; static joydata_t *joys = NULL; static void joy_find_elements(CFMutableDictionaryRef prop, joydata_t *joy); /* Compare two buttons for sorting. */ static int joy_cmp_buttons(const void *e1, const void *e2) { const joy_elemdata_t *b1 = (const joy_elemdata_t *)e1; const joy_elemdata_t *b2 = (const joy_elemdata_t *)e2; return b1->number < b2->number ? -1 : (b1->number > b2->number ? 1 : 0); } /* Get the io_iterator_t object needed to iterate through the list of HID devices. */ static void joy_get_iterator(mach_port_t port, io_iterator_t *iter) { CFMutableDictionaryRef d; IOReturn rv; /* Create a matching dictionary that will be used to search the device tree. */ if(!(d = IOServiceMatching(kIOHIDDeviceKey))) { return; } /* Get all matching devices from IOKit. */ rv = IOServiceGetMatchingServices(port, d, iter); if(rv != kIOReturnSuccess || !(*iter)) { return; } } /* Create the interface needed to do stuff with the device. */ static int joy_create_interface(io_object_t hidDevice, joydata_t *joy) { IOCFPlugInInterface **plugin; SInt32 score = 0; /* Create the plugin that we will use to actually get the device interface that is needed. */ if(IOCreatePlugInInterfaceForService(hidDevice, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin, &score) != kIOReturnSuccess) { return 0; } /* Grab the device interface from the plugin. */ if((*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (LPVOID)&joy->iface) != S_OK) { return 0; } /* The plugin has done all it needs to, release it. */ (*plugin)->Release(plugin); return 1; } /* Fill in a elemdata_t structure with information about the given physical element. */ static void joy_fill_elem(CFTypeRef elem, joy_elemdata_t *ptr) { CFTypeRef ref; int num; memset(ptr, 0, sizeof(joy_elemdata_t)); /* Grab the element cookie. */ if((ref = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementCookieKey)))) { if(CFNumberGetValue(ref, kCFNumberIntType, &num)) { ptr->cookie = (IOHIDElementCookie)num; } } /* Grab the element's minimum value. */ if((ref = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementMinKey)))) { if(CFNumberGetValue(ref, kCFNumberIntType, &num)) { ptr->min = num; } } /* Grab the element's maximum value. */ if((ref = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementMaxKey)))) { if(CFNumberGetValue(ref, kCFNumberIntType, &num)) { ptr->max = num; } } } /* Callback function to handle each sub-element of the controller class. */ static void joy_elem_array_hnd(const void *value, void *parameter) { CFTypeRef elem = (CFTypeRef)value; CFTypeID t = CFGetTypeID(value); joydata_t *joy = (joydata_t *)parameter; long elemType, elemPage, elemUsage; CFTypeRef type, page, usage; void *ptr; int et; /* Make sure we're dealing with a dictionary. */ if(t == CFDictionaryGetTypeID()) { /* Grab the type of the element. */ type = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementTypeKey)); if(!type) { return; } /* Grab the HID usage page. */ page = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementUsagePageKey)); if(!page) { return; } /* Grab the HID usage type. */ usage = CFDictionaryGetValue(elem, CFSTR(kIOHIDElementUsageKey)); if(!usage) { return; } /* Get the integer values from the data grabbed above. */ if(!CFNumberGetValue(type, kCFNumberLongType, &elemType)) { return; } if(!CFNumberGetValue(page, kCFNumberLongType, &elemPage)) { return; } if(!CFNumberGetValue(usage, kCFNumberLongType, &elemUsage)) { return; } /* If the element is listed as a button, axis, or misc input, check what it actually is. */ if(elemType == kIOHIDElementTypeInput_Button || elemType == kIOHIDElementTypeInput_Axis || elemType == kIOHIDElementTypeInput_Misc) { switch(elemPage) { case kHIDPage_Button: ptr = realloc(joy->buttons, (joy->buttons_count + 1) * sizeof(joy_elemdata_t)); if(ptr) { joy->buttons = (joy_elemdata_t *)ptr; joy_fill_elem(elem, joy->buttons + joy->buttons_count); joy->buttons[joy->buttons_count].number = elemUsage; ++joy->buttons_count; } break; case kHIDPage_GenericDesktop: switch(elemUsage) { case kHIDUsage_GD_X: et = JOY_TYPE_X_AXIS; goto axis; case kHIDUsage_GD_Y: et = JOY_TYPE_Y_AXIS; goto axis; case kHIDUsage_GD_Z: et = JOY_TYPE_Z_AXIS; goto axis; case kHIDUsage_GD_Rx: et = JOY_TYPE_X2_AXIS; goto axis; case kHIDUsage_GD_Ry: et = JOY_TYPE_Y2_AXIS; goto axis; case kHIDUsage_GD_Rz: et = JOY_TYPE_Z2_AXIS; axis: ptr = realloc(joy->axes, (joy->axes_count + 1) * sizeof(joy_elemdata_t)); if(ptr) { joy->axes = (joy_elemdata_t *)ptr; joy_fill_elem(elem, joy->axes + joy->axes_count); joy->axes[joy->axes_count].type = et; ++joy->axes_count; } break; case kHIDUsage_GD_Hatswitch: ptr = realloc(joy->hats, (joy->hats_count + 1) * sizeof(joy_elemdata_t)); if(ptr) { joy->hats = (joy_elemdata_t *)ptr; joy_fill_elem(elem, joy->hats + joy->hats_count); ++joy->hats_count; } break; } break; } } /* If we've found another element array, effectively recurse. */ else if(elemType == kIOHIDElementTypeCollection) { joy_find_elements((CFMutableDictionaryRef)elem, joy); } } } /* Process all the sub-elements of a given property list. */ static void joy_find_elements(CFMutableDictionaryRef prop, joydata_t *joy) { CFTypeRef elem; CFTypeID type; if((elem = CFDictionaryGetValue(prop, CFSTR(kIOHIDElementKey)))) { type = CFGetTypeID(elem); if(type == CFArrayGetTypeID()) { /* Call our function on each element of the array. */ CFRange r = { 0, CFArrayGetCount(elem) }; CFArrayApplyFunction(elem, r, &joy_elem_array_hnd, (void *)joy); } } } /* Read the device passed in, and add it to our joystick list if appropriate. */ static void joy_read_device(io_object_t dev) { CFMutableDictionaryRef props = 0; /* Create a dictionary to read the device's properties. */ if(IORegistryEntryCreateCFProperties(dev, &props, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS) { CFTypeRef inf; SInt32 page, usage; void *ptr; /* Grab the primary usage page of the device. */ inf = CFDictionaryGetValue(props, CFSTR(kIOHIDPrimaryUsagePageKey)); if(!inf || !CFNumberGetValue((CFNumberRef)inf, kCFNumberSInt32Type, &page)) { goto out; } /* Ignore devices that are not in the Generic Desktop page. */ if(page != kHIDPage_GenericDesktop) { goto out; } /* Grab the primary device usage. */ inf = CFDictionaryGetValue(props, CFSTR(kIOHIDPrimaryUsageKey)); if(!inf || !CFNumberGetValue((CFNumberRef)inf, kCFNumberSInt32Type, &usage)) { goto out; } /* Ignore devices that are not either a Game Pad or Joystick. */ if(usage != kHIDUsage_GD_GamePad && usage != kHIDUsage_GD_Joystick) { goto out; } /* Allocate space for the new joystick structure. */ ptr = realloc(joys, (joy_count + 1) * sizeof(joydata_t)); if(ptr == NULL) { goto out; } joys = (joydata_t *)ptr; memset(joys + joy_count, 0, sizeof(joydata_t)); /* Grab and store the name of the device. */ inf = CFDictionaryGetValue(props, CFSTR(kIOHIDProductKey)); if(!CFStringGetCString((CFStringRef)inf, joys[joy_count].name, 256, kCFStringEncodingUTF8)) { goto out; } /* Create the device interface needed to interact with the device. */ if(!joy_create_interface(dev, joys + joy_count)) { goto out; } /* Find all elements of the device. */ joy_find_elements(props, joys + joy_count); qsort(joys[joy_count].buttons, joys[joy_count].buttons_count, sizeof(joy_elemdata_t), &joy_cmp_buttons); ++joy_count; } out: CFRelease(props); } /* Release the given joystick's interface and clean up any memory used by it. */ static void joy_release_joystick(joydata_t *joy) { (*joy->iface)->Release(joy->iface); joy->iface = NULL; if(joy->buttons) free(joy->buttons); if(joy->axes) free(joy->axes); if(joy->hats) free(joy->hats); } /* Scan the system for any joysticks connected. */ int joy_scan_joysticks(void) { io_iterator_t iter = 0; io_object_t dev; if(joys != NULL) { return -1; } /* Get the iterator needed for going through the list of devices. */ joy_get_iterator(kIOMasterPortDefault, &iter); if(iter != 0) { while((dev = IOIteratorNext(iter))) { joy_read_device(dev); IOObjectRelease(dev); } /* Release the iterator. */ IOObjectRelease(iter); } return joy_count; } /* Clean up any data allocated by the program for joysticks. */ void joy_release_joysticks(void) { int i; for(i = 0; i < joy_count; ++i) { joy_close_joystick(joys + i); joy_release_joystick(joys + i); } if(joys != NULL) { free(joys); joys = NULL; } joy_count = 0; } /* Get the joystick at a given index in our list of joysticks. */ joydata_t *joy_get_joystick(int index) { if(index < 0 || index >= joy_count) { return NULL; } return &joys[index]; } /* Grab the device for exclusive use by this program. The device must be closed properly (with the joy_close_joystick function, or you may need to unplug/replug the joystick to get it to work again). */ int joy_open_joystick(joydata_t *joy) { if((*joy->iface)->open(joy->iface, 0)) { return 0; } joy->open = 1; return 1; } /* Close the device and return its resources to the system. */ int joy_close_joystick(joydata_t *joy) { IOReturn rv; if(!joy->open) { return 1; } rv = (*joy->iface)->close(joy->iface); if(rv == kIOReturnNotOpen) { /* The device wasn't open so it can't be closed. */ return 1; } else if(rv != kIOReturnSuccess) { return 0; } joy->open = 0; return 1; } /* Read a given element from the joystick. The joystick must be open for this function to actually do anything useful. */ int joy_read_element(joydata_t *joy, joy_elemdata_t *elem) { IOHIDEventStruct ev; memset(&ev, 0, sizeof(IOHIDEventStruct)); (*joy->iface)->getElementValue(joy->iface, elem->cookie, &ev); return ev.value; } /* Read the value of a given button. Returns -1 on failure. */ int joy_read_button(joydata_t *joy, int num) { /* Subtract 1 from the number to get the index. */ --num; if(num >= joy->buttons_count) { return -1; } return joy_read_element(joy, joy->buttons + num); } /* Read the value of a given axis. Returns 0 on failure (or if the axis reports that its value is 0). */ int joy_read_axis(joydata_t *joy, int index) { float value; if(index >= joy->axes_count) { return 0; } value = joy_read_element(joy, joy->axes + index) / (float)(joy->axes[index].max + 1); return (int)(value * 32768); } /* Read the value of a given hat. Returns -1 on failure. */ int joy_read_hat(joydata_t *joy, int index) { int value; if(index >= joy->hats_count) { return -1; } value = joy_read_element(joy, joy->hats + index) - joy->hats[index].min; /* 4-position hat switch -- Make it look like an 8-position one. */ if(joy->hats[index].max - joy->hats[index].min + 1 == 4) { value <<= 1; } switch(value) { case 0: return JOY_HAT_UP; case 1: return JOY_HAT_UP | JOY_HAT_RIGHT; case 2: return JOY_HAT_RIGHT; case 3: return JOY_HAT_RIGHT | JOY_HAT_DOWN; case 4: return JOY_HAT_DOWN; case 5: return JOY_HAT_DOWN | JOY_HAT_LEFT; case 6: return JOY_HAT_LEFT; case 7: return JOY_HAT_LEFT | JOY_HAT_UP; default: return JOY_HAT_CENTER; } } yabause-0.9.13.1/src/cd-windows.c000644 001750 001750 00000023132 12256006174 020423 0ustar00guillaumeguillaume000000 000000 /* Copyright 2004-2005 Theo Berkau Copyright 2005 Joost Peters This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #ifdef HAVE_NTDDCDRM #include #include #else #include "fakeddk.h" #endif #include "cdbase.h" #include "yabause.h" ////////////////////////////////////////////////////////////////////////////// static HANDLE hCDROM; static SCSI_PASS_THROUGH_DIRECT sptd; static int KillCDThread=0; static HANDLE thread_handle=INVALID_HANDLE_VALUE; static CRITICAL_SECTION cd_cs; static int drivestatus=0; static DWORD thread_id; static struct { int enable_read_ahead; u32 FAD; unsigned char data[2352]; int num_sectors_read; int error_flag; } cd_buf; static int SPTICDInit(const char *); static void SPTICDDeInit(void); static s32 SPTICDReadTOC(u32 *); static int SPTICDGetStatus(void); static int SPTICDReadSectorFAD(u32, void *); static void SPTICDReadAheadFAD(u32); CDInterface ArchCD = { CDCORE_ARCH, "Windows SPTI Driver", SPTICDInit, SPTICDDeInit, SPTICDGetStatus, SPTICDReadTOC, SPTICDReadSectorFAD, SPTICDReadAheadFAD, }; ////////////////////////////////////////////////////////////////////////////// // SPTI Interface ////////////////////////////////////////////////////////////////////////////// DWORD WINAPI __stdcall SPTICDThread(void *b); int SPTICDInit(const char *cdrom_name) { char pipe_name[7]; sprintf(pipe_name, "\\\\.\\?:"); pipe_name[4] = cdrom_name[0]; if ((hCDROM = CreateFileA(pipe_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { return -1; } // Setup a separate thread for handling SPTI commands(that way the emulation // code doesn't have to wait for a response) KillCDThread=0; memset(&cd_buf, 0, sizeof(cd_buf)); thread_handle = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE) SPTICDThread,(void *) &KillCDThread,0,&thread_id); InitializeCriticalSection(&cd_cs); return 0; } ////////////////////////////////////////////////////////////////////////////// void SPTICDDeInit() { if (thread_handle != INVALID_HANDLE_VALUE) { // Set the flag telling it to stop KillCDThread=1; if (WaitForSingleObject(thread_handle,INFINITE) == WAIT_TIMEOUT) { // Couldn't close thread cleanly TerminateThread(thread_handle,0); } CloseHandle(thread_handle); thread_handle = INVALID_HANDLE_VALUE; } DeleteCriticalSection(&cd_cs); CloseHandle(hCDROM); } ////////////////////////////////////////////////////////////////////////////// DWORD WINAPI __stdcall SPTICDThread(void *b) { u64 curticks, lastticks = YabauseGetTicks(); while (KillCDThread != 1) { DWORD dwBytesReturned; unsigned char statusbuf[8]; BOOL success; EnterCriticalSection (&cd_cs); // Check to see if we have any work to do(for now, let's just do a drive // status check once a second) if (cd_buf.enable_read_ahead) { u32 FAD; DWORD dwBytesReturned; BOOL success; memset(&sptd, 0, sizeof(sptd)); sptd.Length=sizeof(sptd); sptd.CdbLength=12; sptd.SenseInfoLength=0; // No sense data sptd.DataIn=SCSI_IOCTL_DATA_IN; sptd.TimeOutValue=60; // may need to be changed sptd.DataBuffer=(PVOID)cd_buf.data; sptd.SenseInfoOffset=0; sptd.DataTransferLength=2352; sptd.Cdb[0]=0xBE; // CDB12 code sptd.Cdb[1]=0; // Sector Type, RELADR FAD = cd_buf.FAD - 150; sptd.Cdb[2]=(unsigned char)((FAD & 0xFF000000) >> 24); // lba(byte 1) sptd.Cdb[3]=(unsigned char)((FAD & 0x00FF0000) >> 16); // lba(byte 2) sptd.Cdb[4]=(unsigned char)((FAD & 0x0000FF00) >> 8); // lba(byte 3) sptd.Cdb[5]=(unsigned char)(FAD & 0x000000FF); // lba(byte 4) sptd.Cdb[6]=0; // number of sectors(byte 1) sptd.Cdb[7]=0; // number of sectors(byte 2) sptd.Cdb[8]=1; // number of sectors(byte 3) sptd.Cdb[9]=0xF8; // Sync + All Headers + User data + EDC/ECC success=DeviceIoControl(hCDROM, IOCTL_SCSI_PASS_THROUGH_DIRECT, (PVOID)&sptd, (DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT), NULL, 0, &dwBytesReturned, NULL); if (success) cd_buf.num_sectors_read++; cd_buf.error_flag = success; cd_buf.enable_read_ahead = 0; } curticks = YabauseGetTicks(); if (curticks-lastticks >= yabsys.tickfreq) { memset(&sptd, 0, sizeof(sptd)); sptd.Length=sizeof(sptd); sptd.CdbLength=12; sptd.SenseInfoLength=0; // No sense data sptd.DataIn=SCSI_IOCTL_DATA_IN; sptd.TimeOutValue=60; // may need to be changed sptd.DataBuffer=(PVOID)&(statusbuf); sptd.SenseInfoOffset=0; sptd.DataTransferLength=8; // may need to change this sptd.Cdb[0]=0xBD; // CDB12 code sptd.Cdb[8]=0; // Allocation Length(byte 1) sptd.Cdb[9]=8; // Allocation Length(byte 2) (may have to change this) success=DeviceIoControl(hCDROM, IOCTL_SCSI_PASS_THROUGH_DIRECT, (PVOID)&sptd, (DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT), NULL, 0, &dwBytesReturned, NULL); if (success) { // Figure out drive status // Is door open? if (statusbuf[1] & 0x10) drivestatus = 3; else { // Ok, so the door is closed, now is there a disc there? success = DeviceIoControl(hCDROM, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &dwBytesReturned, NULL); if (!success) drivestatus = 2; else drivestatus = 0; } } //else //drivestatus = 2; lastticks = curticks; } LeaveCriticalSection(&cd_cs); SwitchToThread(); } return 0; } ////////////////////////////////////////////////////////////////////////////// int SPTICDGetStatus() { // This function is called periodically to see what the status of the // drive is. // // Should return one of the following values: // 0 - CD Present, disc spinning // 1 - CD Present, disc not spinning // 2 - CD not present // 3 - Tray open // // If you really don't want to bother too much with this function, just // return status 0. Though it is kind of nice when the bios's cd player, // etc. recognizes when you've ejected the tray and popped in another disc. int status; EnterCriticalSection(&cd_cs); status = drivestatus; LeaveCriticalSection(&cd_cs); return status; } ////////////////////////////////////////////////////////////////////////////// #define MSF_TO_FAD(m,s,f) ((m * 4500) + (s * 75) + f) s32 SPTICDReadTOC(u32 *TOC) { CDROM_TOC ctTOC; DWORD dwNotUsed; int i; if (hCDROM != INVALID_HANDLE_VALUE) { memset(TOC, 0xFF, 0xCC * 2); memset(&ctTOC, 0xFF, sizeof(CDROM_TOC)); if (DeviceIoControl (hCDROM, IOCTL_CDROM_READ_TOC, NULL, 0, &ctTOC, sizeof(ctTOC), &dwNotUsed, NULL) == 0) { return 0; } // convert TOC to saturn format for (i = 0; i < ctTOC.LastTrack; i++) { TOC[i] = (ctTOC.TrackData[i].Control << 28) | (ctTOC.TrackData[i].Adr << 24) | MSF_TO_FAD(ctTOC.TrackData[i].Address[1], ctTOC.TrackData[i].Address[2], ctTOC.TrackData[i].Address[3]); } // Do First, Last, and Lead out sections here TOC[99] = (ctTOC.TrackData[0].Control << 28) | (ctTOC.TrackData[0].Adr << 24) | (ctTOC.FirstTrack << 16); TOC[100] = (ctTOC.TrackData[ctTOC.LastTrack - 1].Control << 28) | (ctTOC.TrackData[ctTOC.LastTrack - 1].Adr << 24) | (ctTOC.LastTrack << 16); TOC[101] = (ctTOC.TrackData[ctTOC.LastTrack].Control << 28) | (ctTOC.TrackData[ctTOC.LastTrack].Adr << 24) | MSF_TO_FAD(ctTOC.TrackData[ctTOC.LastTrack].Address[1], ctTOC.TrackData[ctTOC.LastTrack].Address[2], ctTOC.TrackData[ctTOC.LastTrack].Address[3]); return (0xCC * 2); } return 0; } ////////////////////////////////////////////////////////////////////////////// int SPTICDReadSectorFAD(u32 FAD, void *buffer) { // This function is supposed to read exactly 1 -RAW- 2352-byte sector at // the specified FAD address to buffer. Should return true if successful, // false if there was an error. // // Special Note: To convert from FAD to LBA/LSN, minus 150. // // The whole process needed to be changed since I need more control over // sector detection, etc. Not to mention it means less work for the porter // since they only have to implement raw sector reading as opposed to // implementing mode 1, mode 2 form1/form2, -and- raw sector reading. for (;;) { EnterCriticalSection(&cd_cs); if ((volatile)cd_buf.enable_read_ahead == 0) { if (cd_buf.FAD == FAD) { int ret; memcpy(buffer, cd_buf.data, 2352); ret = cd_buf.error_flag; LeaveCriticalSection(&cd_cs); return ret; } else { cd_buf.FAD = FAD; cd_buf.num_sectors_read = 0; cd_buf.error_flag = 0; cd_buf.enable_read_ahead = 1; } } LeaveCriticalSection(&cd_cs); SwitchToThread(); } } ////////////////////////////////////////////////////////////////////////////// void SPTICDReadAheadFAD(u32 FAD) { EnterCriticalSection(&cd_cs); cd_buf.FAD = FAD; cd_buf.num_sectors_read = 0; cd_buf.error_flag = 0; cd_buf.enable_read_ahead = 1; LeaveCriticalSection(&cd_cs); } ////////////////////////////////////////////////////////////////////////////// yabause-0.9.13.1/src/error.h000644 001750 001750 00000002547 12256006161 017506 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef ERROR_H #define ERROR_H #define YAB_ERR_UNKNOWN 0 #define YAB_ERR_FILENOTFOUND 1 #define YAB_ERR_MEMORYALLOC 2 #define YAB_ERR_FILEREAD 3 #define YAB_ERR_FILEWRITE 4 #define YAB_ERR_CANNOTINIT 5 #define YAB_ERR_SH2INVALIDOPCODE 6 #define YAB_ERR_SH2READ 7 #define YAB_ERR_SH2WRITE 8 #define YAB_ERR_SDL 9 #define YAB_ERR_OTHER 10 void YabSetError(int type, const void *extra); void YabErrorMsg(const char * format, ...); #endif yabause-0.9.13.1/src/sock.h000644 001750 001750 00000004613 12256006124 017307 0ustar00guillaumeguillaume000000 000000 /* Copyright 2013 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SOCK_H #define SOCK_H typedef int YabSock; // YabSockInit: Initializes socket code. // Returns 0 on success. -1 on error. int YabSockInit(); // YabSockDeInit: DeInitialize/frees socket code. // Returns 0 on success. -1 on error. int YabSockDeInit(); // YabSockConnectSocket: Attempts to connect to specified ip and port. // Returns 0 on success. -1 on error. int YabSockConnectSocket(const char *ip, int port, YabSock *sock); // YabSockListenSocket: Listen for connection attempts on specified port. // Returns 0 on success. -1 on error. int YabSockListenSocket(int port, YabSock *sock); // YabSockCloseSocket: Closes previously opened socket. // Returns 0 on success. -1 on error. int YabSockCloseSocket(YabSock sock); // YabSockSelect: Determines the status of one or more sockets. // Returns 0 on success. -1 on error. int YabSockSelect(YabSock sock, int check_read, int check_write); // YabSockIsReadSet: Is socket's read flag set // Returns 1 on true. 0 on false. int YabSockIsReadSet(YabSock sock); // YabSockIsWriteSet: Is socket's write flag set // Returns 1 on true. 0 on false. int YabSockIsWriteSet(YabSock sock); // YabSockAccept: Accept connection from socket. // Returns opened connected socket. YabSock YabSockAccept(YabSock sock); // YabSockSend: Sends data via specified socket // Returns 0 on success. -1 on error. int YabSockSend(YabSock sock, const void *buf, int len, int flags); // YabSockSend: Receive data via specified socket // Returns 0 on success. -1 on error. int YabSockReceive(YabSock sock, void *buf, int len, int flags); #endif // SOCK_H yabause-0.9.13.1/src/gameshw/dsplist.txt000644 001750 001750 00000002235 12256006201 022047 0ustar00guillaumeguillaume000000 000000 Games using SCU dsp -------------------- Battle Monsters Blazing Tornado Blue Chicago Blues - J.B.Harold Blue Seed Bubble Symphony Capcom Generations 1 Capcom Generations 4 Cotton Boomerang Crimewave Daisuki Dark Savior Dead or Alive Don Pachi Doom Dragon Ball Z Shinbuthoden DX Jinsei Game - The Game of Life Fatal Fury 3 Find Love 2 ?Gal Panic SS Galaxy Force 2(sega ages) Grandia Grandia: Digital Museum GT24 Guardian Force Guyferd Jissen Pachinko Hisshouhou! Twin Lunar 2: Eternal Blue Mega Man X4 Momotaroudouchuuki Nadesico Mighty Hits Planet Joker Psychic Killer Taromaru Real Bout FF Real Bout FF Special Samurai Shodown RPG Sega Ages Memorial Selection - Vol. 1 ?Shining Force 3 Scenario 1 Shining Force 3 Scenario 2 Shining Force 3 Scenario 3 Sky Target Slam Dunk - I love Basketball (heavy usage) Slayers Royal Sonic R Stakes Winner SS Steeldom ?Street Fighter Collection Disc 1 Super Real Mahjong P7 Sword & Sorcery Tenga Seiha ?Tengai Makyou Dai Yon no Mokujiroku - The Apocalypse IV Toshinden Ura The Yakuyaken Special ?Thunderforce Gold Pack 1 Victory Boxing Virtua Racing Virtua Fighter Remix Winter Heat Wonder 3 Worms Yellow Brick Road Zero4 Champ DooZy-J Type-R yabause-0.9.13.1/src/memory.c000644 001750 001750 00000127570 12256006161 017664 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef PSP // see FIXME in T1MemoryInit() # include #endif #include #include #include #include #include "memory.h" #include "coffelf.h" #include "cs0.h" #include "cs1.h" #include "cs2.h" #include "debug.h" #include "error.h" #include "sh2core.h" #include "scsp.h" #include "scu.h" #include "smpc.h" #include "vdp1.h" #include "vdp2.h" #include "yabause.h" #include "yui.h" #include "movie.h" #ifdef HAVE_LIBGL #define USE_OPENGL #endif #ifdef USE_OPENGL #include "ygl.h" #endif #include "vidsoft.h" #include "vidogl.h" ////////////////////////////////////////////////////////////////////////////// writebytefunc WriteByteList[0x1000]; writewordfunc WriteWordList[0x1000]; writelongfunc WriteLongList[0x1000]; readbytefunc ReadByteList[0x1000]; readwordfunc ReadWordList[0x1000]; readlongfunc ReadLongList[0x1000]; u8 *HighWram; u8 *LowWram; u8 *BiosRom; u8 *BupRam; /* This flag is set to 1 on every write to backup RAM. Ports can freely * check or clear this flag to determine when backup RAM has been written, * e.g. for implementing autosave of backup RAM. */ u8 BupRamWritten; ////////////////////////////////////////////////////////////////////////////// u8 * T1MemoryInit(u32 size) { #ifdef PSP // FIXME: could be ported to all arches, but requires stdint.h // for uintptr_t u8 * base; u8 * mem; if ((base = calloc((size * sizeof(u8)) + sizeof(u8 *) + 64, 1)) == NULL) return NULL; mem = base + sizeof(u8 *); mem = mem + (64 - ((uintptr_t) mem & 63)); *(u8 **)(mem - sizeof(u8 *)) = base; // Save base pointer below memory block return mem; #else return calloc(size, sizeof(u8)); #endif } ////////////////////////////////////////////////////////////////////////////// void T1MemoryDeInit(u8 * mem) { #ifdef PSP if (mem) free(*(u8 **)(mem - sizeof(u8 *))); #else free(mem); #endif } ////////////////////////////////////////////////////////////////////////////// T3Memory * T3MemoryInit(u32 size) { T3Memory * mem; if ((mem = (T3Memory *) calloc(1, sizeof(T3Memory))) == NULL) return NULL; if ((mem->base_mem = (u8 *) calloc(size, sizeof(u8))) == NULL) return NULL; mem->mem = mem->base_mem + size; return mem; } ////////////////////////////////////////////////////////////////////////////// void T3MemoryDeInit(T3Memory * mem) { free(mem->base_mem); free(mem); } ////////////////////////////////////////////////////////////////////////////// Dummy * DummyInit(UNUSED u32 s) { return NULL; } ////////////////////////////////////////////////////////////////////////////// void DummyDeInit(UNUSED Dummy * d) { } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL UnhandledMemoryReadByte(USED_IF_DEBUG u32 addr) { LOG("Unhandled byte read %08X\n", (unsigned int)addr); return 0; } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL UnhandledMemoryReadWord(USED_IF_DEBUG u32 addr) { LOG("Unhandled word read %08X\n", (unsigned int)addr); return 0; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL UnhandledMemoryReadLong(USED_IF_DEBUG u32 addr) { LOG("Unhandled long read %08X\n", (unsigned int)addr); return 0; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL UnhandledMemoryWriteByte(USED_IF_DEBUG u32 addr, UNUSED u8 val) { LOG("Unhandled byte write %08X\n", (unsigned int)addr); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL UnhandledMemoryWriteWord(USED_IF_DEBUG u32 addr, UNUSED u16 val) { LOG("Unhandled word write %08X\n", (unsigned int)addr); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL UnhandledMemoryWriteLong(USED_IF_DEBUG u32 addr, UNUSED u32 val) { LOG("Unhandled long write %08X\n", (unsigned int)addr); } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL HighWramMemoryReadByte(u32 addr) { return T2ReadByte(HighWram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL HighWramMemoryReadWord(u32 addr) { return T2ReadWord(HighWram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL HighWramMemoryReadLong(u32 addr) { return T2ReadLong(HighWram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL HighWramMemoryWriteByte(u32 addr, u8 val) { T2WriteByte(HighWram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL HighWramMemoryWriteWord(u32 addr, u16 val) { T2WriteWord(HighWram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL HighWramMemoryWriteLong(u32 addr, u32 val) { T2WriteLong(HighWram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL LowWramMemoryReadByte(u32 addr) { return T2ReadByte(LowWram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL LowWramMemoryReadWord(u32 addr) { return T2ReadWord(LowWram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL LowWramMemoryReadLong(u32 addr) { return T2ReadLong(LowWram, addr & 0xFFFFF); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL LowWramMemoryWriteByte(u32 addr, u8 val) { T2WriteByte(LowWram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL LowWramMemoryWriteWord(u32 addr, u16 val) { T2WriteWord(LowWram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL LowWramMemoryWriteLong(u32 addr, u32 val) { T2WriteLong(LowWram, addr & 0xFFFFF, val); } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL BiosRomMemoryReadByte(u32 addr) { return T2ReadByte(BiosRom, addr & 0x7FFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL BiosRomMemoryReadWord(u32 addr) { return T2ReadWord(BiosRom, addr & 0x7FFFF); } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL BiosRomMemoryReadLong(u32 addr) { return T2ReadLong(BiosRom, addr & 0x7FFFF); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BiosRomMemoryWriteByte(UNUSED u32 addr, UNUSED u8 val) { // read-only } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BiosRomMemoryWriteWord(UNUSED u32 addr, UNUSED u16 val) { // read-only } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BiosRomMemoryWriteLong(UNUSED u32 addr, UNUSED u32 val) { // read-only } ////////////////////////////////////////////////////////////////////////////// static u8 FASTCALL BupRamMemoryReadByte(u32 addr) { return T1ReadByte(BupRam, addr & 0xFFFF); } ////////////////////////////////////////////////////////////////////////////// static u16 FASTCALL BupRamMemoryReadWord(USED_IF_DEBUG u32 addr) { LOG("bup\t: BackupRam read word - %08X\n", addr); return 0; } ////////////////////////////////////////////////////////////////////////////// static u32 FASTCALL BupRamMemoryReadLong(USED_IF_DEBUG u32 addr) { LOG("bup\t: BackupRam read long - %08X\n", addr); return 0; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BupRamMemoryWriteByte(u32 addr, u8 val) { T1WriteByte(BupRam, (addr & 0xFFFF) | 0x1, val); BupRamWritten = 1; } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BupRamMemoryWriteWord(USED_IF_DEBUG u32 addr, UNUSED u16 val) { LOG("bup\t: BackupRam write word - %08X\n", addr); } ////////////////////////////////////////////////////////////////////////////// static void FASTCALL BupRamMemoryWriteLong(USED_IF_DEBUG u32 addr, UNUSED u32 val) { LOG("bup\t: BackupRam write long - %08X\n", addr); } ////////////////////////////////////////////////////////////////////////////// static void FillMemoryArea(unsigned short start, unsigned short end, readbytefunc r8func, readwordfunc r16func, readlongfunc r32func, writebytefunc w8func, writewordfunc w16func, writelongfunc w32func) { int i; for (i=start; i < (end+1); i++) { ReadByteList[i] = r8func; ReadWordList[i] = r16func; ReadLongList[i] = r32func; WriteByteList[i] = w8func; WriteWordList[i] = w16func; WriteLongList[i] = w32func; } } ////////////////////////////////////////////////////////////////////////////// void MappedMemoryInit() { // Initialize everyting to unhandled to begin with FillMemoryArea(0x000, 0xFFF, &UnhandledMemoryReadByte, &UnhandledMemoryReadWord, &UnhandledMemoryReadLong, &UnhandledMemoryWriteByte, &UnhandledMemoryWriteWord, &UnhandledMemoryWriteLong); // Fill the rest FillMemoryArea(0x000, 0x00F, &BiosRomMemoryReadByte, &BiosRomMemoryReadWord, &BiosRomMemoryReadLong, &BiosRomMemoryWriteByte, &BiosRomMemoryWriteWord, &BiosRomMemoryWriteLong); FillMemoryArea(0x010, 0x017, &SmpcReadByte, &SmpcReadWord, &SmpcReadLong, &SmpcWriteByte, &SmpcWriteWord, &SmpcWriteLong); FillMemoryArea(0x018, 0x01F, &BupRamMemoryReadByte, &BupRamMemoryReadWord, &BupRamMemoryReadLong, &BupRamMemoryWriteByte, &BupRamMemoryWriteWord, &BupRamMemoryWriteLong); FillMemoryArea(0x020, 0x02F, &LowWramMemoryReadByte, &LowWramMemoryReadWord, &LowWramMemoryReadLong, &LowWramMemoryWriteByte, &LowWramMemoryWriteWord, &LowWramMemoryWriteLong); FillMemoryArea(0x100, 0x17F, &UnhandledMemoryReadByte, &UnhandledMemoryReadWord, &UnhandledMemoryReadLong, &UnhandledMemoryWriteByte, &SSH2InputCaptureWriteWord, &UnhandledMemoryWriteLong); FillMemoryArea(0x180, 0x1FF, &UnhandledMemoryReadByte, &UnhandledMemoryReadWord, &UnhandledMemoryReadLong, &UnhandledMemoryWriteByte, &MSH2InputCaptureWriteWord, &UnhandledMemoryWriteLong); FillMemoryArea(0x200, 0x3FF, CartridgeArea->Cs0ReadByte, CartridgeArea->Cs0ReadWord, CartridgeArea->Cs0ReadLong, CartridgeArea->Cs0WriteByte, CartridgeArea->Cs0WriteWord, CartridgeArea->Cs0WriteLong); FillMemoryArea(0x400, 0x4FF, &Cs1ReadByte, &Cs1ReadWord, &Cs1ReadLong, &Cs1WriteByte, &Cs1WriteWord, &Cs1WriteLong); FillMemoryArea(0x580, 0x58F, &Cs2ReadByte, &Cs2ReadWord, &Cs2ReadLong, &Cs2WriteByte, &Cs2WriteWord, &Cs2WriteLong); FillMemoryArea(0x5A0, 0x5AF, &SoundRamReadByte, &SoundRamReadWord, &SoundRamReadLong, &SoundRamWriteByte, &SoundRamWriteWord, &SoundRamWriteLong); FillMemoryArea(0x5B0, 0x5BF, &scsp_r_b, &scsp_r_w, &scsp_r_d, &scsp_w_b, &scsp_w_w, &scsp_w_d); FillMemoryArea(0x5C0, 0x5C7, &Vdp1RamReadByte, &Vdp1RamReadWord, &Vdp1RamReadLong, &Vdp1RamWriteByte, &Vdp1RamWriteWord, &Vdp1RamWriteLong); FillMemoryArea(0x5C8, 0x5CF, &Vdp1FrameBufferReadByte, &Vdp1FrameBufferReadWord, &Vdp1FrameBufferReadLong, &Vdp1FrameBufferWriteByte, &Vdp1FrameBufferWriteWord, &Vdp1FrameBufferWriteLong); FillMemoryArea(0x5D0, 0x5D7, &Vdp1ReadByte, &Vdp1ReadWord, &Vdp1ReadLong, &Vdp1WriteByte, &Vdp1WriteWord, &Vdp1WriteLong); FillMemoryArea(0x5E0, 0x5EF, &Vdp2RamReadByte, &Vdp2RamReadWord, &Vdp2RamReadLong, &Vdp2RamWriteByte, &Vdp2RamWriteWord, &Vdp2RamWriteLong); FillMemoryArea(0x5F0, 0x5F7, &Vdp2ColorRamReadByte, &Vdp2ColorRamReadWord, &Vdp2ColorRamReadLong, &Vdp2ColorRamWriteByte, &Vdp2ColorRamWriteWord, &Vdp2ColorRamWriteLong); FillMemoryArea(0x5F8, 0x5FB, &Vdp2ReadByte, &Vdp2ReadWord, &Vdp2ReadLong, &Vdp2WriteByte, &Vdp2WriteWord, &Vdp2WriteLong); FillMemoryArea(0x5FE, 0x5FE, &ScuReadByte, &ScuReadWord, &ScuReadLong, &ScuWriteByte, &ScuWriteWord, &ScuWriteLong); FillMemoryArea(0x600, 0x7FF, &HighWramMemoryReadByte, &HighWramMemoryReadWord, &HighWramMemoryReadLong, &HighWramMemoryWriteByte, &HighWramMemoryWriteWord, &HighWramMemoryWriteLong); } ////////////////////////////////////////////////////////////////////////////// u8 FASTCALL MappedMemoryReadByte(u32 addr) { switch (addr >> 29) { case 0x0: case 0x1: case 0x5: { // Cache/Non-Cached return ReadByteList[(addr >> 16) & 0xFFF](addr); } /* case 0x2: { // Purge Area break; } */ case 0x4: case 0x6: // Data Array return DataArrayReadByte(addr); case 0x7: { if (addr >= 0xFFFFFE00) { // Onchip modules addr &= 0x1FF; return OnchipReadByte(addr); } else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) { // ??? } else { // Garbage data } break; } default: { return UnhandledMemoryReadByte(addr); } } return 0; } ////////////////////////////////////////////////////////////////////////////// u16 FASTCALL MappedMemoryReadWord(u32 addr) { switch (addr >> 29) { case 0x0: case 0x1: case 0x5: { // Cache/Non-Cached return ReadWordList[(addr >> 16) & 0xFFF](addr); } /* case 0x2: { // Purge Area break; } */ case 0x4: case 0x6: // Data Array return DataArrayReadWord(addr); case 0x7: { if (addr >= 0xFFFFFE00) { // Onchip modules addr &= 0x1FF; return OnchipReadWord(addr); } else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) { // ??? } else { // Garbage data } break; } default: { return UnhandledMemoryReadWord(addr); } } return 0; } ////////////////////////////////////////////////////////////////////////////// u32 FASTCALL MappedMemoryReadLong(u32 addr) { switch (addr >> 29) { case 0x0: case 0x1: case 0x5: { // Cache/Non-Cached return ReadLongList[(addr >> 16) & 0xFFF](addr); } /* case 0x2: { // Purge Area break; } */ case 0x3: { // Address Array return AddressArrayReadLong(addr); } case 0x4: case 0x6: // Data Array return DataArrayReadLong(addr); case 0x7: { if (addr >= 0xFFFFFE00) { // Onchip modules addr &= 0x1FF; return OnchipReadLong(addr); } else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) { // ??? } else { // Garbage data } break; } default: { return UnhandledMemoryReadLong(addr); } } return 0; } ////////////////////////////////////////////////////////////////////////////// void FASTCALL MappedMemoryWriteByte(u32 addr, u8 val) { switch (addr >> 29) { case 0x0: case 0x1: case 0x5: { // Cache/Non-Cached WriteByteList[(addr >> 16) & 0xFFF](addr, val); return; } /* case 0x2: { // Purge Area return; } */ case 0x4: case 0x6: // Data Array DataArrayWriteByte(addr, val); return; case 0x7: { if (addr >= 0xFFFFFE00) { // Onchip modules addr &= 0x1FF; OnchipWriteByte(addr, val); return; } else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) { // ??? } else { // Garbage data } return; } default: { UnhandledMemoryWriteByte(addr, val); return; } } } ////////////////////////////////////////////////////////////////////////////// void FASTCALL MappedMemoryWriteWord(u32 addr, u16 val) { switch (addr >> 29) { case 0x0: case 0x1: case 0x5: { // Cache/Non-Cached WriteWordList[(addr >> 16) & 0xFFF](addr, val); return; } /* case 0x2: { // Purge Area return; } */ case 0x4: case 0x6: // Data Array DataArrayWriteWord(addr, val); return; case 0x7: { if (addr >= 0xFFFFFE00) { // Onchip modules addr &= 0x1FF; OnchipWriteWord(addr, val); return; } else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) { // ??? } else { // Garbage data } return; } default: { UnhandledMemoryWriteWord(addr, val); return; } } } ////////////////////////////////////////////////////////////////////////////// void FASTCALL MappedMemoryWriteLong(u32 addr, u32 val) { switch (addr >> 29) { case 0x0: case 0x1: case 0x5: { // Cache/Non-Cached WriteLongList[(addr >> 16) & 0xFFF](addr, val); return; } case 0x2: { // Purge Area return; } case 0x3: { // Address Array AddressArrayWriteLong(addr, val); return; } case 0x4: case 0x6: // Data Array DataArrayWriteLong(addr, val); return; case 0x7: { if (addr >= 0xFFFFFE00) { // Onchip modules addr &= 0x1FF; OnchipWriteLong(addr, val); return; } else if (addr >= 0xFFFF8000 && addr < 0xFFFFC000) { // ??? } else { // Garbage data } return; } default: { UnhandledMemoryWriteLong(addr, val); return; } } } ////////////////////////////////////////////////////////////////////////////// int MappedMemoryLoad(const char *filename, u32 addr) { FILE *fp; u32 filesize; u8 *buffer; u32 i; if (!filename) return -1; if ((fp = fopen(filename, "rb")) == NULL) return -1; // Calculate file size fseek(fp, 0, SEEK_END); filesize = ftell(fp); fseek(fp, 0, SEEK_SET); if ((buffer = (u8 *)malloc(filesize)) == NULL) { fclose(fp); return -2; } fread((void *)buffer, 1, filesize, fp); fclose(fp); for (i = 0; i < filesize; i++) MappedMemoryWriteByte(addr+i, buffer[i]); free(buffer); return 0; } ////////////////////////////////////////////////////////////////////////////// int MappedMemorySave(const char *filename, u32 addr, u32 size) { FILE *fp; u8 *buffer; u32 i; if (!filename) return -1; if ((fp = fopen(filename, "wb")) == NULL) return -1; if ((buffer = (u8 *)malloc(size)) == NULL) { fclose(fp); return -2; } for (i = 0; i < size; i++) buffer[i] = MappedMemoryReadByte(addr+i); fwrite((void *)buffer, 1, size, fp); fclose(fp); free(buffer); return 0; } ////////////////////////////////////////////////////////////////////////////// void MappedMemoryLoadExec(const char *filename, u32 pc) { char *p; size_t i; if ((p = strrchr(filename, '.'))) { p = strdup(p); for (i = 0; i < strlen(p); i++) p[i] = toupper(p[i]); if (strcmp(p, ".COF") == 0 || strcmp(p, ".COFF") == 0) { MappedMemoryLoadCoff(filename); free(p); return; } else if(strcmp(p, ".ELF") == 0) { MappedMemoryLoadElf(filename); free(p); return; } free(p); } YabauseResetNoLoad(); // Setup the vector table area, etc. YabauseSpeedySetup(); MappedMemoryLoad(filename, pc); SH2GetRegisters(MSH2, &MSH2->regs); MSH2->regs.PC = pc; SH2SetRegisters(MSH2, &MSH2->regs); } ////////////////////////////////////////////////////////////////////////////// int LoadBios(const char *filename) { return T123Load(BiosRom, 0x80000, 2, filename); } ////////////////////////////////////////////////////////////////////////////// int LoadBackupRam(const char *filename) { return T123Load(BupRam, 0x10000, 1, filename); } ////////////////////////////////////////////////////////////////////////////// void FormatBackupRam(void *mem, u32 size) { int i, i2; u32 i3; u8 header[32] = { 0xFF, 'B', 0xFF, 'a', 0xFF, 'c', 0xFF, 'k', 0xFF, 'U', 0xFF, 'p', 0xFF, 'R', 0xFF, 'a', 0xFF, 'm', 0xFF, ' ', 0xFF, 'F', 0xFF, 'o', 0xFF, 'r', 0xFF, 'm', 0xFF, 'a', 0xFF, 't' }; // Fill in header for(i2 = 0; i2 < 4; i2++) for(i = 0; i < 32; i++) T1WriteByte(mem, (i2 * 32) + i, header[i]); // Clear the rest for(i3 = 0x80; i3 < size; i3+=2) { T1WriteByte(mem, i3, 0xFF); T1WriteByte(mem, i3+1, 0x00); } } ////////////////////////////////////////////////////////////////////////////// // FIXME: Here's a (possibly incomplete) list of data that should be added // to the next version of the save state file: // yabsys.DecilineStop (new format) // yabsys.SH2CycleFrac (new field) // yabsys.DecilineUSed (new field) // yabsys.UsecFrac (new field) // [scsp2.c] It would be nice to redo the format entirely because so // many fields have changed format/size from the old scsp.c // [scsp2.c] scsp_clock, scsp_clock_frac, ScspState.sample_timer (timing) // [scsp2.c] cdda_buf, cdda_next_in, cdda_next_out (CDDA buffer) // [sh2core.c] frc.div changed to frc.shift // [sh2core.c] wdt probably needs to be written as well int YabSaveState(const char *filename) { u32 i; FILE *fp; int offset; IOCheck_struct check; u8 *buf; int totalsize; int outputwidth; int outputheight; int movieposition; int temp; u32 temp32; check.done = 0; check.size = 0; //use a second set of savestates for movies filename = MakeMovieStateName(filename); if (!filename) return -1; if ((fp = fopen(filename, "wb")) == NULL) return -1; // Write signature fprintf(fp, "YSS"); // Write endianness byte #ifdef WORDS_BIGENDIAN fputc(0x00, fp); #else fputc(0x01, fp); #endif // Write version(fix me) i = 2; ywrite(&check, (void *)&i, sizeof(i), 1, fp); // Skip the next 4 bytes for now i = 0; ywrite(&check, (void *)&i, sizeof(i), 1, fp); //write frame number ywrite(&check, (void *)&framecounter, 4, 1, fp); //this will be updated with the movie position later ywrite(&check, (void *)&framecounter, 4, 1, fp); // Go through each area and write each state i += CartSaveState(fp); i += Cs2SaveState(fp); i += SH2SaveState(MSH2, fp); i += SH2SaveState(SSH2, fp); i += SoundSaveState(fp); i += ScuSaveState(fp); i += SmpcSaveState(fp); i += Vdp1SaveState(fp); i += Vdp2SaveState(fp); offset = StateWriteHeader(fp, "OTHR", 1); // Other data ywrite(&check, (void *)BupRam, 0x10000, 1, fp); // do we really want to save this? ywrite(&check, (void *)HighWram, 0x100000, 1, fp); ywrite(&check, (void *)LowWram, 0x100000, 1, fp); ywrite(&check, (void *)&yabsys.DecilineCount, sizeof(int), 1, fp); ywrite(&check, (void *)&yabsys.LineCount, sizeof(int), 1, fp); ywrite(&check, (void *)&yabsys.VBlankLineCount, sizeof(int), 1, fp); ywrite(&check, (void *)&yabsys.MaxLineCount, sizeof(int), 1, fp); temp = yabsys.DecilineStop >> YABSYS_TIMING_BITS; ywrite(&check, (void *)&temp, sizeof(int), 1, fp); temp = (yabsys.CurSH2FreqType == CLKTYPE_26MHZ) ? 268 : 286; ywrite(&check, (void *)&temp, sizeof(int), 1, fp); temp32 = (yabsys.UsecFrac * temp / 10) >> YABSYS_TIMING_BITS; ywrite(&check, (void *)&temp32, sizeof(u32), 1, fp); ywrite(&check, (void *)&yabsys.CurSH2FreqType, sizeof(int), 1, fp); ywrite(&check, (void *)&yabsys.IsPal, sizeof(int), 1, fp); VIDCore->GetGlSize(&outputwidth, &outputheight); totalsize=outputwidth * outputheight * sizeof(u32); if ((buf = (u8 *)malloc(totalsize)) == NULL) { return -2; } YuiSwapBuffers(); #ifdef USE_OPENGL glPixelZoom(1,1); glReadBuffer(GL_BACK); glReadPixels(0, 0, outputwidth, outputheight, GL_RGBA, GL_UNSIGNED_BYTE, buf); #endif YuiSwapBuffers(); ywrite(&check, (void *)&outputwidth, sizeof(outputwidth), 1, fp); ywrite(&check, (void *)&outputheight, sizeof(outputheight), 1, fp); ywrite(&check, (void *)buf, totalsize, 1, fp); movieposition=ftell(fp); //write the movie to the end of the savestate SaveMovieInState(fp, check); i += StateFinishHeader(fp, offset); // Go back and update size fseek(fp, 8, SEEK_SET); ywrite(&check, (void *)&i, sizeof(i), 1, fp); fseek(fp, 16, SEEK_SET); ywrite(&check, (void *)&movieposition, sizeof(movieposition), 1, fp); fclose(fp); OSDPushMessage(OSDMSG_STATUS, 150, "STATE SAVED"); return 0; } ////////////////////////////////////////////////////////////////////////////// int YabLoadState(const char *filename) { FILE *fp; char id[3]; u8 endian; int headerversion, version, size, chunksize, headersize; IOCheck_struct check; u8* buf; int totalsize; int outputwidth; int outputheight; int curroutputwidth; int curroutputheight; int movieposition; int temp; u32 temp32; filename = MakeMovieStateName(filename); if (!filename) return -1; if ((fp = fopen(filename, "rb")) == NULL) return -1; headersize = 0xC; // Read signature yread(&check, (void *)id, 1, 3, fp); if (strncmp(id, "YSS", 3) != 0) { fclose(fp); return -2; } // Read header yread(&check, (void *)&endian, 1, 1, fp); yread(&check, (void *)&headerversion, 4, 1, fp); yread(&check, (void *)&size, 4, 1, fp); switch(headerversion) { case 1: /* This is the "original" version of the format */ break; case 2: /* version 2 adds video recording */ yread(&check, (void *)&framecounter, 4, 1, fp); movieposition=ftell(fp); yread(&check, (void *)&movieposition, 4, 1, fp); headersize = 0x14; break; default: /* we're trying to open a save state using a future version * of the YSS format, that won't work, sorry :) */ fclose(fp); return -3; break; } #ifdef WORDS_BIGENDIAN if (endian == 1) #else if (endian == 0) #endif { // should setup reading so it's byte-swapped YabSetError(YAB_ERR_OTHER, (void *)"Load State byteswapping not supported"); fclose(fp); return -3; } // Make sure size variable matches actual size minus header fseek(fp, 0, SEEK_END); if (size != (ftell(fp) - headersize)) { fclose(fp); return -2; } fseek(fp, headersize, SEEK_SET); // Verify version here ScspMuteAudio(SCSP_MUTE_SYSTEM); if (StateCheckRetrieveHeader(fp, "CART", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } CartLoadState(fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "CS2 ", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } Cs2LoadState(fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "MSH2", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } SH2LoadState(MSH2, fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "SSH2", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } SH2LoadState(SSH2, fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "SCSP", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } SoundLoadState(fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "SCU ", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } ScuLoadState(fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "SMPC", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } SmpcLoadState(fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "VDP1", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } Vdp1LoadState(fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "VDP2", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } Vdp2LoadState(fp, version, chunksize); if (StateCheckRetrieveHeader(fp, "OTHR", &version, &chunksize) != 0) { fclose(fp); // Revert back to old state here ScspUnMuteAudio(SCSP_MUTE_SYSTEM); return -3; } // Other data yread(&check, (void *)BupRam, 0x10000, 1, fp); yread(&check, (void *)HighWram, 0x100000, 1, fp); yread(&check, (void *)LowWram, 0x100000, 1, fp); yread(&check, (void *)&yabsys.DecilineCount, sizeof(int), 1, fp); yread(&check, (void *)&yabsys.LineCount, sizeof(int), 1, fp); yread(&check, (void *)&yabsys.VBlankLineCount, sizeof(int), 1, fp); yread(&check, (void *)&yabsys.MaxLineCount, sizeof(int), 1, fp); yread(&check, (void *)&temp, sizeof(int), 1, fp); yread(&check, (void *)&temp, sizeof(int), 1, fp); yread(&check, (void *)&temp32, sizeof(u32), 1, fp); yread(&check, (void *)&yabsys.CurSH2FreqType, sizeof(int), 1, fp); yread(&check, (void *)&yabsys.IsPal, sizeof(int), 1, fp); YabauseChangeTiming(yabsys.CurSH2FreqType); yabsys.UsecFrac = (temp32 << YABSYS_TIMING_BITS) * temp / 10; if (headerversion > 1) { yread(&check, (void *)&outputwidth, sizeof(outputwidth), 1, fp); yread(&check, (void *)&outputheight, sizeof(outputheight), 1, fp); totalsize=outputwidth * outputheight * sizeof(u32); if ((buf = (u8 *)malloc(totalsize)) == NULL) { return -2; } yread(&check, (void *)buf, totalsize, 1, fp); YuiSwapBuffers(); #ifdef USE_OPENGL if(VIDCore->id == VIDCORE_SOFT) glRasterPos2i(0, outputheight); if(VIDCore->id == VIDCORE_OGL) glRasterPos2i(0, outputheight/2); #endif VIDCore->GetGlSize(&curroutputwidth, &curroutputheight); #ifdef USE_OPENGL glPixelZoom((float)curroutputwidth / (float)outputwidth, ((float)curroutputheight / (float)outputheight)); glDrawPixels(outputwidth, outputheight, GL_RGBA, GL_UNSIGNED_BYTE, buf); #endif YuiSwapBuffers(); fseek(fp, movieposition, SEEK_SET); MovieReadState(fp, filename); } fclose(fp); ScspUnMuteAudio(SCSP_MUTE_SYSTEM); OSDPushMessage(OSDMSG_STATUS, 150, "STATE LOADED"); return 0; } ////////////////////////////////////////////////////////////////////////////// int YabSaveStateSlot(const char *dirpath, u8 slot) { char filename[512]; if (cdip == NULL) return -1; #ifdef WIN32 sprintf(filename, "%s\\%s_%03d.yss", dirpath, cdip->itemnum, slot); #else sprintf(filename, "%s/%s_%03d.yss", dirpath, cdip->itemnum, slot); #endif return YabSaveState(filename); } ////////////////////////////////////////////////////////////////////////////// int YabLoadStateSlot(const char *dirpath, u8 slot) { char filename[512]; if (cdip == NULL) return -1; #ifdef WIN32 sprintf(filename, "%s\\%s_%03d.yss", dirpath, cdip->itemnum, slot); #else sprintf(filename, "%s/%s_%03d.yss", dirpath, cdip->itemnum, slot); #endif return YabLoadState(filename); } ////////////////////////////////////////////////////////////////////////////// static int MappedMemoryAddMatch(u32 addr, u32 val, int searchtype, result_struct *result, u32 *numresults) { result[numresults[0]].addr = addr; result[numresults[0]].val = val; numresults[0]++; return 0; } ////////////////////////////////////////////////////////////////////////////// static INLINE int SearchIncrementAndCheckBounds(result_struct *prevresults, u32 *maxresults, u32 numresults, u32 *i, u32 inc, u32 *newaddr, u32 endaddr) { if (prevresults) { if (i[0] >= maxresults[0]) { maxresults[0] = numresults; return 1; } newaddr[0] = prevresults[i[0]].addr; i[0]++; } else { newaddr[0] = inc; if (newaddr[0] > endaddr || numresults >= maxresults[0]) { maxresults[0] = numresults; return 1; } } return 0; } ////////////////////////////////////////////////////////////////////////////// static int SearchString(u32 startaddr, u32 endaddr, int searchtype, const char *searchstr, result_struct *results, u32 *maxresults) { u8 *buf=NULL; u32 *buf32=NULL; u32 buflen=0; u32 counter; u32 addr; u32 numresults=0; buflen=(u32)strlen(searchstr); if ((buf32=(u32 *)malloc(buflen*sizeof(u32))) == NULL) return 0; buf = (u8 *)buf32; // Copy string to buffer switch (searchtype & 0x70) { case SEARCHSTRING: strcpy((char *)buf, searchstr); break; case SEARCHREL8BIT: case SEARCHREL16BIT: { char *text; char *searchtext=strdup(searchstr); // Calculate buffer length and read values into table buflen = 0; for (text=strtok((char *)searchtext, " ,"); text != NULL; text=strtok(NULL, " ,")) { buf32[buflen] = strtoul(text, NULL, 0); buflen++; } free(searchtext); break; } } addr = startaddr; counter = 0; for (;;) { // Fetch byte/word/etc. switch (searchtype & 0x70) { case SEARCHSTRING: { u8 val = MappedMemoryReadByte(addr); addr++; if (val == buf[counter]) { counter++; if (counter == buflen) MappedMemoryAddMatch(addr-buflen, val, searchtype, results, &numresults); } else counter = 0; break; } case SEARCHREL8BIT: { int diff; u32 j; u8 val2; u8 val = MappedMemoryReadByte(addr); for (j = 1; j < buflen; j++) { // grab the next value val2 = MappedMemoryReadByte(addr+j); // figure out the diff diff = (int)val2 - (int)val; // see if there's a match if (((int)buf32[j] - (int)buf32[j-1]) != diff) break; if (j == (buflen - 1)) MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); val = val2; } addr++; break; } case SEARCHREL16BIT: { int diff; u32 j; u16 val2; u16 val = MappedMemoryReadWord(addr); for (j = 1; j < buflen; j++) { // grab the next value val2 = MappedMemoryReadWord(addr+(j*2)); // figure out the diff diff = (int)val2 - (int)val; // see if there's a match if (((int)buf32[j] - (int)buf32[j-1]) != diff) break; if (j == (buflen - 1)) MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); val = val2; } addr+=2; break; } } if (addr > endaddr || numresults >= maxresults[0]) break; } free(buf); maxresults[0] = numresults; return 1; } ////////////////////////////////////////////////////////////////////////////// result_struct *MappedMemorySearch(u32 startaddr, u32 endaddr, int searchtype, const char *searchstr, result_struct *prevresults, u32 *maxresults) { u32 i=0; result_struct *results; u32 numresults=0; unsigned long searchval; int issigned=0; u32 addr; if ((results = (result_struct *)malloc(sizeof(result_struct) * maxresults[0])) == NULL) return NULL; switch (searchtype & 0x70) { case SEARCHSTRING: case SEARCHREL8BIT: case SEARCHREL16BIT: { // String/8-bit relative/16-bit relative search(not supported, yet) if (SearchString(startaddr, endaddr, searchtype, searchstr, results, maxresults) == 0) { maxresults[0] = 0; free(results); return NULL; } return results; } case SEARCHHEX: sscanf(searchstr, "%08lx", &searchval); break; case SEARCHUNSIGNED: searchval = (unsigned long)strtoul(searchstr, NULL, 10); issigned = 0; break; case SEARCHSIGNED: searchval = (unsigned long)strtol(searchstr, NULL, 10); issigned = 1; break; } if (prevresults) { addr = prevresults[i].addr; i++; } else addr = startaddr; // Regular value search for (;;) { u32 val=0; u32 newaddr; // Fetch byte/word/etc. switch (searchtype & 0x3) { case SEARCHBYTE: val = MappedMemoryReadByte(addr); // sign extend if neccessary if (issigned) val = (s8)val; if (SearchIncrementAndCheckBounds(prevresults, maxresults, numresults, &i, addr+1, &newaddr, endaddr)) return results; break; case SEARCHWORD: val = MappedMemoryReadWord(addr); // sign extend if neccessary if (issigned) val = (s16)val; if (SearchIncrementAndCheckBounds(prevresults, maxresults, numresults, &i, addr+2, &newaddr, endaddr)) return results; break; case SEARCHLONG: val = MappedMemoryReadLong(addr); if (SearchIncrementAndCheckBounds(prevresults, maxresults, numresults, &i, addr+4, &newaddr, endaddr)) return results; break; default: maxresults[0] = 0; if (results) free(results); return NULL; } // Do a comparison switch (searchtype & 0xC) { case SEARCHEXACT: if (val == searchval) MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); break; case SEARCHLESSTHAN: if ((!issigned && val < searchval) || (issigned && (signed)val < (signed)searchval)) MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); break; case SEARCHGREATERTHAN: if ((!issigned && val > searchval) || (issigned && (signed)val > (signed)searchval)) MappedMemoryAddMatch(addr, val, searchtype, results, &numresults); break; default: maxresults[0] = 0; if (results) free(results); return NULL; } addr = newaddr; } maxresults[0] = numresults; return results; } yabause-0.9.13.1/src/perdx.h000644 001750 001750 00000002657 12256006124 017500 0ustar00guillaumeguillaume000000 000000 /* Copyright 2006 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PERDX_H #define PERDX_H #define DIRECTINPUT_VERSION 0x0800 #include #include "dx.h" #include "peripheral.h" #define PERCORE_DIRECTX 2 extern PerInterface_struct PERDIRECTX; typedef struct { LPDIRECTINPUTDEVICE8 lpDIDevice; int type; int emulatetype; #ifdef HAVE_XINPUT int is_xinput_device; int xinput_num; #endif } padconf_struct; enum XIAXIS { XI_THUMBL=1, XI_THUMBLX=1, XI_THUMBLY=5, XI_THUMBR=9, XI_THUMBRX=9, XI_THUMBRY=13, XI_TRIGGERL=17, XI_TRIGGERR=19 }; int PERDXInit(void); void PERDXDeInit(void); int PERDXHandleEvents(void); void PERDXPerSetButtonMapping(void); u32 PERDXScan(u32 flags) ; void PERDXFlush(void); #endif yabause-0.9.13.1/src/dx.h000644 001750 001750 00000002620 12256006161 016760 0ustar00guillaumeguillaume000000 000000 /* Copyright 2008 Theo Berkau This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef DX_H #define DX_H #ifdef __MINGW32__ // I have to do this because for some reason because the dxerr8.h header is fubared const char* __stdcall DXGetErrorString8A(HRESULT hr); #define DXGetErrorString8 DXGetErrorString8A const char* __stdcall DXGetErrorDescription8A(HRESULT hr); #define DXGetErrorDescription8 DXGetErrorDescription8A #else #ifndef DXERRH_IS_BROKEN #include #define DXGetErrorString8 DXGetErrorString9A #define DXGetErrorDescription8 DXGetErrorDescription9A #else #include #define DXGetErrorString8 DXGetErrorStringA #define DXGetErrorDescription8 DXGetErrorDescriptionA #endif #endif #endif yabause-0.9.13.1/src/qt/doc/yabause.1000644 001750 001750 00000005334 12256006055 021107 0ustar00guillaumeguillaume000000 000000 .TH YABAUSE 1 "January 7, 2013" "yabause-0.9.12" .SH NAME yabause \- Yet Another Buggy And Uncomplete Saturn Emulator .SH SYNOPSIS .B yabause [ \fB\-afh\fP ] [ \fB\-b \fP | \fB\-nb\fP ] [ \fB\-c \fP | \fB\-i \fP ] [ \fB-ns\fP ] [ \fB\-\-autoframeskip=0|1\fP ] [ \fB\-\-autoload=\fP ] [ \fB\-\-autostart\fP ] [ \fB\-\-binary=[:
]\fP ] .SH DESCRIPTION \fBYabause\fP is a Sega Saturn emulator. \fBYabause\fP needs either a bios file, a game or a binary to run. Games can be loaded from a real cd device or from dump files. .SH OPTIONS .TP .BI \-\-autoframeskip Enable or disable auto frame skipping / limiting. .TP .BI \-\-autoload Automatically start emulation and load a savestate. .TP .BI \-a .TP .BI \-\-autostart Automatically start emulation. .TP .BI \-b .TP .BI \-\-bios Choose a bios file. .TP .BI \-c .TP .BI \-\-cdrom Choose the cdrom device. .TP .BI \-\-binary Use a binary file. Content of the file will be loaded to 0x06004000 and execution will start from that address. You can provide an alternative address to load and run the binary with the \-\-binary=:
syntax. When using this option, emulation is automatically started and you shouldn't use it in conjunction with \-a. This option is intended for homebrew developers wanting to test their programs in \fBYabause\fP. .TP .BI \-f .TP .BI \-\-fullscreen Start the emulator in fullscreen. .TP .BI \-i .TP .BI \-\-iso Choose a dump file. The dump can be either in iso or bin/cue file format. .TP .BI \-nb .TP .BI \-\-no\-bios Use the emulated bios. .TP .BI \-ns .TP .BI \-\-no\-sound Turns sound off. This option actually set the sound core to the dummy one. .SH FILES .TP \fB\fR An executable for the Saturn, usually with a BIN extension. .TP \fB\fR A Saturn ROM BIOS image. .TP \fB\fR A CDROM device file. .TP \fB\fR A Saturn game dump. .SH AUTHORS Copyright (c) 2002-2013 Yabause Team Web: http://yabause.org Please don't ask for roms, bios files or any other copyrighted stuff. .SH COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA See the GNU General Public License details in COPYING. yabause-0.9.13.1/src/qt/Arguments.h000644 001750 001750 00000000132 12256006055 020734 0ustar00guillaumeguillaume000000 000000 #ifndef ARGUMENTS_H #define ARGUMENTS_H namespace Arguments { void parse(); } #endif yabause-0.9.13.1/src/qt/QtYabause.h000644 001750 001750 00000006504 12256006055 020676 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006, 2013 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef QTYABAUSE_H #define QTYABAUSE_H extern "C" { #ifdef Q_OS_WIN #include #endif #include "../yabause.h" #include "../peripheral.h" #include "../sh2core.h" #include "../sh2int.h" #include "../vidogl.h" #include "../vidsoft.h" #include "../cs0.h" #include "../cdbase.h" #include "../scsp.h" #include "../scu.h" #include "../sndal.h" #ifdef HAVE_DIRECTSOUND #include "../snddx.h" #endif #include "../sndsdl.h" #if HAVE_DIRECTINPUT #include "../perdx.h" #endif #include "../persdljoy.h" #include "../permacjoy.h" #include "../debug.h" #include "../m68kcore.h" #include "../m68kc68k.h" #include "../vdp1.h" #include "../vdp2.h" #include "../cs2.h" #include "../cheat.h" #include "../memory.h" #include "../bios.h" #include "../m68kd.h" #include "../sh2d.h" #include "../vdp2debug.h" #include "PerQt.h" #ifdef ARCH_IS_MACOSX #include "../sndmac.h" #endif } #include #include class UIYabause; class Settings; class VolatileSettings; class QWidget; typedef struct { QString file; QString name; } translation_struct; namespace QtYabause { UIYabause* mainWindow( bool create = true ); Settings* settings( bool create = true ); VolatileSettings* volatileSettings( bool create = true ); QList getTranslationList(); int setTranslationFile(); int logTranslation(); void closeTranslation(); QString translate( const QString& string ); void retranslateWidgetOnly( QWidget* widget ); void retranslateWidget( QWidget* widget ); void retranslateApplication(); // get cd serial const char* getCurrentCdSerial(); // get core by id M68K_struct* getM68KCore( int id ); SH2Interface_struct* getSH2Core( int id ); PerInterface_struct* getPERCore( int id ); CDInterface* getCDCore( int id ); SoundInterface_struct* getSNDCore( int id ); VideoInterface_struct* getVDICore( int id ); // default cores CDInterface defaultCDCore(); SoundInterface_struct defaultSNDCore(); VideoInterface_struct defaultVIDCore(); OSD_struct defaultOSDCore(); PerInterface_struct defaultPERCore(); SH2Interface_struct defaultSH2Core(); // padsbits QMap* portPadsBits( uint portNumber ); void clearPadsBits(); QMap* portAnalogBits( uint portNumber ); void clear3DAnalogBits(); QMap* portMouseBits( uint portNumber ); void clearMouseBits(); }; #endif // QTYABAUSE_H yabause-0.9.13.1/src/qt/resources/icons/controller.png000644 001750 001750 00000275347 12256006101 024652 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRmxøÎ gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<zyIDATxÚì½ ˜eeu.¼«zé꡺ª‡ªžGl¦F„€J@‚J"¿½Æ ‰xM$×)¼šâuÄ\yþK¢ð«QPŒ‰ ¢(* MÏóX=0ô =Ô¿Þ]ç-V/Ö·‡sNUWw«žýœSûì³Ïž¾õ½ë]SCggg%J”(Q¢D‰¥oKc¼Q¢D‰%J”(´E‰%J”(Q¢D‰ -J”(Q¢D‰%‚¶(Q¢D‰%J”(´E‰%J”(Q¢D‰ -J”(Q¢D‰%‚¶(Q¢D‰%J”(´E‰%J”(Q¢D‰ -J”(Q¢D‰%‚¶(Q¢D‰%J”(´E‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”(´E‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”(´E‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”(´E‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰A[”(Q¢D‰%J”Ú¢D‰%J”(Q¢T)ýã%ˆr¢ÉáÇ“'Ÿ|²ÿF=ûì³£6lØÐܯ_¿Ñµzõê1ýû÷ÙØØ8LÖ ‘×! é÷øzôèѤ³³3}=räÈAy=ðÜsÏu:th,‡d?{f̘ñÌСC;† Ò1sæÌŽ &ì6lX¼øQ¢ô€ìر£¿,—/_Þ,ãtüÖ­[ÇuttŒ18LÆóÀ “¥AÆó×Ëò>]0–±@äµSôÃ^ŒiÛ{äó§fÍšõ´¬~Z¾·}êÔ©OÊwŸ7nÜî&².^ü('”4ða¥¯‰€2(ó1[¶l™$¯³×­[7sß¾}Óž~úéiòY«<»£T’׃Nä}‚WQî 2•2(w(zµ XKð¥Ëþýû“½{÷&{öìIžyæ™DöŸ<õÔS†XI`WSSÓã/{ÙËîºþúëÿuþüùOÅ»%Jíòãÿø¬/~ñ‹o]±bÅÅ2ΦÈXzÚi§%£GNF•.#GŽLFŒ‘ÀhÂ4hP:ÆCãZì®DŒ°ôã[Œ°tÁ{îûŸ”ߨ%¿·®µµuìså´iÓVŽ?~Õĉ·Œ3æ ö%JmQ¢ WÀYÓªU«f¯Y³fáæÍ›ÏÚµk×gSEA7‹Å*n*òáǧ 9±ÊS…ŽŠ— ­b…§J J` @m÷îÝÝ ‹ünúŠuø ÛÔíܹ@pÅ×¾öµ7^yå•¿w-J”êDŒ¯ä/þâ/þîî»ïþ;cÄ(J0ÆÐÚ°€J|Nð†m9Þ Ü8¾¡KÀÆ´á〠¯01î1®q ã0Ôð¿è„gEWl}²²¥¥å¡Y³f=ÜÞÞþ¸¹5ãÆ;ïZ”Ú¢œ²‚çN€YÓâÅ‹._¾ü%[·n}‰(Ð3ä£V(í±cÇv+n*jmis8ƒâÆpåM7¨jTÞkPÖaPÔ–Y`£"Çvø¾OwjE¹w|ûÛß~ñYgµ6ÞÉ(QÊË?øÁÿ!ÆÏ?·µµ¥Œ8Æ0Æ8 4€5€6è Üȼ¼iVÝê2prqši§N€. À" `m•裇fΜù˹sç>0þü¥¢£Ç»å”m«W¯N~ûÛS“´€¢zÕ«^•œ*±R}ú“2!<)LJ ç9þmòÿ¶I“&=%û|R€è)†R&³ÚzI0F¶lÙ’:}E0VdLŽ×¦M›6&h¼€š±ëÖ­k‘qÐ*ã¶yÍš5S:::^"€gÆ7ÇcÔ°`?éeXs,z,êqÊm9Þ ì S cpÀÚ?>5ÁÜa=>ðely†Phfz:gë֭髜Çö 6üzÛ¶mw ¨û‘üü¦ødž¼rÍ5×$ÿþïÿ~\á¸gžhVb=…n“MÀRýîw¿[ðÀ\³lÙ²«Dùp6{ö줹¹¹;È@Ö0cÓÒ°42(GQŠ©¢%ÉÐÂ…’& «Z»[xµl•¾v«êÉ`‘“‘s-r|×Èyv»Oï½÷Þô½üÎQ¹·{ä·wÉä°Kþß$Úº‘#G®joo_'Ç·^@]‡\‡'S‡ë…{A[ï®õñ`u`ˆÉÒ¼yóæñdÚÄðl 3}ýúõmH&ñÚ,ãaŒ¬"ÿ7h£ˆ! 0Ú´ÁÄçG¿×ç–už[l‡\´Æ¨+ kØâ`hЈDXÖC/ñ'ˆƒ1ˆE3p0Rdi–åê;w^-ºéª¿PúmyÿŸ¢÷vƧôä3”Ž·Ä’Qê&¢¬šï¹çžW?òÈ#× °ºP@É€3f¤–-”#€Y5+²iP¤P´i`ÐÐdRHA”#”%0 ð=ìƒ@‹ž lYþOPf­y~N«]3un´òñçÃ}èØym”ó9MVŸ&Ê}š¼?W”y÷$Ru»äû›å­œ8qâò¶¶¶'Ю0·VÖíŠn×(Ç[*1Ÿ-Æ&Ëxœ%Ë'–ø¸ 6¼H®[º¾dz]®ÕZ™8–{T@ïby]*€®#¹(=Èž5ˆ4AžÇ9ò<ž¹nݺ³e\Îß¿›ŒÓ&2TŒ!Ãx$£a¤Ý›Ú@² M‡"èX6k8ñûÏ\§œ¯zìéE×eÔ,ÿg\,³I1¡g ³àF€Ã0Ocn¡Ûø=²o*‘aüœ9sÞ&×ômrM/î6Ñoß‘m·Æ§-JmQz]vìØ1ðÞ{ï½ê¿þë¿Þ)ŠýÒ–––†óÎ;ï÷'Ý Ppˆ8HÛ¸qcú® (;X¿éC)¾ÃÀa Êt"‚ž$8iè€`k­k׋(p=yXfͳúõDÁÿõ¢Y:ý™=y¨ì¶fsÍË–-[´xñât½\»§ÆŒ³J¬þGeòøýé§ŸþûiÓ¦-—u{ã“¥¬à9ci´³9&Î[ºté¹;wîœ/ cª<irƘpŒc†qp¼ic‰cÆ#h𥳺9Ž´Q¥Çž6˜hDÜY#ÊcÙø9›á “¬Ñèbü+ '&&€I‹æÅŠ©± ½*2^ÉÄ1»•ñrºÞ# ¾?uêÔsæÍ›wŽè¼._¾üŽ5kÖü‹ló8€!@_¬à%‚¶(=&¢x†ßu×]×=ðÀ!JûL(00k,Ñ Æà^€.(T5‚4™(R6 Ê Ê@ ,X€;ÈÕ`Å4­µ®' k½{‚׆߿ÿú3o¢Ð Ö?k0G H†ÍwÌÙ¡•„üß$“ȹ¢ðÏ ÷6dhÊ5^/“Ä£Þ~=wîÜßÌš5k‰L(;bQÐ(V Ä8jCàLiçË\$cp¾¬o¡[ c˜cN—ÑÑ,™.«¡ &/,ÁƉf1lü\2ÏÀ² œMJÐÛX gÇžvŸr¼¼Áªë»!)aÆ éw*@ Tè¾ööö”…ƒþc‚ƒeß ïdÛ±2^ß-zðzùÿû¯xÅ+¾tÑEÝÇ8P 0û¨îOþäOþ$½>Q"h‹Ò÷ÁÚißûÞ÷Þ,`í]¬fΟ??UTÚÊ P(u(9€´õë×w5¸  Ìh] ¨Ñª'PÃzZçøŒ“€ž,¼x5= p¢±.˘ىC·ºÒ‡¶è58ó& ËÄé‰COv´øõ¾9‰ÓŒ\¥dIûŽ;Úeù“{î¹×o‡€¸%“&Mú€¸_Ξ=û2Il‰.ÕSO` uttLöÒ¥K/\³fÍæÉs4Œñ w,¡ Q´q¼ÐJ`•ÔôX´Ì¶eØìØòÆnÜÙ±¥Ç4A5¨4˜Ócã‹cŽã%€tw„t ÞvåÊ•©î€û6uêÔT/Òp…nÄýÀço²Ý -[¶ü™`¶}ûöœþùŸ¿òÊ+Ò×K…Ær&´Eéû`mè]wÝõæßüæ7ï‹|ú‚ R¥D‹’±j°Ö¡ìàê$XÃ+€˜6(<(B(\fв'|¦6º[4@Ó M+}ý¹žÿæeŠÚÉB—`)›Å¦'/ýûzRÑÕÚUÃßщ|ó×™ª¸.ºÂ»võ°†œž`'VýÅ2 \,÷ ¿ÿt…‰» N@6ª»ïèI¥‹ã…K)Jïî;Ƙ k`ÒÎ}衇. ÿb ³åÙHA¶ëÏ˜Ž ÕLš~µ ¶f¶5ˆ²,·bzœzî?]+MG;Ër{@O'Yã‹áÖà²F~ÓÆŸâ=Ý¥ÐW,ÎMðÆd–Þpœ<òÈ#Ý.T0pp¡Ò Õ%G° @ž´¯úú׿þªýèGÿqÍ5×|梋.º¿¯²å8×(Ç_Ž{¶[o½5yÛÛÞvJ^|(Q±†Ó‚}}M@íß~ûío¼ï¾ûÞ?tèÐy³fÍz³FV J jXP„ë¨ä‚à‚!«f[NiwŒ¶ò­»Å²cZtBÄL©do¾€°nNÍ éIˆÀIƒE;èßÖû׿eêôz›‘ÊãÖ19Ö¥J ÇõœLX·N®÷N¹Hløå¼yóî“×GeBy²ž ¿sÇwDå^o%]Xp¹É8¿iÓ¦³_ô¢]$†ÕEN—û>¢֩u,šM°qgVŒiãȲÚvYЦÙo Þlý5/–4ô¬ZÖMŸjcç8¦,CncNuÌÙ72oÌng}8–hû7* ]\{|ú•]Xøà{Ú)Û|û5¯yÍM^xáâ¾öâQ¶‰!-§¢¼á oHþíßþ-‚¶Úúhƒ‚ vá7¿ù͉Rº` ![É€YøÂv û1aaÂý –…à€‚ M7zæD¢“ È6YW¦cVékPÇ€g­Œ­ËÅí´,ßk Ýî+dõë Ä›4, ´û %1艌“‡—j4 %‹… ÷H&•mbñ?<}úô{.\øK¹ÏÅþ+rïšV®\yæO<ñ2¸<;::Êä?÷•åshü0þS»:­K“l›çæÔÍcÍ<÷¥BÖhñŒ,/50ôܵz‚Ä5ò:1àXð™ ª&xö6þM7φ寘‚±ô¯øÅ/~qÅÏþs°¦&Nœø;QÊ÷Λ7ïiÓ¦=!M¤ÌŽ“ «€€³3dL^ ùÅ2&Ï’qÖÂâÑlÓ¤C Îð¿eЬ«S3>wÌÖÔãP?£hÓë´Ñä±âúy¶F—pàL!Ãdž:èßÖc6« NÀ:Ž+~F7*õÇRÅJï þgöƒñ‡÷0l垦  ]pàB…ËߥûúVôî+VÜø©O}êÚóÎ;ï¦k¯½öÿˆASL£DÐ¥K `î¸ãŽwÝwß}/Öภ/¼°»t–± €ÚÚµkÓ2°mÆ“ Ôè Õýɪ°ÙEƒ4;)XvËSÐ*Ú墙³P`³u«„J€xßÑ ,t¬º]Vh’Ò —i§Ý®^á_3®-]¥:CÕY+׆ ÃU«V]s÷Ýw¬š4iÒƒbéÿB@Üod’YA\ÏÉ“O>9BÀÙ<™Ô/ û"¸>wïÞ=Ù–Þ “Fp¦™´»“`ŒF’u?êWûœYCDO[CͲlûf šPÌ›7ÞøûÚÒû÷X8ÓfÏË‚8^';Þôqj㈅‚5£Í¨t›‚Ýf/UlÝ = w) cxZÖ sÙ‰ž-[¶´=úè£_}ä‘GþŸ7¼á 7^tÑEÅ‘%‚¶S\~ñ‹_œ!€íE)½âüóÏOc0Xº .O¤¹CÉ [бT\ŒYc‡‚5Ë®qÂÐL¨<€V’ …b_40ò8YÓPˆ9³,ƒ—Uj»Œv2±ÀÍžuIé"¡–éÐǧkZ¤ò¾XVÎ&7¼á¾+wÏŒ7ÎwÝ÷¿ÿý£#FŒX3qâćfÏžý+€8÷ËdÒÙ³ÉÊ ®»Œ¥Ñ+W®œÿÄOœ/ÆÐKvíÚµð™gžiýc^0Üt±qìèp Ö¼âÒÚ ò@•5NtGýÜyì2Å‚(ï™×á6ŽÓº3íµÌœØôØäÿ¶„†“^¬Ç[ÆûÔ™ë¶S AØ5èCèLdâ²tXÇX`°oAû†€9€7°ox/º÷’[o½õ—?ü𧯽öÚ} Ž¢Ú¢œb"Ö^£(‚Å’û»Y³f C°,¬;‚5ÄX ©`mçÎÝ}>©,9ñCɳ€.Á›ÎTã{ä¬AŠ*žµío½uiz ß4Ð11¶4ˆe%ø»Ö:nþ–vIÙííï艆´­þ®Y<òÕ%Fè†ÖnU[!^ÇÂU@]£Üÿ›7ož±zõêkQ'NîçF™<O™2å·2Ù€‘{büøñ›äYˆnœ²h¶nÝÚ¾nݺӗ-[vÞ¦M›Î“1uº\Óñtw’MÓàL³j\4h³1iúÑà‹Ï‹eu=×¢}³ê†öA¶Û1–}óŒ!ËtÙßÒLzÈpÒã]0LêZn›®™9ºFuÖ+¯£eÜxï ¡7 âà­ÀÀ†ûŠÏ±IJ¡:1ˆaÔ]6ìÝà2¶ Àÿû~ðƒW p{ïå—_~Y´E9ED,¶é·ÝvÛ—EÁ¼â¥/}iš‚ζ4h,"‰ì&LÜ6Š J– Ý¡lTFzâ±e4pðâÁBŒšW›ÉNN3fÝ&–}ãÿ^›7™hðe-òÐ1[¦Í›¬ôÄaY;ÙY׎þcãlB‡-mÀãÖ‰ t¡’‰Ó±q•ìÉF&oÙ²åÊûî»÷t/Ø¸æææÇÈ¡cÃcíííËåÙÚ*Ï‘SelɘA ®‰2Ï^³fÍ‹¨% mÁ¦È5ÄÂÑ`ÑðÞëö¡ãû›ßüæ=K—.ýè›ßüæO ÐFRmQNV"ýÖ·¾õúÿüÏÿüç3fŒŸ9sf:y@É ¡ÊU¯™jÁ,?&Ð@ FwŽŽ[ÓJÎSâ:@_ÕëIÀ›xì$ ­ã¬þ̶;çα¢­rCoÒ 1fÖÕi3L½€m;Iz“«÷ÇC X_¼ç$£Ÿ[5Þ+†¹ÖòÙyŠS«¼à5tíÚµ#å³ÓdiB4™ ›äœÇifÚG6®Õc’½´¨ 1cº°ÇÂy,¥e!¬½/ž~á}äqP‡Òðë† ›fcyÎh›…8cØHZ@‚œ9sæZ~üã[Œ‹¿ýíoÿ(â"£DÐå–ïÿû/¿óÎ;ÿ¯Lz^Ž }‘ JfM³%ZùBqÊ·qk^Í„Šfj@`-éPf¦çN Åàx¾VÐ!ëÝ_(£ÓSØTÒlÓe{0z…’Œzì†Çôy““uöúfU ·etéͺY’f\lW+gc#õö•ÿûËkœôa\èïþô§?}ðxþR3(çÜ;H±ç-ÖÁ¥KhØÖjž;ÓÖ@óÜœü-ÛͲµösëÚ³c$T|6ÄBÙg…ìŽÕ²‰¶o¯çö·€.j[»eNý<†Æž~V=CÅ»f›¯fˆ5´Ì·½VšÑÖã÷ž@ž ¡&`ÏàùÀsϸa}_4xóÆLÄ%?úè£ùøÇ?>ï/ÿò/¯Ÿ>}úSqæ‹ -Ê (_øÂnxøá‡ÿiÞ¼yýÁ®ÁJcÙ]5ßNºP>,á¡Ëw0¥]ׇ²˜eØ,8ųiåéYêÚ7[Þƒû×iúÖêϪ·fËx® óâÖô±‡z&Ú‰'TOÊNÆ€³Û–Eð\½¡–Eº«€(ÔéÉÏÆÅÙLW‚5Ý‹U÷\õbuí9§¯dƒvkÐk™UËŠè ͆yYš˜…¶ó˜ËÜx /Öé¹Ç½òYl±ç ÌrÍ{l±çºµfÓãÆ ðŽ+”`Ç´vëz‰ž+×;çPé>s6^ÍÖDôB64ø³†(=<6ÝVŒº•uùPË%BùÖVÀxCÒÂüùó¡ßÿô“Ÿü䬷¼å-o¸ð ‹3`mQNÁ`þâ¿øÙ7þÍܹsÓx 4o'³¦ƒÊ­ò$]ÏX5/+T»{l+)f»Yw…â)Sën´JÕ«½dÝ3àóX½ÐDæ^{ÀÌ‚$›}j•¶ž¼‰Í‚©P ,/nÍN,¶è°ä,ˆ¶.W}/íoYÖD = "ôD¦]]v×ŒŠ¾§@Ïrýy•ûí±Úÿul™ Ì×÷X±õög^L—evìsé1žÞsÀëFw·ÍdöÀ}V½C J4¸¶Ï¨jÙa}ì¶{‰£ÖhóŽÝ²tú:iöÍc*µ‘äa!&Ú`!†1k¬êX9ºÜñÊÖWԳпÐãoÌB¥cÁ¶a3zÑÕg|éK_ºGÖ½þª«®º'Ά´Eéã"ƒuðM7ÝtëîÝ»ÿ 5"t;Ä=°F¥ÉàX]Wg…z.PËâ„”®Uêpó•m9¯3€­µê`àõ?ôXPüŠ=w0zŒ›]o­u †lÑP¯Â{´èLW›mçÅYPÊ´àS_' ö,[j7¤Y8ËÌZ€‘7Iz-Ì,8°Ï¢>í"´nJ½½fÙ¼kä¹ÒBîµP°{¨}›³fŸwÏH á[@¤[RÙvjsì%Xý¢ÙTÏòÀ“YFHŒYpnï§“!Fß²6œÀê”P9í…€è–~:^˜ìÜ¡0¼¹ÞŠ„ÄZΘ1cÜw¾ó»÷ïßÿ¶×½îu·Ç…Ú¢ÔQ0ë%K—.}Ë-·|K륈`cTn•´®äí%ð½×³Ðº‚tL‡Wõ<4iXi™3/nÄ.E•9ðŠ`(z:í¾ìÄŽÚUk«Í{Jßüõ¼½Ì7oÒÕ@!KgA•Þsd6 ê-ÐÕ®¢PH Î-(ó€xÖ$å5<÷\—ÚÅjÝw¶&ž½~ðØs·ÏyÖ5·Ûyׯ«sfëAÄN}œ^ÿ_Í‚Ù{á#>Ü¿eÓ¬Ñçi¡ß´`0dPdõ9պ̧ú½8=«hèØøI7fù#QA»ñuÖ6{œ¢¤ 簾¦!ßýîw¿±mÛ¶ñ7ÜpÃ?׸Õs®ŠAÛ )¼èç `U‹`ðnß¾½õK_úÒ]òïy´Î,»f•™fÖtÌšk:6ÈkÄìeMy T¨¨§7Ùh¥êõëÆêž»Î+/â±N!¶ÏÆ …²2C“i¨‡!!ð©33½FØ^ ç&µµðôÿ–ɳlD¨Ö¿§cÇB(4ùÙ ÐÆEêû–åJ¢gÕÐóž ËY¶Ë^{ êB.1ϘÉ+¾lÁ…Ç4f¹ûm+5»ñ¡û{†ÀFˆ}óê+ÚºgÞyz@J¯Ó: ËXñÆŸe—½lOíNöš×{Oì1k–ÒËVµ]"l\"™3½0c™ºÙÖfø__+\èk¼3Öíþûï¿YÖ¿âŠ+>a]ùeåGòúÄF‰ í¤€ªK/½4©Å ‚r5jTë¹çžûb]-ÄÀµ‰(Ò5Ö¨¯¦ lv ²ñ˜k°g3!mŒ—·äe+z±-Z!{¶^gûzñ3¡Éκ|t\•d½ ð^,VVË 5¥kR»*­;Ë;Iê,Pˆ®Ýg_uW½­f^¼IÞcC‹°dCæMÎ! è¹.óâ=P•æôgœ(½:l¶N›®z’µW¢¬8³Ðõ±J–õ‚ùCãÏ3b¼Ô,ãM?ç`áÙ²ÀÇë5l:/«•††×ò+ [`j /ý{zÿ6¾Öô6V’…ÊuÆ)f˜¤é{F¯ Á’ɸ}ü¶ÛnúØc} 4~Š’ ˜³¢DÐvJK­,› ʶ… Þ-VÕ LÕ¥ìÎò¤ßIÁ{­tòÒè³&P!°JÔ&xÀ)dÍkËØënŠ«óÀ’e1ô„®Ý@Öµ˜W Ô›@¼šUÞ5Õ§mnK'𻕢¶Ç”q±=Em¯Qý¹W_Ÿë¬Né™èlìdˆ)jÁ[*ÔšÚ줒¬Ï½ò.`³LµÎ´©ÚE¦cHu“xÀ®×éÿõö ¶¼£´¡6v+T»Ï²R¶(­»Ö J†°l+ŸoËÆz l^9!ízõâÞB¬›W~'豑^Üžç }Lð¥õ2CY°°'4ÖY=£{ â{Ó§O?²Q—.]ú8ëFÐå8Ɉ#Z-ZtWSSÓº?¤ÖȺ€]#«ÆW;XW¨el|Z¨¦Y[bZBV«uù؉‚Š;TdÔÆ™xŠÝþF(3Ïs™XÅžÕÁs‹ècÖîF(fïéöQ,VËæîº­]ã¦CA÷¢›v¡kPfßgu®(RËÞ—¬¸7Ï}zŽBÏUÖ³bk<†*ëw<·ì¤o5[.D—Óñû¹n0ï5œ·=u‹% ü43è%Y·©W8—ÏM^ )=v=ß–úÐ÷Æ‚*/éÇ{,سàËcéóÜÄ6ÌÀ—¡bÀ!–Ó‹Ól-ï'´$pó~›à ß™?þûeýÑ'žxâCqöŒ -J/ËðáÛÎ=÷Üï p[Èd]ã¦uFEÍB¢Úšçà¶“ˆµ8CYuše YÏÖ¢´@I»mss¯,W6ÂS²6E³X^ñ_O±êó,ÆËNô\ œlÈŽj †u\ø¿]¯A?³ ÌöÕÏ„žtaX>|F4Û£X€ØµPÇŒ…úË]çMz^ÜXˆ½ =hÐÛXæÑ~Oà0¶÷É2™ëÅrçöÕ&é2>ºà«ft¸?[óÀ[¨c„ÖÖõÌÏ<–Ôsw3{Ð@Ë:iÎçÁ>'Þó¢ã_« A³ç•ÊÉêÊšÖn]]ŽÃÓ;61$TúÇ«ág}Bq€¶N[è¹±¸öóPbU¨Î¢u·kÚŒbݨ<½I½7cÆŒOËû§W®\ùÕ8›FÐ¥ç[rÆg|­©©é26ÏÚä §rµq2´ÙÚk¡ÀjüÌø•P+ÍÂh Ø /@­•˜-™¡]r6Ë,䢳å6²2ÿ¼Tþ¯Þxaå‚l.|Ž÷´á3º2m@2'StµÀýdX6œÖ“©žlum=ïÞ‡èCI'p‡€‹É!Àã¦<€–=6¯È¾òj—å8ï¹õbþBål,XóÜÓœyï-ÀÓÏ•ee¹ ž?$.ér@CºZ¿~ÆøœÙD&Vó·åƒtOV­ý;¢×.Ú·o߆8ÃFÐ¥Ž2f̘K°}N»½²:xXOäöÿP†T(kÎ&#è,.Ï•rizÕÿ-ë¥SñCíŠBUÐmAQ›Ð`A• Ý”bpi€¦2hœ¤¨8±Œ92a#FŒH Î4Ûb'tиÝ–AËkušä½æì¶ëDØ(²”aÎúzÑÎ2®ÓjÏÍ!·a€gYèÈóX:ýjAœ °@ެ²f—é~%CDCÆ7m¸è÷+ôè‚Ρúyz|Ûø1ª¡¯×¡Ä+ýáíËs͆Ê!ylZ¨¹WûÑêPàÕ53ñ à†ÖVLbðž¹¶í3gμýñÇ¿LîéÁ8ÓFÐ¥" «]äë2ð„Ú«` BÑÙÉ‹-”i'{ÍÎØúG–­²Ö¢Í Íêùé%X±] ä²2 Z™ÌÐZ]‡'2àêI¶-技»¬&òžq¦1Íî„Jžde³zlœÎ¨Ö ¯»2c— =à{‚8Œ_‚8ÝêÉf¡f•±±dv\y¡–±ô@™çðÆL˜³á :¹ ïq-h ã:lÞ¼9ýï™Õ/ãÆûgÑký›8óFÐ¥ ÅóÚ¦¦¦w‡Ë@°÷$] ^ƒw È4«pó©ÓÂ$±µ¡B½ ½85[Ķ›ÑÙ\ìP†~|Pîx%`#Pc -Q\·ÖÖÖ ¤Y—¦Ç˜Ñ½iÝ™YÌ™Í^{}ý«ù, å•稘å±r§ãVäÕЪ9®"à®ZVφY0YÆÖÌœrs^|œMúÁ˜~úé§»õÙ86¼Ç+3±éjÕ«^¢’u;†âר“¼ztž1l“l)!¯$ˆU¸^¸Óá$ºV$´žGœ›. ¢[î× nÿ¯\ãE²ÝqŽ -J ‘AÕ6f̘¯xYŸÛTÚ«nOåÁl+=ˆ5ð²Ô¹ÇºY¦}µ"³ûÉ˵–¬µ^ùÊÚRt£X€FàÆ"£dѹ‰²X´‹SÇÇhp¦cͪfÞ±kà•U­(@+ʆ…þ/úUËÈeÉ(µ±mEX.k[k¸e=/X—§ð²]u„u©jN³q {À+bäàdÇ‚8]FÇ&Ѹ e¨Z&˺N½‚½xy=‡C%a¼±aÝ©Vwê8<ëÕീÞãz0n¸†Ì,ÕDÎijjúÜÎ;ßGamQŠ+ì~2Ⱦ&¨YÇhp b°Aáx-ƒ¸Äé8ãa™6M¯[EJ‡÷”ŒVR¡ÆÑ^2…ýœï™ÕIPÆ…  ë¡´ÉâÚL˜0!UTdÑt³-þiÁ™×ëÑK °À¬È’޲ص"ý\C®ÑÐÿµ±²@-²jõgáz¨aá<0gŸƒÐïÚ. ÖÍJVÎs­j&Ž œ.Ë&n×®]ÝYõnL â{Æ­²kŒ—hå±q^‘pV¢=üœŒ¿×tÞ²mVGZPæy.ì{fëâ›››ÓßÞ¸qcz½pÎÜëFŠÞ|³\ÇŸÉòõ8GÐ¥€È@z·(’—“Ê×ì3«¬ËÔ–÷ðbàlH=±2îC"‡ÊwXå`Óâ=å‘URÄÖNÂÿPÊPÀfH[›†W6üÎ$Œ––Ó¦@ ͺ9½ í^Z}=Ë‚²²àÍÛwÖ](•kõfßB¬À‰äúJf©šêµŸ"¯!¦­ §·Ë­«Èz‡€œeâôÂä#,hó„LÊ £Ô¿JæM‡Jè¤m€ZýfÇf¨æšeÛl«>ͨÙkf8Ï  ×c×P¾iӦܸ1ºÂÌý³\¿{eŸ›âŒA[”le5KÌǙήÁ›ŽMÓÌ×’ŠÎZrº0¥M µnòU óã)kó%ÖŒ ^a-ó(á ¨M“ššš^ÒÔ²4›¥™Õ©7–z°kàÊ`õiYÉ'X;žà¬¶­^îÑzÄ»e1n¡uE[Ì›àÂ9#ç¹R Þ˜ˆ´}ûö´{[B‘uÓà ú…ì¼×)ÆÓ‹^âƒÍœ×qº6Íúõ®¨u¡o:ÛU·%ƒ‘‹’E0nnºm™\±d?/º÷µqVŽ -J†î–õ%0#Xõžƒƒ Ö .«! Öl_¾¬ªîšÅ³ B-`,ób)õ5¤þÃ9¸a݆ ÒëÁkÇ:’rί‘kôzÃwÄ©9‚¶(Žˆ"x»(‹ËȲi† ŠD+ *)=±ë>¢¶„„תÅ+4i3™Þ8˜­KÀ«mäe‹j—„eÒð ° E…IFM'脬’61À*ÿÞbÔ<–Çì9yàª,ó–·Mˆ e–ép²€³zºÞÊ0Íú½²nT`uæ=Ý_8ÔA÷ã…þd¢ƒqÀ±†ã¶mÛR޵5ó¦ÛÚ2^Qr¯Æš>¯˜¹ï!†Í²l܇ž/tæ+³èL5ã¦[¥ÞÍ¢—.›l‹3tmQŽ•QH>Âv- ˆÕ.Q(ºõ4¨ój|ém BêjØ:c”Ý‚/Òìø}€o[Uy™VZ´fˆ-!³†ëñ]fy¨1Ë“@ÍcÒŠ¸9- ÕJ«·]¢!ð–ǰ•aÙjI@(²M™.!€3G«REÀS=X¶z²tyÌ[ íG¤¡þ£º©:ƒò-ˆÓ­êX× À@³0±ì¸iæ_·‹Ò¨7mv½uej¶-äõðB]lácíÉÐÉ\z&æœ ¾ à†ëÀëTn-¢ŸÿFÎáÆ8 #h‹r¬‚ý#@Yÿ  `ÊÅ*›€ ³=m=·,MȲΛ-ßa¿ª·æ±ztE¨aHÃ+5œ#Üžj:€@-ĤU“¹™ÅlÕ eȬߪՕXÆ ZüŠY } ¬Õ“ë ß)òõv\‘$…j¬w½Š8Y ŽI À1K%E °FªN‚Â]¥ÈÛÚ–dzñmö^Ùïx€Íöwö2Yµ.'x#Åz¸J ÜT»½?•kóùú8SGÐåye3‰Ö cØt‰/Éb?³JÊ£ßmusKËë ðlì„V¬¡FF †u°øt2*Š“ãpàÀ[[[_bÛIÙúk¶Bh·½B5XÓ왵ð´u Ñõ~ÔªÁRå±àº?Ñ× @ͺ>³µ,`Ú“ª§bØò˜¿,†­–2e²Ak­ã– O…ăjXµ@0 l•eåª-ïQÏR"!¥ðfÒ¨ËnZgÚ¢µLcãz&$0yÆ+Šù’}hÃ+¼ xϤ3Û2Ë‚·¬^šq³‰ t…Ò=ê%™Ù{ãÁ÷pN“&Mb­»ŸóÒáçãTAÛ©.é@8í´Ó¦,X°`¬S°Ùþuú½×óÓVñöºØâŽ:T‹ø($5¾b¾ƒN…e¦›«ç5Oïé¬Íã]<·7iË–”ÊfŠVÔ²J}„ .Ÿª å'³Wä·ê Ü<2«Ðn(\#«¶[H³bÙ´HóšÂÛíÞèF…d  ‡1 wãÖ­[ÓÐÔG#úR϶i^9ë¶@MW °-°l-7&¥lN™2^“W­[·îÿÈÇ{"p‹ -6Ç{ÁÜ)ƒ~¶­b­[Ryì· )Höžó[¶M‹VLÌ%£F°ÆJÚ`ÕXKå9Bí¡Ê.µº@û*C—ǰգÌGv­Z V¶6[”ÚÀYY†­Þëj]«VŠå-ÜB­¨,ˆó€šWºC8ÍÀ!T„à:ºàq¾:qÇb[]ÙR úX5³€O£i4Æõ6uLB œ3gÎ%DÿuÛ¶m¯“ÍÅAÛ©,20†œsÎ9wØy‘.¾èõÕqg^{©¬ÔnkÕÚ V ³Á85(Ðýxã„Ò¦Y5f}æ°“e+û›E&¬¢. |•]_¨yÇxª¶Þrƒö¯lÒ"îјàöjn_”ùàqÚŽ í” ŠóÎ;ï+2P/ƒëQ/¯Ä‡Í&õ&MO1Zú[SîšÉ㶺.›,CÉ@Ø¥€µ Э«VÜÕ þ4x²@*Äx•=ž¼ø¶j¶jË|”e׊2j'Rm¶¾”zðeÎzƒÅ+ÂÒy`¬'ÀYQà–ä¼–ÞØ$˜Ó…ÐÁÂÁåp¦[÷ÁuŠÄ7èZ°ndátÏS{=m]6ï>èƒ6$ÆîÏ&:ðüpü8n×/^¼xÃþýûoJ¢›4‚¶SA0[ZZ’õë×§üüùóÿ¾µµõM¬Vmk²e KéÛØ5¯µ”–jX³¦ °¬áXHáƒa³Íì‹ô÷,Ðê• ê²Þr{Ö¿*…ÅleÕF«‡‹ÔŽYÝBÙÉ̦õÛV+ëI`Vö|Ë7ÏÈ©ÐYÏBpóX7ßæ·ht’ô&ËQƒÌš–ˆy#hƒî¥Î…¡Ìzoš Ó¼§GÇÆ¹Ycß^fÍ¢¶æ¬Y³>¹dÉ’u2g}?³`Á‚Ns)ãWo°ýéOêÞ¯Ú¢ôŠTJd¤€Mþ7Íž=û£:nA»D½,Q«ølM0›T épÛÚŠßÇÀYÍBQ@i`Áÿ`Ñš››SÅEÃìÏ, V sVMæe=¶žøÝÐ>ʲkY¨ˆ›²'2L=&. TžÈ¬Û‰Ìðõ6¨«`–q—†€[ óÊe·,Ö-ëVä9g‹+¿p…‚ÉbÓzíÅ+ô.õ/Áݦ–IÓ:Å‚:Ë zF½>?½_^//€#J8pà_V­ZµAæ®ûåxn¿ýöNöÁ®§€}œ}zrã7FTA[XðÝvÛmi°"‚k¬q¥ÏG‡æØ~¥Üs%ž}öÙO¬Y³f±Ì+?|ë[ßÚð£ý¨óÌ3ÏŒƒ8‚¶S÷â9Ÿ8qâųgÏþG;èÞô&vm yÙTÖ5jâÖà XÃj ®dÄ­±ï]»V,Ñ,VM¼ZQF+T­Lvg=›ÐçM¦Õ$ xŠ·^`­( +òÙÉ æú×ÓîDaÛŠ‚7ï÷=ðbÕ,X ¹I³˜é<=áé-‚7fü¯[·.mˆKÆ:&+ FÙ3è¼kdDZ7'Ùóf ^kkkÿ}ûöݺyóæ ;::V]wÝu ÷ß'd”ÚN8À&lÒ‹^ô¢ÿ+ù n=hBÝ ,•®ÞŽÙ§ü ,³BÉ®á=kÄ´‘Yó¶¢@ÍK¨µUUmìš§øêQú# Ùov­ €«7XË:žS¥!|=»ôp;Q\=[Ñd†¢ãÎ,€ó:&h KY¬»èn9ÐñÐшeƒñÍR!xxCì6»,°Dˆe-8³ý§³æ#½ O˜0¡eÿþý·É\óò%K–ìË[ÞÒpçwFÚ=‚¶ °ÉÀêöÙgß*v;øZàæYwö½X6à”ÿ¬aਰa°3X•m¦4h ¹A˸?³ÀZ½Ëe”q€Õ°w^ï×jòÀ[“f÷f5 °CaYŽžwYq9y1;õnå”5 —•ðèiðy<\O±m¡÷¡{˜æ¬ÞõôwV­l«Ao±Àßg²’ÞT†Úfxã†1oøàJ5[ßÍ;N[”×^+ê=vLhkk»@æ Ïx{÷]wÝ•|ö³ŸMÞ÷¾÷E4AÛ‰#óçÏÿˆ¦Wèx!Ô ›,^<‚­³¦ !\Ÿd×¶oßžX¶LÑ Ü­K´˦kÇe¶z'Ô«BQƬ֬Ðj“ªeߊvA( Äì¤çM¬z¿ìiHÅ®ÿÇ¢3Ðô6Y v „:€Øº…Y®¥PGï³j’EŠ´)ó&=ï;E³kaЏ ‹l[ïx·2À¬LüZÓæu(2>CÏ‹‰ƒþÔý=ñÉhˆoÆ+ô7{ˆêÞÒZÇZgAt<Œs0k˜à2xüÀÊp«êVˆœK¬Ñc_'Ë鱬ױP0~¿µµõ¯ä·-Çtûûßÿþ™;ÿøÿ8‚Úú>Ë6yòäWM›6íC¶xnˆñJuxu5»ƒÁƒ:œ±k`×° *¬/5 ÚŠºDC€-¯¦ZXó&_ïX×jH™ý­žoõÎ ­Ö5Z+Ó@‹ &ýžÿóŽÞž öƒmò@]8…€Pˆ•Åé„úBf·j™1ŸšU[ïÛö1ºú»úù/b()È›€ŽÛVaÔŠî'œ—‚7Ž™¤žèk,H¢ƒw„㈿çñhˆƒgÝ̼ v|ºû†«T3nìgÊOo„ ñÚúvœ×XëøÛ¿¸mÛ¶ÅòÙão~ó›~ÿûßwNš4)"ƒÚú.`1bÄäÓO?ýyøiÍh ¥“¼š>:kÇ6 Æ÷1è¨È®!ÑÖ¶œ*ʰÙöYy€-¯V„Yó€™7Éé‰>äôñ{®g ìªneÖÛu¡‰¨¬k4¤D³€¯ŸZx¥uWû^/0Öô¶z±@÷‹Ÿ…Àšu»xî# tò@›w«aÚŠLÎYF—w ¡gD3E !Nºž!£ÇCÙÌí3˜ÅFÕʶe±lEãÖjq“æ¹@‹²—ž°Ÿ3ÂàAìþçØâ¸ÑÇKÝKý 07'^ÁªéÚžÞÂí7lØ‚E°n˜3 ÀD4K ØsÅo`Ük7°ŽiÓs~ ­­­Mò[·>óÌ3—tttìÓ›ÞÔðÓŸþ´³ž-"h‹R7À&o¿… Þ"–ÑD°jhÈåCpfË}ðŠ€ìã×°§´Èòâ׊fˆ†[hÈE^!]&OäMðÞ„¬'Ï` þO[ï’YlG5“¿·Þ›€híZ&L..x^°ð½^Ï÷Ú]ã4˼i€ÆûÄÉÇsƒzÁÚ¥X®m‘úwÕL.yîÎЫ58ŠôçÍ2¨ô8× .oüçé€,wuQ¶®ˆk¶—m=âàB ®Lè‚6–0–”c¶yóæîì}2k€'Í c;n Ù»oo·¾Ç¶pCênx£ ôuƒQûë†8gèH+§¯§u—zúNëeý6€Ë–––óP äàÁƒƒ‚öùÈG’O|â%DÐÖ÷dΜ9#â Æ(h& ƒC§a‡jö謙9üK ²·@v E¶ZÊy”Éü,Â8ñ|4°ÀÀ¯@¤Uúü4xƒbÃʼnﱟ‡˜2à-‹E+¸Y¦‡ F³Vi†:|xW,\Ç÷záw¼x}4{¦tV 8Š5Ûª|ÞÿynVì…s2t!ÒOYáE\´EÀ\`ÕS€-ÒÊ€yŽ€",Ä•°¼aY±bEª£adã¼/¿ü“«^uU2eJ»œÿnàækävÊ(×mè°¡)p1b¤€©!ɤ‰“’ë®».¹è¢‹’ÿøÁ$?þÉÓ}3 ¿Íи±Üè9ŽL ݰnpkòûžñf“BÉé&P9At×çåw^‹ß{ûÛßÞðï|'Zƒ´WÀ–Êüùóo’ ÿ >m‰Ø¢¸¶¾—Í÷Tlˆ`²„ŽÕ ËÍcײ²2ËôÝÔŒš%ž;ÎÆL‘ýñ€˜ÝÑŠD/DZ©h§ãÜè.¥ËŠŒ  qX 4 æ¬2´|;Õ!;wvÅ ¯_·>Ù¸aC²m[GòÌîgº\§‡töÐKÐ?ãÇ7'“ÛÚ’öö¶´]Ö]ó§šÌ˜1#ùæ·¾™lÞ²9=6¸Rñ wi¸Ù{‡mpŒÛ¶m;Fç`™f‰ÚD >;özêŽ=<9®Woڴ鲯[¾ûÝï6Üzë­É[ßúÖˆ"h;~‘I“&]9yòä¿Â$kÝš°YpfÝ~Ú E€ÁDw(hq PÜ !Å—å-ã-⥢'`²î°q!øÀ~éÂ$¨"èÌ ‚ö˜$›À ã²x,TŠ6ÞM»KÔº¬Ü©å‰ï Þ{¢™3ðF0¦Ù3íÒÔÀÌÆôÙ^= ŽÊì¿·b­û¯ÖUy¢·j›½‡^ÜYÈò¨óXo/QHƒ7»Î38­¨¥®›÷ý²l¿k1¾qãÆ”aK ÛîÛ›L›:-¹æµ×$mííÉ~Ñ7nHV®X™¬Z¹:5¾¡¼Î4Ô¡ó{ìqlã’éÔfÍš™Œ`6uÚ´äoxcòÝ;¿›¬X¹"ý>À®[/ô®7@¦ÖEp_B'z ¨Ö»–ѵs>Ç~åœ>³cÇŽûeKn¸á††—¾ô¥³gÏŽè!‚¶Þw‹ÊC9nÞ¼y_´!T4Óúý=E§&}Ö^ÂÉB¹y€­ÚVTEêžñ\¬«O5í¾#`ãDZ4éàè,pè)MÛVÆËfÔ1\Œd´ð{pšycœ8,XOPIe­Ý›dδ+Óº1½$‹ã Æz °h±q'2óV¬•¹Yµÿôxô2½iÄz! !àV„‘Ëcå<7f^òAž»ÔføcŒ0!† € z»}r{rù+/ãï´tÝÆ “¥O<‘lÞ¼%Õ ^ÿèе‡>Ù ßß¾£‹¡›3wNÒ>¥=5._ñòWŠ>’¬Y»:Ý {èT¸Jó2åíý`’õ9À[`éóåqé6]6ƒÔäПÜF¢~›\ŸWÈëáw¼ã ÷Þ{ot“FÐÖû"€í&y0§êö èôÊzxS+¦Š3~ V]xYŠ,«æšW ÀF aÙ%‚ ÞRX-› H'Kd¹e5¨ÍºnZ™ä±oº(,'A”–žPl¹ÍXàj31 ̪u_æ}§€,¶Ú¿ž€®-µ²Ž¯H<`*Ì\d_4X¬ÁêÕ% 9+g3[½’BEA\Q ¦×a¬C?¯Y³&õˆì?°?LçŸw¾èº!ÉÞ½{’µk×%Ë—-Oõ¹îfPöþæ ¿5}ú´dÐàAÉù‹ÎcqŸ€ºíé5pÓeDB€MǹqÜ¥›6mêöà\NS™­,¦ë1µ¡®&¬3‡ã]{±ìûùíºï¾û?úÑv~ìc‹ "‚¶ÞcÙ&MštukkëÛu‘A/~ÍfÚX·)& «Mµâ*’Z&vÍ{ÕŠUƒ§ec ÈZáU·Ñò@d¨0iÞdª¯¥]g—PÅÍâl™.k –f]ïjb¨ú:h«ÕÅx2g ÖâJíÉ~¢µ²jy¿W¤dG0§k—é¢ÃY‰E€³Y®ÞØ´¡Žé+Ô4ëcnQ%èÀý$sfÎI‹Þƒ.\½jU²råªÔ D–h- ŸÇйá‰%KÒý#ÃtРɜYs“Ý{vwüð̰‹‚Õåy¬ôÎG‡²ÐÃÃý°Ä‡fÝ8÷y]|ð] ìÿïwîÜùcÙä±›nº©áŠ+®è\´hQD´õ<`“‡°yîܹÿ;d¥Ù,B[òÃ94Pä ÁCNv*T­'\¡ZYi7¨-A¡]€Ø–±A«{ýNCY–yLd–Uï¹h¼Eÿ6ŽG8…¥Dfpâ3*+/6ǺnòŽZÕ‰ØNv°ÖÓ ^µ€± X+ÃÂ…¶ e–|Þ±X7«6N.ÄÀ…<žqêoV9è èj$@’ÿ'ŒoM†ˆîÃvs«VÂeºßLêéeß³g¯ÂÕéû1cF Hœ´4·&6¯OuØ?- ÚøšWÄ×䎳E\ší @°¦cÛl¼÷‰ûƒyAö3\t*²I_)û=týõ×7<øàƒ˜ç¢DÐÖ£ mΜ9àÖŽ ]'è€M›íè5ˆg« 4v8€pÐe¹Có€Z5½:9ø,³æe8ÌÀŠb© .ËTWÏšÔŠNç1sZ9óøX[ˆ—yÞpIã\YIœºl#†m<ðf;^Ô”õ W ¨8ÕÀÚñL\(Óü½ °+ 芀µj>ůÈ1áK³r¶‰W‚Ä®Ï*œw<Ð ˆe³…ãØ@2xPW¯g¸0QÞF8[O£ãÝt§lyBös±\³w‰ýÜâÅ‹?ñ‰OtÞtÓMYDÐÖc€íèøñã_·¨.1a³Eu9/ù€lÀ(o,mØ–}ãòqCÀ­Úž ¬±€£ldÖ°?–ǰ±jžk¢L,G–U^¦økѲÖ%£ƒh™]Š{Ö q¸¸FØ> ¼ÙÄ“´õVØ(I)Àe·Õ®½"ÏxcVf»](9¡( ÌÊè £—Íjõ_¨°î<ï…×%…] ``Câ> 2¼Ëƒr´3Ù¶u[òÌÓÏôè˜À¾w?³;ý-ĞᇚìÞûLw è+è.&rÞ¼y7ËÙêj™5ec96|‡eÄÌÊ,×[Q†­(P³¡k¶ò>Á~Ÿe0Øt8Ĭå±iµ‚x«¦~™ÝÖ‚lÒûp¬¯Æ>6Uf&ªeCÍËëÍÂõ&ÁZï³md3Ê€½ZÝ›yÛMN( Ôò’òX9^/éÁ+œÞBe” Â2ïýú§ >Ã:àlUgK»µàŸzêÉ4!A×£‚¶º³l³fÍú[V§S)èø4* ¯h®N8`:6Ðè|˜Cñk^±ÙPVh™E—ÆðÀÙ5ìñjlÖ ª³UË0jµ°lõRˆE]J¤øqž,Bð†ë¦“t橳úš–­¯¶j÷S¤z¾÷;'êÄb¶Ê²fE®¿WK2 ÐÙGÑïu«aóô8÷âäl‘`ËÎyï¬O£mÀ€ç{wB—CgÖª“t‰$ÔiÃYnïØþàvðà³)ãF4pPrð¹ƒÝs ô‘޳%Yìµð7â³È60nÜ/·cIyå|¥Ù6¹NW‹îüoò½oüîw¿küüç?ßùž÷¼'"ÚêØÄjYØÖÖö׺8¬×‹ÍN$úߢ;“>b¶¼˜‹ZãÕ¼.,äËb°º/Ž®AÄØéŒôÄ„]æó2îÒZ€Î÷ ÷Ž1oº3Pm¹jcÝúhëK1oE“¼¶'ÛVô8jty ^µ±jÕ~*l:žÁFƒíTc eê<¸F±¤¯²‹þýº÷é£,ÑUhŽ52M2hßœ®{ðßï÷¾ýûR°–’ýi` ýÊ"µeH²ºà°è/…}¯5Èôz‘jW©.@öù¿öìÙsìnÛ‡?üá†Ë/¿<Ý ­.€­Aº~sæÌùy‡°¬ªV6¡ìA¾°¬¥u}*1:CÔKYÕ4+Ôô€¤ÂÒµÊl¯K|†Á… «ˆI^3èzOØÕIJ٦çÞRõÈ®e«²oì‚ Áu«'è:[-É)'2ë–ʲÎ; Ðeí;ì…Ͳ€¬ ãæu ñÆiѬU½íø`{H#”Eg•35”¡%Ež[5è„Ô(n•Œ9"ýqÍc»;°`›ûïûe²sÇΤÿº>ôÜ¡¤±¡±{¶b’¾Ëã±Þž~ źq¾ÁüÀÊ{À7/üÇ+{¥ë`Âð•¹e²ã‡\¾{ïÞ½ 7Þxcç÷¿ÿýˆ:"h«ekooÓèÑ£/ÓY¡6èÒ³Zô¶dØØ`ᡵAü¶X¤-”[­+TÇב]cË% Øè EÜ\¡Úe›8zb¢®5¡“chÓ÷×…ñ}oìAÖ™©Y åñLP8¶™!+[§­°U/v¬PU óA‘®¥¨™¯OÈÀ,ä,˜Ã諒B>­4Öòž·4Ô䴮ޢб8ÝÈÔÜ´9iÐ’ÉÐØ÷¯î u‹â\²˜:Ö¹óׯQôK¿®sÆzLÈ2öuV¨ÕwnÈ,åg˜+p¼Öò2Jé©Ðñ°Êõ¼^Þÿ²í¯î¾ûîÆo|ãG¯»îºˆ<"h«°¡†LËôéÓ?¦„¶X¼@Q«°XÖƒ´:¶"îÐjÜŸ!«“®²kŒ_ƒò€…‡AˆÁ„¥ûÖ«xl-@­·A›Zpßrq½4릻2xݪ™0ëÉÂÕ›‰+ËîôàÖÛ¬[µ…oCüë]v£Ègeö™ÅÖ…ÀKf±3lC÷/¶û¡þdèÆ£Öµy‚¬ë  §ûh”åhCwUÓå >Ÿ0qBrú‚ù©û³ckGòè'{vïIž}îÙdÆÌÉì9³»cgÀ¶eó–îC¡RBÔëÌ&ÇÖØAÀKÐG†’®KK.h§]ž¸±×±oVÉ+ý»n•n?åÞ~Zöy°ï>ð†W¿úÕY@5‚¶ST00n¼ñÆ”âµòÉO~+eÙf̘ñ?囬b/‹Ë+šK ŒíG €X­:”yé1k!ÀV¦±;Ù5‚56 @ü&À›cRyéNPë„UÄ .ë«'P(ºér.¿¸vÌ0£òädcï{ÞDx¢¸N#ÚWX·¾Öl¾š® Eƒü˺@ó:%Xæˆq¶€Ç ²kyBÖàF(b¨˜D2BóÂt0ZGñ?ÿòžsù¸SŽ,ÛŽŽÉïü}¥i’L›>59ãE ºùÁ_ÿ6Y·v}Ò¯WèÅ”iSÒóXòØ’tqH¯YúÛø®B¾ÜÕS¬éNd±n¥7ÏX"Çœ;BiÜl ,}muÁ]ö&•ûûR™'ß,úñ–76~êSŸê|ßûÞZm/Øý×6ÇÕòÃþ“-“Îmmm}'{µñAôÚPy‰IlL×ǽÞyE\ EcØlû)](–îP‘mnn>&34K±•Œke‡a ŶÕ;ÎÍ‹ÿ c—Î ÃõÕ5ŒlFïÉÚN$©•uëIàVm=·2,r5ñnÞ=Ϫ»ú#èL¸ß‰IKh`LMž49mš>qÂÄTgA¥ýƒ÷ìNkŠmX¿!Y¿a}ºNw7Á>uÞö¹Ï}®áœsÎ鼅‡*JmÝÅ Aú;ßùÎQòœ5ô›5kÖ'äá¬k²…²‘X³‹(‹â¡Ã÷½¢¡lÐ"@Í‚*ÝÎɵ™¡hØ,ãØ±cSeÆøºZ‚åë1i÷t,[¨®[(iÁsIå¹N ıî;²×õdIP8‘ÁZ¦·è>Žç5¨&3´–b¸yû/:–¨/aPB_Bo2v 2wÎÜäâ‹/NÎ9÷ÜdJû#» Kêcê<´yÚ¸qCòÐÃ'÷Ý{oòèâG»Ës0Ž— V¡°–¾Ð¸~®òy±{žö,ÀƦí0–Ê\ þýÃ)`7nl2fìèÐÁÅ»ì‰åÉ“»žLAÜðÃÓİ{@jG+z$Õ/X×3`}¨V›ÜB ž±ŠmØ1‡Ý|0—P÷ñ;6Þ›úןáBÇŸ(÷üoåÿ÷Ê<Õø¾÷½¯ûŽA[¦|úÓŸF߸”ekmm½F@Í+ñPi×§¶FlQ=!CÁÁjñbÕ<·§ÒlCtÝS“€ŠJw4À±´´´¤  ÖŒùè 6¦žà­7~;4é„@v·¥’e au}Ï4ãæ]ÿ¾ëv¢‚¸²íšêµ²±L׃z7‘/ʪ­½Æ,G0aЗdÈ°í¼¹ó’«¯¾:YtÞ¢¤itSº=ÀÎŽ;*MͧlâËú÷ Ð?6nò䤭­-¹ìÒK“?üá‘ä{ß»+ùýC¿ïv¯‚"`*Ê´qLw$‡¤À©=F“|–•€mô˜ÑÉ9‹ÎîÎÎ ‡s8ïüóRÆlÿ¾ýÉúµëÅÐ>Ð5Y‹~2dp³ÖuHTe;ta8zôHZ³MÞ¥Åu9àu¹`;Ý9Åž£MX •(²ó–fÜØÕG7  °uü!æÇʱ«õoò½‡×¬YÓôD°ÚNY¾|yróÍ7§O¯;nœìgX²`Á‚dÊ”)É/ï¿?ùÎßé*+`çèŽç{j¨*b°j±_‡ž;œÆ’ueä?øÍ1cÇ$½ì¥ÉP9¾û&;¶oJ::¶'{÷ìM:¶ut1ð4¢3RÔ¿_Ò/=¾Æ >p•À#‡ÓŸÇkrøP÷q´q³:)Ôª‹s]ž¾öJ‚ ¨ `q‹ÌëŒRfûªÄ„Áþ>&Ë«`‹À-‚6_>ô¡–OY6±Ò®+ìtÒóÖ7¯-3ËÀ±a/ãÅl}³+ÔþO—«h¸Yw“hý°a-L/®£–€ø² Á‚¬¼˜3[Ä8+¬^Åt‹¸ŠÄ¸éEtOëØ6ƺõFrB-Û–-Z„~£ H- ~Š\›ZZ½î‡Q"{QïB¶erÞúPïP6LôÔUˆU{ÝŸ½.™7o^ºíæM›’ 6$+W®‚$yêɧºˆ¨ÀP÷lÒ¤IÉŒÓSÀ†hçž{n ò­ÿ¶ìkE ð»ø›¡‡\‚ú\ìxîìì*¨ÛØ/ŸiÃ>0þgΚ‘lÚ´9Ù²ykz>ô~ 28mþ>p í™Qe<^φÖƒµÜÀ¶Øq>!³hÝ“€ózE{-´ôu×ÍçYÚŠŒ®©Ö¿v~£1‹ãcí6yÿ*Y®”ÏQ°­1‚¶Ú\ùÉO~’Üu×])+N[{{ûº!¼¥Œ5G‹‹J‰°ÙA hcwþ3 C -4Añ»°aÁ`Æ:ÐðãÇO_R¤Hn=&]õ›å1˜Á`Të2¶çÅó`¥ï.÷ȡĖb)ëÁ¾É*ÕÁ ÔX=5‹,ƒR¤xññ.šÛ×cØêáöì  Vû2+Ð?¯#B™fíeŽ· C§cØØÚÚÚ“×\ýêd„ ©»¬Úҥ˒µkצMص­ã´ôþ1®ÐHõÍV ÐkkoKæÎ›Lš<)ÍŽÍÕ¯I~ð?HžXº$Ý€ƧÎ~ v] 65º\á#0ý Ð/ÿºaÌ?ô»‡»z‡Ž1<X)±¤Ûà `ë_émÚ¯ÑeáSýtÓ¯1eãŽöëx$ °oß¾»äã'³Ø¶×¾öµÉ¢E‹rÏ 1æŸýìgO8ÝA›>yy˜…ôþ÷¿Dʲ͜9óÊúáH°4ƒ ¬äÎzlxøðÀå±j¨U;qf6¼”¢ZµîbÙªu‘’Ão¨!^¿ÅzK[·ní®·²æÊt[àB7)ëø0†ÎyÆŒéõÁç¸Ï¸?te× ™ð4c¦³Jq<Œq#Ø%ÈÔ -«†[o2oõJBèíòeê…½§Õ>7õkE[™ºlEظ¼D‘¬û }Áß]F^C2¥mª€Ÿ¡éÿkÖ¬IV®X™‚ºzÔ½ãù`+d¿‡å7&Mš˜êŠ)íS“={÷tÝÐWtM†Î“F#!ôÍîgv§ïS`Õ¯Ïhpv6kxh Ð&¯`ںݤýÓ¬R´AoìÞR&Ï¥å>ta]êú¢íºŠ€¸PÁ]}M¸ ÜxM=’€:Žñ¾ ‘s›&Ë;äºÞ”Ŷ½æ5¯IÞð†7䞀ûÍ7ßAÛ‰,W_ýêW“•+W¦,[SSÓãÆ»V³lº¿¤´z0€)! «eÁšgEØ´¢®Ža³€ Ç”6"®´Ñ}ø¼8¶jÊ|\¬AáÀpAA2Øž¿ç)ÂjHˆ!cM"ü>¬*v&Ë8kÖ¬ô3 \ZÝ^·‡PbD=7Ó;îYãbbÜ4Ðî Pv<’Šî'+›²L1ØPmÅ2Ï] æ1je€Z5÷­§[ ^Æ= Å‚ÓiÙ§%ÆKŸï26׬^Ó Øêñœéñ‡ð‰uk×¥¿Ý0dð¤é´¦dç“;Ó1‡Ï±^èö\llWZTvÔiÉöŽI¿ÃmýÒlÏPRË®e`°=Þ¸¾Ko<¦®gê…>­ËHöíß'ºöà1Œ·¯¦]W‘yÇ^[«Ó¸/7Ö*¥›”Ç©¿OІ{@à†ùDtâ»QD¾²1ĶáY*"x¦N ²éTm¸‰·ÜrKCåahœ:uêÿ”×:³ÅSÜzðr 0ÂkµÄOeÅŒÙ,Q¶I3¨*€M÷ 1€E' ¦‘ã»jD€ÖÍ›7§ÇÀkÃjSÉ«l!ক¡m2{ëŠ÷Ùc­­­)°Û´iSúy¼Õ“Ýñ²±tb ®ã|R×I%07ïzÏD„ZÙ·2à®Ú.µ0be®µfÝ˰ˆEYµ,f«€­lüZÑs³û뻚®ír'24}…Ûºek:Á×£> ôt ~“ã }°i㦴–[ZCqбø.@z&BÏ;çÆcáwЩ] ^È<=º‚î»–6‚7²mæ-eÛRWé€î¸¹þìW\<Ð]èA ³¡óÀVƃ‘¸‹¸,ðàF¾eä4A¢3ì+®ãñrOþBîå’ÛA[eRL — ¹’Y¡Z[Сk ‘ækÇØ­2ñiÕ”sÐí©4`£rbð'6ß`™Ÿ2–kø¿££#U|Ø 3 ´Uܲ&pýJ‹÷“bdpŸ”qÆg¤ÿ¯_¿Þoµ´ ÊJFá%pÃ3ÅÿµKÙ‚? ÔÊàVnú²»áxt6¨€•Ýg=[ÆØ/! é­³uÃŽ9š 2¬»¹ùÓ2aLÕò<¥Æìá#)(š=wv Œ–=±ìÛ@èìþ¡ƒ‡&{öïî6œlïZ£¾¥µ%Y·f]òÜ¡g+ã4IÝ™ÇL¸ýúWâÕ˜pWè  ³Ôµ "ëV‰o <%ý+: ìbñ Ó$€Ef˜ÚVõгyá3!}‡ëŒûÍŒR0™¶L•.¥Å….dYà"½Uv½:é c:±ý›´ÕF†T‚Æööö¿•¥Ÿ'²ŠVǦ(°‘j/Óײ,X³ ’Î%`ÃdãÓ1lº ¯LZä™, °ƒßEœÎ]SÛeÝ­š¨¤Y—£Ç¶ñUãøQï ׇà 7×­[—2Ö®†Y }r“ê¬Ò®àâ}©{Û^ÏP<^ðt*—©7X«&þ¬VPgû÷`«—£Ëô¤5Õê.¡ôôÓ{’ZÚ±ZK{K2cÖ 1 ɽ?»/ý= ° ça°A§¡«Aê8Ø¿;ÄǪÛF¡!LýˆñÚ:±5-Ì*»ú??f"Òl? cxXaÜ\l*9!eåug’b·ûöîKct7lXß•Dѯ±;V— }-LZ³^„Óº.ûÐã˜Éö¦Ë”L!KAÉÒ$sÜ r_n¨Ì×§t Údü677_9vìØ—ë¶Pv’Õ±Y2 |1É$åMšeÝX! BÀ¦ËzaÃg`Á˜%lYnJ{ lr*ãØ.PÍ…X4 ø². \¥Ø5ðê¾iÁq’þJªpµåkï™ÑÀáØp>P ¨_·páÂ4k M§yµ‚·j²J™‹{ +Õ²•õª«W´6W=úÈöÕ Õ&#T[W­Ð)Sc-o_y…që¿V„qÓE^©g¤Áõ]S{#í*Qã[Æ'3gÏ=8:¹ïg¿Hõ£Ç˜A0¾Œðƒ`©Ò„Ǻ3+k•çIØåŠ`‚éCá\´¢Â2 s`ʸ ÐïùÄ‚L2èÊíŠãêZºbÛT’‚[¸GÑÅaŸõQhxe²}{G7 Ô±lxÏòAÕ>õ`àì³]Ðfû½j½¦;MèWrÿ\Îé+²é²Ú"ËÖ¯­­í½ò€5ØÉQ+› %ƒuϪ©à^¨y€ k°±ÎÛ±x=N½†óYÇI·‹ñ"°ŸU®™ä1kÚÂKYKù5rTZéU½*Ê…cŸ7è Öä…õR§^CÔA9"w×®'“½û÷t+›Rî7Íâ8Ö` ¢Š:/Zšáþ†~5àÀsg7N8áºá²¶Üku‡–qEÕºB¿Ó›`®Ú²ÕÄÓ•-RaW°ɘ-»®Úšr é  è?°û=tX®ÒPî™Òã*5Â,Í_0O^·$[·lË¿tÕò8û÷‚6²ñ,á‘õû؆^ ö5Ü69ýíÚu•Ϧ`­klw½P)éA÷çÀ «––Œ`C­Oè´~=·+ å@¡a0l¬R@·"Ï•u*«Í¼-ëRÍÛžz—±jŒoÃyê9µÛˆ¯Ì1LTI;E æžY°¦Ý±:ÞI˜8V­Z•&+œy晩 `®È5+ãzÊ[´ÕŒkÈ÷nTleʤÔÕ hÕ;•eÈêXP ËcßBרÚI»L¢AËÂÙšâ3ÀñEŸl‹òiRÃÝMØçÌ-zfXòÀý¿.=w¨ò vÓA€º6/T‚á4Зœ plÓ¦OK†žÖšÛ»wO¥·iCWAÝA“!Gwé°I3ƒvÖMâ”-ëʲEI’;¶§^tˆ@ƒxfbÒs@7­-­TrÕ¸xÃ5B※áèd#ºHYÃ’^¥Š[ù2ï}Y6]z*³m§*hcÆè€)S¦¼:D?0!&Š ÊõÈÊ(àjéjx@–V»/0¨6 °…ZVikû‡B€Ëq eCeô,ìô:íÛŸ<µ««¸â¡8ÆaI〶’{Áøî QÇ®CšûN9F(4d¢5i:†ô\JÖUª?gWœ7” Šõ"ãtÙ²e™n–<7–«ä±m:£”FŸ7Æ·y%Dê Ìz£øn½]à*s¼µ|§^¥=êQ€·³Xm92É º<‘fSU†½M¸ êÁ#G“á#‡'ÓgLKY©¥K–v5m—q 6±l+–­HŠÄ¦òw;˯3ÿóž)²r0õ:­½½=iל,[¾,eþ~&ÕS‡‹@öìsH {69ˆEtô%bÜ£ýVíé§žîʆm•êfyŽãÁ¶˜“Š\Çj“Ê|/Þpq¬˜¶Q½ ‹!x«$eŒ”¹™¤ï>•Ù¶S ´©H³l¯°-–™4óZ8YÀÆìB¼Â-jK{”aÙ´ŒX.ü2CÓÌ® 0-°Y¯K’ìoìjKà ԣeØé0_ ¨…˜1 ,µÔ{øPêz°mWB€ÍcÄôÂï2K–íüùó“Õ«W§.ŠzĹå7­¸CI×·Žoó&ã"ÌTWTo1cÇÛ˜«Ws÷zÆÎåº"Œ—÷ìce»%xûÖÿë~À]c½ò—£+¡kÛ§¶§ìôC*æÀXé|þØà6=¼ýpÌöÊV@-µnŸKg—ÎOõHçáÔÌÊú ]ŒO†«0\ÚÚ&§†v‡›;wîHû«¦%£öï;&–:ßEÁÞÑM£“‘£F¦%Bè^¦N€þÃþXB%‹@èÉzmEY8}ýX» çO²!Üðªú±6Éö×Ë}_¿tÊ6§p;¥@Û7¾ñn–M&â ey%kiÚžãžtcr2Õ(—xÖ-JÀFj®;ÖòÑ€-«&›çÀ¶°äà eÿ=ÝA¡XóÀàÑ"X÷pŸ—E½ ìB6»&„ÄíXQ„i³L€VìIˆk>uêÔJ€ñÚÌå¬I9T²Ë¬x@gõS-˨•q7¯ø·bÝëÖ<Z^&hÞ1u–]—4«éGZ4☠ð—~ÄÈéü݃¿Ý;8C± äbØÐ4t€±eŸçžwNÊ’oÙ¼5YS…$¨Æ÷RÏvvux®!9|ôÇœ§§¸Ž5Æ+Ž‹àû„ž…7ƒÛb>Iãê:ãL{Žöëß LuRô~‡Y®õUõún^i³†yŒ UÏ·ºØ.î)ëæUZþýw¹&_”]mÀÏȼAÛÉ(`L¾üå/3c´³­­í]ò@  [PSâ:®A³.°¢ˆú‹Ö^ÊS–¡Z::[” ïqƒ+íN,`³Ve–kûÄ:€?*X=zŸ`+â"¥BlxþƒÔ²­¶qÞ䨠Òì¹ç1my“ ]ÃP®è €n 8+V¼À:, Š€_/9Ç@V•φe†{¼õ­fÑï×ã;õÈÍcMªé^PÄ%ZM|\gc7“ Û–wÿ:¶u¤Á÷ϳñÏÆÃ%—^œ–ú€ dbù²åÉöí;RWç0uдU˜´ÊHKãÌ@¼ÁÈìwèù¡{2¾´ç‚ٰЩ™oˆ;fœ*3$Y Î2’Ú5ËkÉÞ¨k:C´,«…‰+[p7´ŽsÙ6\ {Út±ÝJ|Û8™Ü> óÕ¯~µó]ïzW'®sm'‘|îsŸ“”v? h¡X–)Öë‰z¶¢ý^Ë0nڨ˩žK8ù_U÷?­‰Ö>9yæéÝÉ’ÇžHõådѳN%ŠØö«µ)ÛF£¶¡ÂþwÇ!‹•F,Ô€[Ñ–vY`['y±¯)À› ÚáyWè ª¦7h5ŒZµ†u€Ëkƒy ×ó«öðغm¶K‚\Ë6Yÿ:¹6Ÿ޹å–[ŽFÐvÉwܘHãdéžiµ‚5»Þ¾Ïr‹z [È%j…€ ÁõˆÁ~™p`[Y—è1ßkHºc1mÈôj à>rLǯY‘ÉË2nzÑÅ+Ñó±)Ó§OO«“ç%'d)ý2qnü}f.ëàîjV½AY_y½Á´Õcûz¹jzkµ¹G¹Ng<ÓØ@¹OíÊÎãªNuWú ª«êÔ±™;W®½÷ÚÐhÄŠ¤0X$V›oò›°i9ßEê‰sËÉÎÜ•¹¬*e*õ9íÊ,I™¦0 §-†Õm®µm»*Ç„±bqíw\ðÆû›àÆ`xLÆÂ㜬òCÕ+ …ÿ­·ù½ÉÍßÿþ÷ÙW_}µøÔSO-€¶ù¾ 1ˆX¶©+Vü™~à½ÏÅ (H¬ †]R(™}犲Y6váb†ÆñtAnÑ ÀÂ+3vÐad†­Àô*ãÌä¾²ÙôA[Ö¸F1S.Æ0\µfÇÉÒ]h/^ôˆQ·”u¡’Ä·Å1ÔÒÊI ,ü)åjMJ˜Ol[#Å´5*ÓÔU ÀâLz‚ú·Ól>§¦Æ ‹”-UÚò,Lkа3{ÐÑÚœn¾{´ =[¶½ÓR²?Åa¹’¸¤Ï¾ÚX² }Ùž^eª´9ÌrÙ•vÂlzÜJ Ø/‰$¶'À|ÿ9 Cž‹9Ûôg_××òc`™¿ÿû¿/~ãßH5nz´ÍÁòÃþ™DÔbôC^¦ÙïÉX6›Y‘µÜ8cÔË–°Åc“,ƒ,Oœëÿ„űÕ¨ä}"{ nVìÏfíjq‡:8ºÇʬÿïô˜¶Œ)k¥w:=s Šë HHØ p,„xQú ÏnÓ(AÏj7ˆF›”±mµ«4AÙ­Ê´¥ÅxÌ&Ó–vÙª¤ÍfU|¹b®sj2ãÙ;fÁâÖ±3†šœÐfkBÍNʸ4å ´yÖódßrTãÔô³COçáűM‹‰²´«òÜ]“ë°¸á$ -)¬ÄÙnZIìE’5¢]v‰ë¥ùEíͤ߮””}þx\ry-ìJ1\MA¿þom ÿ CܻロùøãK÷Þ{ïh›ÏË¿þë¿*fÙžnkk[/å\C7Æ`Ì$i,[\—©Ü/w,žõH]™ ¦(ï—+p|žÃ7N- ¤U|žaPå¹0ëãÍø³ærÍÓL$ëT+€ã> î%ÜÌp“"« ÏÊ–©FàÕuO¥å€dYÅ#ª´Õ­Â¶U{?keÚ’©zm[K6iœÏªt®÷Aç*“„Ð y1%ãÍbô?°jÃÖB¯ Úˆmk®´•Þš£¢ímÜO&' ¾Ñ”)ogá×›e«õ·qvÇb,ƒ‡¯xÏã‹—Zd6''@Ú„+òH†2Ƀ³Gylr±³RþC<ÛGõûô¶o¡iüÓ?ýÓ4¡@Û<]~ñ‹_¨?ü\£úÁw¬\¹ò/dã¶“¸±x!³]’eKZ34 ó&uÙ˜  Û¶6'-íε'/à=:-ËzÄlÕ°lÞµq‘áI…áÓm|LcÀ3&€˜k&aÚì™4Ö} RkgàÆŒf‰HLøì³Ï*ªT F#X76IJlN#²FxsÓV˶IXŒ$Ÿ…ÜGU?ˆo®}HàÖ•û š¦½Ìͨ‰k±± ó[KÙEJÀ‹°ç©8{…wBƒ6®Ð1ÝoGÆF½Ò€…É ]AL[÷c½\œ¶Ã¬O&a£8Nš÷…ëEÕ—+V¨ËW¨¾¾>²)ô}ÆÓ„»rùŠ:wþœ<;¨.^¾äÇã±ÝCè@¤£à¤‹3è<]mŠcÛ¤ªLÀàqŽÙ6bU››súzþ—^߯åüô§?-þÃ?üCiÆ  m>.ÿüÏÿì³lÝÝÝéFõ€íNb¶BºF¹Á£q ÒS«†isýVÆ0UÍÚpÜ ¢âØ‚´ØX¼Œ+cÉAéîq^+…E•q_f+‚keIüûÆ÷!ã¹<ä3²2]‚¦ö±pO`€`ˆàFŽ¢ô9«Û±Ø1·}ûöùF+¬ÚEµ¬¿ç2cv¦U5ìÒ|bi°lÕ2mõrƒÆÝ6j›¤ šL²±µÁ¤-‹›oög¶„ Vsk‹Wô=ã1mpeXMy_|ìZKpkf¦­É€·r)Ž?j€Ìˆ¶¯lèK’EŠº¯IV\Ö) –½D°c°ù,öËIh î¸ãuÿ}÷«Ûo¿C­^³šÓZ,¦Œ÷t©ëׯQe›Ý»w«÷ß_íٻǗÂâòŽm؈gⲾ؞ì™m³í-ïÇCóÙk0ºUïë;3ÿò/ÿRúÇüÇÐ6ß–={ö¨7Þxƒe>Šz&ñ]e ûf€ Üø•ýüh[(U6”ô’âµA¢²<óÇ}Æ °‡™+âÛäl³Zð”X™ÍXíYmµàêVcÚÒt6*Ó7ù H´›Y}h“}ÌŠ#rê‹Åo°±­EMÑIŠ­š$F¾YeœGHE¿ÉdžÔ¸D¸1ûV.ydVfÜr~Ñõ¡êÊ•+>HpÙÖZX¶4Ù·$ žuáÖ0å˜l,Èvâ‰'Ô£_}TmÜ´‰´ì×WÐm`Xo{µ0Enb€´’¨ÐÀÀ·Uƒ=”ó»í¶ÛÕsß~N9zDýæ7¿Q¿úÕ¯ÔÅKý)úŸMÌ›«Þ¬Ý^Ø–2Àä )Û:éá š¬ÆÓ¡·íÒëŸèmþ˜æ?øAéïþîïJ2u´ÍƒE?8øàš1!F€&È¥j3—ål\ĉ8‘´º¹¬ŸF÷®Ï¥€ßÀÀ}úé§Tž ׆éý°R5.ãÃr,Ø'ž%€*&¸Šw×zÍòo<ç¸u,¿hlÛ|aÚâî·¦'…\çR Ëm¤&¦«³K-Ò2û›#7Õµ«×ˆEÁ`.+´°}ä BW¢ Áûá™bÔ]¼¸K]¿vÝ+oe‚Ó9Ο&“ J1kådƒfɸ!£tpk"oll\]¸xA°q#‡[û‘ŠÒ.Ðew6¦b†@cpPƒXéŽ (QC[YSÇ1#ÏeøþäMqúißð¸jÊóÆÌ³P5¸`”ð7^íd‚(Ð&‹=ãžcC I(áݤl‘ÀFL¶Õze‘Χ¸¶ù´¥µýìÙ®ð$”Ý¡¼ \]>ø lï¢Idô}™Œ°€ƒZ»|ø!Å2;7XábÅþeö`XÌ›«–èI€Í¹sçõ@~R?vœô·®^»J²RÃ‹Ø =x.Ò ª Ÿb]×oX¯V®\©A_›züñ'ÔªU«ÕO~òcuìø1úͨ© %[sÄö `ÛH²ÝÅïÑwp~`Ì&4ŸS£c#ªýæjmk÷bêÀÆ4åIV¨dl8®dtÄckšš)®ªMŸ³Œb{Ë’Õ¸5ƒ@W-¬\˜Ý’¶çÀÍÚfÝÝ=êñ¯=AÏ ×0µßº‡”à‘²¸¹pÃ>˜=‹Òuƒ­ÀypÜ€ÿ1Sˆ·C\‚è±~Öœ¤Ä#š‹W„_àß!¡L7&c’ý§ë2q`\ƒ9,™'­X¶ZÙ7Y³çÏš’¬ ðÀ}÷«^T½Ñ6ð„ã$·i­bßqûÎ cðĤW& ™ª½½}êþûPo½ý[ ³l ˆ‰°ªD«ÆíX2´rÒ-bÚ`o7éçù¨¾G?żãßÿýßKó7Sº•ê‘ÞR íÇ?þ1:aÆ{¶™î%K–ü‰«–š,¥K‰ã*ØÕ€%P‚D-¥°`œbða,ƒ[j#Œqtý #Éû²;•ý*+:LMMS°°Ated¥a,Xò¢ǃ:­Ó¡©òv’‚,SÖ‚ ¬ë^H¶ pSØT Èmʘ¶t@_œmd­d dÔWúŠÚuç.Šm<{VØôµ¥É¶1,®Iì9@\ŽgÏœUÛ¶oS4€`xìѯÑq±of°ÐÖ9ž6ªÜú@ö×(\h°Ã,§ƒìG8€ÅÕƒ?ú93‰ˆmű =wk{G; ÷]ç>Î"°8ö%\šòõdßXZˆ'–Ûv@ÂÖ'OT‡" *‘¶-ƹABš™È@îèS}}ýjÛÖmj÷Þݪ0Uð«òÈ×õòó’‰e’h‘ Í*(ÿ“PúüóÏ‹o¾ùæ-•pË€6<ÌŸüä'Äêãmww÷ºóÞδ·Œa€Qž¡ºôêÅ´I† ïy†Ç-jÏ^y,NÈ/(–-.ëæ2èAÀσÄKÊ””iöÓ¶ëeŽMnž„l[œª a,›}?L²[Ù¤nA3¾¤ȶ.–Kd÷Vkk$¦­ÛVÃÜ1»=)Âvnß©6mÚL™˜ì‡ÍéS§}fÒþè':MˆQøáº»°;wïº[¿R—._ô]´R¦(ˆi“žŽ-Ã9¼a*ÅYÁš¡/9ë¦MÆg«™5€5œwXÂRÒRRIë·ÖYC”[Áó*,_¶R ô/!=:„y9t„ØÈ¹ê£8.ãáÇ)v`zÉÀRZ/\<¯&³“¼ñ9-×½ävƒgÇàζ{4à‹BòÆ+ô¸~Ý¢·9€]"d  íV‘ÿ¸e@ÛG}¤Þ{ï=v–À²é‡–±Õ·%3cj—lEZ5Eã0m²3ÚºAîQ{¡ãt}Ž ˆ*sßæiAEºE'' ~†Ï´9¦¢‚c`x`(LêclBµ¶´9Õ¸£@ZØwA€Í+' Ø6°Õf’º˜U{Ò’&°JËàß*L[£gŽòd‰K_?e/Sø=€:¤Îœ9[11­µý!žêÀþ”¸°fír¿nÖ qhè%°­uÉ6¹#ù:89 ¬%CÃî²eóT¥÷‚¥“˜•b@UN)­x¶¤2 qöÍ2,+P¯µ¥¹•ž5Dpq}'Oœô3ÖçšDk¶ùD³Z»n-=·¾uíúUÿÙ²~h‡€m( AˬSfØØžJi,½vêïÿH·øës/½ôRQƒõ’µºÚæxùþ÷¿Ï,[F?œ•z6ö )£!éU©zÍ€}çÒ5šF¦hø³]£Ô uÃd?TšHFM WU ˆË´E:ûXê‚ï#€”)3R7šž 7:+Êáà"šHí‡`g“©UhŸ·Ì0•Ï$,~-(ùÀfÛd 7<›ÅFõ=Èàe•• ª šqms%ëQo€IJ‘f_Ñc\—,£× mWN>M€­' ¼Çާýv÷ô{rIÿuzð´À¤~[P6¤ ž¤«Çà ËŠ°Æ›Ü¯¬øPÀÙ³|oÂúRZñli°lAŸs XHd(©®îÅzrêMŒÏŸ¿@L›ëZçb!W©>ŸÖ6O”—²‘ixýŠŸù W6W.½Q\ëøIŠ­IénßÖ¿ùýõ8B¦~þóŸ—¾ùÍo.€¶†¸&Ý_y宀0ÝÛÛûUÝ@–s ·dÙ¸sË-Ö(rQ¯Õ°jQÛJ†Mfò¹ÆIB°Y6™b•-Þ’üm»gÙ½ × Š7C/©Up¸f¸ö]-FØO}±ÎÖvÊvƒ^ä à&Í™zƒvì„í2•† ¨ D¹E¥+VÆ\°Š7Ü:˜}†M ÂZ`«f2±À´5f©ª8쎋eƒ ›RD-OORÒÈáÊä>X  Ò!ÔÖuC¿xC@:ƒ(ŽmuõŸ°÷kÌîV0o²¤–+LĶ­aö,.`ª'˼±”_»Ù‹áƒ0˜üz>çjŒ H Á˜6ç‹°„ìg7©ë>ñ8Ɖò™Ë±Î‘Ez¯Þf—Þæìæ‡?üauWoé¼m@ê/¿ü²:{ö¬¯ÍÖßßÿœ+!½0’·¿«Óf»Fð¹4à ÿ³Ofœ‚2FÃ’\@Nþm‹eÚ5NÙõ€N‰ŒQÌ„± S‹Ó*n_ÉÄ’ ¸èeŒêûF«b/©¤0¡ Ci”î,£b¡¨$ˆYs%5ÈgÇÏ÷m•õ‡‚ÜðÕ»Žãlª¦-{–@sõYCm}g rΟ?O¬s-÷™‚øuŸFpyÐ6Sôàܵ¸Ë“ïhë ÐÆIœÙîšh$yï wܯ°xÛ° N’rTq@Y öy²” _{[»?Y¿võªmÈ1€’2°ÍhŸízr}ãæ ?†‘í¡´]6‹Æmœ™/-+¬(¯×§õ1Úrï½÷Þôûï¿_@”z|  m<¼Ÿþô§Þx¯HTvcWW×W]´ùU¢uY> *H†!Mgӻ쳗%>âH|0 5×]13‰Òä ‹o bõf¸õ¬ÿÌ.î\L ê˜m{¨ €'lçc™Ú+ð2•&'&ÉU€Ì±l[Ö+…£=Qð\ \§ÐUÎ+޹lO2™Åuol¶ ¯86«!/â2PõC_T¶­Ñ™¶4Ü¥Ì2û±­í¾}âÆÕÚV,ø€[5*ƒ&8¿³Ë’ôZ˜ö›í ¨¼ÅeÏ‚ÀZ\¦­žnÒ¤1nóÌJ{áþRâ™=Hi·¨«mø-ëìU9સ®VªRá¾v´'Üù:]DŒ%¹Ðöÿé¯kûŸýÑ~4q²–>±Új\ð<ˆ*̲ûûûŸÐŸwá!I]Ødñ䤂ºÕ¸H%}ÏñlÜø¢’ìkáMv=ÄÑv³ˆë{—KUž3¹= ^QâE‹TG{‡ÿªS§AÅ·M™VO}zHíŸ4Ç$º¨Ã¿'™N .áZ©¸×2–"¬è}6ÇkÒPóì÷ ±pcTd¢~×횘jD06›L[£Ä³±=A;ƒŠmÄeY¢šýã®/í"÷çõ«×ýÉXÐÂY|Ž-zU)øvʶ»qÀZj6Üõ]=ÒØ·üœÝÌü¬á%Û’ËÒ„”íZ£.ã&ÎÒ,ÜNÇÆÇüØE&-\¶LÖ•‰e/,“ö8ñËL¶ê×/écüLþ/~ñ‹¢~_Ró|™×  ./h³]ºt‰A[sOOÏ·¤«SfëÉZJ%H÷i ,Ëfë³%jüžgÙ £›‹e‹Ò0“ E¦Ñ a¦·¸«[uuv’m«<-'ÎԈ¶*`K#Ò€èvm¼²Æ%ÙÙÕI®RÆ6J/î¦XŠñ‰q*– ÷ͤUA¡ò~ãúÕ ÀêŠI”í˾Ç2ñ…ƒ¨Ù^ Û`~QëÎ…€nÚ-Šå‘íýÛKßT˰¡ÜÓ®»î¤ òÏ>ùƒ8¿€ Û ÉŸ|YÉ>L:)àJã·Q aÌ\Ð3ª6+5Îç×ç¹3ÄVE.æ2[4Ž 7WpàöÙœoVã™qß}Ε‚Ú>{7\É`¶GC&€é¿¿e@[NZ)µÚær¯üÅ_¤gj@ܦÎÎÎûÙ@Ø™|.%g©õÌu ü 4Xt5N©*{–Í¢‘L#Kà ã?¢\žaçÎ3P؈£ðj¶“y‘ܤÞý×@³'€6 Lcgg1ø“ƒu•¹Tfñb?–‚fx™Õ¹hÁÄb‰…€ ÄÄ‘ëÜ^u…UY( Ä¶ˆé‘퉪2÷y„‚„'gdBñ¬_q>,>šÆDÀn_I$Eæ[\[’cÞj¥ª\¼¬‰…0Ó’͘Nβa×X‰2DK—¨?ú„&r@µk9Ë…â“ò9ê{Ù¬Wpž¥8˜½‹Ã¤Õ ¬¹¶ MAbÛ.Ìz2m2.ì|6O÷6 vªžÉ/¡0œKÉØTG²Uœ¾911IIb2Ö—Û'ÒÅ"â’`q‹2A•ÿº¶ëKôf—Sâ@[- >½ú®ÑÞÞÞÇ›››;8ÐP‚1™1*¶™A²Ë¥Þ\_‚6?FÁŒ¬ø¼íßòuÁhg©1ç¨ó¡ÉÊ``|ŽY5ölÎ×CiŠ™â¸p9ʘ”ëËQvhÁdv ù¥UX¼r.ʇ°–ƒI¯JJÚp2Š×cššÏä©n¡WK©ÊGÈFÊëé%~chA‹FÐ@Aß—¨LKÞévæZ Š(ËSv,M€µÀ´Í.Ó–t[ö —Ëû‰ú_±”ø^q-^TLV§OœÒûÎV„ º1cËý„ xtIëÔßV X“ ¹Ê8g›…g0Àúsl3íLÖ4™¶’¨ê@“ô\“?~’f,›”ÉÂ5!31Âzm3:y^â3¨£I½¶y»?ÛCí&8îc]Ž/ŽÀg±4]q}®ûÆŸó³± ¶­åê9¿’†þÍý÷=úõCîç;Ûv+è´1hkíïïÚ¦‰¥£|ÈÒíÔÉÒiaL+øËX(›sul™ Ï¢²öŒ‡¾kmS«V®R‹»»©¤K–™8Þ`÷øs:>ó‰ò]„†Uò݃˜ý±Hâð0µ›7G|6›Ýœíçðˆû‹×öávÕ¹¨Ó°„­>ÍÑ=ðš‘wº™¤VŽf‰×¯]S—/]ö’2Ú[+2Um@s“„$€&®xólª¦mv¥=¢ø¬™yØu6ãô–]ðbÒ2j݆uêð¡#ÔÇ»º:©Øz§žøôõ÷0<|ð°Ç¦e²þ$¦r‚’¥á‘änŠÓž ÄŸz‚5i7%@a–‡WžD»dCì¾ÌÌú:À Mr…&=ù®ÀI %K òµV×hY¯4G¶iqÏbÕ××§úûûœ#Á ™©£y1ºwð,_±Luw/¦±vôıêêå«vŸ=<'†§fÒÄ<ÛaJöýd„í’íJÊeñ÷ÆEúšmóÞE:ßAk³•4xÙ¨ÜÝÜá\Y|üÀÙÈqa´x½X6™9æ ¤kÔN¬ ]›Lž„f;»ŠzÆÔêg˜Ê›rææ8^q˜"u˜iÒ?›œœPc£ãêæÈM5¢Ú('LO§"¢‡HºqŒ2˜ ÐÖ ‘N}O âÞL‰M*—7>“ൌãʃ€w8 ðH.ÀëXÓæüŒç"){6ò¸o¶ÐnµU6d›H Í0û¢0mõÎL”R<Ñ*Åî}^³n Õ8ëÓƒ5€޼rõ _(5ŠÌöñgÔß èedm¹³äó9oR7^»ÌGÌšì²ä¯¤?=‰ n-,›íÒàX…°——±x²¶ŸìdŒ&¼X¹¦æ&ÕCb²s|Ã.:¯Ç¤•|)tPŠí2"ˆpB]þãFmœËÍxçâ©pT‡×2ä¦-Ï~§fÌœdrDu´?$A¦i¦8<ñcHl)%çq‹%üÉZÞ¡(ûΓd»Â½Ø yú¼à"Ý¥ïãë/Ìké|mYUÎýº µM[Ë%hV×=Z €³™6»ƒm„ÃŒ5S»¶¦,ç‚ÙÀÐùs9¢¢‘5©ŒaçàR?³ËßM=5í³ä6˜*P€þ”‰Ÿp©WK®ñ3‚¡C-Ã! ¨~LÜP‘èîß°0hªiðׯcªè]_Å®˜U3P¦òCIgxR#F?‰dÙÙÕì×cž¬%Gi±mó9®­Q˜¶zij¹·5q—¥p>'|ÛwnW;ôŠ>fÛD¸ÃÓ†í–-[¦>ûèS5xv€X4ЇÍe Ó–¡¸·Œ_áÅ9™sÏå²°œ¦Íf$¹Ä‹¹2³†e``@Ý}÷ÝêT;¶o§ëíX´ˆâzq%Çý›4Úhg4xÛ½{·zçÝwèûg+lX7¸Ñß9&ØuîA.ÈÖò;SÊTÝ/vÞq›Z³vuÅd€h÷g»ÕÉ“§fxf\m.i=O/6¸œ•oÇaGX¯üØj7-%eD"oÿ„~}Ãà…©Ð67K†M“nh뺺ºîã`F{–"k–ÉÂ~t; ¡Z•ú¤¢º2 ÂÖ\¿—†ÃUA·£™ßø„שKŠ\þñKîó²ý-ïeüJÍPïÆïÂÅ êÜùs4‹%ƒZœ®< êaø– ,QË—/§Ø4rÚÆ™À2_—¢,ÎŒ÷|/x¬,Z`Íâü¼`Ä]ƒË\£[‘m›ïL[übå[î äø²¡“¯;îºC­\¹‚´³®]½F.0Ø‹·ïTï½óž:{fÚö굫Ôɧ(é:ÌD“¬±l–Ìe4=ÆÙ ®aÒBÕ¼«çƼ¬EÊ6˶­ÛÔ7¾ñ õàƒªå+Vx‚¯f¢z‰JB(œ&ª¢¹¬/ÛÜì uoÞ´YmÙºU=ýôÓêèÑ£êÍ7ßT¿úõ¯(\‚Y<ö ÷]ƼE=cÛîW”z*Uß÷0Aß¼m³Ú°q½€Ø6á\Ï ž«(9V¯¾ëyg2å#]÷FÖ —¿s•oäD³ý×ô.zõz]Ísé|gÚØ5úndÝÜeê°ô}ËìQ鸒qØTØÝõÊÆˆ+(m÷æ±gÚpLTfÌÎ GÂÂÏY¯Ö:Ä?OŸ>¥5`ÁNñ‚ì3bà '2rS­]³VõööÑ€1ã>×Rß´š«•l𵇰Šì2‰*=§ŽmZÌp½AÙ|eÚfÈU³MyÐB-ùŠYåŽmärTpƒž=}FíýÃ^2)lwìºjãž==HYƒ°/ˆY‹ÖI.Âe¨zƒaÖdŸK¦Í£¦!¡355éË4d²™Š‡4ãÛlfÍž”3»Æ®XÖ¬Y£žûãçÔC_~ˆ\ÀØIDC¸"ÔáâÅKêÊåËêúëúúG=qôS0ä-­ËÕÛÛ£úÔ’%KT_ݟիר?ÿóïª'žxR½öú«êµW_SÃ7‡+°¬¸nãmAôjûÚÀêÕ«fj§ÅøgÒcì'~EÒ@œÉ1vŒ›«_XÞ­úõ6½ÿ·Õ}šÜ¾ÇW'O¢âæˆ5å²IAÏ÷À‚ÝË–/SëׯSëÖ­SK–.%`ö'/ü©Úuç]êÅÿúäÓO*äD AÄîÒ(–M~Wa#Kª¦¤¯¼)7fWâxŰ’eÒÛƒkÀ$‰nœ¼ÀqÚa}ÏN0ªåìºrü“ÛIðhkÈé¿súïGõ6o©yî"Ï  OCÛÜRÝIàØ0Fá¶Ô‡|ÈòÁW3@V˲¹˜6{À;hÙfÄÀ™øÖ KzŒz\÷¥ËzF{éb…{Úåž•€”; \9p§rF[1Á̰>×$¨úŒ ­Da«vW#ûĶÅIÔ˜Ë,Ò¦­v€æšä•º¢·ã¦[&ç܃^³ºaírök0S,zr>¬Ç…Áì \£e¶­´q»&w`a’ÌOaÊ ô—lJ½5ÚlÀÆñeˆ[ûS©]»° ¸ý©“'ÕÁƒ‡ÔÉ'©nª-¿Æa;¸–û‡d„ƒÒ16nÜ ¶lÝB@nÅòåê/¾÷Äì½òÊ+t_¤§Ä‹ÌGjºÙ¬óWÙ—ð{€ª«W®¨¥KH]À.«¸aÓzb¹jKP.²îo»c§Z»n­ïÅ:î<ÝÇPÐÆ o@O–\UÔÄÕŽkc6·R¯Ðw‰ú«q•~Ueõ15]¤ó´1ËVÒ³‚;õ¬pµ”î°E]%—Æ%8¶¤¿•ï㦗íÃîP3@‚b—_¦¡@Œé•+—ýZrA€-ˆòÆríú5u]¯¬/ÕL›_W&ÀèÊ÷œ©·ôYµ@äV«Œ0Ù£Õn[mFhÜX6û{,4ѵL6²ÏÃBgûŽmjdxDæW‰ƒ1m´uËÆ2¬)!Wá… oŽÎ™õk2ÞɈ®û€mýºõêùç_ ð„ì÷‹§N©û¨#GŽP<Ÿ¬_™¸Ï3€ÓÇ;¥÷‹øÜ£ÇŽ©­[·ª-[6«öÈÃè×Åêçÿù³Šbåø=&ž.WiÓ†˜_¿JA5l‡ÞÏç{öQˆ X2×…)ÿþÁåûÀC÷«cG©¡ÃåJ ФÓmçÜÓÛM2!=½=#-7\[)F6+g®Òu ×yŠƒ«ýMð¥ä‡-j®?»CµA¯{Õ<Úï -ÓÝÝý°«cÛ™7®ÒUA%jpq˜69[©e?R¼ÕÙÐ-–m®Aw*ÄyÀ K¹‹8…Ù+KšL©C7T__?‘¹*^>P0…¶¡ç™|P­¨ù¢3mQ} MíµzƲŸ‘äm¦§³†ÅÈE‚¶­Û·Ñà¼oï> ¶‡(w3¶æÀÍs“æü o׆nbbœjܤx¸Lh2B\pÅÒ±ç ›lÖoTÏ<ý´êëí%Pvmß¾ýêÜÙApÒŽÖÒÿyAlàéS§ ‚Û±c•C·Ÿ}N½ôòS¬œnavÃNö(qö?¤‡’Fض7õ¦Ú¸iƒZ³vêZ¼ØK®hi&&oÕšUÐ-÷²âÇ`:×c.zRU—/^Qǧç|sØÓÂŒª‚À ®|lBç/ iá*UÉÇf¯›Íë×½¢Ìåƒæ¥Ðî|m$õ¡D§^‘ ®´a[i™¥6jgIÉ8ñsÕìÇÕɽWó?“g;› Íu(š÷jƒ²0pÒA«bv;>FÙ]ííms FËÞèRÄ3qM,â<÷(~ó­i­ç™$غZF®VÍsõíŸÌ3Å ±eh a+àÕö2ˆcC<[¹|]U!ÐfâÚ¼·r '?‹ÔÄ©¢þ0ÜcÃ6>#3>¨&g­5He–(gˆ¢Ìc>F™›ÃT;z”Ü¡ÐYã½8d‡ô±à¶B d¾í±ÇÕëo¼FIXØåö¨\”¾÷aù’i+æ4`)x)W•£L)îÑþ}ÔÑ#ÇHP±lÚX§c™ÄÒì,L›“é™U#T‰ö‡rWE[§+¾¯h»ªd2ì‹äf·UO‚«²FT›ŽGõï¨ßÔ‡[ •ZZZiÀæZÃ-¦Þ0â–ÊÀ-O•Cf²mY¨Ú \ °víÚÕÀ ó$`,næ(€ŽÏú’ÝÝ=ê¾{笠±ÑÑ1uìØ1uäðQ³‹ý#& ÷‚Þ87è¿!îíÞ{îU¿{çwtž™²VfP C5îĩҔÉvÇ.Æ9?î>’2mA`/Î`à ™”bÚÒø=Ï&¥ÐdP /×uÛ:|síöužw)Þ}‹“êÕæøÞ„•™MÆm>ƒ·¤m<éïÒÔf =®a?0¡awš«}°ì •r£¤ƒ²þX“©Œà­ÆUê·|EÁtôË›##Ä`AB¬)ƒC^ë{yÍ<ùæ °ó8çÛvÜFÌĉ'|À6›<w4x„}n==ÝjéÒej˦-jß}~ ë·UHaEŸç¼X0²‡X©ntük(ƒ»»Ifs‚IÞcNÙíÍ€MjuÚ•sÖ¤î)XÞ<ƒ6r•OQ[¤íŒûîôœ#‘%Žý±Cœ$û&ÇÛíj˜¶•úózû³¸­­­1JÓgÏžUßûÞ÷—2[mV£ÿÚ×¾¦V¬XAïß{ï=&¨ ÂâÅ‹¿”P`ûºm³ZX’ïk•`Œ‘±`¬ìŸÜÔ˨ù‚‡b–îªÀWv`Àmg9áÚò†boˆ˜6•It/ƒÄuãĸU«øEgàâþ&ˆsefzÙ4±Ú¬6oÞL÷qÙ²å”=ùÊe:O„ŒŒˆå&µm Š2…ÓE |Š^|X\ûÛl˜S/¹¤ÉÚLµ ‡œKcÆ™Êl Ú¦<ÐF"ÅS†‰-xåQ‘§8é醶¶µÌØÜ„{{î ¶äa±l̲>¨7ûÜF ˜cUzì±ÇÔàà`C$ëÍkІ™—W^y…]£úÐ3©»¥ ®«¤EPvIš Z¦-Éï¢Üf®äi,Êõ231m™Y·`žhšÚð9bOP¼:G0*x¶xþø`í$Ûµœ§2*³ßÑʬZÉ>Î&*¾êr÷&m.˜f±øÙH³ö¼fÀÕã7AñÊYôü=ÐÒ¤¡óÍþ@ɬ^›àM2nä*­nM”EŠø¥Ã#ÄP ž¤>ŠìI)·Àçe÷ºî¤•p,lX;;:©äÎvºa¨æ0×}€|/_Ö÷²E­^³Zµjp6з„Α¯öMºHå}ÉXu:±Ýä¸Ç,²+3ªýÓ³1Šeàf@¸`Os’iËf- Ê1‡2̈ï?ƒ6*Ô^ÐÀ-W ˆýàóq³¾N›eKâa°í¨-ʱì’Í3 îý]‡þɰÄþçŸzê© ¬±ÚRXNž<©>øàeX¶R{{ûVÝWÉ„[ ÕV›ç>(1M€ÆŒ…eKÆaÕbe[r+¨ä‹jlWš“ÿEœŽŽøq'P&ïëë#Á\€6m¨IŠ™*Ü/صmðÛzÎŽ¢÷]Ä$±D±kIÜFh«`õfÒj|vF;Au„,õ/«´DÌŠ·]΋O3qjì.+¯ÆUÚ$ݤހ䃂W¯]%qÚ³ƒƒjdä&Än1 ‡m9A Œes±8¬ƒT\Ûú®Î.º>¼¿pþÙŽFiÕ@y+È¡ªâ-êT×o\«p“r‰;W’n¿|SŽö9 6«˜ n ؈55Lª;+Ø{nù\®RƒO(˜yªð|‰9«K  Ú `ËitÈ+÷°véÒEªkŒ2s8& ÆbXóc)‹ûÞKj*ølOGû"BÍÍ-¿)™ÇFXðÀF®lZ©ZZõ¤SÛ/Äz gŠîY“©VÆ´aýž&û‡g”˜æ¶–JðÝ\ Ò+*^¼ääèHûÆ Í›x÷ÞIJ™¬QŠcÃ~4`+d ž,Éø¸˦w‚JAÂðImµ])ÁžÈ¤|»õŸwêõ 69qâDæøñã%Ü—ЖâòÖ[oyã£ÚÚô`ÿ VáÀõ€mmœ´ÀY’ý$9¾«”Gä12Y¿³5‚VÎ`kù²å4Ã;@ +ÀÞ¤Á€xŸm~Ò^y& i‚¥K°g¦ªŸÊõ(e x³DYv­–­– È[˜Í WOWhÒí9ƒÒ“nh­p¡OuóZ-ÛîW¥æŠ"Û9ês4?¦Â餦W€!cAk‘²$''¨`Æ`®>¬vi¡4ÁK71ã‘àq£ÃÖh âþnègÁÉ8ï¡›7|VÏ.(!Á¶%à¥Aõ举ÿ¹‚ÿü¤‹Äl-•Œ)³mÍä"mò¥\ò¹²›´ìRâá%ÏêyîQvýgAnQ´0 Ù‚/¸Lß¡”˜>®Q Ëæ²²*‚Í>»Æ=s]÷ëß¼ˆMõv…_þò—ê¹çž[mi,ìòüíoËñlýЗéÝí®lBùj—‰’îÓ¤™{i±lòó sˆë™ša'i¦av½LǬZ¾|…*–¼:|jXñ7³j ÃJ*]Ã(t´w¨ö¶ŽÐ’)³q©lË2™dñHv¡å(V-ª´KµÏu!!×ì¾G@ú’oõÀ‹ú¢7n Qaô¡á´íøä8¯Ž‘vbÉÐ÷¸î¨ÚXNc‚\ [ªašÓÅì{6° —²ÁVT¼ZÜí%ˆÏ[[Ú|Æl€Ñl´mÓE¿€;‚ù£%î žª ÀMHîê±<é—±»Wf×ÛÏ\ŽeШ„l ~Ã0Êú外q©dÛšý˜Æ¼È öÊ”å¼Re×f\¤2{´83žA˜¿BÁcþð7Ä–!ƒ] ]Ù€­Ú‰Œ,YéŠ_çrV¶4ß!¾S¿EJqö7Þ˜~æ™g@[ 4vöìÙà Æh«nlö€Í‘]vU›é¨8K˜…ét3þÍÔ¸¶É̳d¢œ7Í&uǵÁ>‡ã`R¾NkÄàCiýùfÛVšs—/gŽúÏSEβ‘«¥Í¹& · @Kó\Ó´ à\!°‹`Äð7ú}c˜›Z¶”àfpsx˜^áE<ì-ÒvSž¸*ú¡ÏÃ"éXn2òØ,á¸lÒ%•$^-îö |WaK«FõõàÔÛ†áÐMÝ=^ò˜ÅÁ3ƒ¦”Xø3Ľjoó²ß3¸Ÿð„a×`]û‰vXðluvг,LRWÆË mn*6ÖÛƒë¸Ìº5U$˜äEÙŠ,Ò@÷¨Ébm¶¼زÙIúl¢0©FF½8do"­ Ql÷eR;h{Ù˜u“,›Œyàm³~]©¿CZqö“O>É\¸p¡$ãà@[• ïo~óe`>Î7£g„wñC`´”ö.Q¶|˜iƒ³¸L›Ýàªaù$h u£ffJ~4BJ3c6©:<À\:4ÃoªÌ,ezŸÜÈͪÇF»žÁ£qï•Ì~ò’>¢Á– Ú’²lAŒm£‚³¹<§Z“æ ÀU Þ¸˜a°DØú³a˜!‹ž#¦ ¢ªS¥Y¬¤ßf\hím‹T›'Œ XCûe6`` +þvéH¦Ï&m9%=ä›üÁ•RêÕÞØæâþ­Z½R ,ðËPQFçdA ž -6ÏlÛ¸n †ZšZHƒ¿£¬PK[Oj2JíQ0¤ˆ+ô²3ÄŠåô³*˸4Yº{ö*cÛšL&)g‘ ¦M< –ü(gz,d>Ø(St|\·Áët^Ù|v`c&,ª BP—2P.QbžÐº˜[ýy—þ{»þl?šÐ™3g2{÷î-a¬A^m5\ÚЊ[»ºº¾d?,éB“Ì›ëÁÛ¿­ËÞìj Qúk.p»xnEº¸ÊTˆ"F¥V[«kQ6ÄÄù Ë ³|0nœÇô?¿‚ ¸rùŠºvõ*ýŽgùi‚2WmXº %«H1ß³RùÔèCìÆôTd{°A[’,Ò4b0o%@Vë¹6²H’ã¸ìÚ\•p893›™7ÎÜ^’[R®¦ l£´ÒÆõÇMŒÀ!»CƒôÔÒo2ž·Ù”]bwL=ì‰ãöö¨-Û¶PY*隣X:Ø ÄbÙ¥âþ&}p—óuïJ3XDyýLPð3d×7þ†mà ¢í¯Ød²€}9ã*m)»I›-ñäœIJð³Hg€6e£²U´e'½û¶z}”½«ÿa_%U®ÄÀYÎIË@Êçá*ÏÞ6y¿lר÷èþ ›éÉMæw¿ûšÉ Úps!óðþûïûñlÐgÓ3›6H‘€Í@”µGãÔ8K›e cÚ€]p“³ΆíSØÓþß±ª((é^ÍÙ¥j¬½Yñ8uz0¾té²êèS‹»“AA¬D1Ñ.ëï º>)ƒ›“& ˆó†ÛgÚÜ??cjÊîŸ6JßEºåÙe…±€°1ZÄ>4åA \9³.Nör€KÄ¿ Àl6Y¸F’qmÃEÔÑÖ0ñãÆZˆ`ˆäo8ÙÇ5‘” ZX ³oqõ×¢ÞÛt>g|‹ÚÆöä½f¯€¾>Htlß±M­\µ’ØþK/©¥Ë–úƒ<^?ùðýùeM.Ü6F²þ)\pG²}±‰yùw<ÁåØ>Ê+—¯RüU•i'æiò5—5ù|–­¥²êE³—” A[ÎèµùL=Ò¢qNšÑ¶y̰kçΟS—/_òÜꆜȰÉç\«2¬î¨]1Á÷kö.ÝžÛõÛ›øè7¿ùÍt£³l ÚИ<Ek¿t•nˆ›t#X!ŠÔ_“Ù5<+a¶FÖ,KcPªµ”•=àºbÓãÕ¨øzaf•±?håÜ, px’ 6:¥¯œ-˜´Œ’îS/[AȈJº=BÇ'/„e iˆ Q|€AÆ€ÎWïWÖ¯óŸ[6¡š¢!¯œ ÅNP€"4d7áá~±š÷´i?\4¹|‹ó&8œŒG÷çiP ¢êe{eAPWy¡¸Ù£IâÙ¾èàìVpq«(Øß1ØBÛØ"­°ŽZÁ¶á=¨Ò#Áñè'ø='ð v>i»Dƒ&/¹l^q$i”»-É‚ëÃ=Ù±c»Z»aºzåŠzû·¿S7®]§àú{ï¿Wmݶ…úòîÏv«S§N©•«WÐon\¢{Ö?™Q“ž—âTÑ^aq‹SeÀnK–Pæìˆ~Fì²m5Ü„KÔw¶°€r¹Î¬¬/Ëc¨ õÙXâc̸¥ÏŸ?¯Nž:I¯ø®ÉLb9Iûá’ciõW Ãl°¬–#±aš·êóX®¿:‚Ÿh¬áÏë@[• ä;# MYoôN}³3ÌRð¬ÊÖh‘™¢A±\ië²%aÚäL,Nå¼Ij+Žó5ãÁÀNDŸ–TY%¨’qoB°æ=Ðæ´¶Ð3i#É /Æ¥Ù¤pû .iua 4éUC(çÌ”jà%kÆ ©÷ ƒ‚ì·áadÀyÁ×`›ô˜2ÜGÐ*žA71~ÀdNiw6vâØ®™tR÷è=k4*f®Ü\I}Ç6í,6Çò Êƒ4ÛT{Òd— s}†±fµ‚7xN¶Þ¿¾ÚY6RË–/SÛvl%ûñÁ»ï«+ ÉJаÃy  «Î®Nõð£ûY—##£êÝß½šÝ^YRÑ‹UCNùK–ŠA À΀à‹5ñÀ "”䦶wCu>óªYƒ·¶\¶\K•Y¥ÍMM¢TYž®‡µÚ”ï©ðX6œ„Ò/]¾D5^Oœ€•Œ¢‰t•™Áý†±D¬‹L×w•cÑà f-î«LïŸom¶äêp«MDH“MKÞ‚>gûi³Á:™H@–ô³¤`-(ÉÌg½U±&›I6­W«V¯RR>?àY7‚ߨæŽ]·« 7Pÿëè÷cʤtvz™—A÷_“çÐ!ͧjgìbcÊç‡Ïqîˆ'놺°3ê:¸?ÛÚІL~x2pÍ~­Y9&ãÁ»{Ô®kµ?lìö"½)ü™­ §ÿÞ¤_zõz΀¶†w‘·2V¾¨®î›uCjã‡a£kà‹±šÏí€Äz¹n’‚—w)ÓÎì"Eç”ׇAFÛ¡#ÊûäZƒÜÉ O‰@~ ¬TÞfl”€LjÝS™^Õ>ËÀÍ Ð¤Ùäå+—)©áŠ6,(n )®m £ÆJáRŽÐ Ý5»ÄÖÚ@ØŽ3dà€ƒœ«eÚjq÷¥9I Jžq¹›íHºvq¯¶mÛ¦n»í6ŠÁìÏê±ÇS¿þõ¯ÕòåËé»·ß~›ˆ;3îãû￯V¬X¡vîÜ©~ö³ŸQ¦ñí·ß®xàj§o¼ñE3(Â1}ôQzþü1ղݰaƒZ¹r%÷½÷Þ£}á\~ûÛßR›Ùµk <8¶Û¾}»zñÅiðsõ™  Q–Ì®9Yû´šX·4?¯–u«F"Äö$]À¨oÚ´É‹Õv‹ë¶úýMÿÛqÛvmC6Ñâíß¾­†o û†þ {èW‘Ðï:¬öüa/±VüØ/¶ÎœG=ü íëÃ÷?"a]¸s>`3²HøÃšwÞÐ6ÃEŒOdœ.ý°>jeœ•ŽW°n¬}Éûƒû“H£ `D÷›±ñ ²áèe×Λ >qíèŸl?™å“¤ -c,°CwRB‚ÆŽí ²ÍÒ³%%šõºYÿý‘š'qmÚ˜eÃÚ¦AÛNév5ǘŰ™4YÇR2 i2lQ¿o̶„izD¦ã%3»ø;îp‚nÀ0© >Ð)щÁˆ`övñÂEoùËy?C Á¬,RoFˆ$NFn’ñáÀ9‚Y¾b¹_‹ KõHvÍ5 ¸Oi„øo¨Ý7Ž?Á쳚¬Q¹º¤Bê ÐB‹€ª ®‰ˆË}ŠgÀt×]w»ÀuôèQZÑÞ §à:ž#Úáž={èý#ñ=ܨoŒLu)x”äzÖqEŽë©åV ³6ßX7;¦—®KÙ’ªµ† lÂþ}(“NY±¤]œ›6¨O?ùŒÚ \¦ˆ;ðù~’]Á$€˜üº>Û&&Æ2iÅÄ ç ¹Ð¤ƒ0–-è™Â>£`|€=æXM¶ƒr?*nOZm9->.KJ¡oâ8잓藆ݑã•ç·.2GùùìÔç Ÿž.Òù´¡‘­Ðë’ rU2ûKvh#âbRêíb ŠM“ÁÐQô®«Ózõî&½ĺù€_K4jµœÁ…`ú™ÕÃÀKYwdvI曚êê&‚¼ ‚bÇôqÇá.nïP«V­¢Ù1VÌ€°¹D]iöAY¼.PeƒnÓÆq2å<è·aí  ÌuRBØD(ÈÏ¿Ã@‚@p°mŸ}ö­Ï>û,0€1Ü7×@.AÜÝwßMnQÅ`éúúú(f ìÀ¼d¢ÁÈ!mtóæÍ4H¿õÖ[3À¹«$Žë‡e&e©ªfÕ2ksɺÅeâ\çì?—ŠþQªš,A›AáuÈ|Üu÷®„uÖ©ÅÝ‹©:ÂÙÓgÕàà9g°-äQ±±9'çÚÒîz'oX1=ñ-ä*ܶ¸'\ŒÞäUŒ=ªÄX”Sº”a41#µƒ×ƒ~´ÿ QæÙ4–a®Ñ$ŒïÇ=÷ÜCÏ ±dTðÌ>LÌ™tErv)r:$ÀþÉ'ŸP†€±/}éK´ âåÀ¶¡=`’߃CÆ)6쌞?°áZÅ€†óÁoáf gA #*1íº¤ó%Ö­A^{R^*ÍZ¢ø¶Zæ†xÖGQëÖ­%Á\Ø ´O¯’Ê„:°ÿ 1d²Š‰ËÒO¶­b,¨nÛ¬à'ž-阽#ìÊDŸá¦¬½iÛqé!à„7®^cgfÎ@°4W]ž,™ˆ`' êRý~ÞæŒš"»ÚØÏœÓd‹ @ÂX˜H›a‹³¯0éÎB’ƒ|X°»Mõbå— }‰ Çl®û.ƒø@!K 5èµÉÅÔu,ª¨kX 8 ¾·žöY‚paL½LQΈbÀ'°7Œe‹jaLÎ>€,ÇXÆÙ§ë»ZD¡ë=I©Ö-0ûÒK/Àfiž_ýêW±hœ°€ä—_~ÙÜð& ¯¿þº?È2‹äºœ”ƒlT$q, `¯½öš/óÁ®1¼ÊcÕ³¾h-îÓ¨˜§¤€«v-ì:˜²jAž´ûÅ¢éÙÚ$%XFiïžÏÕC_yìö{âøIÊ õ<­­´þÊ¡>p+3mYË=Ê.LbÄŠÓT aFyÀôƒîƒô8ùÚ›Ùòµ´ézØ&Û+d»pƒîC@†i^¯›ôoßîѦ­J–™¶Ýy¶Ú:OvQ]ȰðlZqËY±z´]OU Úné&ÃïÀ¶±‘qýÖÅ8º€Bðf ò5𬬻{±º1ìÍδÄ È­Õ22AK±P$ƒ7Míæ>õØÂŒA”V^£ËÆ®‰8‚ÏaîÑ[µÖ(ÎAšl[ò~¡=!ƒÓÞÆ•¨pª`T&`vÍžôÈmù˜ö±\²%à⬧ɺÙí×Îvõ팣T^W`˜{Ôa2Ìñš­ÑÃ…ý¢¿„]8„r¯^½¦Zµ-k”Ø©¼ãF=O«z€W³3ï»GË™˜ ȼámŠJVy÷‹…g]‰Qi´Ÿ$îÆù`7\¡ A1xòÞÙn\61A¤\dw>0mz‚’éjnnÞ`7,ŽÃHÄÕ@›-¦M~Δ´kÀ—0®m¸¾G@(Ü Û»\Ò ›’ÏðÅ"×£C‡ª#GŽÐ{Y?ší³ Þb±çîeK—d5Ï¿ñÿ3TE¥’Uk*T‡%½çâêÀ‘Ý1l[¾¬QÉlÕcÖ§:Bõ‡ÇÈ> ݠ󎪓TöãVaÕž•IîÒ¸³=)ÒîŠm—ë¿ôë%ÕಠÚ8 !«áµvƧdÖìr0—Í.ÎÞ(L8.ükþ®Ø4W)ö¦ AÝ\_Q–øpŵÙl›íRÆ>¡)†ÁÁAÒ΂ ÖÜ\ŒÛtašš~P¡î4ï1ƒ_»þgXã6@õꫯÒïQd‹ Ô xP‹ˆÁ €šãþâ®öÀ¥Û´ÎÄÕÀÅ`M Þ\“^6¬ß ~äaõÀý¨7úavÿðöm\ƒ° / vå~mS0Qzïý÷(ÎPþVÊgÄoå‰Î´ššÆñ¦ 8USîë–™Xµ¦¦¼Úš›ÊÂá Üò’uËKÆ-GàÑ»†²ës²0I‰Z^u¯»«>i\F5Mö*ªm4ÊäÐÖe³•!‚d¿dÝQyŸõ3êÔcÊYíS žŒÐ¨ }ÊÜÚÚÚ6ØÒRÞvÛÙÐ%Yw–:[Žur¤”L[X\7F€ Öž bÛÂÊy¹ ñBï òØ7j9¸¡4”*ig›€hvÌ›S°°ÉÇO´¡Mm&:¶•¶v}|!|ï½÷Òõ! ¿…tÄ“O>YÉæÅnvÕ< ¸Fm;{瀢°ñœM2¾ÈKZ ®‘“ìï‚â5±ÜvÛíêÙgžQ÷Üs¯êëë¥ÏÐo.^¼D5ˆGGPŸrŒ€M¨1ÈhÛ~$I‘àvk›ZÔÑ¡zè!uß}÷«³gÏP–/2xÏ迹º Û®êâbZÊc‚çŽõÜ‘bº’.Þ¤Sf…J`VŽiknòJZ5 Æ­ ÜL\[^JdH*ä&ªnܸNÀ öv"*Y*m·~’¾Ÿ¶MHk®¶ «"±lOÑ!ë’›±l½*{ø6a>0m­zð_ç*d3jr†(µfä`i«(ÏÕ èŠkc©W|› ¼´zm¶ Lfoœ9É®† ,R€ãýq OdcÂ5Š5˜7üÍ3ë ß2hT™r©™z*“1ǵ2Ò‚À/èØ(V neÔ£„N4½PÖßI·+öÌàXˆ3iÜ+ j¶ÌG ®=“¬§ ¤Z½Î)­{˜ÆàÙÈÉ.À†eõªÕêùçŸW>ø MѾ‘Y{ùòunp„fQêš¶£” Í󄃔ÕÝÓÛ«–-[J%Æ–-_®zzºµ-éVß~î9õàª×ßx]½òÊ+TŽŽûöÓZûúgL³%’ýåÚ™Ù!ÞÄ/ç6J0 Ö-G¯yö±kÔs•VºP}mFúg929¢®^»ªmê5m†éo™ˆa3GQÏq6ûÁ\ˆç&½ÛUêÄvýÆüNf6l2B#3m2steV˜T­vÑ¢’¡`æˆ?o”†ÈŸ3Sf6y]Òoïšmð>ànl›ÍP†%#HÕxÜ+”‚ø)bUÀFÕ–“éüž^QÖã™Kõ. mŽ›ËÆfÙd»€+ . *ÿÑý‰²þþ÷¿§x™¥äŠa³ž˜h|á9¸J…¹U]±·@k´ÄˆzĵÎv2B5•‚~/c1%ƒÿøc«§Ÿy†Øu¢“'O©ÓÚ9rTÛ‚Óêªn£c£~Ù'×yÉ㡟ÁŽ`¢´ný:µqãµ\ƒ8°püÇßV;¶ïP?ý??UŸïû¼â\8¹ÁŽeª°_d{<¶-Sð†‘¦|üfš<çôqòÆ=jÜœ’mËW¸D›ÀÍzRlÇ7ÌÚ=1¹9BöãøøØA¶x>¯zÛ¶‰Ü^m+ï•+{Ô%®¯?ß _PÎjR5pe„FmÈí× ‹J¥æ 'šœØ( 5(®M–â @Æ;ØåU¤Áa7)êÅĶE1Ž¡ùÖ·¾EAÆ`Ý@l§¤»Dw™m£Î£$KM J†Áb626$¨4~‡6ðà *ú¬é”x`ƒ-™|€ýàorÛíØ$LƒÉù"³Fr³MZK<›v.w(&}Ï}ûyuÿ}÷QÍK/`;pàU­¸qý†Ô8ž4΂>ºxÈØÞ»g¯Ú ÛÖm[©®'*[üÅ÷þB½úÚ«ê×oþzÆ+›œ(úï?Vò²@3Ž#Ö+òžfo¹Jmà–÷]“‘ã‡IÜ•+—ÕéÓ§È5 [Ê@l+±ÊÉpµ¡;õLN¨WÿN²_Û«æ’ö²Y6Wb¢ ;2ý`¹þx‘^‡U'#4¾ïp“ÊΦÉ&Vf€?h³ñ,:Ž„‹k¦‡e[HJ¨ßµ5B6iœßÖÞ\±™ý}ýêùçž§¬PØ’ÁsçÔ}û‰]»ví*ÕúE?®†–}”ö=8H“£SnÓÀmÓ–ÍŽñÄãOP áW_Õ¯`7¾6ŒÌÌqGR‚þ½q4pËeEíм·6™5×ä±m9ÓÍp&W/¾­Ùd˜6Ж1€ ÉHG£W¸D/kÇö‘½!\§ÖöÓèzŒiM¬Hï™,[%ãm[.Ç(Q<¾O¿öê÷T'#42hËжJ¦êÚ4¾!â Nt1A `€]½­«S"öCºH%X bØ\` i˜É|tI¶-Lõ_Î<`8ad#'³¸¢*+T€¶L¶Üò3^Aç´'‚œÛɨк}AÀ-HÄÖåú sâùØÂíÈÖJš9Wlv Í?W- Wðf6Hv<óô³jÅŠ•jllœcdzž9s–2Ü©m¦$ÝÃç`Ãd–îØ±]uw÷¨;î¸CÛþiõ?¿|ƒºmû"mgÌc¬ ´ “5 plÓd„ÌÎC:*À›Y›rf9~Þr6ãæÅ¹QV)VØp¿Îj[€Œôóøâú`ü U«Ç‰aí#j¢<íAÒý¹â×eb`]Þ Ç±»ôöKôç@[䉹i9ij…•I±u­$õérÚà†¥+fk  Û· ¸•ÐÖg‹ª­Æ³77¸8a$`L\…‹Ã·êy UÅÿùçšÍT†tfÒwÒª2NYŒ µö8å¨Â[P¶(äCàòx«FâÃ68³œæ‹î¬¡˜®©FÍ& Þì6ß¹¨S=úÈc”:11®NŸ>£ìßOÙ¡.[™æýÆña¯ì?@Î;v¨ÞÞµyÓf52rS½ýû·}[Èucåä+€lú"kŸkà‰kÁ÷ÍÅfŠU³“•Ê.ÊJà–•uDs9¿¶¨Ÿ!*3K›9‹4Oå´†® ©Óˆ;z”€(4®\½âËq8 €ŒLÚ>nµ¾Et¸bº]™ö.ÛÊ“qû-{‹e¥$@[ü…3GuûίræRò—”©ì˜A2ÕúÒëŶ±{™XÃfƒ»1³ñ[Ö Ä´±Hc5ãq1n@SU¾¦5˜rj&ëvÕÄaÙ‚\žanQW¶(I6Ë{›D“-ê¾ß*m®Ï!Ê„ š®ŒéZlD½“˜¹Ì|éî{4Pê¥ö|êäi ‹@{–R õ^&Æ'ÈU –lÛömjñâ.µnÝzr¡îÝ·×ïo¶ýã,mLVŠX:„¼Ã7M}ÏiZ tåÊZŽ,€›5òÞ²9¿–hÖˆææüÄuk¦^†¸RÃ7‡é\!%tòÔ)5¬(Ü´øœ…¿¹â tY 2.Ã6W‚º³Yö1êú]Ù xVRæÃåµë“:ޱRF ™AÚ¨L[Ö¬mº‘/w JÃ)µÌì‡êBèü7:hé°,«z4Ú(‡ŽÍÕìk cÛ쉈±‚‹ûíéé©p-T“÷~y3ã' $2:q SÅ5‡½°‚ïIÀ›$‚ÆYµpñØ1ua±k.0Y/ñá/0 »—®I\Øùºâ%í!¶šßW›Ij_ÃÆ›(ÛÁ^:x˜›v2#&›ˆ£Ã‚¸:Èh¬]»N]º|I]¸x¡"km—ŒÃ¹b’JâÞ&¾ !"ÃCžú+i¬™ƒ¬‘'Êpõ‚,3lY#!€–îR#éÑTþ=Ž344¦nh€vÑ„Gà\ùþ˜<ò>p}°<þ$yþ²PÍùÕJžÈP§è ™0‹½[£WdŽ5li#3mûèÔb©üÂ6 ÒðÈÎkæ–±\nQÆRjBÒiÏô9e?ÔÙ b´ä• `°88ìÒí]uîj餒åœÁÉ'š­ >K´eýY6#ëlÜ–¢Ü£QŒDZa;vì ’e³%<\ça3-ó-¥¿Q‘°{Äž¹4 íßÉix¾xee,Z5¿Ošu(ß÷t÷¨%KÈ^\¿v]=rÌlsµLiûC°Zk×­Ó ¬U­^¹š€×ÄäD…GÅÿ™@áÚ »ˆI+_þîZÜEzrp¿Næ&)€“r&Á€Ú€aß|×h–]§& _ÿ+h †g_(LÒþ¼¬Ð+”¨1U˜¢Œ[vÃáØls,ÁÊKÀVow|£ôóZK?ÊqË~vUHˆš¸KÎËMáø±ЖœiƒÜG~=r° Ê"åƒ5™¤ k>ʇÏÁõU3íÛÆ A)ËüÞvŸJÇîVüA° ì¡¹foãœâºEm0=£Ó0Ñr¼ªÑ=¶¬÷ª¡U «æê¼q„rƒÀŒ1 /³ˆnH li ì_Dpv¾3Î9£üìe›yjÿ¶Ýá¶ŽgM@@ƒØˆã2$õ’‰Ã¼Éö @Òß; Z5(B›>£mÜ{s Øx)p;¯P ¹mÛÚÚUoOŸ:waÐgºÙ–óõ2pÃ÷ëÆ6Ï á+AØÀ+h»Á @81Ù¦ÚÌ+žé˜`Í£M~uTw™š*Ðw8ö­:fõ:»:ýóãßpÖj3Eo5–-Îö²UŽpŵË6”tÈX@d.ÓuèuH-¸G“ƒ6ý€ôºHÊ^ØF”]£²\“ëaK  +&àw\ ¸–ÆWk°¨ý=×Ï ªñÅ.Ø  Ô<®2 Øïš5küý²he˜Iêv™‘IšÉ˜©O"‚Iz(N—fˆÇeÚ‚5ÛÆ€ †ÿСC”€`³»A-°Å³ 嬔(•Ö¢Zô¤„u±²ô J~;ã˜G\Ø<7 Ì““Þ«PîOe,#Ž`€ÐÒ£MÄ !¨w6iÐ6ö³]ÔÑI€ ,ì'4Êfê<€›~ΘÌBç;n’ ìøež´ãwœ  ÈÛ,[¾L-]¶”&²`Þ¤P¡¥¸FFšývÕÔÒìÛâkN†³TaØÀƒP(Çü–c×ÀbbÇÃ|aØêMR$ÙN&žØ“*þÛî϶7MÊ‚Ø@O׫ÿîÑßW šAÚˆ ãÙ ÷±Ä6>’AsF»Tÿ^²Q2³”gAµ ÖÃϲvÌ^Ø9ÚiãÜÈq˜bA6)>GQt¾6Û­€w`c®²e`•¦âYI‹ÐsŒÏp•¥ bÙdûK—.‘Ð(iWÕƒ¸`-îàûEh-†aèP½}}ª·§Wuuuú5,›òM~å ¯d¼i±$z3Æà9ÒóÃ:¥ý ØÉµuý±KˆC‚[à+¨íÛ™—Üð=’}à*w1øi¸jÁ·OÓw´u+oRŸ/ Üý¶TPvw{åóµw#ƶÊ.ïÇŸ³ëׄçVv•í"Ä|QR ß#Yahxˆê§ÐŽ*¥Wí¸p£@Âv¬£-²€6Ÿ‹Žk8×! †­Ö¸É¹°#I6{²æJ(Þ6×DÝž٠δŸNýQªLFXm,[Óf«u»hN)"ôËϤV›K¯­–àÏZim¹dÛìD6šŒ“ŒpÇõ²~¶úx\¶' ÜÚ©Ø3Ϋ¼eÚõâ­P¹¥ÛG·(×hô—¨bÀרk0ð b ͰÎÕ¶iã&µ~ýzµté2’¡à Eر½‚µ˜ÂßÄxL™lAÛ {f…³ý r7Ú¹hÁ6zåÞtÛ×-¤XräèQuæÌéá.Ù!¤ ¶nÝJí£™ŒjR’€7iGÚÛÚ)È÷bèÆ‹F\¨ŠÂµë#ØìÉe´4·ÛƶÎU›”Vý FL=T\ÀܦgFl£nÈR]]íÿ†™4îÿ²ž2 õf3@MŽ?8X<€Ö`‹ÌkIDiT¦­Z€6c‚Áµ¬ø²ãÛdíQe×—r_–½ÎÝЖ¸áfåuÇ\j»¨OWÉ~H.?³Ýª\>$ŽYÚŒ[\¶Í5³°}™–/…y¨rAyTI€QpÃv̸±«4LÔÕž­ÅU‚&CK›âÍ^•ù¸™1†V•8êÈufK¿¢ÿ)>DÑ·X ò£¤7ÂbÙä6œtÀ¶ÿ~ÊsÕ8±k®6Uã;ŸâÐÖ¯[O5_‘%Hî¯ñ rY]¾ä•üP„Aàãî´¶t‹2ë3²¢&.4·¼àžÛ­µ­•X à]‹Õ}÷Þ§îÕ+˜ÔÝ»ÿ öìÝC°Ý?ìÄœ#@Á† Èu^˳KðÙ.%°•l0É-j/è{]y‹ÜÖÒFmAö× i'fÝÀ~âù œvω³˜¥GÙ)´é…°Ÿµ‹(àóXƒ}µsÓbØÒòT4Š„GÜ磊̖ûr¹Kù=K¸$Â\ÊzY¢*U,*®­™6_£Mß쥮 p¾é²ƒºJ4Ùe*lñ½°ÙsP‰Ó8ÓfÛ8“T~o³nrÆ!œÍáZ9õq Ü@õó1åÀd³haŠ &§H{ † î,;No FÕå­¿ågb[¸H0 £óØøµ »ÍØÇJ.úŒgÞ0º’a Š™‹Ö¢ØË[¤ñ‚ÁñË~YíÜy=ç‹.’«+dÐNY,Õåòà¿Ëâ¨yÒ8³¸,;ÃåÆ&K>3' <Ú[{¹a{zz©BÀ#|•D^ßzû-5xnp†Í°>pÈg á:jÕx¬GÒ˜*–·@?âji1cÞ9dÌ3¨=,÷6vÊÓ¡l©ˆe¶Ù6×½“à û‚+öÀ]lçk¾ÛbÚxÎhŸØ^ñ^Vï©vl˜Ëp‰Ù"$â0lráça4iGe“v©wj³s¶-Ö¯ý‡,0m €[KsssŸ(ˆH f‘w× Û3ûƒå2tÕ”I³qs&©Ì6’³[ËM–½²õĸR‚TäÆŠ˜ ìõêÕäÚ¡†!ꕺfÿöý sÞ¹I€ 5÷óƒ"à  SôÀZ KèMÿ ®,C‚c#kË%Â¦ÇÆ®1`£r4gÏ’Ø(bkdlX[5àþVirAû»ïžûõ¤aµ:{æŒ:áÝ_¨Ç#q ä˜˜Ø÷0gŠy˲Cž\C9Ö‘b)U¦‚ØõÛ1³ÓóVœ.ú8¦üêKT¿oÜÿ zçÝßSíHn.&†AËÒ¥K ´¥ñœÓ~²½´á~€Ǥ*-– ûY¹j%Ék ݸ¡'U#´Óå’rÅ¥Ù ž U0bÚ`IÁ¶Ù”¨{'í"€× –v‘m¥Œ£â®ƒU²½Qý;ip=¶´mHZIa¡7ç²ßÏ&ËæÒXcÁ¥{)'u®ú¢arÒó&–å ìæÔ‚{4h£UwxëvÛƒlPµ¸¸hЬӋíh¥›¨¥©§Öˆ™m³UŸm–ÑõêZ¸áºáÀ W×ÍCÀ%Wx l.aR ’G)ƒêšª§ÍáŒ7ÖIc Ú¢˜7¬¸ßœ(°‚}‘ìJÒØµjÛ­Z´ÚJ±D¢Ï¸¯¨ ö4JúÄ“RÈ›D&ÚÊjõÙ¤\ùƒ7¥$ÕÆ1L’±•  É (VŽBå`ý®\¹êOBplèzE ² ,ƒb›f“u³õ®H¢¤œ,Q5ûÆ>¶nÛ¢6oÛì3œ|ý-­-ª»»Ë€£¢ŸÑ96:¦ïç¨êèh'¶ôØÍä¦*&´Aí°Ie˜÷ h*ûn«UŸo6¶ÙdÚ’¸A]ŸI–ÍÎ º÷6X³e?ä8jýîÑV½ L²À´E0mm]úæµËB{•@!(HQÒî6óÆ €]¤ì÷Ž*Ö—qK+#°4Üê«}»fv,›+¦ÍÚæ¬RÄs 0±D,:‰870’Åp•óqUš¥Â:»QF=KÁ¨©Š¬Rñ>Ã!3ãïŠ_d”? #£ !§ÞK uF½ÿÎût¸CAÛt´QbS&볞òÆv%eâÂ]›§oχ‚ð³‘hàúœ•˜icñj› «ld.Û»ŒuûìÕk›*{ýjiXЦ—Ezmå”k×CB @uÍš$H ³­†qK›mãx fmvÈŽe³]¤Aâ±l@»ƒ"¡ˆß‚;:F¨ö&ëf+ÊÛY;<ÓF‰Š=1Aº¶!Œz­n—„tõr…k†årcºDs‘öì‘»‚¤,Û|0Îi°gIfß“(GÔ'P=CìÚ¿—œãÕ~äv™7R¹¼´ÉöZÆlΉ ƒö<60m°é5WÈ©‰ŒW²HeÂkKán´¥jd?ê Üp¿8_ÛÈ®æ>?yò$±kǶ[¸V0óE`Ô¤¶`Ô‚Î$ãœq—T#`ŽçKk“ŸUME¿á*å¸6Á {Å¿³^lœßFù¾«rLÀšÚ°å˜i+(ÌïÁ²Mip0¡ÛÅtiÚÓê M‚6>>²GÓÎN¸e2Ù@&¢ês1}ñã>¡2Q=½=$©Ñ×ß«Öo\Op,诟ïý\}úñ§T”=vy;?³Ü¼ÉðÑ£ïË­–„u‹Ðê!:Û“¿z³laŸñ²\aã8ûr|´%Á¸l¥Í„[cX¶NÕ »ìí±kWY"û³ ÄÂñ Í  nANHcÜæ*¾ Ç@â»Iíì&Qå™xEÇ€;Æ  êîß=rä%)@”±nˆ d2,Á^8]Æ-ˆe‰[–Ïç$Áz³ÆÍÈ@Pe5|éBJZ’ªZ€?ßéʳu­ˆÕÀ­µ¹µ\R-›s0©Í”㵉^¸y ­Éw“r|€ƒ¶lE%/®Øk´M{ÕØþ ÝXðX(ä`ØC)?kµ]É®q[wUVhàV*'¥c w3³sƒçhýÊ#_VçΞS»ÿ°Gõj ·óöæ>úðcµw÷ÞŠ¸´¤¶±Dun†L5¬[à-.€«…ak=ÇjÝ qÁì3ÇUËe—çŒÁÇ}ÛŠ¶À~D]RŒïXm A›¾±Ý®Îb+!ÛÀE2#A¶-DËÆ€ˆKÒ$eÜâȃÔÚá˜=Ccæàû  &“2‚jiº2*99a``€@bÛ= }2H^€‰Ãw\Fl¶c“l0Êjaµ\õç‚î wp¸¯PÖ ` ®PWô¶/Jœî+SÇä"j¡81èæNù\“çæÌæ|×w³aØHŽÁ6€7v‘únRWih“‰R8Y€6ò“ôÌ;J|Ý ×&³5WØd?à놖_\ÆqÖ›#;;­óPFB²¹zøAŠS{û7¿#à‹>ÖíÀþƒjÿçû6 ÖìóÊrO㳨ÉW½ã×ÒÈ®7 K Ò\ŸÛï9Öœ«Sà>ÈP()Ý!Ù6{ÜŠS·“ÍvذK‰ÄHÕ@¤êÍi#Ü#}Ór¶†LFµ¬¥i[t¿c¶M~V+ã–vv3̹€[X ú\ÞgNN`¶ +Þçà†Ì9.ãbÞì:°6Ãd※óI|Øl#@9ž1\WË#‚ûȃ®ỵ́ش0CûEjöý#…ö—:«Ð‡-ÐÁhåóS*?÷\¡My¯zI˜!×d¹Hhk"¶M¸Hs•µi}÷(—RÑmSS’¦ÀdhÈ(Û¤á`cdt¤2#U¸AÙƒkƵ½à–fÖh\;‹­ñ¯?öÉ®è’q9°=òØÃä&ýøÃÉ´ã^mذ^=|T9t”žaµ U*k9ª÷Úl0lõŽ_k´úÃi&¸Æ7ÙA·m°-ùe‹åJI&鉓Urloœ{,Ö}wiKÜrpÚE"b9HKUlWY¥  'g2CTƒ¯ZÆ-­Dù[4h¦ƒ@JT}Í @G™Ÿd‘°f͵nÝ:µsçNm(å„2=p©¼±&•Ôä« ž£bÕ왯k†TcÔ·Ö„ O°A΃gm¶òyƒT‹{à‹Ü Æ–âšúúè~,-TjzŒbÅ8²üL¤î¹J3ÍʸݛH¿ÙpÍeù¼—Á˜#Ж«`Ú¼¬QSè©pÑ’ôÄMj¨Â0ts˜„a¯]¿ækÇIÁiÙ®ñ9"ÀCÛµÏ&`Kκ•Ru‹–ÎiÕ½c×TÙb÷g»+úÖ©“§ ć6ÛåìVÀÏÊeïÂ@ðU xK‹aK›]K{œ©Ëư±íÆ8ÌZv² ãÒ“d‡<¹¤¿‚Ž%Aùl±ª,cµÚߤ®}#í#ž‹Ù`0c×µÑùÿeï˃+¹ªó[»F3šñŒÆË0ã¼€1l0üH)Ö !lBØRP€Y0«Y*˜"@U¡ä,U ˜@’"l±Íx÷xöE³f´ïÛëßùZýIGgÎm=IOÒ“æÕ­÷ôÖ~Ý÷žóÝïlúõ Èç.¹ˆ-)j‹Çä} vöÖÇï%#x€mº&ÕÙƒçàÆ—®R7°n7Þxãë†à},06cf«,´ÙÆÎsQh³7 Ø` p-aLÙI_[ ÖæRoÍSìç2P 7œk€šÍ›7g Ì&ÀQè\¡¤Ø·‘±‘,Ãt÷tOÆ•­Éz³Œ52Ï0×Pô¶©i"n¥´!©ÇüΊÚNϽR®ÌÒFGF3F ¥h‡óVDƒI_?¶îl¾pžh÷ÿ”â=5ß… Ø[°¶˜_ø\¯Gp5·’ê.²Ðò!š½C‚Ã3žyC²ã‰É®'wåþ´%9´˜Ñv8®ÉËÕÌÐÕ£5#“}Šç‘lPè*€0W·›¾å(éáérÞ'yBw¨×¾R1Ú«fÄk¡K|xÇ©\«ë h‹îÑ"¦]Fƒœ¼ËÙ–^‘= Ðl?N àxqg ŶÑMZ´ûZ(Ûb‘æ* ú.nEÃV€ç} ÞpÞ`¤] —)ÁÛå—_ž1oHV€ëq<`ô`ÌÀ¾áøÈRÌÆ´Ív=—¥­³Fw›7ƒáaÿJÝe.»ÄÐ÷†æB”ia7 \€zd"€‘yÓ}uÏbÞ†&ƒÿëëû2÷(É7k ÖÜ”e•62³4+¶K÷H%r7 “ ÆóìP‚·l®ÈqÀj2Íú°‰xÖP^ÀÖCÖ1áÌ™lC€9f t/´‹Áb·IÝ ]Z¹2Tø&$ƒ<öðÎd§€6°Ÿ³ýn2•~"»Y¨ÂȹéĵC鱉1·#Îb2l+]«¬dgƒÙ€›eÙ°l.]dW»>õu] Û8^Ïݹ¤‘|¦l‘i+`ÚpÛ('°%TlO'Ø ¯Û\Ù´_ë÷6 ™%(áP‰ŠJ•©uÍ6W^Ù"GP¦66ûNl2 p‰‚=Aë+€¸+¯¼29räH–´€LL2°PÞìº0il›Îqå*8ˤ¤ÁXÂøä­r0t]8-±Ùf‘][<ÁuˆÆ5“ 6à›€¬µ‘<§›ÂŸÅ¾å &6Ø>ºÓÝê2`ê¶ Ò~>×2E/Ÿ1’Í•4È\ŸÝ=ÝÙ÷³p'™4|&æ*Ž— ÅaLð~Š/ƒ5Äœ³¥‚f›3ËÜf„…L±ð•cÚ&?wš]]׺nFÏd¬Éõšª±WvÝdÊÌ’œ-­Q¿i$™*ªÇµX Û|ãת]›Ï÷V"Ù`¶Í8Y6Û«yÄ‹òâ›u½N›¼H›€9ä•“×ÁÓW—T¡‹´Z³Gѯ¥(VIgŽh…¤Q¸fßôëu°¢ö}ë÷¸A™‡´ÙÀÚ|wSóY¸øL3{ùñ÷z=Hõm‘‹Ô‚7¨@ƒ†ÅF Œ0@Ø™ ðV œ~ït¡Ô†©Z<^ÇÍ¢ÑxÃÀPbÀ€â¿]gëé&ÃÞnn>ìÚYÆ/¶9 Ë«| aî`Mº('ÓôÊàz{­çðyÛ.ß–ÜvÛmɵ×NöÀ0„ëss€khh0Ó8,Ñyˆ9ÍAVeð9˜×˜Ï8VÝ}®Fo©œ$™n@¦,  `íÂç0{½âº]|éÅIcSc¦?w<¾ã¬×²1ûY}cY¹¶vªnÛ v>ûÄxòè:ó\®ËÁ°U»¶P»²Ø%=Bà ó†µ ªlì7rV{$@¨ü—.¬ïàŒ ÉtÓøª’jÌEÆ-¢T[Š f‘?Üf“h ÕÆµià¦wõ¬Y¦ãŸB‹¼Œ[¥sÖ1.: Sƒ·"À¦Ý¢6¾Íº«©€±ÈÈNÀÀÑ}ã‰C àcMŒqx¡ÅæÅ.zÒ¾Ÿ -Ôf£çgÆå€¾(³ÏU.ÌÌÌÜÈa~¼Ñ({néÐõc\*Úš æ¼ÀüÉâØT£r¬ÌU®n$4;‡ã£†ù‹Mæn¥¬/•Ñöô'²eÑï3ë÷š.¼Ì ×Ý•W^‘}î÷ýê¬ø¢ÌhCÞÉ"¿v‘æ ­Nµ «™nC6UôZưbG½þÒóaÓ*™€P©Mz¥Ùª‰eÓëš›¦,f1ßyÝG¼ób±‚íÏímÂmmS´Õ†¤ kµU£{ ·YF£võÙ¦ñÞÎ[§úÚ j ¼W»ES©Œmƒ! ¥È-:Û¢®Ô«œÜŠŠ˜¶r2Lõî™eBÔ‡ K5`1â\Ó ë:ï;¼ÅFZŽ-X¡¤†"Fu¡-¢øÌÀ]‘[l*Žù‚[ÃZ䢯\„ûn{$Ìà9|>—îyÎG<†ï&ˆ#PÃq8¨é9»˜€j)Ù–lÍ5LÆù¥µu úþ4ïv€sºsç®äÁœZ«ümY½©¾±Ó‘§Û±LKî"­«u[æeï(e1ƒ³¨…$%”˜æÊ*­û+ý•,éQîãdÙ²^¿*TBw>²U$¼khí iJâ¦Ïßßœã£ÓVØÔ|®Y#£©È€{‘5º¶Œ›-îª/žmuûPИ@tÛè‚~s]Ô‹½SòĦJ% Ø$u ] ËŽ1#Š™Ezqu¥j(iI.L통`q¶ïh•ÔÊeÊf”pzàF©œàübÅPâÜ¡û,A–w1ÿð¸èQ¦¯ÅÆó @ ÷¹Î¹YàÎßϘH~W(.k%ƒ·,+OÀ]¤“ºp~ö çnãÆµÙ9G‰G~tFüè$ƒ™w±´ «Ë€[Ýt²ÚÉø¶© ÒšÉ R°ƒ¬Í†¬ßŒu««]†m©b׿Ulx‘ËzTŠeó×-±‰â¦Éó²i{æÅ¬é˜X/)…öÉÖ}åÛºm5dýV²¦×Î!€R ]¥5 sú;-Y\+µ ¤åƒ¬[öxãY½cÙ‚¬^%$@†‡‡ò8ÃɘGdózú})¶åfÖ*aW*ÕŠo>Ù£\—H\Ãõg¨ƒ·ª¯©“ÒB±o¶¨¾Å:AA±v`Ùš"ÓV&p““Ø*šçÀ{®º¢£ ïê §8/$À |îp©è î!ð6×¾”pˆZšn!ºŸB€M›{Ô6‹/ê¸å4Ÿæíœ[†1ºAW¾ðš.F†çjbÝÎj÷—NdnÒš¤1s=f,f]ý¼?댉Ó ¬q*áì7Ý7vfÝ6ÄØÑ=ޏbd‹=vt*`<ô›*ɰ­´æï‹¾æúÚr:à²ÃCxt‚€.c3‰5¸ó@¡g¶"…&|L–)@[£!”"h3`mêääÙ£n}5½@u,š¾šµ‹Õ‹ƒ³‰ö‚k7)v(Ì¢jC‹w¾Ôùbì²øÝf¬èðÆ¢¦tm°~–Wd×;šQ³`m.àm¾ç¦œó¯ZL0ˆr.²n\t+ÁMŠ„„ºÚÉD¹ššIàV72 ÔîhœiS ŒÛY‰ õó7Y±~,éèOÚO·gzêø‰ãSñüý:°€;WVÍìÚb®J±rEɸ¾èˆ‚ëÇîC,Ýåp[ÂÃÞ¹Ö8Â<½Ñ0)êsЙ¶ÙX¶|4èàBÍ®XÊÔfŒÚÀDïBz¾m‹Äí„A<ëqÓnRoÁÏ%Ab¹vfü~…2DlŠnE`Ìp9íž¶IÚEe?—Ë´Í÷÷†v[d_VZS”(‹Áº‘¥ÂšÖ¥Fs–-ŸTÇx~®ÀM‡[d£¡>âšò~±3bÜ&ßW›Å'}ý}YÖogW§·ÓYÖ/7;ž”“0T)†m)âÖ#¬f1\§E¯™­9<æ Š_sÒ-ªKpY[Ê*Ö–{öÚë;nY:Ï‹§^[™¶òA[’—ü˜q2­!Ö Kw4Ŧ٠ìÕpá}]ÿE_`¸FZÀ¶±*ºÄŸmg7Û"_*Ö'¤x˜M72ì0pŸ@Îsã±ÅþÝ6a>âç»sÔl vïF %‚·ia?Ö¯^Ïn êó¦lÏÞ"°qh«¯Ï³Cë•›”±m3X7ÝÎÇÓÝÕ´>œœ& ékhÙ3¯´—k¶ºöîÌvß…Ëß0ŠÒ(•€Ò OyÊS’­[·f…JÑEKÑ_;%°Žpóºè2 ³eŸu[(ê;kû‘Z¦ÝŸQ¢œkà­\6€‡ì¹.v¸6AYÉh¶ÆJp›¬½V;«þ`< ¦ºäÀ­¡£a*ž­Q¹Lñ¶QѧϜNµµ%§NžHºº»³là‰ñ‰)‡Ïär>¿}>àm.€l6\‰¥´ é3jKpàÚÁ-J"DÇ{=C½yfí¾×Á2^X”eÝØDÞ·ª’juÖ† §zuÚ4÷ZYhÎÖ³Ñ^t;QX» @l”×V®›´ÀZˆÄä%ãvðàÁì÷jàðŠ7¼Êñ't·bà,P‚: ¯Kƒw?Ô3•†&ÖU‹en¬;¿°ä‚.¬°©üh)ëMÚXjšêÇú\†DÔOu8¨Ÿ¾¯þŸÞÂä>ŽºOt˜5lÜÀ𣾾ñµ°CÏÑ\‡Ì´½v±|-ó6—²ö>6û¨ÉÆÍ½W*TͶ¦š­Ð{ȶyýImr¦´aÙbÉpƒ{Ô."`ïk·¦½o3Pôó º÷z‘†èS€w ˜tP,–m+×MºP¶m1©t}>ð›Ac;v,Ù±cGưañ]tÑEÉ…^˜ (Uv;`Ù8 ÛÈvY Ó Z¨/jìT%ÊÂÁÙqºJYà”kktdt*A©±†«!+Çfí5ÉLý<Ýð½n*ûspËÊzœ]XG9(ú¡«§;ó^?~,é«=Óqfj“ÌN Œ¹ 8”ˈ•ÎR$½Ú7ëå~Æ|›Âk€»ýyg“¾&:ÙÐ+ßá%*xÉ‹¡"½º–«~:ž5éä‡D÷hRkÝœ¶ž T 5÷ŠóZ`WîÂÕ &@ iúP¡_oñΗ[.z\ŸWü^dÐbìÚµkF«!Vª§›„ Ãî%8³õt,³¯‹rî èbLÐÐ\ùa|„®ÝOó•l)›Õ^¤œ!΀: ç–ëkzxh8ÛtOÈyŸl;Ÿ*v•@-cÛêØK´nÚ=š%?L7n³^¯ý› Qıe×ml4;& A ®Ø{¼§œÐ‡¹¸Kç Ê–Bg{Þš¥Öý -ë¡®%<3Ü(XÀdËyYhQëH>æ¹<-(³@c ƒ!¦ZkVÓz®V÷hE¾ @õZyľÆ.†èRàÙ…M7)v{ˆË ”WÐ[|åtM¨¦]™}-TˆÅeŠ1M=Ý»>QVŸ0“` n:$û`GÎlm”¤ÁZƒâçÆ€ ½öyKw½`|9g1´ Ÿ‰F«ÔÍÆ>¸áuw`–8þÇýÖõ­ š¬S9š3nõS}BkòÞ  Í&{»«ò>ò‡¡è:(ïsRø,œûîžž¤_náŽÅgfýE 1V‚ײõX¨lÃBÛ\ÀØb—pZJm.¯ŸKYý?æ ¼2ì ëª­çÅ3ë/^Ü[Ȇë×i6N'0z¥»Ô¦ ÆÜFÐæœ¯´‡pE麳-"¯™ì,‹§éWíV€‘Á­UŠ@Y(Îm±°…vƒ^ oΦ䪩q”ʃ3€® 8À€k®vÜâãØèµm¦lv¨& ÈÊÙ–T–©³àÍzæl'%{½Möj]µ¶ªeÚ º¤Í´¬—_^Q¼XD=k–N Ë.Q¡@y±H ””}hñÎ¥ÐãJYг^Ô u@ˆ²r„uýXíëãüóÏÏ67¸H#{Ö,« ¦Šjê¸Ef"ë,a]oOß÷”¹­¨ã¯8ÀæáXlÝEºWad˜XƒõÍÙ©S§²[ vY)s5¤wX‚`pÝpù:Ü?ÿ‚ó“¡Á¡¤»§;éíé ð5<2œ4 N²p¹[Tƒí”º“^®;Î;\ÛÑŒ‰Å}œsfœ[¯kÉ|j].ë¶n©Ãbæ¿æm¦¸ÖØtË9¯Ç¨&Ot±]{>­ý¶@-ÔIA“4ácZd"¶> K´….:.. @†Üd–"õ. k/¤Þ½é×ò1/ð‘…CúÇ e3×öV•dÛ–šV êÜhØAœ19À ;k‚3€6(m|‚3Ëv1æŒ.Lý?“W&OpE£îm®Îj”·k#“‡[ÞçÐÿãØ^tI Ö9\vnŒñÄ ÃcÕÎÈy: ׅŵX º® l›75gà-{-bÞ`”3z£M£›óü<¯‘ϧûHãµÌ>Çy8ƹåu®ÀZ Öm6ݸTEÕ—²¬G°A/`îðº²D‹&QlA][kMÛjÛh^—çÙl Ì,éãÅ¿éužD÷hyäLQº¯‡–m)•F(˜Ñ+ bôwuW€@Qa;Ê`9r${¬@¶Ð¬£ÅL+å3£,½@Q²0&€æ<â:Yþ ‡É(\GLD¡!ÆzÁÈÑÜp,(d*k¹$ï›÷OÈ:œÏ›Ïˆ¬õHyèWy.ͯ$§ò¼·–Ç,¯¯Ëü: yÉNõò\­Œº†††:9Þ:i5º…›x à¿ ôŠ+®Èt›×ƒxC%dC²æ!~;Ûõ¬ðFw)® Žà zŽ ®1®õùlÊê¸MI\Žšä,–C³4®Ád2Á\²å\˜¡Ån•dÌŠtáRW¨DYÐã¸y"BÀf;h–ÇŠyñkÚóæáyóH”P™/åiIb«b°–L' ÔÚL¼mlí·ñi¶^›eÕìÄðZd苪}ïZ ØXKè’K.É€›ÎŽ)§­•7I—KyW ‹`¯:@Œ5@ÙÅ_œWÆ`»†çt¸Þ˜ÎÏøº1Ÿ”wÙ ËDÖÊ”¤|ߨ ÜŽã¾€¨‰¦¦&´ ¤a¿–+àÒ$›š i=ñ†,ü"ߢd-œ ¹zU r8 XÐJ¯I^×$/opÒ ÇÒ(ÇRGöç…@•®^Ô1¼üòË3ÐCÀÐð†úcHZ¨@)7r¹À‹ðp"Ë“ñˆdOqí'ß6½qNê’Y½ ¹º–£Mþ˜k–æb±¥ˆY[H|Ùbþ\³HÁPCW@`ÝÓ>†€UèÚ Þ& ¨E™fåì:³½HmE U¤Z™¶Q[,Õ»x¼t·pGoÝ™¡‰`cX4«f³Smß2 !P@4r0lnsíy·\øE¡xŒ€A8Ã\†Æ®c~³uùÄ0ÿÁÌè ~cQÚNFdmËü’µ5(ïc?"Ÿ5*÷a¥'&—t©$¯+ÉÚOå³Kò>»©ÇòÛì~iRÒ\¨dS³c¯Q·5òÝ5ò}`×jr– ÌZ-Ãû5D²ûxmÞƒ°N¾¯^F£O£ü¦fYÛ-ò=kä5kä|5¯]»¶.aœ'ÜØ€•"yýõ×gÆ ç   à …cq‹ø8žÃjoZoL*Áodf/G¼ø@º¶Yó‘L«vu/ˆÍŒ-¤0n5Æ"Ïõýsu‹ÎØ`1ßæ™)j™0û¯{vsÚÒ¡–“6QE»[õwhoœ×s<å.¤ ¥*cÚäŽØ“i©LoáY¿¸G›jÑLØl=DC™2:`aWÁVW˜¸pÌ¥òöR·åèsdu æ+Ø# €4t»ÀÀ}.Ê¥r.VéÅôxÌ×-:Ûk²°ÁÃÆEÕ­ÇÉÖIó>ƒ%9<ÖÌËøÔlËKÛMÁ^'ÎGý½ù{†“ØÆªl¦mØ2j:=Ø&Dªµf‹òzÁˆºŒWZÄ2ndútÖØX |“ç×¶vq|U§`Î@9}ò¾ùÜa4fcÀpèÆå3ƨeCD°Û8ÐY6=Jέ7R3ŠüŒ.XËo37)bàò8¸ºüñ:=‘ß ×°fÍšl¨m#U/ç `@.Ö&9_-rŽÖïØ±c½¼·U€ñpµÃlvŽŒæ 7Ücœ[7¸RÁÄÁ­ºÜ,\Q¡R]ȸl%ˆ[ª$+¶¬ÛR¸…êÓJµ¢×ñÜAŸ€°`X„¼,³e™5¯/¸=×$ŽWöÃiþ~‰£?Ãio ½4–ʶ¹6o7æùÁ½ e'A¨ŽX¨u†ez·@ i'.ÂÖVØUãµPÄå(‘P7……(‚嬨Y¶êj iW^ye²mÛ¶ŒÒ¥À¢Ðµ Æ9Hã Ö-À¥Wt¿àìŽ)0e1*Š{TÚ¨„;ŒåаhL¨Ûˆó›fÝŠ@›oµÎ¨S ­^ý_˜7ùu2êå÷ÕçÏ7ȹh€Èùh×(ç¤Inà6Ežƒüþ&9-ÄÖïÛ·o£<¶AÎy‹œûZí~Æu¸êª«²kÇó¶,Ü¡C‡20Ç:iË çé°…”Ƙ¯.«FÖm®z¬;°˜ú³ÒîSèlɳx­%VBqåÉb µm÷Ø7Kìh÷©ý~ïw(@ }5ªüÚ¼ëŸ=C)#†MÍËÀiD­Ñr9`ÈËXñ õZ7ª7Aìd6î3ƒ†ÅwËq,悎`êÜÌW5ì€ÐÔð?gÚ<\œ`tp ° ÷ÇE ÷É|ìjnnîjiiéÅ<$knLÀÝXww÷˜lPF0HРä»ÔÙÆl€­\†M»E½ j›>7¼éQozg5ÌɹhÀ ‹Çå¼7Ç ˆ€k0Ö„!ç®d¼§Y@Ü:bçíÞ½{“3”£¸úꫳlF5ÖÃÒ.O€4”¨À­€ƒ¡±±±®ÆÆÆùœ™»IV’pdL@ݨ(åaiCiòذRp£¡AšÖfji`m¶qsë8ÄYwi½q f4Šni”s“ 9Ÿ¨Û$ç³YÎ'’ÖÈuÀhÚ´iS£ç&9ßkä|nxä‘G6=üðì[·n½¶:89&ÆðÚk¯Jf€Ó©ìh°”:¡R.ÅÅfÝæËÒ-6x[Lý¸®TÌIÄk†[¨¯èÉõ‘׋Ôëpà…Jy,šwÍl• ;14Ç´E¦­¦MNÜ Œ!9™Í–¶Ô.ËEj©{›‰b/–WfC—ñôÄÔŸa'3gÈvà \gS ËÝe¹@Tl XÌ¢ü <áúPÀÑGi *F¶i"Pƒ±XÐ'ë«£¹¹ùŒì˜{L0 Mæë°(ãA¹?†D!ùºá¨(À6b€Ú¨jå²i³´ÔÑsm³¸ÙX¸ÚÇÛF5šFGGe4ɹn’sÞ, nMKK ÀÛÚ 6à¶Yne 4ËuZðàÁówïÞ}‘¼n£¸Fp\O.Æ-\©0”¸–op¡"léR1p•dÝæ Þ“u é¥ÅÖËKÙÉf¾®RØ4èfEëâ¹|ÙóÏǬ-µ Ë^3[̳½åd”üÙ¬Ôkô'ÓîѪnÕÚf(c9yrò6Ú´]{Âç“®\„Ƚ é)[qÙÆÌéIÀæ¸Ø-CÀ¸ÙÂ~€ôãJlÕVómµ ܈‹‚¿æšk2V »_°m˜cdÔ ã£ž5dÌœàpZÀ^l„†ƒÖÝÝ=,cPÀÀ€ÌÝAQÄhC ¨é[‚µ1‡U›/“–$ÓñiiÞ8<{@%7˜1Y˜ñ2R°äÜ@PÕäFG·Ä¸ÙbáŠX¸ ¼å·Íp2šå\7£tˆH˺uëÖžwÞykä·ÇÆnT¹n´µµ=E@õb(›p àÔâàú§ ×™1p‹ à–Óeº¬ÛlúªR-û–Jo.„±cØ\øÔ/:%ÔVJ9>o i_½dƒÙηuj;­Hú„¼mò_®ÇŠ6‹ç<Ó–(å="'Fä2ëçÉÕõØ4#fƒ- *Ê: 5·ÁÐéV,žp¢¸á–•ä1iÜlJ9nÓ¥Øí-5€Š€mn‚ùC öåºë®ËÀ€]Ì $`~‘QC ;†p(£vjí²èFÝ4#§OŸð0µ0Ý9P#XÓC³j¡ø´H›R€©(ýÊ_Fеqã7fáÏxF¶ƒÇï”Ǧj1â5Ô5Suà 0<{öìÉîã¼=öØcÙú|ðÁg›ÃæÁ6‹´À8UÐÝì’ ÓE÷­Ô {8¾·dHèX<i¨´Wð³ö]Áv£Ë"ÓlDµ(ÀyF_H/uW#Ï/¢KCÔmQ Ï7¯ òy;7ïqîêsc Y,O.Ä5º\À)¶Ê @ bÕžþô§g÷¡81'$X’†9/ 1.s팣ã2ÏN‹áïG&€€ÎÎÎ~İå@ ¾ÁÁ|XfM³i:>ÍsyêÒåĘe ûöí«áòX…>‘øñr!Ç1#.™LÄjF’‚€kŒµ¸Ö¶¶¶®€“_ë…^xRtоóöïßÉîÝ»/•ç62S  îsü,T$1hæìÛbÇ¿-'x[IIóqVZ‡Þ;‡!X\âF û{݃l}5ØJÝ‹X{Á0צY8›ù©ãÕØ–J'êÐ&ݺª(^ܯ"v:’*tV#ÓÆ:ÚÕœ¶ )ÄjéªÇE`ÇK)ö9ï3BnIb-ªMæî€6"dãe6Ëöv[žÛ¶*­WÌ­˜`¾Àå6 îB\^°m˜wPœt¨a ƒËó¸ê“b¼{äuCІÄ0÷Éæ W6}yí`¬ÙdDÀ‘­U0P—^ziúìg?;¹é¦›R$>Ü~ûíY@r¸Z glœpkàä·È€['àmœÌrmw ¾ðÑG½ì±Ç»DŒìfc.!þ 0œ¬æ Œ,æ®Ýb¹O+Þ–ª®Û|7Ê˵Ù],·)m}°ãlUDZ6dɆ*i×£-Œ«m®­•*”om,?˾GöÕ^8vJ°ýÈÕ¹@‹<‚6½) ­€iK夶çíkjC±j¡‚{6°Ðf™°X®—:Œ êÕŠ -è`³ÇfwS`Õ°SÁ}´ bc], ÔYôy+5=JXࢃ âiO{Zϰ…‰¹ ÀÏn`Õ8€[²=%óè¨æ™×‚ý†ÚÚÚúÄ0÷È|ê…  †1´Aêi ¦]Ÿ%Ô²qýõ×§/~ñ‹“_ûµ_ËŒ?Ž Ç ƒ*bÅr¥WpÍp¢#Zà>·îÈ‘#­6l8O®ÿyÌ:°–¹²A®ÿÅÿû¿ÿûÔíÛ·_(À­¸MÁ¾¡”®9&¨€}ËëóeÆY7o¯ð¶T¬[‘N«»ŒÛ’Á&aÇmbYã­Y/;VîyªÈ¾Ùsí¹?-°‡a%«¶ÿ~·¹æÇÖ¯_\Œt·Ì'0jx2<<Ü«€švƒzîO Ôl6çÔÆw,þÝßýÝ’u=‡@œu¥Z7ž¡q½€ökÛÛÛÑ.kƒÌ¸ blÛeþì]rÁ®]»žºsçÎ˨µ€}ƒË±µˆ빇Ǒ} ðÆÚoã^Üm%tÆRºL+]š¤’`n!MÛ+ý}ø`Ö`#é…Â<€'É96ËŠá`¦X”Eq¥Ä‡³I ÀµÈœi‘ù±VÆ:™3`ß6Ê<Ù(à¬KæØ1_çuuu]úÓŸþtÛC=tb#‘¨€älɾáq6Ì;€7°oÈFÅ|¬$û¶\.Ójé ê½w)2{‹Ø5öFÛ0•æÕ‹Ùž=óbÉ-Ø›ÁÞ˜2Ö½ŠI³ œg ½ßo §r}icv#Óf”•mÐ Èx;!“èr{¢mM‹ÔõÄaТSšUs.ØY™/úýE &”Ä ?ÏÛ0ë“  ;_ºJ‘ £MWê\Ýb³p¬-LÊÁv<ó™ÏÌ % '@: $\ `<öîÝ ×º¬—çoÞ¼¹SæÊ 2?å5ÝR=9Hë3ÌšfÕ< VJÊLˆ²lzÑcáê7žÀŽŽ®=-"ú£U6h“µñ /Ä8#ú`¿L›§<ñÄÛvíÚu©€µ€7fž‚íg*_‚7¶ÎbG¥ÝtKå2­¶øàåj<`úÁ¬ÁaÓˆë†Þ $ÐÙº¢ôVÙ6ÚþyÙ£¡ŽD–ðð - ÅÄÙßnÙ6›  Ÿ—çŽ)=Ý£e2m™›Fä°œÈçÏV÷ÅR¦¶'YȪ»V49,b·àÏ*¯ §40ñÙd,7(FŸcaç«ËÌEñT‚Ò_jš~µ U°i7ÝtSƬeÛ†kx5\g”¼@Ͱ“'OöÊ5:"×ÿ¨(U¸<Å€öŠáì’ùÑ-k¤7jšYLfv,ˆ@mu±p ®1ì[½aßÞÖˆÎYÛßßß!c݉'6ˆ~p;Ó¦MÝ2ŽÊãç ÛzèС§Êãëè:pÃ\ÅF.2l*t ¸P)7ù¹ÞK–û¹ˆU«† #Ø5\÷¼&áŒâð^€˜¼LXk‡µ ²ä‰µ×ÞgZ×§~ŽmÓÿÛøxk£óòbG’™Ý["h›hâÍÇ{l¶ö!¤oº×]ÁNÒP;‰õë<§‘½G‹ƒ‚Æç¤AY²=€C¼ ¨j¯›Â|”OdË–Np=þ¬g=+k`5°»eÉ\[ÄŠ¬ ‘]o›(ТLûd^ ˆ¡ìîèèèìFø@Ö4³f] ãÅ/úêp¥ü:×æ×žŽÝÖäc­l :e® æ?sJæ#ÀÛ&=2OÉs{d~]öóŸÿüŠÇ{l6îER Xa¸Pc ö › 7‚7è$Æ;Ux[ˆ\I]gòY°Akìý‰k °æu9ÐaEú1&ði7¦µ™,Áaš¶·^Èï“Ù³îPkŸC=N-xÓÿ+Ü@î-%3c|#h›´M’Oír2aœ6ÌVÃ2cA{Ì[¬¶ŽŒÍ‚ñ˜=^ðP?4=‰ìdõŽAçXD¬åwîC‰²U ^µÓþ0NºAao¾ùæŒ]CÜ\ߨÁÂè!^hß¾}pƒ¦¢,O‰ÒÁŽ`# °FmcÄBÅâ½Ä»PÌšG¨xŠšÄ‡’H’x0 ätì¥>ºnåþ…?¨[cG„Y˜6½;D\[›¾HO¼P¡jÊ–¦ M€ÐÄôšÚez» ýzÏŸ¯3pBÇDzPx`ß°ÃÅn ÃÕʼnàu(Jf¡Î¶è#ûVy0C7Á\GP”¬íÞ½®Ðq¹vGZ[[ÛÖÄX"¹ G®!‚Å;嚬YfÍÆ«MDV-J™à­&™§Cöm(ßôçó­GtM7]d#xZt bÞ.Ñ's»]æí^Ñ=W=zô©bð›Þ°)ó¹Ž5€ûlÐSoð€ÙYhÜÛRõ$]nV‰-À® ÀÀì@4®R )ãºxnP˜ðH {^=¢Äû,¯ûAˆÑÓ]ôqÎÖI:š|Ñ Ö‚¼æ¨ZãIl_–‚ÑLÛ˜œÌã2ºäÄn ¡w¯i¬f¼4ªY,]O¦xÙZl^œ[9éÉ^2C(õÙQ¦^cauƒ»Œ €X7780tEn‰åp«•]ƒbÄùG;'6°lPŠØµ‚E¥ù]»vY—1k]tQ§÷¹™»JÒkッƒG[[[ Ös°Öx"Öz“™5ÖÖ¦Êu¼èE/JÞõ®w¥/}éK³VW¸ÖQ¢”³¹øâ¿˜|ìcË€Û×¾öµôW¿ú•vjöà­É`(/ƒ…€/€7Hÿ–-[0—Ê<ÞúË_þr›l × á†à – P@,.»z¼U"ia¡àm©ÜbÒÅ5`†Âùgë;Øxh ¯ ·°áÇóéÙ;[žö–ÿ[Û[”lPtß&ÿ‘I³áJ#çÙ[Ë é÷uAÈë¥îÍõîYäÚŠA[ÉÐ÷#ÚöÈ„»Ù˱¨Ú[€¶ãçÿÖ Ï2b–…+¢ník¼Ý…ýl=QíDó*Mc‡„ßeÇÝ,åŽ;2Ö©ùP’Ìè*GATº«Âjk8'8·(Š‹þ›ÊعB9‚M€`MÆ„€² ¬!+/Öº XÓ1k3’ ^ýêW'wÞygzÛm·eÇÁ¸”(QÊ]‹Ÿ€Îxó›ßœÜ~ûíé¿ýÛ¿¥_ýêWk~ö³ŸÙ ó°aßzQfFæºntɆp“² d2(@ Kæ~ÀÛ}÷Ý·mß¾}xƒ.Xce}ľA/aS‰ %ÁËK,d>W¢-Vh½WZßUR/âøèeÜþ‡`5¸¬1Àn°ńë¦ë´;šèðܧ|Ÿ—€ç…… Ú{@K»4½ø9Û*Ò– ñιÆ¥IÙ—Ï Úª*s´Ú™6Òöcˆk“²FÓ·p¥ë ­'€E³ Äf¤xßïôµ(ß+ù¡'•M°T2„}KɺA)‚áA’” b«à–ÀN‹”™¦å‚·ù*³Õ^Æçó†nHž÷¼çe=B¡1ŸX· 1kÖàWB÷‚ƒbÔ:r°Ö#¯9#;[ Ö˜dÐkøÒ;î¸#}ãߘ}§U:Q¢ÌW [^ÿú×c¤?ÿùÏÓ{î¹§æ{ßûž+¦ët@±o}2{D×t¡ž ¬‰ sð†d†ÃòØ6€7ÙL6!T@ƒ7¶çÃ-Úe±˜4t’©ÞÒçt1\ŸÕªÓpýtÌÎ/Ø4/6tfx #M8·q¡³P,X(Á«º`íØ>  ìgÛ~¡ñá]Û"Pf3M½ö’êµí2N$~6~UIµ3mâ•“yÀM&àÓàôÜ¡,ð§ËqX0§›¾‡ÈzÀͺ>½ÊÍEŸg'Ÿu{ÚŠÌ!°ç¹Q±8à°±ƒÆ.‹¬îÃUx+Ü'° x«T*þ¹ ÆOúÓ3f …qÁnâ÷ #¶ðÉ'ŸL;::NŠX;ƒƒcÇŽõ(f­K5Æ­¹Ìš¾âÞóž÷dn×(QSÀÞÊxK¼•xÓºx8™÷Ö+z§7g7 0#xë•Ê0o÷ßÿÖƒ6¢ô \sLX xcË,€6‚72o )²šãÖØÈ]3køŸ^Æ>3v ˜:ßóR…6áÚ>yv¯5£¶Ÿ^õOf¿×sj²DÛB_îÅ[pfcë ±³7×Éž—#2me²mcŠ¢`ò$@›FÕ*M×mBö^F‹þë> &ͳ»Í†è‰WÁÙxú¸ìΆ  »TÄ'°s-\uÈ hëÅ ·3M±YR$Š/PˆèÉ– µÖàzƵ€› ±9è €,÷O‹=(`î4Jw ÕXk9³Ö•ƒµÖ†"X‹Ràíg?ûÀ[rï½÷Ž'g{?\ðvôèÑÑ7›ræmè²Ë.ë‘Ç‚yð¶E6Žõô¼´¼d´A/iðÐ1_Vy%/Ö P#XcžcÜ€tôìÎ!ë{Z[Vm,¹ íŸSÖmjmª¶ƒshËzÙs`o½ãòZDê×[û ¿¨¼fW¾9Ñ:¹êâÙª´Ù¬&¸H I¶œô^‹¼5°ñZ^X ¤/¤¾àžûÒ2ož+3”!SN’‚-hÓš½l J±Ë€ÃeÑK9 5(L¸$XÝÊÀÏaÁë–%çº`ç ×  à ¥ #‚óµÿþä‰'ž@üZ—¼ö tïðÖ°&¯ëPÌZ—k6Ø5‚µ(U%/|á 1,x+%~Æé”ë4gÞÞPã Ù¦C[¶lA˜·Ëe}\*€¢ë À` à·ðhð†Í¨fÞV"x[³FV àŒ-§ØØÉiØ@˜áµÐë8Ðûqìl@Ñ™¢ÖöxY›ÞïÐ1Ö^½5 šDbؤÛäÝ‚JëõŽ«¨ü‡¼gWrv—™ªÌ­vЦ³˜²¸™œOȉ¾Ñ«‹¦Y7 fëOf!í$¶›M·¬âwÙX5‹ô½‘^öªmŸ5[M8²noXÔØqaAc`— … ðÆx<ÎzJÌ8=—\§´È½å–[²RŒX”¹AwïÞ="çµMÎéQQ¬ý¢4ÑÁ@n;PO0k:v­/™Y¾cª(î«^õª,†1J”•Þ>ð”î¿ÿ~[ ݺMûEõ ô‹êxC7‘‘­[·v ¨¸PÖÔÇ¿‰ ØHÂ3]eÁtØ7ê.– ©x«!PcÉ‚4º@ñ8Ž:¿ç¿ç‡=Càè Õ.Ak{4ãæ5v·€ÇÖD³í-² Ζáðˆ /ÆÛº]=òÅ‚9MÓ¿Á±Ëcò;óùK=MÐV•ÌE}•ê Ú2†B&Ù!ír²/²1^¡V¡^¤–‰›­®Í0õ¢—QêËPÑ@ݪ· ^,=¾ éö úÅ¢F 1¸ù֘ݗþÇâ'xÃ-Ájeßp^®¿þú ¬!3ç`•‰(ßñä“ONôöö¢|Ça¨[5$Ï£j|‡¼Vƒ5ºBYƒ;7Æ­¥Ï{ÞóÒ¿ú«¿JQo-J”•Þ~ñ‹_d¥B¾ð…/¤=ôfÞ´w„§o¢‡f€7Yc#²Ž:|l>pàÀå²1Ú€M$tt;¿°¼aâ7¸Mɼ-$æm¹švr°ñ>7÷пH:c1\œèk¼—Œ6è::Ô/T–`œ¸%1èEâ{˜èÂ'É2\ö¸´ý ¹4½X;ýÓ6ß&j‘ßqPž?©qFRÅñlÕ Ú¼dœÐ¹ HH¸H_ËpyEù8élGobè݇¥u-èÒ”¬­Ers†Ê‰XfÐîd¼‰ÚEi‰Æ*äP|P€pI`‘ÃÝp†X*L°px1]:î Ê b¡mhªE øà’XC\$ipNJߌX@¸BåœEzH M—àá¶¶¶n1b,: Xó\¡SI×^{múñ<ýýßÿý³*xG‰²Rs÷ oxCòš×¼&ýò—¿Œ:oé‘#GB¥Bè6ÝÖ/Ÿ~ÑC}oycúQÑIgD.ݽ{÷Vo-oÐG¬í6xæ’Õþ«¼Q÷“QÓ`ŒY5Ú€5 Êð:œö2†€-¨ìßóÙ˜lm·<²C5 Èl9 ¯¨n¨÷§®âPÔ—Ô’'–ô(j}êâàØÍÇÕƒÝg"h[Û6¦hKœÔQ ;eRß"÷›mÚoÙ€G”©Á–eѬûÑ+0hýé^òƒuq†šízÕ+lÙ¹¢ÔxžPëa,° …—Àܾ}û2 ÁÝØÑA9⽺š9”ÈJtŸBYâ7!É®(J(?€SÙùgñˆÉ‘ÇmÚ´©]”â°œŸ>´œB­5Ô:ß:!  é;ÞñŽÌàD‰²ÀáCúPòÖ·¾5ýä'?™~ó›ßLE—„Šô2Þ­_to}¢‡Þq:"ú¦]tË–;vl‘µÖÀBáÐCÐU4`¢h†õÄdè5è²…¸M+ ÒØÚÉj,ÛÁߟj0èV ÖðZèfv\&PÕöA{i<{c«"xå2f‹÷³¡9áÅÉi›FðfÝ©6{ÕºM½r ¨i{[äeËmærŒa½"U™„PíLwnº¸ã Šà PØ%ç&Ë¥š2ÖØkIåµ³òº.„:„2of«SÇcã®@  ™¦Po¾‡›”J“éø`çà:…‚dv—pP(ÕæBøDQ\ÝÉJǸµ;w"Ñ`Bæ11Gd~Á˜ ËóÝò›:U’A§a׬+4ëú›¿ù›‰€µôÅ/~q´êQÎ A\è½÷Þ›þà?Hîºë®ÒÃ?JV˜á6E]Cg²ÎúE¿ ·é˜lrÀÄ”õ·UôÊ`ܰáb²Ë_hðý°ƒûÐCÐkìmj ªWŠE#H#“¦5Ͱ‘Q£þ‡§ú:—¥;p¼kØ$ã·±'3BCIÖSc» x¯'à)Úä—SD^Û4ÝŽÑcú¬m²1ÛÚ–†¾W'n5s畱€3g|™€Ð¯<$Uí]  »5 ÚÀ‚“~@&ÛÕ^¶¨ZúB[ÿ{9k-¬'‡®yc zqjüës×ÎÖÀ±¿Q7îµECÁ™öØôyx£Ò óF¬@ @Þñâ5P¢x ]¨:E¿ ¡"Päè °†ÌP?;ÀÅp víäÉ“§Å´‰Ñè–s1‚â¸òš.1(¬ivM§ˆg`Mmz÷Ýw§·ß~ûŠ/è%Ê|äå/yò’—¼$ýë¿þëôsŸû\*z%”¬0¨ÀÛ Ú½‰¾ët£ètXß¶mÖ!2M·‰nY„ØSVû¸® ÀÁæz : : :ú € ©¨€k`PÇ“E#“f5€6 Ôä,Pñ¬áØ.Yäǎ߇ߣÃYpŸõÖŠBhÈP‘iòâ¿lÜ´í3ê5a÷:•O±ž©7Ë$^¡\sç•ßò’ó4ÛfÁ¶œË's}>èl¼«Ö5Zí -Q ‹œ¸×Ë{X.Ê•rQjmܘö«ÛIe3:-Ëew"ÞâUyÖ; äi0æ¥TëIUä:µµß¼²%^M;‰-µMæ `‹à ;[îôÀJAQB©`ÈáuPšx;C¼Ÿ)éoÀ‘Ö ·Ø »p”2yÎsž“Ü|óÍ™{ÊdžcjkkË’ <ˆzkmˆ§‘…^DzL¸×@ײ&µmž»Ï+‡a_gÃt¬Ý±ÉQ64Çûl¯(| è•Åòº éÆïLÅÜñµ:+Ö{<>$¯{<'ƒú”>§k´TÍk«ÚA›v‘jЖÅm“‹p•Eòv·`ѹ-‚ê¡f'¢ü¶Î ?ÏfÅXðêº`ƒ(uÖ«ÝUx Å۩؆ó^Ÿ7ý>;Á€"” ]â9(ôÙZeQ<"»›+äBÔZš8ÔgÔöJ Õ‹ÑõjlFKQ‘¢×{,›íñÚv#¡ S¯q¯‘Euå¼ÅBz¬Á]A(Ç 8ÿF‡Çð°NØãh ÀÆÏfQH(Ub ¹1pÊplp ¹1kˆ¹Ã1@9²l 2dóírlmDQ?jT~G¯C·|W·q…2vͲkYV¨üÞôóŸÿ|zÇwDWh”(òŠW¼"s™~æ3ŸÉܦ*ËÔ+Î;5DÇ£ÛÈʼÉz=Oô Bådšnýqô ]¦Ð;oØl¼A§@·@¯ÐÕ¨õ»ÕÃ6fͺG½D5‚4|¾ß 0ÆbÀx/Ž :Lj׳ޜNæ •¯²›¯Ÿ¨­ êy•ôqÏöÙE• <Æ/æô뽘l ClŸ÷ý–}´$ˆq'¢H¿bÙ4h«Êñ+ ´%ÉÌ,R‚6ÑrËÛ+ áZ/K$4Á4 îQ³–Òõ&ólÉKMö²`ì·Œ›^4TZÙ¥MźYV.ô[ PFto2nD³oPFp7"û’.U <ÏVZx]wø.*8­ØÜôŽ –JPÓäü,|v«L˜À÷ı6Ž Ý :4(çõÖNËçŽ!~¦³³³[>›IjÉÌ~¡–]+½ímo˲BÁêE‰evÁº¼û׿þõY–é~ðHͺ '3“2NtÀЉ'†dsà¶AÖðÄÖ­[ûPœWÖñVvÐXÿdýñ}pY2ã”›8né y*¬Þ× .O|t{>kV è,|?³@3ä ÏÃãÀ²º?¨‘^qu †ôBdDÈ•êé|?¦”Æôwi×§v [{êÙm¿t·PÖ«Í^µÞ+¯*—óŒŒÑ39Pë¥ç.©ò.+´a—£êÔ©‰d¦‹´77¨ëä"?$ãr¹ßhicP×úâyì—×Å€,›] ¶›WÆóÓ‡X˜Pgê¶“8TÞÄ2¶Y¯G_{Ôs(6I qT„dÕp ÌÝ“` nX$PìÞ½%8K‡&¤-àëMd/ðÑcÀôdµÁ÷¡f÷vñ„ÞS´›²ŸçQßši´©Ú¡êÚlµeGw@î㽺Ñ1Zg˜ÑÕ Æ. ÛÒEgmé]Ý«P†d×ÐÕ ;¯ut\vÜmÈDð8ÜÙÙÙ+÷äó¦Kµ‡]KfÆ8¤?ùÉO’ýèG«¶k”(K-/øú׿ž¼êU¯Jßÿþ÷—ÀŠ+ð¦ëtjð– d{Ÿ9sfD6cëe·Ê8rÉ%—ôÈ¿\ôÁÔ3ì¨Ý¢“,hÓnS2jܤ2Ž`Ÿ‰ (=ÜÐÂõI÷¨—h²$ôæ?”Tç%¬yñg¡þ£!ö̲fž- ‘ ÖCe+Ø$9Wèõê¶RT^˦È5<*ïÙ“Ï!²lÚ5:ÏŒÝÚæ)X(#qçw¢­ÙÖ›Úõ2šå¢l€ð’üÍ@굪©i½´ Ž;ýZK•ñÀ]¨…ˆeå¼Åm'°žÜ–U´µæ¼Þlº0a(óÈ;ý~} ÜbÒDxAa Q±11¥D¨xtaJ€7 Ú˜¤ÀÄî„ù[e Éw°Ö~üøñù޹ߗ/VÍ®…b×H•Ï Ë«}1G‰R$X¿d€ªM^ùÊW&¿õ[¿&;ýû¿ÿ{Ôv³í°,x#€}€s`ßÞ&6mÚ4¸eË–‹E_lýЈzŒØÌA?½×,>õ™fÛtI&<éÌTê%n©ó˜¡Jäé` ~<öÌV °d„ÕËÚ-Éã²á4¡Bìž±¶Ó²“EŸg+„@^ˆlÐ œWïÍ6«÷ìÌí˜\Ó_Ü ]£Ùæüƒü`úÙÏ~6‚¶J°mh°-»°´¿¿ßs‘fl›\˜ýØa‰Á¾‚ÀÍë< '‚e¼RzaÛX5‹ò-¨Ó­Eìó^xÔª·æ1gÀÒ#ÄÒY:TÛRó^¬… аÂç²12cáè¶Ð±tMà¼Ñý Þ«3Y âð=pvtt…Pî÷ɑ؟ÓáÝe°kS‰d×¢©²Z:ñþûïÏÖO5 6loûÛÑà­$ë×cÝty©!ºcDlŨèŒOk¸Êèðvå ^ð‚ó¡7K†˜7ö*e¶:]–sdäÒtYêjœG]®ˆµ'1´¾Õå:B:ÔÏU©7È^=Q}ß+Nk]®EÝ~<×iQ¬µf¾<Ûh½^úÜxBàÎ#+ìÿÖfòXåº Žíx®ç»ÓFJé…/|aæÕûË¿ü˪_ÇUÚ°(@?#[ïK_úÙ6&$ÐÍÕ*£E^»]þfQJ-4ävb„R¨mÑ@Ûm ÔrÃ+T«'×;T/`=‹v2 Õʱ“:²ôó¡’&vÑ{eUx<¶-ŠÝq1K Ê44[»@I’‘cì]¤dßÀÔ±|n¡ ‘h€2¸Ef¨(ä½òÙ'E‰ŽÊùN¦ëúuÍ—]‹eµÀ ŒSµ Ö½è‰TmœBµÝÎbÝDOÁ]:" jÝéÓ§G9Ò·gÏžKo¾ùæ+o¹å–$AW´}CÌ-nZ†Å²mÐYìê‚çù˜W:ŠzOƒ€z Ç‹Yöj†zöÁ`6Ðß#B ϳ^âç…ÑÇ­“¿Û«Sjíf¨S‚WíÁ~†.B,·§e<žÏnÚf$  å ìÐJ}©_) ç}ï{_Ú\Œ2w\ƒŠêhíÒ!{»øè±H!Y7Ëé n‹êEhÎ.Õ“DOžPŒöB}áfËî µ«òœ÷˜]x<–móÀW>Å‚[}¾taJívæ.—1#t©°@â@Ø5¼Ù©yVhVjääÉ“Gä:ïÍ"A}_23v­#²kQ¢T¿@hµ£Ö¦Çº Ö ÿˆýß*cL6sû¸¹ï¾û®½í¶Û.¼é¦›2"€KXPœ-øÞBeA,H(ÒÁ8…ÊhXp¤É{ –½Óžˆˆ²v ä)±Ï{ßêdÏý­E½PíoðŽ=TùÁ;¯¹ ûò@®ï{“éD³±lÏyÎsJ¿ýÛ¿%®­Y1  ±mïyÏ{àsf±]æî°a´È…Û';­‹ÅÀ_T6,J.7sÆ.¯}T(!Á£·½ªÍ|½§$ô..æô$/*Ähid TõoÔ‹U» ¼sGvM9‚5¯-ŒvA0–îR°kÈÔdË,< †^¡èÈ »h¤þï’¯;‘Lgë¸Í®uص‰Ö¢DI²LË¿ù›¿É6HU&éöíÛáiAéu€·Ñ|C¿VôW—åíÇߺoß¾«¼5 µ€t~3ô ôº¦À` ݆ô¬W0ÝËlô\›ž{2ÔßÓc˜¬g¤YH[ÀЦjÛJâšaã}ÝöE÷ëÓ÷q½¸‘ÍÀ†]1® ä¢ÛÉ“'ËŽ|w¾'’éÆ¿ºîÚ™dºîZOd×¢D ÖÝ›Þô¦ª<6Ľò•¯LÿüÏÿܶÂÒMè-pÓà öa؆¿üå/;ņ<í/xÁÏ|æ3§²K¡oP®L¾l³.*ðÖxí=ðaCelÖ¥WyÀ‹Ó6È ¯±¬’N®³ vü-¶ø­eß,ÀóHË€ñL™ «hWÈ}ëÙçPèkxŽŽŽ‘ï~2Ÿºt¯aÙ²$˜•$+ ´a'ôá8}×»ÞÅ]Ö°2Ô-j4H»¯££ãÿ]vÙeMX”Œ‘ÂбU–ñÒMsËmàn'm¨Q®žàvXpgã¼Åâæõèoۢݻv¤™¶P.»S õí³ @ǵÐ` ;\Ü"ž »_*P¼1'=öXæ•]ðpww÷n9¾cÊM¢Ù5í ÕtxŒ]‹¥ˆÎ’µ ýˆ5XrÛm·%?ÿùϳºn÷ÜsOšÇ½…bÝ4hÎ_ŸÛ‡®Ã‡ooooßzðàÁ«n½õÖô(Æl#u»¼ P»lmi ¯§Ý¬{)ä±6Æ+ÉäU(êè*'bm Š †æM(QMÛÓ*kj[å¡÷ɼ5X¯\/f‹êM¼Že—÷—¾üå/§!—lm’w¾óÉ?þã?&Û·o·lÛšœiâ¿RåC²{¼48k€±]cÝBÙ¤ET®­?ã5Ôµ“˶§²»4»­ËÖféxiÙ¡Ö!ž+ÔfÙ^«Ü5ñý:ÐÖ* ‚4¿FVÏÑ ªY6Ä®Qc÷ÜâûÑ+ôÑGÍâ äz—²f×B±kjWÕÙµ(QV‡@îsŸK^þò—[Ö­`ÝxÃãrÖ ™ý÷Ýw_‡³k_ð‚\xóÍ7g‰NÛ¶mËâhâð?˜}¸Lá­Í ÛVP{Atp¼­f uª K¡gHoª-ô\Œò<;öø‹ Ýzî]¯{Žw,xN%ˆ õö."%ô±å-ÂÆäºÞŸëÿ>eø?3F'^ûÚצÏþóWÜXq  “ ‡e×¥ë¶Á07åÀmMÚšäBî—Ŷ^€Úõ_|qFƒÓUŠ%û[raxY˜^‡PoÒPüXˆý²€Ï£uV©fâ< ¹gC4¾·Ë±¿‹ç<”ÄÁçô­u‡êA°Æ¢¹,ÿA Æ¦Ï …œ<ùä“Èð’Ýîî<>Ác×B±k‘]‹eõ Y·O}êSé¿øE²nÉÙ±nCðÖšoò»Ž?þà½÷ÞûTgW xk¼úê«3І$l˜7Ô|³Ý=FLoîm–?7À:TÇëm™3k,8òêªYpdm›%¼X²rb±½nA³… yµêŠì¤=çú½lxØô‡T›‘a,[Öý@^y³"ç~ýJÿùÏ'¯xÅ+Òw¿ûÝ¥Gy¤(ÖÍs™2Ö­Yôÿ_ýêWG޹æùÏþSÀºÁEŠÞ¡ÖeŠ2!ØT2ËTÛ .¼x-½á •‰²É`åÕá,jì ð÷B]tïX4£hí”<=2Äs§zmuX„ÇÆsÆnÝÝÝ;ä¹}Ét[‡aÙ¦J|ì£GmK(Ÿþô§“ï~÷»igg§ÎlÌ·¦´áÿú÷ïßß$3€ë‚!vEá.Å-cìBѱnd”4å]T °xñ¾Œº°^Ú…©w*¡ÀUá³.M½s²ŠÀö®ãyÐY¡hº”šN.Ð}Eá¢f3y ìfáEqÜ|0s‡;vl «« ìÚÉ\ ó÷Ø5/vm"²kQ¢œ¬ÛÇ?þñŒuË×»íaê嵬[÷É“'þþ÷¿Ù¡C‡aÚtÕUWeº .SÆÚÂm ð†ú¨ï¦m†eÍô¦9ªbÙ6¯AQ¨f›ý.ÇlíŠNNÓ¶Fƒ³aÁ[ òBÅã= ²µÚ6êcÀs,Ø.¶cÿøøø °1,Û`>&žýìg—ÞûÞ÷®Øù¾bAv@H¿ãŽ;´›F»A·Æ|Ôõöö>pàÀÈD8 ,ÃUŠûŒw³%B¼µ6È Ì÷he Ì,ð³ _OÒPcz[zÃÛµèx¯ü‡›`ƒIµ”ßÉx5/#”¥=»†Av Ÿ‡$Ùé&{öìIÛÛÛ‹"ÜŸƒ4Æ®Yv­Sí¢lìZÆ®Ýzë­%d›¢f”(QV¯@¯ÜsÏ=(œõ0•Mºo¡ ÓAu;ôÐCu»VtÈf°n¯¡½ÀƬ’0Þ°á´íË à·±m^œ±%BåGŠØ6û¸­Ÿf?'·ç}§F-óæÅx‡ÎI¨æ§=~¶Ü[vdttô‘üšöävát2í}™J>Ï*}å+_IVZòÁªmÛo¿=ùñœ~ç;ßÑI õ ¬¸á±Z1Þ÷Ëb¾U.ØF.t"u Þ0à6Eì«_‡&£e´láÝ"¶K¿GÓãv±Y=4ñíâ°‹Þ‚;ýžPV‘®»f“ ¬k”®O²l8¯ºŒÎ/56€5(A(;€5°k¢$å´÷"Ñ ]í’CìšÍ %õ=!ÇQúüç?ŸþéŸþiríµ×FÐ%Ê9"¯~õ«Á¼¥úЇÒø‡ÐI ^¬Û`€uCD{zúô釸Ã^"6ãjo-Ð%Ð[Øô#î·p(‚x7xk«F½hãÆl餢öƒ6KÕ2h¡>×ìÑŠk¶¶¡‘ý?” kÁ›íIʂš¤…"w©&+`kÀ€ÊùFøÌƒÉt#ø39`#˦ÛUM úÄJL>X5  ÔüÓŸþ4mkkÓF¾> ê>ÐGmGGÇ}r{‹\ø¸øl …PAÐÖ ‹]ó¦Y7»uL—7éC­9B…p5Û¦AU¨2´—:mc<¥ ²m.O壓 ,Û`†ÿuéë `Ó®P|2CÑ Q”â„\—²[jK¦‹±k:vm»vÓM7¥2'R´ëaÛ™(Q¢Ì.XÃÕZîc.ÿßøFòGôGéwÞ‰ Ó1ÑuE¥Af´ÂJ&KƒduÝDÙ±cêº]yóÍ7oyîsŸ[³uëÖŒá¹ôÒK³'X8Üg¼À¼5t9êLR«ƒ)E ÎÖæô˜3Êâ±h³¾P)§›×v ².T4â|Ør ^Qᢾ¸¶º³³ó8: %ÓIidØN'Ž[nô»ï¾{ÅÏïÚ¾ó錄þë¿.sªÄø¶~Úx#p»_.þsåõçk €ƒ…±Ú4ˆ£ÛÔ+¢'±Bv'ãì ±yzQèÅë5Ô;5/nAÖ®Pý¼ÎÕ5ØlW°–ú>Ù5$൷·gìÚ#<‚l§å¼îÉXI]?Í®uÎÆ® ,½ï}ïK?ö±M5Ä&ÈŽ%ÊìÏ—¾ô¥jìˆ0/ ]óš×¼&=pà@ªšÏkðæ¹Ky_Ǻ¥²yâ'?ùÉ©ƒ^#Àmà 7ܹH˜ýŽÿàÀºÁe =Gðæm ½RÞfßöãÔåA4è³ïõ2V½ìS› `Ù;MXWÔ Õ֗󘹢~¨Hâøq¾a[Μ9ƒÖ…ÀÖž3É´[4Ë»ž¢ fÈUAÛ X•Ï}îs Äu6i]>êÕ} \µAé÷Ë.êæÑÑÑ,9iø…ápƒ"xƒ nSôÌİM|½ Ó®S;iuýª¶îJý:ÒΡ2%š!ó öêîšmãwé÷ë®¶8.ÿ'»FW(Ù5Þûî»/l‡F}ªHîX»ª»–±kÏ|æ33víE/zQ´¼Q¢ÌS ÛÐãy• AÛ˜n¼ &~yŒuýxêСCHVxê¾}û.ÎsžÓ—)ôlì¼ o¨ëÖ à ýLa;B}E½¸2¯š€ÎìôbCuÎB}°=OfÁì÷Øî:^y(›Dç…ìh¢Â‹-ÓÉ <&5œ[ØÙèÀöX2]5Â6-šÅ±}õ«_MŸþô§¯Š‰\¿ZVä>ðä¿øEzï½÷r1æ`mhÃ%µ]ß###[áFC )À,‹ñbAba´±¾›®óféoõ£'§u[jÀæe–jpVÌI—¥uz4ºÞ9Ùb»¶ì‡fÕÜt¿PJv »!í Åk÷ìÙ“¹Bå¶„D9×óP)_\Ü1õص~]{ï{ß‹¬±)v-J”(Q €[ªÁ›Í. °nY¬›l>÷=üðÃímmmW>ëYϺ èQ>zx@ YÀ ]][ ¢C6ŠX+[ŠIÛ˶…€™×!ԹǾP?R}Ü0óÂ}4Aá……ܳ´¿°Ë°µr.wæí G Ãv*¿íHLâÿàþ`ÕLâUÚp¿ùÍo&/{ÙËJÛ·oWàÌ‚67º=pà€Ü >íâ‹/®ØÀ¢ãâÀ¤H!ó  åB˜´€iieÆÖµpìBðn½89o'f)t¯Ô¹MçÖîO=l¿P‚6»FÆD¼ÊŠe.6óp~˜t°v2¿= °½êU¯J?ûÙÏ®ªÉ[¿š~ Õ·¿ýíD y* ƒÛ™5 Ø(²xvËŽ¨_ÅCCCÍØ-aA1Ó…,€ Ø7€7458&.à1$.ÀuÊ÷뉩©c§Ÿ·õtô¤Ö‹Ó닪BÔ´¥Áµ;”‹‹HlºX.Ëxà|`äÒ E…ÇЯróšký¨£#¿ëd®u¢AŸÖè =«îš|ïTìYÑ(Q¢DY 릻)XÖm@m7$Ó±np™žlkkë@qÙÞ½{/ö³ŸÝŒŽ °!ÐOoo(È àFð†oÜð{l™Îl¡s‹"4¼ ¾u!—©½oãßBql^Q/Oÿ6vÈaýTØ\ØR±½]]]ÉkN+ÀÖá¶e/ÆŸ÷¼ç•¾õ­o•=!VJúÕ¶á·þá¦4<<ÌÄ=›y¿¤Æ™Çd È{nEtÒ¹1aˆöé.%ppÃsgxl¬ñ÷Ã…ÏÀódàØƒ€È[(ºƒl¡6Uv§¢™4¯ ®eÕ,æÁš.çA÷(“ X —` ŠÌ’ :4"`íüÞ£ùv±ºgh—l=9`cm™¡haöâ¿8šœ(Q¢T’uK¬ÛaÝ —P2j}2í2-¡£ÂŽ;N>|x›Ø -7Þxc ó¼1̤ ó"ÓT@^6°¹Å€ÇF·T$Øa¼²·±e[ÚrE]yìë½²SÚîØØ»ƒbm×!V =ÁÀÿ°™p' ¸=*6õÉd:© [6޳$#ñ ³Ó°Ñå‹$Gж òÜç>7ùÚ×¾–¾å-o!n›]¨¥|Õ-äWG޹RЕ²àê°àòcë+ ,@ÖŠaÏ3N52pÈáqL ‚<›þì-(/»4Ø,5®¿Þå„Àšíjû‡ÒM¬û„æôuòÐC%?þxrðàÁqÙU‘ßy8™Ž[UJ¯G6ë e¢ÁTÏP9Ç¥O|âé{ÞóžìœG‰%Ê"±nEyÔØ˜?¶ž¬Þ+ö<ðÀq°n×]wÝæg<ã5즻æ öà nRlrq æ …qo6ÑÍ2A¡d7mGl6[åÀ²{Ôÿ|í j¿ÛVF•0ñ˜>VHƒMaV(l#¼V§NEGùœ¶d:ŒF×a 6|ÅÐÐPúú׿~N@ €y%T¨_­«ðÿø1qÒ·¾õ­ºcB’ø¨n©,–Ý'Nœèx­€“M,ý @f™7‘eìÜ„˜˜0`ÚHÓ•Êø7ÆÀqx}àŠjàXZ7ÆÄÌQ Òtö(“ð[X¤÷±˜XÃŽ` ¿ÊÝ vî܉ŒÐ’€·ò›å ,M¦³BìÁš.ã1Êk‚˜5°kÏzÖ³¢i‰%Êr±nÆmãL¦cÝÞP/EðFWï/~ñ‹Ã{öìÙvýõ×?EÀ[ïÛ»=ŠÍ/À*¼qà0oº/¶v…zÅiÏú!Ò€y%<,øÓ$€Ç¼éÿ½"½0Ò^²v'ìì ìâyØ8ü.üN9WƒrXŠç»?™.ÿt:mºxn¿²çDŸéúÕþßúÖ·fsJ€]¥©³“3ƒÀ-•IµOP»•+dA]*‹­ Ž“€† 虨 ë–± Vbb³¼A™9,LUÀÛ…×]w]Í–-[¦˜7è_7lŽaØÀŒ›Nv£·¯¥§†ì˜Ç²áy»Á×̺0µ{S^÷ÈmW4HÓ-"ׇçq,°k¬*ve\nQêP~þ˜¨FÛA—¨nOÕ—L»D'ÎÀvN€6 ÜÞùÎw–dÒUê7Ÿr ^¦`Ü—q¬¯¯o›L°§Ø¼aaÜðè]€tŒþÇ$ÖŒ‰ i¼e,†fâäôgè]“u}’$“ÆR&ßÁªÒº‹vü\ Ʋ²Àä¹ãò½'rÐ¥‹S2ËŠ`­G±jdÖ¸;bÜZ–h ç§ôgögé'>ñ‰XÆ#J”(ÕÞBšźyàö›ÿ漑Íp—ŒÖ]»v]rÙe—m¾òÊ+›áY`rl ³*a Ÿà8è.¥gFÇJ[/ í>ƒ`N³fºjו6ƒÞͨÙN8$)˜õŠÁc$³&cB@<4GrA/po2]\`­KmøuÜsz.¶s ´AP*å@Þð†7”Ð. §yë~tº• ^ڙȢè±Q@Ì¥²°.jmmmPÓI Xpl¸ÏÚføŸ“^÷ðÔlc æl!\[òšïÚÝioÞj%€Ïx 6ÄÊý òš²;K9‘œãÑ«Ø5¬ÑÊØÁÒ«_ýj4ñMQ˜2J”(QªUпR6þ¥x@Ǻ&3]¦¸õ*ÖY¦¼u‹ÞíݳgÏ!‘‹}ôÑKD6lݺµyÙ]A{l`W„lˆfÛôÐá5:ÌF61©ã¡µm¡·H'­1.MwÑá÷ðx&óø¼aùÿ”Ó±üÜè$A]Š€m »’™îÐñs°s  ‚r ÿ÷ÿ—þáþaòßÿýßI8SH×ça¼c°èš°…Ñ)c€·§ {бõŒwPc¼‚öñsêëú–t³.ÁA—¥mÿ¡[QÙ¦Á\\ \à¤ÑY’„”;;<`ÉmŸ,ºÓòúSù‚á¹7`Mg…²«AO¬MÑÙ×]w]ú©O}*ºB£D‰²"á0?ùÉOÒú§Jïºë®ôÔ©SÚnØÒ }j«[«oMù(‰žm;>)ëŸ|òÉ‹6oÞ|Ñ¥—^Ú‚ìR€7ľÀ1|…  öF22kl|Ž%-¬÷ƶÇÒI Œ{¶É ušÉ#£Æ˜¼<ƒ3•û§ØôŸÎÏ §)*²®Ë@ÀvNJý¹ø£ðù_ÿõ_éG>ò4š·ÀM»Imz÷†d:VAï˜údÒÊÜì?,;‰ ~.@¶IÛ:v à p£‹R»,m{(]'Mïht¦¾o!”MzÐÝp«ú©Èo8#¯=-ŸÑ›ŸÝòeÔk½ °¬õ'3“ ¦Jx èá;ÞñŽôÎ;ïÌ”P”(Q¢¬…¾}ÛÛÞ–¼ò•¯L¿ð…/¤hD.ú4M‰ Ô‘ÔàæŸa7´#øœNÑÃû¶Q6þ ì”èÍ5`ßÙœžqÓ¶ 7ÿ tA›Ž†ð~($‡›¾Ÿ¶…˜TÀÚ¤jr¿O~GfKòßÏ$í¡¡K™›ý.Öt¨±äŒ_[‘ m± ;&çg?ûÙôe/{Yòö·¿½´{÷n›âí-¾ù$[ŸÌ,ªÈEW’ ~F&q‡Œ™Àëem¶QQ«€¸z˲´ÑuJw(㸫²ý@ù(\`X@xœ ‹LSåºvœÜŽËÀâê’ÉŒœQE;{&ûÕΨG¶¾³–¹Aåw–ÞøÆ7¦wß}wV›(J”(QVšÐ•öÅ/~õ@Ó~ô£¥ï}ï{%³ñ×ÀDon×+Ö6ÄÚŒ!ÑéÃbCNÊhjkk[/ ìüõë×oжvÓ¦MuèB§«Юèͽ¯±Þ Øèt ëÂvà>ÃiðAZNŒ¨Áó”Û’\ÿ'M¿¶!lºf'c×è-%ç¨;tE6Lš¯ýë3¶X‚ÝÈk_ûÚôK_úRVE99;ÐÔ–¯Ð»&™rÑ5å ®N&p‡Œ:ùì5}}}­ÂäÖËâj‘EÖ$€¬F3kzÀ¼A¸uD]ÏM/6,B.0€8¹•V]½ò\¯¼Ž;PÔ<™‹©W >ÆîŠØŒ¹ô¦7½)ýà?˜Ä¸µ(Q¢¬&Nûîw¿›þèG?Jî¹çžÒüãý0 e½dÞZxkÊíHäjOàP;´áøñãk`C¬mhmm]ßÒÒ²1ÕLˆcÆ&=7r^=O‚PQze¸Ñ§§FW0È R¹‘Ñ/¯éÅãëK¦ËoØMÿ¨9ý Ĩi°¦Ë@Evm¥¶»îºk)¿R»í®É.<½cÒq o\tõdïä÷ô£iºLò:™ð ²pše´È‚j‘ŵFn›¬5Ém½üQ«S¬m†j ˜”1yl\n‡äfC^‡1dV’„Ëž ™E¦Ak¿bÕ XËb —ÀbÞyçé-·Ü²,ó€|¥4Ž%Ju t \’ž¼ô¥/ÅHï»ï¾D6¨%¹MEÏ{%B´ i5co-xk }¦{``à˜Œúü5ͲÉ_+mÜ®À&w›än¼;u:6šÚXíþdE¹…—Ûa ¹ öoPÆÒ÷š#P³2Ž–uÔzh´‰ìÚJmË$^z7Á “´¾ÕÙ-±I³bÞàê’¢dtçÌVQmþ|=‡,®:Œü>ý¡õò¾’ü;‘NJ)göFÕ‚™ìRRõÜx23Ê6Hî7C5‚ºQ Öð}P P`¿ó;¿“uL@?R[h)„±Q¢D‰²PAÜïßþíßûCïåzÝë^‡Vˆéý÷ßo=76Û¾/·ëƒ$€õâ4äv¢N‘ `½:ºòÇk•AÙ¹­Å©Ë7ý9xƒý˜Èÿ×ݬI ÙÌ7`6ý}ÉÌŽg…Ó`´¶¶¦ì%‚¶¹°n¡º<œ„ŒIЋMÇ)Xæ­A-¦:µÀ0jÔâ®!“V&àIùxêzt¡cÉÙÔµ]d¼4;¡aÖøùÙw„þû¿ÿ{6¢D‰e5²êßýîw/„ÐÀ†à­92-jÿNݶ$3cÞʱ%ùíhnÒ܆ÔЖ”Ù=ul¡í×­m‰fÖBÿeKl7œ$@r”ÚæÜ3Ymvi¿g\`úVÇ)è@S½cª7àŽ·º;os|za…Ú¯L 6’Ìtƒ&3Kœ *FÍ5kã ¢D‰evðV“øÉ ¹ÎmÊmmF‘-i2ì›ouƆÔ*ÛQ°sÖŽ¤ 6^Ö†Œ Ñ m0°éŸÐ›þ8]"h[,ð¦én½à8Ö$3éíf³àÍ‚k0»¥:ÚjœÅæíæì1ZÚzÔ,0˲i6¢vBžË5.°(Q¢D™›™HüÚ ÜÈ7){²¦`”cKê ;’ªãwÀšµ%ÚŽØA[Â÷LW›þÚ–kÑéX…F5ôâón-ëæ1ouÎb«IÎŽ-°;" Ö¼]ÃÎ}½°ÆœPÜ E‰%Jel‰Wfª>×ÇõÆŽ4ûamISÀ–x!8s±#E¶dÔØ‘Ç–h÷çxÜôGж܋ް&™Y&£NíxÔbj4@­Ñ¼¦>ñc껤¢…6–ÌÌõœ~Ì‚4Ž4.°(Q¢DYTð–$3]§µ ¼Y[bíI“cO¬Ë´6Ü<;âyj<–m,`GF“³“â¦?‚¶ª^t:T3gzÖ™…Uo^kš“Zl΢3·:Ë4”%W”(Q¢,­-ÑDÀ˜ÒÿÖŽÔ'3]¢õΨ lþiG´í*Ї/v³o]ŸÑ–DжbܸpzÑÔPWWÔŠâÚf[pE£iqqE‰%Ju¸šÕ V_`GBÉmIâ3mé,¶¤4‹±1jÑ–Dжâ]bž^t5æ~(Û§Æ,6OJI84uTÉ™Q¢D‰¥ºÉ®qdžÔ&ÅŠìHêŒRý°Y¦¤EжêAœÞíè[ͦy»¢¢’‰³Ó‰‹*J”(QV7ˆ«„)²iÜèGЖÉ9^í¸\0U3‡ÏŠ%J”(ÑŽD;RaZöc¨YŽöBZÐæýÚ¢D‰%J”(QªU.¿üòäùÏþ¹ Ú¢D‰%J”(Q¢Ì.µñD‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰%‚¶(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰A[”(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰A[”(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰A[”(Q¢D‰%J”Ú¢D‰%J”(Q¢DÐ%J”(Q¢D‰A[”(Q¢D‰%J”Ú¢D‰%J”(Q"h‹%J”(Q¢D‰A[”(Q¢D‰%J”Ú¢D‰%J”(QV“üÓ±¬ÁiOIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/3dcontroller.png000644 001750 001750 00000600104 12256006101 025060 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRCxÎŽ›i pHYs$é$éP$çø OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-Û cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFõoIDATxÚìýwœ¥éYß ïû~òsBåªîê8Ý“³„PB PF @`0A€lö5Áay³özÍ’¼`übl0`c,@ „å4(ÍH“sžéÜUÝ•NzrØ?®ûœÖ»¯M Ž>óÍLwu…s®s…ßïûSg}Œ¿¬‡R m Û[—8ûô)ân?ˆº]ÂNÌöÆ&Žã¢´¡ÓïÓéu¸¼±Á`we4Ú8ÔUFuQ°}y‹ko¾‰×]Ç_å£* >ý¡ã9®ç±~ò$k‡ý™_wî©§ØÞÜäæ—¿œÝ­->ýþb| ¤y†ã8diJKK¯?G–¦$I‚ë:ŒFc²<ǃRš¹Åy|ã°±±øL†#Ò,'Š"¼À# #Æ{C®½õf^ñêWÍ>‡;>þqn{ÿIËŠ–ÇqH&4 /ðA)ªªBiù{žQH¯ÛåÀÚç9pdÈ 8ýØ3¼á›¿žüÉ{øê¯þ*¾á5oà5¯{{Cê¶Å÷<®¹öj¶.]"Cövœ=so¾žßý­ßãú[nàÖÞÌ}÷<ÄÙÓgÙÛÛåÅ/{1ý~—Á,A7´MK‹¢×›Ciű“ÇYXœã³úqWVyù«¾”{ﺟŹ>wÝ}ï|Ç»8ppnº}ÑñSÿÛÿÆ'{нñäõ¯øòW}Å–®^_•ÕB]ׯqÜ¢¬Ë'G£ñ{ãNüa¥ÔN]Õ´4yɹ³ç9wá,W_u PUÕì{YV5s½˜Ð3ô£/{éKéõzÃÁ³ç|ŸµCëäYÎÇÞ÷~zssÜüâÑë÷ÑJáy>eSÑívyôžxôÁ1žÃÎη½ïì:À›¿ý[¸ùE_LÔëPWßÿ ãáé$awg‡O~ø6²ªä‹^ü"^ùšWSÏ<ö+k+Üñ‰Oóð½ñÛ¿ûv¾èå/ãþøQLRöövÙÝÛƒº¦¿0hΟ9‹ç»TMƒV0'L&c®¸â ´ë0©ó‚°SU5ý~ºihÚ–/ÄGÛ¶8ì?ö_˜O_â(âÔSOwj¥ÿÞ÷|Û[ÿ¿WßxC¿m[ʲ mʲDjSû²8¿Ã(…2æ?·mû£U]_Þÿ>î?¦ýb¸ÿø‚z(¥¨ë ­ s½÷ÝwßuNÞùÕoù–hiy‰ógÏ‘ç9qw:xeIÓ6¸mK¥kò¼ª· y‹kœï(Úò7Û/Ðnfÿ±_ ÷›ûÁ¶Å÷|Š"ç‰'Ÿ:²täЧ^ø‚›¢¹~Ÿ­ÍËx®G·ÓÁq]h[Â0 ¬Jꪦ®Œch놦m(«ê?y¾s]×?Ú4 Jí÷‹áþcÿñWøÐJ¡µBkÒ ¥ÿ¯h­qGþn aâ8†º©¹úÊ«ÆÁòó{í¿r®¬ \ÇcuíEž3 ©ÒÏuÑÆ@ ®ë‚ªM`ÿ]CQVäEù#n§×\hßÖ6ûâ~1Üì?þû†V´RA@Etçú´UÃ^³¸0QŠ8 YZ\ ŒÒ¨¦Å(…FÑT5mYƒÓ¢Z)’®1Çù>®ãu+M–YñL†[mY¯¹þ¾á-ßüöþêò½^<Ïñ=hiê†8ŠíH­ÉóŒªnð‘"l E—(ŠÙÞÞåä‰ß3žŒü¢,¿=b@}ÎX®”fº£ÜìÃýÇßúùTÊøÌ-ÎÓT£IJ–¦œ~öÙÞ™S§×Ò¬ôGÃqxáâfwswûÀ…K—ײ;îì?uæìÂÆ…KK“ñ(Ê‹,øÐÇ?á9Žã•EÝ ¨ê¦¢•Kdë¹®Ai/¾-ìjÇ=˜çY?Š"Þþ‡Äh°7.«j÷À¡CÍ«ßø•G–—–I&)QÓ45mqÑë÷ÐJ“eÃDQ„ëºdYJžç”e…çв¤¥Åw]úóoÙ‡‹Kæ•im©‡¶hhÚzÿ¥²_ ÷Û;?¦Ç£©@ßwßý¯ÈëúVíº­Ì ´ú"çéSWæe廯¥7ßçеWƒçò¦ïü»YAš¦œ¸á:ÂÀÇõ|²<‡â8¤i!Ïrªº¦È в ªò,§¥Åõª²äñG£;iÛvž<}škÏ^ '( d~®ï{Ç¥(JʲÄq]”ÖTeAQ–L—‚Q17·Àd2¦, ªªbii™‹×?xÿ=xžëºøžÏ(™°¾~þêêsß‹ýÇ~1Üü­iQ€Và¸nçò™óoöï•ßûíßõڣǭ{AÀòÊ ½^—¥å%¢(Â|c¨ëš¶i¨êÇ1”EA]5”UI‘å`Ò4ÁhVІ×8ÐBYU”Mçz”EEYæ´@že|ÉK_†ã¹TUÍÞÞ>ò8·ßþiƒŽ£‰ã.®ëâù>ós}ÖVWY?tˆ~¯‡ï/ Cš¦L&”Òv„έÎUqüŠc$É$zì±GYœ_d8²µ³CšçX]íàþc¿î?þv=´Ö„AHÜéþèÑkOþÜ©3§XZ^äg~á瘌FdiÊææ»Û{lœ¿Àh4 OrÆIÂx<!w‘S uÝ’çZ+hZªºBC[·ø‡çù4MM„øž‡qÜÀÃs=9ž>£á¥5Žcp]'ÓëuxöÙÓ<ûì3<òè#ŒC’4!/ ßgnnޏÓaiq‘#GŽpèÐ:Ga~a ŠèucÒt‘Ë[—HӔ뮿6¾áúëxò©')ÏTT4Ôm‹RŠvg¸_ ÷Óa¹àcˆÂˆ¥¥%ÖF{Ãß9wþü—]¾¼ÅÞÎ[›—ÙÞÞao0`o°ËÎÞ.YV0™ŒI’ i–“å¹t…UM]WÔmCkÿ÷ü?oúÏJi”íBã µÁó\\cp}fôônªºÆhãÚ¶GŠª®pŒÁ÷|¼ÀG;Š}=Î~1Ü|a?¥0Ž‹ã¸¾GÓ4$IŠã9«Ãáàk>ý©O½áÒöÖÏ?ëž»pí­]Æã„$K¨©ÿïzI´6¶˜yà(”Ö„®‹£ J+Z ®kPP%Zk´ÒhG“g9EQ€ªÈª‚¶©ÿ¿Ïñèt:ôûsÌÏÍEÆ÷ˆ¢ˆååe¢("MSF#)Öy‘Ó4-ã,cot–ÇŸ~Šº©‰Ãˆ¥ån¼þz6.m¼ðÂÅóïÝ«_wO^”ïH‹ü¾ÑpÌh<&ƒkŽ9öï•ëîGÃKÁå­;—Ýï Â`§mÅò¨Ù‚íÃýÇ_ow÷çïýäªp‡0 ½/ðH³ìØ`0¼êüù½m^ºüÊKWî †‡·wöô¥ËÿÕÈìá¸.½…⸃£ŒÑøïûDa„ÑJA(E)Å. 꺡mÁhEÝ4h­išÆ~‚Ò5VeEYU(©õm7M-…z’0IƤYFa ZU•dYÂÎÞ;{;<{ú¹Ï×õº˜^¯çz£év»ÄM<ëX§#asààëâèÒï÷ñ\×ui0p—0 h[¨ëŠÀ÷É‹Ïõ@K×ç:.u]†JiJ+a ÃZp—¦©©êZ,Mƒï‹Vq2[³Ëh8ÂqPÐ4-“É„4MÑ ’4e41™LÈ‹Œ4MìíQ9;;9;;Û³ïQ„Äq,bßÇs=|/@©–²ªq]‡8ЍªŠI’å9Á( K³ÕÕµÕ·ö{ý·ª0âôÙ3<þÄ“tº1+ËK=tĹéÆëoºêê«nJž}æ‡Ú¦©Ã ø£……¥ÛBߟçyO:ž‹qôþ%z¿î?þ2-07?Ç“=ÆÛÿÓoð¦¯þšx4¬?óø^º¼ÅcO>ù5›ÿŸ _qac“Q:±ÝžÛ„AÔ.-Pósó„aD·ÛÁ÷}æú}Ç!š¶¡( \×EkMQ´­\[Z¢0”Þ®m)« c AÈ%ZkªºÆqÚ(\ÇÃw=ò"g4Ñëv@‰ ¥©kÐÐ45q'¦, ´6DaÈd’à8u]Ó;”uIÛ2s°ÔuÃx2¦©ÃI’0ØÛc8‘å)ÉdL^ädÙö´_Æ8aJaô||ÏG1†ªªÑZáyq'Æs=¢8"ZX䨱c´ Ã!O<þO=ý wßs¬qå•Wríµ×šÅÅ…¯»¸yùë:aøó½~÷c-íGšªþ Çq6ŒÒ(cØ_3îÃýÇ_XlQ@·Ûe¡7GÓ6þÆ¥K_þÏÿé?ýÎ'ž|êÅÏœ>9¡?œŒÈ‹ß#Ž:YZcqi^·¯}Ï'Š#Ùñ!~ߺ©ñ}×qiê ×xcp]‡Öþ™RŒãà.YV`#ÝcÓâû“IBÛ¶x®‹Ñ£4aàãùišâEÛ6Ö–g0®KMÃd2¡®*|ßCkñO ¶ º´“šV+‚0àù€ßó8th¼È[Z^äiÆxE^Ò6òk£pMU–¸®K’¦Œ'ªªžÞeQ’¤¶pzµ[kŽïQžçÊN±,q´ÆqPZ®Òm‹ë¹´L¨ê €(éÏõi›¥¹½t—EÁå­-òù8>üw~ö³\yò$·Üz+kרØÚz­ç¸¯& ®cÞÑ¢þç¶®Ÿõ<Çuiëz_¾³_ ÷ÿÍPi|ßg~aÐõؼtùEwÞ}×w=üØcßpÿC-]غ<+•Ýî"++ˬ®,Ç<×cn~Ntrm‹ë8cȋǕ£GÛBšLP@†@K]×di&ãkÛR5 ŽÑ”MCS74"ü(Àw=êªF)…ã&T·(ò¼¤®…)hŒ&I2:ÝmÛréòe¢0¢®’$Á1Žçâx.­R$É×uê¦&ô}ªºF%E³ÈqŒ¡(Jà-Ç‘«6-xžËh$ºÇÅÅŠ¢”ŽÒBH÷ö ‡Cƒ!£Ñ€ñxÄp8¤(SvwwØÝÝá‚Òtâ˜ùùy–—XZ\dn~žª,1ÆÐ4ˆ¬­­rüØ1†{ž|êI>ð‘ð™»ïæøÑ£ÜzËÍœ8yYéÑ4í7®^qìUUÝöø£ýî±+®øõ…¥Å êI²_÷‹áþcúhÚÇhé¡·»³sí=÷ÜóÖ»ï¹÷›ï{ø‘•óih1ŽÇÊÊAú½9晟Ÿ£ÛéFÑløªêš¦nÀú½ŽQŒFc#…¶*k²,ÇõÇTU -TeEÓ6´ZaŒ¶ò…q FkÒ4¥©›ç\+ZÓ¶ eQb<ÏwÉó‚tœ07×§(KêºFkCY¸®CÇÔuEc]+uYáyBœÉËœ8¶àdT7ZÓ´-Æq¨«šÈñ—$k]–ºÜ`Œ&ð#©úmÃ\¿OÛ¶$iÆ!‡"MR9œ¤);»»\ÞºÌî΃Á.U]0ŽGœ9{†~·Ïòò+Ë+Ì/,Ðíuq]_Æ]ÕRå%apÝu×Ò\s-Î_àÁGãÑÇçàÚ 7Ýx37Ýt#½ƒk¤YùʽÑð•?üð¿ïö~çøÉÿKØë>ë…!Ú¾í?ö‹á߯&¶½ÚÒÒ@üÔO~ÏÝwÝýãŸúìg—}æ)ê¦`n~™C‡Ñ;ôz=|ß'|?@kEU”xžGQ•diŠçy8Æ%ꆸ®C]•žR¢ýSFãûŽk¤“²ðÔ²(qß8”U…¶`U)® ]TÔuC–e([›j+‹ééœê†¨Ó¶2ê;ŽƒÒŠ2“CIúTµ¡.k´ÑÔºYû´Öbß3ž7=”¤IBíûÄQH[7dE)o$ÍôH¢ÑZIÎ ”ÖaÈh0$ŒBù3«ZÆu§diq‰0 ØÛÛãè±£LÆ._¾ÌÞÞ;;Û †{äyÂÞhÀÞhÀ©Ó§Y˜_dyi‘•Õ5ð}ŸÆvŠÏ÷Y?tëœ=s†÷¼ï}Üu÷Ý\{Í5¼ìå/§7×§njµ=}óÖ]w}óêÊÊìü—uU"Š"ЦÚqìÿ-EPì^q‡{ƒ½ƒ<ôà÷Þu÷=?òñOº;H&vÖgmm…¹yú½A2??OÝ4­ÐJË ±’ÌŠ²,©›jv$)ËŠ4ÍP¶[¬ŠJFe£©ËR:»R®1Ža<œFižÑ6-M-…Ñõ<ò¼ i[&‰ìûŒì–Ö.Y*ã«ñ¤#4Zщ#\ÏesóÝnW´ˆyAÉ5º,Kê²&Šê¶¥(2Ê¢°#¯f007'ã~VHgiŒA#R4uC]×:t£@)»{²UÂGTFQq‘ç9y–¾ïQV%a‰.3èÏõ©ÊŠË—.±µµÍp4d°·ÇÎÞe]±¹µÉæÖ&Ó§Y[[åàƒ,¯¬àyþ¬+׎aàsÅÇigž}–?zÏ{x𡇸îºëøâ} ‹‹ÔF“Õõëî¿÷¾×9¨÷ì ?þ)×siÛýáy¿þM…›ÇuXZ\ / övv¿ò“Ÿ¾ý‡?yÇí¯¾çᇨÛÇ 8pàý~•¥¢8"ðéÃßîÄÚVÓ´5­R¢ãó<”‘Ä´u#—YžSÕ²ãò<$Keôt†£¡2ãf)Aàã9Ž„T9.MÛ¢PhÇÐÖ5 ÑðEa@]×¶“SDa "é<§×ë`Cšd¶ʸ†Æu(³‚¬È‰âˆ<¯hÛ†8 HR48ÚÐëv)gE] òË8ÆÑäYISK1UÆH·:ÉÐF‘—%r$*Š‚(ŒiÛ–4ÏÐJEÁÌ­’e9aQiM]ø®‡ç¸¬8ÀüüùéÛ9yò ’Ñm4kÖ8yÅßà†á7,­ø×ƒ½½ŠÂ¥÷å8ûÅð xn‘ëæÒÊ2éx²ôðÃÿèo»íG?ö‰O¸ƒÉ”bn~‰Õå5Ž9L§SWr˜Ã4Ieoæ»dY&/ê¶¡,k:Q‡ñDô~¾ç‘¤)¾ë‘¥Ù¬k­É‹\–“MC«4UQ·eù}ÐÒ6 ˆu-ÏÑÆ¡i[²<Ãs Æ‘b‘e3±QÕ ü5Ks+ˆ®iÊÊú¡ žãRW’5Ò6à8†É8·c}E§Ñ¢ŽÇ„¾è=OÞmÓÒ´þªBùuYÑ…j­7ZK·;XG‹8^ÒTеïò½S"j€Išâ:†$Kq—þ\¼(äP¤!Œ|[ô!Ë$hÊ¡ú,//3?¿Àòê*»;;lom1nSU»Ã=vÙãôéSuUâMVTÔu…ë9¬j‘Û¸®§1ŽC‘ d¬ÓJãºí8mÐZŠ—çûÔuc4aÐÅX¡ÒB–.‹m|ס¨ ”£Ä)bŒ‰N)À†ñh"ÿÎ1ÖÂ×д²?u=—,ÍÙwÖmE¯×¥,Kò¬ ™$tºò,''ôú=ÜNÄp4" D]Ún·®kš¦E›’¼(ð=ÇuÈÒßóˆÂ¦mi¨ †¸¾GGloï`´an®Ïx4¦®JŠ<'zÝ€4Í8°¶ÆÚê ƒáá`ÈÙsçØ¼´IšŽIÓ ÃɘáSOpêÌ)Ö®sôÈ1V×VQH‡îº.ZÁÁk¬¯dooƒ²°0OÇeÅx4‚_}MÿÝï}ÿ{O\sÍo_ó‚›ßªµÉµÖû»Äýbøùü1Æ1kÖ¨švéÓ·ßþ³øž?ùÎÛn¿¼*q\õƒG9|è(ss=<ÏCiM…"K1ÆêØjÚ¦ª `…Ñ Jkšª"/ ´ãH ¦’t’¤h¥ñ}¥ ž¯hjLW•\{QŠºiQÔTUE’&aˆÖFÈ1ÊÅ1†²*hjpCÕÔ€"ò”ÑäYÁh<¡ªkâ(DµP– V>Ǫ¶’«p–Ó6µýü;1EžÏÆ}×1¸®‘YVèP ­º(iùšA:ÝÉ$%Ï3|ß§EäBaÈ%=/˜Œ&Q@§Û¡Èr&£±íð+ñièt:”ee»Õ†´È™ó=;Ž{•cÃÇq˜L&d™ì$Û”6,Ì/0?7ÏâÒ"›—¸¼¹ÉÆæãɈtˆ…ùy{½tñ\—¼(0ÚàudÄUJ<²M][ „&e!rסi¢8"ÍsêºÆu\&ãD¸€ ð•ªq=ùQ;®A)‡¼((‹Rœ%„(ù^@2Iðã8TU -‚̧Ák=Ùjƒö<êªDC–fÖg¥:J3™Lð=¶×s)Ë’¦‘]¦ge/m#t­4EYYQ¸\ÆU+`Ú–4M¡m1&&›ä”U…ç À!îÄT¥PbÇ!Ë‹ƒP®°PUi–ázžçËn°’Ïs4šøÒ¹ž‹ñ¼™:ŠC JˆØ®H{”+ Ñ†"/¬Ÿºƒã8Œ†cz½N‡¥…E®8q.\äÌÙÓìînÓ´ yQðìÙS\ÜÜàÀÚ*GŽåàú: H&‰ìL«š$ÍH½ŒI’Ì®ñªU\sõÕ\ÞÞæ}ú0ïü½wýÇ p¾ö¾ák¾í†ë®›<üÐ#"{Ú¯ŠûÅð¯züýsÚÁvem•á…‹ñoýâ¿ùéw¼ëÿþG>ý)’"Ç8k+Y?x•å<_vQÊ.ç‹¢ÀhM]W”e!ÿ]ª¢ ¨*ÂЗ£G&bUJ§Ô´-®qñœ†Òjø×!IÆ / mÕLÃW–ÕÌÜT5Æq@â^ Z:À¬(Q-8®ƒÓ²²¢ãûÔeIÓ˜Á÷})n¾VR¨•3õ1k@Ñ´5y6•¼4´ì£(¢i*{\ÑhZZ)ä¾G§“¥²“4Ž8MÆ£„ºn£?ði›ç uÝàxŽè(›Ç5³B]Цm©Ê†8vh¡ùp8Äu\‚°ƒ±]î´c£X.yAS7du6sœm»vdô/mæJr‰OSææp]—8Žéu{ììí²yé"{;;äEFQç<{æ4›—.sts“cG³°´@ÝB•Õ8ŽA!o.eQЉc\ÏãòåËx¾Çu7^Ïh<擟øÔ×þÄ?ûÉ­7¼úËÿÉË_òâ_\]YæÜæÆ¾`{¿þÕ<‚(æÒùó\òÑý™{îÿáXµßv͵Wja~ÞÄýËó~1üËÜjE“×ìnoã*ƒÎ î:÷‘×¼óÿè]ÿéw;¾<€28x„Õåèt»ÐJxzÆ8®!MÒYÑÓZ=‡¿÷$¬(o¤óˆ£ˆ²,Ȳ?ÈòL½FFf×uq—4I@Kޱétý€I6ªª±$¨ë†ZÕ×!ö„PW%ªUMi-u-®ç‘¥)Y’Æ¡XÝBŸ¦(kÓÊH&˜<Ï™ì¥(ZÁì{¾Xè´"MR<Ïát^Pä9E!yÄZÙ0€¶Á÷üÙÄõ\9$)ïá{>Ž£™Œš¶% }Ê¢Âñ=ÚÊ¢@)0Ʊ4íÏ54-£ñÏwi)€E^Ö™Ý_Ö¨¡…Ë—·ˆ¢ã8ÌÏ/жÅLPdAà…!•ãR%eU„!´­d¾(Å»;»ôçæ˜›ŸãÀêAΜ=ÃææEªº¤njÒ<ç¡GåÔ©S;r”#GÑítg@\ÇqH“ Ç8&Ï2Š¼ä §9¼~oþÖoæÑÇ?vÏgîüäé}âæûóo}Ý›¾j¸¹µóçM1ûýbøóhêÇs9~ìE–/ô¶Û~ã×ÿËo½ñîG`uu¥¥%æX^Z¢®k<Ç–»Z•–Tu…Ñ‚È*f:;GºC«sSJQVyQRÖ%¡ñ]Ÿ²’_ïÙN¯®+K‚Ñ(`ww—¸Û¡nP qè“ç9uÝ€%R£ }CQVÔ¥ÕVRl=ßÇñÆàhé(GÉZEÛ*‚@>—"ÏQ`‹LÉÒÒ"e.#¾1Z+šV¬y'N–4Ídé”Öäi†Öß÷P Ê,§®*Œë’åÅÌèû. ù¹•ìÌ:í‘BeŒ°ëB’úZ¥ð}W5ZQµÈ±IC‘ôû}‚@v…E!ß“º‘µ«Åÿì8š$Iq=‡ÅÅyšª¡¨E>žç‘Ú¨SÏóq=H—yAžçxÏRÑ45ÝN‡~¿ÇµvŸ¸‹ëú”eÎÞhÀÃ>ÊÖÖÇŽemm cÌLË©e`¡¼Š‰u(¹ŽËµW]ÉM7ÞÀ'>ñ©7ÿòo¾ýë*×ù'ßñßþ³kÖDßìÎûÅð/jOHKÓ4,,.z>ŸþøÇÿù¯üÇÿøï½í£TMC÷9vì(‹ ‹Ì÷焞Äݘ¶”²1šBõýÀÊe*êJ$8MÓJÁ²;9ß÷HӜҢ°ŠB.›®c ¨Oû3[_†â¶E´È ‚ȧ­kÒLFâ)pAÙ® %$f׊©ÍÆHg¦¦mj*»ÓS­è•½„†A@ž—ö(!#ÿ$IpŒƒçº~u´=r¸¢m¬D#(ìB/hm¨›æyºÂVvyÖÒïu©kùs<Ïgo0$ |ʪ"ËÒç…[i²t‚ç(ã 4£pŒC–—8Ž\“˪¤GTUIQ(’4¥(J|Ï%/ –—H’c¤3›<š¶%IRT A2 Å¿í‹޷ȳ8 I’dv©öŒ¡F<žïF!®ÑŸïsîì9667˜ŒGv¡¸¼½ÅÎÞërâÄ º(-o,ÆP †tº]’IFn;ÔN§CKË+^öVVWÕoÿÁÿÌÞÿáïû®·¾åk¾ò«ßø°1öãö‹áÿX¤m[ãpôȲÑè5¿ð+?ÿŽßø·Ïm öp\ŸC°~pù…y<×›É0h¥8 Ò!MÓŠÖó©êŠÉd<3÷Ó¶¤I*/Ç¥Všª¬H[w;]Zž“ØUIše3 ŽVÂú+Š‚,͈â¥åEÆ£ é$“c–®¶mÑÚ9¡üúL:2¹äJ`Tm2™J¡Õ„aH^ÐHñ,ËŠ¦i©ëŒº(Š m‘`®ãP%^G:ʦiètb&‰ÈeÇØNÜ/Óµª*<ë<1Ž¡²×ñ 0(%;´$Í1â`´znÍ` FÙ,æ¦%Žcö<ß'ò}’±Èw&“„n¯ƒk9‰ãIBGø:°oV ¯°åj,N—4I Çt¢<Ëñ}ªªŽ't¢Pö«Ïp<Æ5AàQ”yYÐ42®K«äÃt»]:qÌ‘#Gxúé§8wî,e]âGeYòÌ©S\ºt‰“'OpèÐaÂÈÐÔ U+ÏѱJ§(Âub\qô0G¾û;øèGo;ñ ¿ô+=üð£ÿòÖë®ûñ¥¥EjöÇæýbøß=7¸¾ÏÁøØ‡>üïÿí¯ýÚ÷Þþàý ––pèà: ó‚s Â`&oðlèù$Iq‡²,gGá º(„Ï'¿¶âK)™"~vOØZ1´­éDUUKñ°±›i’ÆM[[´¸ÇÕ’$‰P­ë†²Êp]ÕŠOZ+…£5Ú1$¶‚˜ñdŒ²]O^Tv”–Îθ–®¬i[´Ñ„n@–åøžku9m-ÿM)%“×uiê†<³aóEa=Çï[¿p€ë:BÕ¶‚teMÞØ".:Ä¢.ÑÚd7™¦ÙŒ¶ƒ'˜Ÿë¡µ!M3æiéN’$ÄQ„çÊϬ,Kñg[‘yU×VÂÓhE^^,IStSÓ¶[[;ôçº4Ä#4M+_CàÇ1[;;h¥ðý`vhYX˜§wÐZÇ1/^dwwGöÇ¡¦lj~ì .oípÅñc¬¬,ã8.y6…âÖ–â#Búé×Ûï÷xÓW%O?sŠ÷ýÉ{ÿ—{xð+W£îk#ãoi«×ÜìÃÿ7Í KŒæòé3ßð³?ý³¿ùïøí0«kºÝ9<ĵUËú!q'Æq¤;kê†|ªyÓš¢,ídŸÕÚ`$e;™$I‚@ä"X_¬ã µ¦, é°ÚÇjôòLlnAAÓf ùÄ-”èAÓ¢´¶tê†,ÍèÄ”jÙÚÞ‘1½*ñ] ôI’Œ², ½$W-N ¹&ŽÅÿk­È•DZ9&•UEU‰WÙó\šº%ð=BËžÆ`y„MÛ ¯EEQ”L£6]ÇP7ØÏK`J)Ò,£ªò,›}Ohåg$Ø0ùš¦!ð]h[jÛ6Öù>Y^RÕ5žÇ1ß1",/ Ò$!/Jº×5¤i¶›¯-ض¶{ÞÐ÷åS”hZ\×#Ë º½FTÛ01Æ!´ºÍáh„ë8r•Ÿ:‹´&MR‚ dýàA<Ï¥?×çÜ™s\Þ¾Äd2&Š»EÎå­Ë$“ ÖÖ8vìÝÞIšâØï1 MùÆQU’)süØþÎßý;Ü{ßý·>ùô©ÍIå$I>°9°_ ÿß]ŒŸS%\ù¾ßùÝö£ÿèË£çΰ²²ÎÚê*GŽ¡ÛéжàÙq©²‘—UUJaÒŠª, l´f–e¶+ c÷tžç‹nÐ^˪¦ij\c$}Îd‰?IŽÆt;1“I†.s‘‘‡ÈÈÒ”¶­èv:â±­j¢PÒê\WŽeUà{ž½ª¶diJ]W(+ßDzƒÔF>wÏg’$rÌÐZ¸‹®tºm+ǘ)ÔÀw]j{±ní5<ÏâjŒˆ„–£ã8´M!yÃöuÙívI’D ¹…Rø¾Ïd<ÉŒ'ÌE×up}O|¾M‹x¸Êµ 'rÕ6ÆÁu]Ò,£®ä‚^V2bWMM]T3øítG)bséºZ+úVZa¬G»H3<_:…eGN]5QÎ\2e]ÉÊq(+ë Ò'”¯×q\ꪦ®jÆ£aÒïÏ¡Pø^ÀÜü7.pá‚À§7×g<žðÔ3Ï09yâ ‘ÿh×õÉrÉŸȺmɲlÖ·uÃ×ÝÀÑ#GtZï?~âÄ÷º_ØÙiïÃÿ‡jÅhôÿËýè?ÿ¹_ÿUûBçÀÚ‡’N0˜B§×¡( ‹·ûZ„v±ŸYô”ìzk“ z>-å úY!CãH¾ˆë8²[s\×PYèAè…´­Â÷%8/ryA;â’È‹´' ²¼Àñ'I‚Ò Çu)òB:Õ¦¡)J_ãYÑrYÔ(ÕÚN“™­ð\‡Öƒšº¥Ó‰HÒ”<Ïihì®TI×X n+·G Dšg~€ãˆ‹F[ñôÄŽŽBÀi­ƒFvƒÆ:®8b”ÈuÚ¶%Ž"²4£¶"vÏõ$ú³,Å}’fp5Í^©KꦡL¢(ÕÒÔÌ&EQŠ(p1Ú±Þè£k¢0Äu\Š¢Âq¥[Îr92EaHQ–4–Õ¸0?ÿܤRtâ€ÝÝ-²›uŒ¦UJì†Vçwb:½.Æ8øžG¯×#ðC.\¸@2IéÄ1uSsqsƒÑhÀñ£Ç8txÝØËøß4 ž…¡=l ‚@¢Zixé—| {;Û?÷]÷ðÂ[nù…I2¡m›¿õ}¢ù‘üÁ¿¼zbÐi2a¸»‡ç{2ù¾H+Æ´6" <ß#å…£å÷¶MZœ­Ý,¯­²°¼üßûiý¿Y–tøôíoóW¿éïýÞ‡>J³¾~„£G޲ººBÇÄq?ðg{ÀÜúkåÎ@ž'À²,Ér+8Ž#*‡¶Ñ]’$¥ð‚`–=Å‘u¦ˆGY!Ine-RÇuEÌÜ4’N×¶8®ÁÑÆBV5½^—4ÍäÉ®UÕÈÈguvŽïZÉ…¤EC';>£5ÚvJŽ1h£É²\¿öJ>y^dN,þåܾ)”¥í-µÚ8òyhøO0ž/$îcȲ\ÈÞ­@R;ql÷€ ÚhâNgFñ°tXØçœkÁ°(EU´ª%ŠÅº×¶­Œ¼M‹±Ï-±9–´XùŒ+ÌÆ¶m«F+é¶8އVâšq‡$´ëºdyŽë:äYVϰ3¿h]‡ñxŒÖp_U•ÄD!¾çÑÔí,qЇ~¯EÎx<¶>éˆÉdÂæ¥Ë¤IJ¯Ó±PYe_-UYRÅL¢Õ¶ ss}ØÝÝãO?þñ×çIb®ºêÊeYF‘e„Q(Fƒ!ƦRP%eY0??²Bô¶®q=O„õÿ ýÛÖ®óÀ£ÿ_óòÿøó¿ðÉðOþ1ò$Žç8´¾Îêê sýy|K‡™eì=£JS·8Æ¥m*)¾J¨ÎU]‰èÚ5´mCšdÇ¥ª¤ó‰¢h&Ž•wùÖv›F‹mNCK;³j':±¸ ʺÄե݋EaHÛ´ŒFc¢8¤.k²4µÈ-h¨kùÜ<ó9½ì:ÚE‘¤ª–Bi1YÓ‘8˳Yצµ¡whêš,/ˆâ¥4YÞàmF‹=/Ï mpxŸ[‹glÚf¶j˜Œ'Ä¡÷”y2j6F·mƒk<²$Áñߣ¬j|?ÀuŠ¢‚VFÃ鋹meŒžLDî3}‰ÑÞˆ<+ðeçe®”Ýß6¢Ñ´ʺ.¡n©©išL,†uMcm®ë0IS¢HÞÄš¦%ôŒý>6M‹¨¶edD[9Ý‹N­‡ (+y3Q®¢ë¨!ú½.>ú(ƒáσùùEʪäÂÆE’$áøñc¬­­Ñ"Ò%É¿†ñX’ük•ìt;¼à·2¿0Ï'>{ç?=ñâ‰/ûÒ/ùÖ¹þM݈èüoáã î«VHVÈÇo›·ÅãùG’ç¤ñå­7××¾ù¶·þÈ3¬¬æ†ëoàäÉ,--Ç1Z:ݘª,™ŒÇr)dä£UöÝØf”%y–ccár`ÐZvFBuŽñ<É'–ŽE(ùXÆ1¸¾Vê¹®¦•=Z7ŽÉ³LðReEš§äE5³µ9Žª(Ï%’ж~¯G'Œd lž“÷ŒÆïꚦÍa–Êï×F 䡪ñ}íh Âríh[sý> ȳŒªªÈóŒ( iÚ†¬È-œµb0çÒqcdW×àU6ÔU‰ãš¦¨ƒë!y^PÕ¥h.›–¸ 3už ¦+p}Ú¦‘üeÏAµ-Ôìì,äÁh‡²Ã“wZª¼" Ñ}:.ó sÒ5Ò&)uUÏFSÃÊ4Ðít ÀÉd‚6ÝnGè7HwÙëvÐæ9oGVã)r¢,ËÈs‰'èõz¸ŽËÊÊ×_wG¥m (rºK++¤eÁC<ÊãO<Áp°GYdÔUE2IgÎh öØÛ0¸âØ1ÞøÕod7I¾åí¿ó{÷ïlo/¯¬,É:äoáñ nLF)&££Á€2/ðÃÀ¾èÿ;`¸dϯ¶áxÑÃw|æ÷Þü¦7ýÃ÷|ú“:ˆ{=tŒãÇ®`iy‘Új¹|?˜¹Dç¦Éò ×õìTke ö‚â¸ÆJ¦²yÁ@+v+£) ‹±RJÞÕ]—E‰q¤‹Èóœ|yY7”U‰ë¹tâ…=<ßÒëõI& UY±°0Ïd’$)žïÉ5µßkûð\¡áäyAø4´ÒÍÙBä{Ž•yâ_žL»®CÛ4âf±dIÔìVÝÈqbúº®+£´1ä¹mâ(¢jÄ{ܶ2Å;Ž¡,eœ”·A#nžn·C4uEYV”µtåM݆’³’f"MrŒ|~y^Päbe4®ìh˲Ä(E§ɱÅ83c’ýb»EבcTÛÊ÷±oxq±½³c;†Ìf?‹I1Iвb~~ή5Œ–‹9Ja´¡¬JysU0ØÌ eÓÔ,..¢JÆŒÆ#|Ïg®ß77ÉÒœ8 ­w½±‚|f©„UÕ ¬€¶åĉ¤E±ú™»îúþ««:xpýÂåÍK"¶çoϘüW ÛV²$¶66¹pö,Û—¶Ø½´Éd8¶v%ÉÔçꨚçÁç=V€_ùÕŸù¹_ü;ßò-‡Om]fa~•+O\É‘#‡èõ»hk—€Ý4·ƒë9Äq<£Î´¶×œê=׳£hU]Q•â>‘¨ÌZ„ÎZX~Æî§û;ו=š±ú½¼Ëk”y!Ù$ÖRV6“$²¸~Ä‚–¦™Ð²£¼È)Š’~·G–e$©EÏ“ÀvÁÛ ‚ßw]ªº²W:B­ÄꦟZd…ØçP3·Æô [×5Q,ãÿtYWÚ~ÎMÑSv—&îcµfö"ÔZ²]F㉙‹Ü(ˆä˜QYÑzÛ6dY.—áÆ®¬ƒ¥*K°€ˆÊvƒ½^‡0¥ j<×è„2âj*\ÏbÐìèXÕ—ÔJÏÆü0 …®c´fe±Hs!~žG2Iq}ìi< ~- ÑZ±»³‡ã:t:TUUÆÆ<4MM¯×†ãXlyAâû>yš‘eâq¡¿Ëh<âÒÖ6žëÒ‰BŒql1mPJÏž“i–Š]±m9yâ^øŸúôß[fÙ3Gz ¬eå¢íQi¿~žꪄÂìûøa(û–ɘt+ Æ£ ®cˆ;I¬jš¶! |‹ ›å¥}ƒ)åXbÅà“I‚k/ÓYšàze%ݸ€fÕlÈÒŒ4ËgP… hU‹™²=±Ùök,JWu%tžãú¹}N‰K\<ãñd¶N1ÆÌÞ„ãNL^äŒGc”R¬¬®…!;;;\ÞÞ†¶%¶:ÇÖ®˜ê¦†Æ>G•¦©kŠEYà:.‡Žæ‰gŸ}õ¥Í僫«ïõ}Ñ™–eù7ºþÍ:ÙËœv ®ïá>®ï‹ ÅúQĽÿäßûîoü»wýÝï}[ÿôÖ%>έ·ÜÊÒÒŽv ÁÚÞdÌœ†¦‹§·¥,Ê,KTö¬3Ãh3#·¤y*Ö4[(}ß§mrË Œ¦Vº\^,"¾¡c¥8µÝ›Ém¢¡ÈóYJ‹ìશ±oääû>•=¢~$»Ä¦±Ò5Ûñù¾ìýò,·»/±ÀyžG[×ÈJI–¹ë¹³|bÇŽ³-¬Y–IU+#jšæTV:ƒÍqn,ÙÚ(+ÿPâH‘ý©\¶ÖPÏ:k¥4i’QMÛXHƒ‹ëû"”Õu]ÇÙ³,£±9ÊŽãZÉŒ"Ë Û=4ös-r‰Õ–Žù!®a¨ëšÁP|åF+ŒÏr:I©ËÊêDõŒ›èû~°³7`2™àºaÈx^VTµx³åª-’¥dõ’f;»{@+¿¾®,azÝŽdÌ”Q'²úÉZÅÜÜ'Ožduõ{{»diƱ£ÇXZ^âÜÅ <úØcln\”k,- xww‡ÑxLšfÂjô\^ñe_JŽþûúèÇÞmŒ¦DzcÜ? |þv†Æ8h«æÀ©;óÉ*­¡m8|ø0ªinøã?þ“üØ?ûgoýà§?©âxŽ›n¼‰ãdz°0o¯®"ÖFôlJiŠRtub³ÙÁ¶ TU5“!´Ó¯×ZAàmûœW<£²›.§Ã °DÇŠ¬[›fg¯æV¦¬”'°té¼(ñ¬·¹ießåz®ìÌìR?Ë,"ß—ÿæC–‹+Å÷=… ¡ìçe¬ô¢n¤+sŒCYW¶°šLVÙÐv­uÝ¢mGìé { p]¶VUMÝ6Äa(]]]ÓÔ5aaŒa4R·5¾•³`™„Ó4¿©¸\ÛãÂôÀ0• ÕõsEÒhCQ4m=ó|ë‘Kô´P6V%nœ^¿Gšæ³âcŒ¦,Jò¢²Ç¡O{žOk¡ŽãÑ6Â}ÔF„ÓA`Ã¥šš2ÏiÛ?7¨"—?«¨Jò¢$´ATÚúœS«U¬ëšt’àX×cww¸És²pQôçú‘ð<–—WÐJ3I›¨X]YÁu].]¾ÌÞî®ãÇ‘µy–V¡Ð<¯{¾±¶=z„Ë»»W?þøã¯Ÿïõ3ü&Žcäîw†_ј9,¯¬°´°Ø¿ï®»ÿË?øÑøà÷ýÈßðð3O±¾~Œ[n¾™Ã‡Ñíôh›v&ê-Ë×ñe] îL¤;m?cfPìmbÁk¬´Ä÷‘á-àSp©ïy3‘öÔÖ†Á,ã¤mZ†£ŒŽ®Z°EEp_MÛŠN°iÐŽ¡.%—èBÜe Â0ÂXNVä@‹q´í *üÀ³×T;ø¾'¾ãZ%V´²¨Qhy1>Z‹EpÏ9…Á¢Z°¹ËSáwž6÷Dvp™]8Ž+ÖÂÁP¾>Ç™½ÅUeMžgV #v>­‹{' äèÚ«êp4¤(KëHilN´ŒûY*Bå,ËåîºÂaŒ<ס*kªªdogWB°¬}M\#S…Ñ˲²ú=—¸‹=ÑFÃ1ZiææÆ¸Ž¦ßïÙ碢ÈK|Ï ²³W—¥p*›FtŠ(y®<_b%Ñr ËŠß“®k{k›8ŽÈ3ëÇÖš½½ìLKyÃp=W¨66æòåË4uÃú¡u<ßç왳<ñÔÓÔuÃáCë(¥ ‚ zwãÑx¶r µæÆëo@k}ÕíwÝ}ßâââ‹=|i2ï‹®??;AiÓº½.««+ÑÞöÎOÿÊ¿ÿwã¿û–oýþñ ?ÏÆÞˆ+®¸Š—|ñ‹8|ø Ý~‡ª,GrLhŸšF¡ä^LÉ(XШpõ$”<³;¶0>g%€0RÉsá6H‘šîÅæå…Œ”ŽC’LHSá 87§õ«åÍ$I2êZŠkQ–³]dÓÔT¥H‘ê¦ÅTMƒë{¤YA’¤ÔU%!×±»Zëa@UWdIFYJÔ锳èú>yVXßqk5˜Û[;6íψ5ѦÊδ ä v0Í2rP“qÓ¶ †ã™=/M3Œ–#•„W‰Ô¦nš™(»¶RŸ¦nfë?ðYX˜çàƒ¬,¯’ç%çÏŸ' ®ºòJŒãðèãOðì©3Enã©L¶[‡$IJ–ç(ÕrâøqW׎ýñûÞÿÈÅ ®X\Zü·Cü‚Þ¶vgÇ«kkyñúw¿ë]ýßæ§^÷oÿm.lípäÈ\}ÕÕ:´.û˜FH¿e)®ßólq°ÅO1;–eAms‹š¦mfA?Ž1”¥ ºaØŸ¸JP$©Á–Ä‘ýy6 MSãØ ñîÎÞÌî:â(I“‰Éqx·SV݃Ê÷ÕqŒì_m•(Ërö\p\±Köû]peU2׷Ŷ© í´ »hϾÛ˾uǨD+±{NsµãX¨×SNæÞ`@žç¬®®°²´ÌÎÞ.Û»;¢suå€'?ßÚ²"™isÅ{-AöYžwÝy×[®­¾ûÀúúåñxLûÅð¯²6•ˆS{Ý.sýžšŒÇßø™;îxÇ¿ýÕ_ýÑ_û­ÿ²úà3·´Â5W_ÍÑ#‡éõºø¡o½¦R³,µ][(×9{¨ÏlKF¢ì/òÙJ®¢.ãd,Ò—©¾Î÷í¨œu”r Ö¸Ž;»¼N3 Á½ö -c–…Ÿ6-aÎ,SQ´²«©±˜Ù‘}Êßkf{GéVjÉñ\!ÊdJ+ÂHh8Ž1øA L@GuSÛË»èòJ»¤‘Æà¿ä’<•éhÇພý:ÄÜ6 ;ÇŠ¦ëÙ×é8b»+ò‚47†`ñk“î4Ieì½é¦›xÃß@·Û¥®k¾üË¿ Çqéöº„aÈx""äd,|GãJ¦qS7´JöªUUIº–´A)”âø1ŽCQŠѸªÕµ#»])èM-/ï©¥Pi¹¬­HÓ|vèð}Y™4­(ŒcÑ_v|7®nU–¬ã¸SÈDEšdVq€µXV„QhE•ÐÇ•¶4qÜäY6ëì§1²ŽëÌÈ9ƈærÊל›Ÿƒƒƒ½=ææ9räƒÁ›—.QU ¡à¹f¦x0FÛ•D5[_ø¾OÅhÏõïüì]o]?°öî•••ËÓº@ùËÚÚ˰V‚C÷º=’$YÜÜÜøŸ½óηýé'>±ö™{ïc”f¬¬¬rÝá#ôº]bëå<{ö¬ÝïµEnG?)fAàÓ 0„ù¹>ós‹(#Z±N·‹ë96¯C.ËuSá)Bâ”hg V­5ž‘¢’9Ø®E™Ò,›ye¥«1v<´O)ʦD×"°n,6R· ®ïRÛÝ¡\h¥“«)\ºUt»¢›Œ¥ûɆ°õ²´ ±ŒRÚRZl6o#ð‡ÒÂ`•Ö«ef,¾$M¬fÒ±²vöF‘¦©˪¡,¤ãK³tv¸ÁrE†¢Çó]Z› ]Æ8$yÆh8âØÑc|é—¾‚ãWccó½^—#‡sûí·Óëõét:äYÆÒÒEQŠã'®àÂÅ‹¤iÆx2¦ÕxÆ¡ßï3eˆ´fŒã8„QH2I¨›Æví…ᬸäVÀÝ4õLRÅ1iš†6?ZA§+  °¶JhT•å"Qrä8&ŽÅh4!0Z+&AYaÅóš°ß#/òJ¬© $Qe´²¬›š,ψÂhÉ`Œ¼!úœÂÕ,Š’^·‡:,~ùK›—xæ™g¹öºk¸ñÆyðÁ9ñ4-G¦ÛÓ”eNžçÄqgFcϲ cÆ£9vô(É$Þùî?úì×~õW¾øÚëoxhóÒ%¾“š??‹¡ ¿|¿ÓÁøžÉ’äkî}ð¡ü™;?ûâOßy'<þE]ÓíÏqteU¬ZYÊvQpöìYÒ4’‰çZ»‡“£Êv-Ͻ—yn`G܆ù¹9ºÝ.ss ,.,077çEt:1ƒÁñhŒçy¸ž7ÃE5­Ù(ŽI'‰¼ÚvV|µÖkk-£þ‹ÖF^°²8’‘ªÈɳœÊ†‰õ£¥iä÷UUxøÆ¡, ›žW‚’ëm’$t:‘ˆ•‹Ú‚M+;rËX+…绘º¡­°ö²¦nÀŒqiÛz6¦ ±Ú̲GŠ¢˜Y÷<ߺ9òÚj¥SÓˆ¸Y¬wµÝÊH½½7d0á97ßr3ßòmßÌü\Ÿ?~÷Ÿ`ŒæÈÑ£$IB–ôz°¹±1/wwö¸îúkùáùlll°»½Ë`8äü…ó<ðÀCœ9}FìoÀÒòƒÝyQàN-ˆM…†¢UÓݘ"™Lf:Q¥˜%Ní„A ­&‘Ëû­æÇ5”iFYW,Ì÷Å¢7•f5R´”†,Ë$Úµ+`ݶ²ƒõ540ñ=—NƒV ¬¨êRvÖ¾#+ëN©êÚN ²ò0Žƒo;Õ¹ù9ò4Ã÷8@§súô¼ÿ!n}Á-Üzë-<ðÀƒlllR·5W;N‹Ïy<ÑívQJ1Ë.<Š"(K®¹ú*´VÑŸ¼çŸžŸ_¼emýà3—._K¥úÂ)‰3µÂÙGûKýC´1lo]âìÓ§ˆ»ü êv ;1Û›’¢ ^Ÿî\Ý­ËiФYþÊ ç/|ã½Ü÷]wÜq‡ÿÐ#±5P·"cñìá‹´*Šç´{SÀjk/MÛ<ïYÑÚ ²œê?7h[)Ѳuº]8È‘#GètzâŽ|™b–Öʤ´’"‹·9/ ׂ´µ}XתҲ놆ÇZßÐjæ|É É vm76µÃ%IŠcÄv¥•äç6`¢0 IRв$Š$«7Ï ‘e%m[ã­ÅBX×2εªE+»s=É(|é˜,^*ŒüÞi:"‡AHš¦4ry!MRâXvoe)(1ÇuƒH"ß¹³çˆ+®¸‚#Gr`m•Þz —.oréòZi¶.]bo0`aq‘á`ȹsçˆ;1UU±µµÖŠ]¾ë»¾ƒ¯ÿÆoø3?¿ó‹¿Ä»þà9¸~8 &Ôøl;Ý.E&LÆN§ƒãÚi&Þg5íS¡ EQDU7diJ·“Y×JE6ÎuJ˜—Žx×€; ûªdŽÖt¬¯XiC’f`Á Ò]ÊŽÕw%3%IEw鸮}>·øGfÓ»KwC`‰ÛSëc¯ßºx’‘å){{{\ºt™K—6év»Ü|ËMTUÅ}÷ÝϹóçY[]åø‘#"ÎV²V_´¶´ñŽØ`‡8îðØãsîԩݯ|ýkn^?tè¬8¤¾°†æ¶mÿú;ö•CA·ßeqq‘||”cÇŽÓëöåÏi…ÒØ?Ó±Kü<ÏPJx|…Ý×¹®+ûÈZ޹M½««šFÕv´ò(K9ÖøE-ª×8ÒÝ8.ÚhqÊ äIë‹{f0IfF&™ì=—ÂF …ìwć¬,–ª%på²nŒüÙEYâ¾=‰m®i,XÂuÉÒ”0N‰qŠ4Ås\¢0°®ÊÚÝZ09wî"/|á ùš7½‘ë®»†cÇŽ¢ÃÇoû§Oaaq~¯ÇÓO>…ëyL’ Û;Ûôû=&É„‹7äðâºt»]Þû¾rúÔiâ0âСC\sÝÕ:z„2/$˜ªÅþL@+ƒçÉ×,²žY–1ôÇÖ†éƒjiž<’T:9c´v¨*Y”¥¼Á2½' Æ•‹wfõZ‰TKyfF š‚•ÒÒºmƒÕXŠòÀMfCè›¶ÆhGˆ6Ñf2å”e)×j­qüðÃÔuC®Ç¥K—¸´¹%‰~žëZ$×3O=M™ËŽlýÈ:Ú‘ÕÁú¡ƒ2’ÚcDeA·žq0Æ¡Õ-º‰–¡jÒ4±ìFÏj…<3]³ˆhºžE£N¹ˆïãCšå”¥tâeUQä%ý~ož¥]ñƒ;Z>Ç0I“T–m‹c4U-Ï¿À÷¹xña—¹e?:äYŽg;R£%’!ޤFt:ëyw‡ÄQ<»f;®Ãp8d4±µu‰‹6™ŸŸçÆ›n mî»ÿ~Î;Çüü<'Ž'}\ßÃØà©(Šp]w¶6ÉZËC=H™&Ÿù¦oúÆ—ÌÍÏ1޾°Æå¿êbèù>Q§ËüÊäƒÝÁn¿óÎ_þ£÷üÉÑÏÞ{/;Ã!aÒíõñ¼€4MH“ qø!Q±¼´ÂüâËKËô{}‰„lk²,e<ž¦)'€Ó©vO¥M]‘%i‘ϺŸ,MÉs SO’„$IHÓ1UU>O^ðÜ×%ö:©’ óó\sõµ=vœ…ù%áõ·¶¨øb´k[AÈÃLô¬Ñ”•ŒÑUY“["±ãÆã‰€IÛfÆ œ¢°â8²H:®ý}…¾ç2žL„wˆä+­Ÿ4¬–fв¯g~j㺤“TSÔX]£4D±.dÔõíqÉA¡ó,NßdŒ6¢ÃTJÍrIÒ4a2Nù‘ýa¾öÍoàãùSü ®»á:ËÞ‹yôñ'¸xñ"n‡ÑhÄSO=ç¹\¾¼Å…ó(kV¬:H§Û# ®ºêJ:QÌ`o@…$ã[Û;<ûì)F“««HlºŸçYùH#²Ç5"š®kŒ‘5GS×¢ç³èµ ðɲß÷qŒf{G|®ëR•µ-®Ï°6BãZ2ØNCä§c®gÁE&(m L2žŒÂæT'“‰Í)’žeš´öŠìÉeÚ^À}ÏXl+1S™”1ZÜ1¹Gêª&ÉÆl\Üd°7¤Ó¸å·’gwÜq››:x#‡…ž¬k<ÏóÅ7í1*Žbëˆúìg>Kä9úïÿ¾×¶Àd2ù‚)ˆ¥Å0êtð|õ#Gèu»'ßýÎ?øÍßùƒ?xé]>À`2¡ÛŸc~n^®•uÍÞÞ.EY²0¿@¯Ûgaa‘••e9ÌáCë„ÏöÖãñ„¦©)ŠœÁh({«,£.Khƒá€4MÉÒŒ¢ª0Žž…ÿLÍþ­µJeYN] ™f<ÏÜ$³» lŽèó»ÅNóâ¿”k®¹Npó6¶Qàž¾È=Pm÷HÆ8L&‰]Êû¤©ÈS<ßÅ8ŽKi3T´qð\ÙóMÆ+¡ñ¨,è´±…ÊíkifÑœMÛ¢ì‹Ãq&ã±ÀW-U:KSÉ®ŸÇ^´º1‰Ï¬lؼdŽH²H «qs]‡²*gG£<Σë¹ll\â{¿÷m|ûw~›.òŽw¼“_ÿ÷¿ÎK^öþÕ¿þY{øQΞ;Ï4ä¾ûàÐáuŒqxôáG˜›Ÿgeuc4ËËK¬¬¬07×§ßëÓév|ßêi[š²æÑÇŸàÿõ¿a0E¥•K‰Ü †Ã E¯×`¯²oöª´–bfí{…fgiN‹,YÛâz.•u¿h­ÙÝÚ!êvP­’Ÿ³# BÇG‘LIÈ}]×h ×à®ã¹%‰‡Ó¢W× ÃñÈbÌ´]Í´Da8“_•y1 ‘jšv¦=4ÚÐíuɳœ¼ÈE–4[BÆq .^`ãÂ&ãɈÕ+=r¥wß}/.\àСu8€ë:xOà^`Gy)´óós–£xÿ}÷±ññO¼íÃþȳ_ñ_þLQaûÅÐŒc8tà O?ñäýo?ýÓ?yÛg?ÃüÂ2W%ÃÁ€­m¹ÎÏÏãC‡sà 7píµWщB†Ã£dDY\¸p3gÎráÂEK›–ÝOÝÀ5×]ËêʪõjÌÏÏS”…µ=µ3ªtšeŒÇc¶··ÙÚÚfssƒí._Ú˜R´±Âç¦yÞq广OÉ-u]óÄ“2æ½øÅ/áäÉ«I=OF´²¢*$%p+1ѾcÓáZK[)‡8ÆÐítf¾^ }—ñ°°T낉b¡b;¢‘L“tƬ« ÇhÙOÙc·T}Y”4MŠk„F]T…kµMMÓ*²F®ÌÆ3’bGz‰-„ô\”h½^‡l2á/¸…ÿ§?†q^ú²—pió2W?&â{ÏåÚ뮄VfmsÓ¯(8õô)ŒQŒÇcΟ½Àü›4uÃC=Ì©gOqþüE¾ï¾—¯}ó›X][! C†£!Q(ÝLÛ´TuI«ž+uÛX QKžgGØyVd©dŠx¾h0]9 ¥iAccLµï·6°J#…¬®íïÉ‹‚mI ´ÝwÝÖ·–Ñë÷¨ÚŠ<“¨ßså ¢5q'¶x¶v¦P(ò‚4“]£6ÚÆÓNu¡™°ý½=ha~~Nœ*Zã¸}Æ£1E.Ý¢q”=Ξ= ®½öZ®¹újŠñ§úŸê—þßöØé3¬¯eueÇ5 ‡#9ld™`‹´ÁsBn¸ñ^þò—°¼¼HžæLÆ’$áþûàá‡"Ësz½9úý9®ºzååe\ס×본¼D[ ¾n[†ƒÊzZ‹²´Âa Îîv:3™MQ•ìííqêÔ)NŸ:Í“O>Å…‹gÉë|Ö NGƒçDÑ Êr4ñá|ˆÝ^x니——ä¹ø¡•í>jKäv= ÈÒÔ^˜%3Ø5FFeWðþBËiäÀÓÈ.Î8Fd(é,,°Q²¨¦ˆz¥ˆÃÜ”$I2££A@^v(»¾éÁÈsÅ1”&9mSÌŽReÙ’¦‰ ·÷éÏõĶ—h=%úê<§Q^={æ ûèm|ë·¯}ãëxí_'G§ÑˆKÛã†!eU’КÉdÌp0À±;ЇxXHÔ£[ÛÛôú= x²ÌÃ'Ÿ~†ûî¾—›n¾‘µƒk`4O=þÏ<ó ë‡Ö ƒHT¹€T«¢d¸7”¬k­ñ=—d’JAÌr!¿h±ñ‰Ç¼Á5£ ƒÉ˜,Í㈪­­g¼¥©DKÙéÄ­¨k‰~QºdG»ÖR™e9q'Dµ°·;`8¡µˆ¶#]ze‘ÿ~ Á›ºA+M¿ßg<¡*áQV–¦-ûÉFºW ™–e> %K“DŽ G[Ãx¬¨Ã†þ|—–†K›[ÄñYÖ×r“º‘Ûo¿ƒ³çÎ…ý^oV|«ÊµÓ“¬SŒcˆã«Ë+Üxã Üþ™Ïþ~…/>vüØgEõùˆQç{ü/µ†qŒ«áíÿé7ëýÙÿó[wÓŒ“'® Š"´ÍßÝÞÚ¦ªKÕjXY9Àk^ó*n¾é@ž˜›|æ3wðä“OF1ÖÖ8zô‡¡ß“+èÎΞ؆š¥ U-E" .om£´ØáKýpŒƒÒäY–2:y’ù¾GE .n\à‰§žà3Ÿù »;—mQ”.ôùÅðùÚŠ‹\ç+_ÿU8®Çîîö,ÞÒ÷\Œã|Ná] ì&“”¼ÈíE²Fµíì]¼UÒìîìáºâ±îõ»vÜ/ñ<Ç‚iKYÎÛ]ÒT H*žå&6˜Ýw}<_8ƒX¥ ¼ÙÅ9¼YôÔØïX-œXÄdOìy’Ý1íØ›¦&Ï ^ñ%_ ^p++++”yÁêÁUæú" έ;Âq\ê¦æÌ™³Äq̹³çxìñ'Y^^•ÄxÂòÊòŒ^ó‚¾€Ç}œÛ?õiÞüõ_Ç—¿ê•,./qþô~ò_þ4—w¶éu{ ٵŃáˆÀ÷g4jAâK¨VQV2ÆÆÔÞ4-Æqmž xžC’ÈÌó=’ÉsØ£4EU„ªª.‰âØFßÈZ%Ï䵚Aì:!ËíôbP§­…4/ |ßÅu$rBÛ¸!‡ »êql78õ-ƒÂ|Áû;ŽHË”B+ˆ:±dz[·k’dÂöå]ò2çêk®dmm'ŸzŠ{î¾ÍñWÐï÷ñtKFø^€ÖšÐUÉçÐòÔSO±µ¹qþë¾îk®ëv»Ã$I>o;Duæ/é€ÒZ$þúú:ïøÏÿùç¿ÿGÿÑ¥Jqűcôº]«û›0™LO&²ór<®»îz^óšWqøÐAŒ6\¼x‘~ðÃ<ñø“tû]®½öZ®¿î::ÈÆÅMöövñý€4I(«NDXs9mÝÐívGL& a7–bÙ °3Í2Úº¡µ4䶪gJŽ+Ö­ÅÅúý9.\Üà™gžæ3Ÿý <üÀçtŠÏ/ˆÓŸumÓüæææyýëßÈÁë\º´I™ôz=Ñ¥¥éÌu…"ÅÀæ(% ÓªüV[W8®Hb\×0±ûHÁrÙ8Ȧ¦c-Tä#¾X£µ„#¹â[ŽÆäYN‡³îjºÇ,‹ÒZ sq2ø>E‘á˜çäE!AâßmÈòlEÕ¶œ&¼Ñ¶v—ç²yqƒÁÞˆåÕe²4ã ¯{-ÿäÇþ1gž=Íåm´1\ºt‰Áހť%\ÇáüùóvÜÛ¼´É‘ÃGØÝÙ!ISŽ9Šëúý>_úÊ/!êtxêÑÇøÅóoyêégX][%Ïs,Dœ0 졬Á1š²’p/ÚÇq™$kÛ H&‰¼y¢±Ù‹-4F1ÚÉZÀs‚<ÍÑŽŒûíÌ3^Ù±{Nã¥xI€RÌdXŽc¨ŠŠº©mÖŒ¼YOótÚVˆÝžˆ­­œ¶Å <ß— 4×µÁñ"±F2ÚZ—‘1Fv‡E1³ÿeyÆp8`ëò6;»;h 7Þt#ËËËÜyç<ñÄ“t£˜Cëët:SÜ$þ…a< 6ëv»³ƒ>ˆnÛ|Û·~Ó«‹i<íçaAT÷|òS)…¥¸òê+ùø>ø¾çïýO?³S\uò$ss}ʲ"Ïs._¾,WÜ¢¢×›ã¥/{)_òò—r`u•Ñhć?ô>{ç]ø~ÀñqË­· æ™§ŸÁ³£åh8‚H–Ð4 ƒ½]y'Ísƒu]S–BBÉÒ Ë„F!âSÏóèD1s ôº]Œ#¼¿8Ž&8ž±Âp䊹¼Œq]yäþøOþ„Ç{h6"ψÏÏ}{g¹ùÞôÕ_KÈ‘¥hê†4ËètbÉ-±×̺vÓH®²cÌLçV×’+<± e@Žãâ8ZþD¥äš\78ëDÓÒÒàÙçx2Æu”1¸Ú¡¶cÕôÅ–¤F+ ,j准ÆG HWÑΊK^ÒÑØqÒÙØm3”—Æ\¾|™¢(øéŸüœ¼òJ¶¶·˜L&L&) óœ:uŠ]n¸ñzžyæ[[Û,,Ì3ÑJÑíõØÜ¼ÄÉ“Wð†¯z§Ÿ~–}àƒ¼ëÿˆ¬(X_?HiÅæyQÚ¨R#WN”íð\м¤¨„à¢,¬mkÊJÖ ˆ ÁZ;™UÓT•ø­­íε²e߃À§È«ç2`!xÖ±téòeüÀ' C²4ÉÕÈ*­¨‹ŠÈ:zò"“qÛ1b“CI2žVôz]+rØÚÚ¶P…èsv¡¾ïã[Ù™¶«ž½½q'¦mZ 9öÙ¼´Éöö»»;¬XáØ±cAÄã?ÆÝwÞÉêê*ë‡a´& }+/ èv{´@àûAhñj5÷Ü}ë+Ë¿ôÅ/zánoïÎV7ŸgÅð“)Åpyy™gyôËà‡~ä£OmlHÖÈüÝ^—ÁÞ€ÍÍM NhéwçxÝë_ÇË_þ¢(âÎ;>˾ûO¾ô_ÂK^òbš¶áÒ¥-±&Ù…vàû vœ¿pŽ ϳuù2ÛÛÛììì0ÉÒÿ¿>gßs™ëÏÉ_ó ,-¯°¼´LuìØVá{žD‡Ã‡×q—ûx€w¿ûÝœ¿pF–°ŽÃsõPYŽŒDýþ_ÿõßH'î°·³+¨,ûÂs=Ç:ZꪚeÜÊO“XL¿ãH4ç4y®mZ¼Ð§)+›¶ˆö¯©Ñ­z.É q¯yiaâœi•Æ1zö>=d¹x޳Lä¾ ŠJ­LÂhcW Æ’`¤(7MKš‹Þ.²ÓüñxÌw}÷wñº×½†gž~†4ɸ¸±Áµ®½ö.m^booÏI’„ñhJŠRmÉËÏ<ó,¾ë²´²Ä3Ï<ˑÇxá oááG呇ãî»îâÂÅ‹Ì-,E¢ÓWo÷«“I†kÜ©ZÖ%Jä5ZGy–Ûx0ŽP)„J¥ž'TE!tiì öläƒd•ÔU5˹q]‡ªn­ã§&MrºýÆh¶·vÒàöú+Qm&•ý}–‹hWARMIEöˆâ¹.EQQ”¢8p,@cª3­ëÆ‚†5Ð’åÅ,raŠë*Š‚I2!Í676Ùº¼Åõ5¾ìK¿Œª®øÃ?üC.om±º*¯‘À£„£ôæúÒ (E¯×Gk‰f¸ãS·ó¢Þò]/}ÙKcooïó¯3¼|áü_x!\XZ¤Ží¿ö~öö¹ë®¿‘^§‹6òÝÞÞ–<²âè‘ã¼á+_Ï­·ÞD6Iù½ß{'·Ýö ®ºúJÞüõo¦Ó‰ÙÛÝ¡È Û%“1§Oæé§Ÿ`cã"66Èòü¿õe>wð°ò„ÙOÿÿV# ½nCë‡9yòJ–WVq(( )LapÕÕW¢”æ#»w½ë÷$TÇ‚žß%N5‰‡å+¾üÕÔeM·Ï^\ÆÛVö9‚ÿwI’ÉŒ£7ÅHM ¥6‚Ö6Öc,?ŽëÅUYâZyÅxþ(¸ùæùÔ§>ÍÇoûS.ooѶ0¿°hQ[à—$M1ŽC'ŠÅmSèV±³³CÇ„Aª!Ír=ÕJV³8Tmñqâ¾±ø2@‡$M-ö-´¹.×q-Ý&”ìåd"#¸ÏqA«Y‡,‚v)öU%ée!X·ºAUW"·ï 7³´œ¸c”œYšø¾…ÍŠ)ŽBªº™eé-[Kn³ô—kßÀ§R±4Ë,è¤`8ðÌÓÏÒÔ ×\w/|Á ¸pqƒ|àlmmqäÈaV—–qÇ3„dµ„a$H1Oò»Qll^⑇æï|ý×¾àøñc÷noï|^TÔ½ú‰¿Ø¨5Gâgúg>ø“¿üK¯¹òêëXœ›'Š"Ò,acc“( I&)×òMßôMÜ|Ó <úÈcü›_ü%ªºæë¿áÍ\yå•ä6:±®¤ðìlmñÄñÐÃòäÓOSÿí’šµÞ^ þœNpVºmW3!õsþäÏ},--sôÈ®»îzV–× øž8æú>r” .òk¿þ8}úÙÙ.ñùiA¼òÊ«yÅ+¾Ìú„mÒYžÏôbS³~’¤$“1žïÏVJ‹GxšvÖµDq$°X-czQ PVDöbï9·Üz ‹óóÄQÈÂÒ’8ˆê†£G³´¼Ì¥Ë[œ=sŽ––…¥ô1š–W–dI…”EÁŸ½‹÷¾÷}(£Y\X°íª[KܱW%þdqÉŒH±±E‘Ф‹R ºž#Âb£Ä,R5 S¯j¹.?//Fh2µ ãž溮M¡+„À…`¹„m=eH aH޵ÄÓV ®-„ã‘8z¢(²ov¥Ä£1Ц–Ýt³Ïó,<"•7¦¶¥ªjzýžìÑ›šÒ¾ w»ÆÙ‘Æ–çØX6áÞšíímNŸ:C«^ö²—rë­·rÛm·qÛÇnC͑ÇévzDaHÈÎÐõ|± 6-žëáùAòÔÓOsñÜÙ‡^ûêWÝh1hýù1.«üîïÿ…}°¦iX?x»îøÌ·üO?ñÿ%X\àäñcø~@S7œ¿x^ŽÊamu·|Ç·rݵ×òGïúcÞþößáäUWñš×¾Jöuã U]øy–ðÀ}÷rû·sîâ…ÿj''/jû¼z^üo‘ÕÚÏùýÓËç^†ÕìkâyŽ0yÁ ^À5×\‹çJªÝÜ\ŸÑxÌÁCéõ{üþs}ìC€tQ^‡ø²—¿‚—¾ø¥lmm“[ïl‹°EŸ&Ä×u„4Ó4‚}/K+—Ѿ(«™`Øu$I®°éfÒ=šç²y³L„²®Çh4¢®kºqŒø$i"üAOÕãñ„~¿‡ïɯu=—¶? fùÈ~àS”özo}ËeUãy­‹Ô—‘l4š0?¿Àh8bgo­44 ^à:ž#)ßò-—/õ« Gìíì27?ÇÞPŒFc”‚_ÿõÿÈ<ÄñãGgß×I’â{aÚ#…1Ö±aÁ F)’,µH*‰wâˆl’’—% bi̳Œ ÑJè5Íôd´ Éš†ÒF®N¿v‘jIUiåNÆ8`ã"ªªÁ¸y–¢µ!ŠÚÙeÛë¯ë:Vú%@²¬¬«¤¶Çflׄ6&¡¶ÐFmÄY4×ïãuÕÌ~_ÝÚLmËbRwj'ÉÙ.+±XzžÇ`oO&‰¢`’N¸|é2çÎçð‘unºù&T«xø‘‡¹çî{ðƒ€ÕµUæú}<Ç% |{ÕŽ ,ô6Š£™’ã®»î&ô¼_ùÆo|ó÷Æ ^þë/ˆæ-ßôM´JýÿÕaá:Æù©ý‹ï|jãâÂ5W_EDøÇ3Ï>#,?ípʼn“¼õ»¿“'Nð«ÿî?ðû¿ÿ.^ÿú×ñ•_õÒ4c0Ø®ÅððC÷óá½Ÿê“ F£çu€z6Ž*»WšR¥ÿl±T³¿¤-Wö×ò_]€Õçü¾é¿WJÍŽ!UUqöìYΜ9 ´ÌÍ-¢´Œ…£áÑhÈË_þr–WxàÁ¨ëj†{~ÇzáÂEâ8â䉓¿%X­ÆÆ|–e5 {o컵£E85´(êF>f…òâ)Š’¨Í>F…T¥Ð–æ†l\ÜäÄÉ+8´¾ÀÞÞ‹/ ý;Í$q-ù§•(ס©+Z‘ô¸.J·–U(ñžïÛ Ѻ9F¸{YšŠd…†À÷X[[£ÛïḠãˆÝ½]>öÑ?åÞ»î¦IAJÆc–yæégyæé§ùÌŸáíoÿÎ;ϺKlilÞ³@Nåà„yÊÔqSÛ1w<²3e@ÒÚ7.§j%û7cD2S…¬*šZHÚuƒ²{7@4£¹=J€Í­±h-5Í™B4<ë5¶Z½RŽ*y™ ÎÍ^‹µRòý´G­)Y]ºM`ɱpÚn¾*+ù)ŠçûŒF#«DÉÂó={$4œP2[Ä%—&µÏ…N·CU•œ?w‘¢Ìí `½Á;;;”E!ÙÝÖœ`l¸˜o?ÿ)3D]ñÔÓO¿0ð¼§n¾é¦ëº&°aY©O¾ç½a»Â#‡ñ;¿õÛÿüÇî_ýÄ‘+OpøàA´Ò\¸p‘½Ážër͵×ò¶·}ëò/ÿ÷Ÿâ®;ïâ;¿û­\}Õ•lnlZºnDšŒyÏ{þ˜ÏÜuןƒ§V³iÖÅ4ëöù#òôÅ1-”2þ>ä¨þœ øó°bÏGy=÷g3óÿNÿÛáCGxÅ+¾”Åù%²<'Šʪâ†oàÙgŸåÿõ/™¥cK—9 ÷ü¯yÓ×pôÈ1.œ¿`=ÅBkÑJ‹äÈ>¹Ça’$+š­ìŽj öt,Øu=WF£ü­ßù=Ï>töê«èÆ1»{{looãûÖòC?òX?¸Æ?ù‘ãÜ…‹|ï÷½ ô-¥ž}æiÞóÞ÷pñÒæçÁé²uZà›t§µúœ®N),i¹~Þ¿Ÿâ¸ÚÙŽpZe—ø\ç6ýu¶0ªÏw§¿Æq^üÅ/áÚ«¯›TQÒéD¿â.\äWõ?°7ØžådÈÇÓÔMÍú¼æµ¯›K¾ï îʲõß§­É*iöv#âÖ¢*g…ÙhËëÓ×÷h,°4ÆïòÓOŸâû¿ÿmü¿óõü§ßøÏtû=nºé=L§ÓáÃüÿêgÿO~ò§þäÉ'ž`{k›ÁpÈ£?Ʃӧу¡ ÖªÙÚºÌÚÚ2+«kìî¬tDáOt’Mm÷P’ Òµy |og?¿)`ªQ+ì•´(ŠYr^–¥VÜ,!QJµTuk_´šÈºiûæ1ͽQZI0—§)E]Ç1ežã‡4K †TU!ú8-ä—¦iÉŠ MÜéàûã±hc×ÃuäÂ,#fcGòš4‚ÑÚv¦BŸ$)8¢®+‚0dÿ2ܶõSZ°ª-ZÓ±â¹ñ–?³|î¬ÌçiþëãË´PN_¸Mó¹ãù—“'Nòâ—¼Œ¥¥%Ò$¥ij®½îZFÃ1ÿî—ÿ=—.Êhfá­Õê}Ñ _È­·¾€­­\ÇP’Éø>y™ãƒ£å:èXŠHX$ãW(?™\ëÕvo(6­ª®þý¯üΟç¾ç1Z„Ý~—7|Õ¸pá"çó£ÿ臸paƒÅ…yæå0ñìÓÏò]o}ÐòÅ/z/xá-,,.ðð#qÏÝ÷pùÒ%‚0´éq¢éÛÞÙûÖ\"†ëz#•Y采¶‡­¨ÓÔ-Éd$Ë|+6Ÿ†b) eQÒò\˜}ÓÔ²“nTx!“Dtwõ † Ec šúi˜ŒÇh-7Jh5­í¨ÅªiqÚiU•CW•$&Z‰P`ŒM7œŽ˜™À?P8ޱFcãIý `<ÉQÄQVm1M_l¬:M%«º×íS5ŽëàWdCU-cûyfiFÞüT uÛЉc\×='ýÐg2Ihl ¬Ðwä{”çâ××JY»`$Å0oö÷ßá–Ýgy7þY½îrÚœ3}FÓÕ%5Ù¸Æ4cl°Ó!$ÒHòRBùI€b á  Ž1Y’Õ­^§kúéçìºzyÿx¾{ÍH6`á'®u]øhæèœ½÷ú®§Ü÷çN–—–YYYe×îÜ~ûm,-/ñØ£söÜYʲdûÖmtÛmlÛÁ <%óƒ€v»ÛÜ£O>õ ssÿüu¯¹ãç767øÿ' Ûøæ·½Bm©þ®—¡i¤ã1üíý•Ñpjë–-ø~ÀêÊ k«ëÌÌÎñ}ß÷=ÜvÛ­üø¿ø·œ;{‘ü¡ï§,K6z´ÚmlÓäãû(ùÙÏH™¯ÉÌA×µ¦r˜>²-–Ÿ‰÷vÒB\Y¾|òò òK„_\õrÅŒQ{ÙæK»¢ ¯ÙØÜàü…slߺngZr{GC¦º]^ñŠ›9vìc%•‘´Eôú}fçf˜Û2§>¤2ì–Ô½×õU”¦À¢Îc©øF [rˆÆ"­°l%”j;­k:žºÚ­oýê¯ä›¿ùxÍkïàª}{ |OüÙ'yàþyÿw¼,Iù–w¾‡¿ü‹OñÈC°¼´Ì<ÈÒâ"ÿößü+>ð}ßŵ×]˾ýû¸õ¶[xûÛ¿š_<ÃñcÇ Ÿš®ñæ·¼‰Vòâ‹g™™›!M%K¤ÈÅñ¶B²4‘wÓbÐày®Š40M[^‡,C7MÊRµªŠ¤®•X:{ j,Jbj4K¼ÚeQJŪ´y“ˆã8Í;Ei:É*®ŒJø^(}žeY´Û-òIJ «ÖÔØ–ÄËš¦E”ĤIB'lID,âê©…ì+i‚–E]kM—#`ù'-ºi C ²).óLZŽ-ùÙ^ Dõ² Õnaèi’aè—=ôà¯n˜âÙÏE#9G :M²‡äu‘è‘Ô•­u5ƒL“DLZÍ–¹ylÛf<Éb./Ô"N°gšr$i2 Ðt%=²Ù2¿…Ž}³¡éŸÜ¶}ÛÅ4M_2ãÿ‡¼Œ·½ö â ý;^Iã;.<òø{>qϽ:m|Ï'IbVVWp=Ÿïùžïæ¿ñëùþõOñÂó/ðƒÿè( q¡8ŽC‘&üñþ<òp³ÖÐÕ!¨7W¡rgg瘞ž%Ž£†™w¹rä‹(“úõrÅ÷Ҫ𥋔¿^’3yá¾øÏÈÏ™Ä1'Ož`Ë–y润 ƒþí;¶±ÿÀANŸ:-–5e‰‡LF%\}äjòBXˆ–iˆ„š@vt•E¬ÓMSžþEYaڮ뒥’n«\ÝI«X•%ÃÁî<͘ž›eßýÜôÊ›yÝ^Ïþýû¸ëÓwqÇkïà–[^I·ÝbûŽídiƧþâ“ÜõÙÏñÿûÅÿÈëÞx'üÕÿÎGÿøOù̧>ÍÉã'xõm·233Í'ÿü/›T¹ïýžï滿÷»xÝë^ÃSO=%X~¥5LCYØFÃ1kkkhš€@‡£¡ðò\Ÿ4˰m‹<›T+º´ˆÈü­(Ôfˉ… ~kS Ï5ƒÎ²œ~€ëº40tP¢öÉ,k²yµm‡².›ìÄfè:Zb‘x¯­¥ô&%°ùóž+`_Mt‰ÀUŸ'Ó”pªÒ'á\š¦ªQÊBü"á’?ã«…Xže”Aܱ$ ¯¬J¨ä¡0Y,úAÐÈi*µÕžDeTU¥²M 2ÕeØŽ#[éH,²¥ú=55«¶LKE­ GCÆ£ss3Ì/Ìc&ôú›è5*+ÅTzUéÞ&VÀI—6ñ¿;vü+nyõ+?8¿°¥°‡V»EØ ÿA/ã+_§P}ÿŽWšg´Z!Ÿ½ÿÁŸzêÔñ#³33†ÎÚÚ:ƒþw}ó;ù‘ý'üê¯üûÓóƒÿè°,‹á`D«R•¿ÿáñÔ³ÏJ5¨O ¼¤”ê'àÈ‘#¸®ÇÅ‹çˆã±‚%hWl~¯<´þªƒþ¦@×~¯/õïô+¾^¿¤RÌ‹œ“§NÐîthwºdiÆx4b÷îtÚ.]ZR­RÞÌÃ66×A½œl• MüÊ …6ÉĵÙ˜…SêªÂSaíè:ÃáP=Õ¥%Çì½j/¯xåÍüÞ‡>ÌGÿðO¹û®{ùã?ú–—–xó›ßÈ™Ó/rý ×qøš«¹ñ7sÛknçM_ùföìÙM]U¼ïýïãßýô¿çÿ÷¬,-qñÜyî¹û^>ö'çø±ãX–ÉâҲЬßñõ<úð£ìÚ³‹n«Å§>ýY\×Qº®cš6+++TeÍk_÷î¸ãvö8€çúdYÆòÊ Ô•ŠÕ•KÈn ¬ºa !7˜ïû8¶Ó¤ êj{ š|=/)Ê’¼”.Â÷ÞïHâ„$IÿA/ãÇ~ø‡™Ý2÷w¾ö:À`spøwþð#ÿ5Ó5_’Ò.^¼Èm·ßÆ¿ÿÙŸáî»îá?ýüæýßñm´ÛmV——Eò‘¥|èw›çSm±~¹2Ta¡Ðý;vìæ†®g8ò Ï6þÍ/o¢ù׈Ú_y0þõ³ÜúeRYÊ\‰Ë?ýâ)fgçØ27O’$\ºx‘ý÷£©œ  dJüëE;¶ï Ýj1E¤YŽiʰ¼Ìå&›XĪRª>Çq¤Â,J•O\âûžÜ Eå8ØŽÃúÚ:oùÊ·ðÃÿ䇸áÆë¥6MÖ×VI“„ë®»– /rã7ðŸþãácýß\¼x‘$ŽøÜ]÷pà ×QÕ¿ú+ÿù­ó|ÿ|ßúÞoáСƒÌLO³¸¸Ìñã'¹í¶[øÇÿô‡ù½ßù=þùýKîßÇͯ|%Ÿýìçˆã1¾_0ŒùgÿìGø®ïý7Ý|·ÜújÞúUoáõ¯½ƒÙ¹Y.\¸ÈÆÚ:¾HfÇ0²Lzý¾çÉF5M%™O:E^¨ªQ¤'ŽëR¥Šäôä}B6õš¡Sæy™!Ãþ°©F'NRI†d6¨7tÓd§JÌmc™&£qÜв'ñº®©¥ŽA–f¤i® Z3;¶MAˉ¶Ö¹œß¬þ[eUÉ×+d—E‰Ž†ë¹¤¹Ì˜5]GS‡O]×–¡\4yQøeU²±¹ÙšÒ4ŶmLˤª¥{C_1MÒ$Ás=U»øžO4ŽÄP Aš¦ úCâ$bËÜ®ë1I»'1¶c)˜ƒÂÞ••jùõæbYN‡'ž|êæ–<±sçöc£Ñ¨©Êÿ¡.sÿ‘ÃÿG"ë‡ñ̳üæ£gO³s÷–––™ß2Ïÿø¿"ŽS~ú'~†·¼õÍ\µw/+««„:¿ÿáßãùãÇãx0ù§(r|?àºë®cëÖmœÈ³Ï<ËÞ«®âU¯~·¿ö6ÞñÎwpòÄi>qòψã˜í;vòïúvíÚÉýáóñIæÙµk'/.Ñï¤u¬*,Ó¦¬ ¶.Ì lÃ2Ý£çz”EÞHvúãè5í°CØò‰“˜Á`€çË¿—ňf†¦“æµFex¶®1 1Ér|ßU´ BhA†Žd/kº@ ß“ ,I¨«Z´ºê³h™–úì—èJϘežë²±Ñ#lÒf[&y–+·< µA¯ÊR ÷ Ð /”x?Me g˜˜êïEyÁh4Äó…^†U]ch%“ !fÇyQ¶B½H |¡z¯®¬±:¿Æôô4W]µWëWéõúÌN›ä†ª(mGI²å½D\§Ýaß¾}Ü÷ЃܹkÛ}¾ïmHð·]6¾ë}ï!Mâ¿ÓUW%ù ÏúÏ¿òÁãçÏÏÎÍÍ‘Ä1½^Ÿù‘Â7½ó|ÿ÷ü Y‘ó ïøzÖÖ60MƒvèóÑ?þ#¾ðÄãª5Ö›lòÄ(Ë‚mÛ¶qóͯ$M3ž}öY.\8aèX–£æE¹ññ?ç÷ÿß±¹Ùã¶;nã·~ý°ÿÀÞûíïeiq‘C‡qøða‚ÀçÕ·¼Šž?Ê3Ïþ±?ã>üö\µ—xñOÿí?fïþ«øå_øeÂN‹ïüîïà¦o`ÒJ:|%´S>GÑhÌ/üÇ_ä+Þúf>ñgÎSO=I§Ó&ËrZ­6gÏœ¡( æ·Ì 0˚뱕fi×Ìó¬Y6”…`ÔªêªD×t"•yâ`«{\ǶMFã\ZeW|ìV+ IDr3YdPK$BYýÚÐe{ì8Žp(Gcâ8Áó…:£I*ŽßwIÓ\"Ou‰XH’ú·„¸¶K– Çó¢$B×Mß&ËRt]'Nb±ã•e%ô¤n§+‡]^ˆŸº®™žšV •BÄÔÈòkue•‹/²wÏ^vìØN¿ß#Ž#áHé£ñX>—Y&‘¯NEYZ8®D*Üpà øk¿ÎW¼é8®Ã`0`nv–c/<ËGÿô£R–zS¥]nW :6®ëñøã‘e©¢†H`:@´Ø¾};A2ÉlBb>‡JTª©!qÕ ´&DM=$—æv—-[¦‰ã1½Þæ›ö²¨Ð—‹´iÚš]»ö„-Nœ8J‘gÍ|©ª*Ž}¹Ùy¹š8³ººÎž=»•/Ô`8<ëºÿvûÖml¬o¦)~àA­‰•͔͟çû*´$I3|ÏÅ0MêBü¦aÈÒ N¨<[(&Šn½uëV n¶“UUã¸ÇŽ糟½‹™éilËbjv†C‡qþÜ9,×ágþÝ I¾úk¿†ïù¡-áò2?öOÿoz뛸éU7óÓÿú§X]Yášë¯ezfšn§Ãë^÷¦§:ÔÔø¾PMÐ4î½ç^¾óýßÍÛ¿ökؽkíç¦W½’›^õJvïÙË/ü§ÿÂ×~íÛxË[ßÌ/ÿâ¯ðóÿñ8xøïû¶÷pððA~îg~ŽÇOø>ïÿŽ÷±{÷.{äQö<À™³çÙ±sw¼æ6>õÉ¿d³·)lÓæÄñã¼ç½ßJü×_úU<ßk #yžÅ2# C_ÍsвRŒ Ïó5D–åX–M§Ýx«'ðÖ<ÏD[H혌´UCfI”óÑq€šñp$U™&Õ¢«Ki^PižRGa¢i:Qcê²°™Ì…JnÉÁª  Y–S×¥Ú KØ”m‰_ºªJ+¢öôÌ,5RDº¡î« Ó²šLr×uGcÐ4¿MQV,lÛB|:æÌ™³lݺ•í;¶ÓÛìÑëõ%¾a4”†š$70M쩸¬ ƒn§ÃŽ;xò©g~qëÖ­¯Iâ¨Q ü_? ;íÎßé/Z¶^ÔÆÝwßû]™ ,:}ê$ï~÷·ðMßôÞó-ïG7Ln½õ–——iµZ¬¯­òÑ?ù(‚̯ëªY–ˆžJüœQ±¹¹!BPË¡(2꺠Õê°{÷nZ­6EQ0è÷‰Ô/ŽÇE‰(Ê—Ì5/o'‡`Õ´¼ÒÞV ‡CªªfzzJ/{EÞ´ð—«Àú‹$82ÛlsçÞB»ÓâÆoæž»?Í¥K!nUU<õÌlݺ _…­é©ÖÖVÙ¾}X–ÅÒÒ³33´:mâ(¦,ª•çB]®+¨K”ã¥Pˆ§J‘JÔ~à¶Zôúe1“Y•aÜ´;Õ…º&Šb×eÿÁh¤iÆÒÒ2çÏŸ§Óí²mçvý>¿þë¿É§>õYæ¶àØ6O>ñO>ù$ïyÿ{¸ÿžÏó±?û8Ýn—玽@šHõú‰Oü jlˆqƒ¦ó?ùoyú‰§ù×ÿüDZ‡Ý{÷?õ3?ÁßòF~õ¿þßüÎw’Ž"––¹óMwròÄIžîyvíÞÅÔÌ këëÜûùÏó®oýfV–WøÉÿç§9tÍ^8z”|×Á“ÍÎÌ¡¡S2:x×·¾“SÇNñ†;ïäí_÷5xžMœÄ¬¬¬ÑëõyðyæÙç˜!Šb©h\—V§-ÖÂD d1"­,Ïèõz’#lÈFßu,CDÇEQ6.!Ã2©sAõ‡aH­ÕD£¸Ñ>NðüQÉ,¯¢kQv®H9UQ)'Š…ãÚdi ¦šÆ8ŠÔƹht¶†®Si‚)Ëq2åy“¯S×ÒIØ–ÅÆÆf3›7 ñÂK—% `]“Ï~„TeÁôÌ‹‹‹œ={ŽÃ‡±gïÖÖ×9}zÌ`ÐÃ2MÒ9¦.›îL©R !µýû÷qÿ}Üñô“O~ÿ 7^ÿkëëÿ0Ù)æ‰Çžü;ýÅN§ÃÒ¥ÅW=ýüó;§æfY__gvvŽù‘§?õY>w×=|ç¾]°M¶¡k|îsŸá¼: ®<¥J4šƒE6p¦j]S,ËaÿþýÌÏ/HIGÄqÌh?ò¯¹ý5LMM1™™é2öð ŸÍM©H'y!ss³Íö-Šc…Ó‘m^ŠƒAn¦€4ɹd"ù‘E‹aR©¤ùåvºTÕ£e©™œGYÊÖv„J㺦iI¸º®“%®ëa96/;ÆãO<)¸ý©‡¯>Âoþæo1Ù·Ÿª–D¿·´´Â‹gÎ1 äFsÖWר¾c'ßøÎoäï~'oûWóØ£sìÄ .œ=‡<óä3ÜpãõÜðŠøÙŸÿYò$åäÉ“ÌÎβ¶¾ÉÔÔƒá€Ûï¸ë®½šO|â/0=—^Û¶¹ñÆë¸tá"gÏœÅ÷<<ßãÂùó\ý ~ÀC÷?È7}ó7pýM7HÖr)Mhðßðu|ìcçWÿÛ¯sÛ­¯æõ¯¿ƒ_ú¥ÿJEM’ ]”¬­n0¿0'ÅBqK[­ÈØ•Y+iJF™—ª ¶‰¢„qáy®hñÓ|†Ã–‚˜¦Ùĵš¦ Ö S4ýÍf[’øÊ¢’¿gÛê¾3ˆ²Óаí@—–‰nY úC<_²n&™8Ží2X‹äe"öN’¬ “*’|=ŽcÑ––à{>­°ÅÅ iµB¶ïØÎ®];G,-]`0ìáùV%Ñ4ÑT4®ÛÄ—‚³_}„ŽýÙCGŽüÉü¶mKÿaô¦f§¿¶Cž¹ëù¯½¸ºÂܶÎY⻿û¼êU¯âu¯}#;wmgÿ¾«XZZ" CΜ>ÁçïûüKDÊ“ŠÉTÔKû\–%EQ0==˵×^G«%Aßiš°¶¶ÊÅK—ˆÆž0;3Ïôô4ó[æq\W¨*¦Ø•¢(¢·¹ÉúÚ:kë«$éX=….§ÜM¶ÍºÒ`EÎh4`jjš-[ $Iü2=!ß´\®»öZ|Ç¡*K–—d+ûõ_ÿ>üáßUOVXŸ={šùùyŽº×µÐt©©iFý!s³³œ;žv»Åp<`<î6öý™O“—¥jë+ZX­ ¦iD˹ p¯ºj?‡i¾¶±±ÁéS§ ûtÛ³ÜpëMìÞ³›ù-sÌÌÎÒiw1 “¼›¿,%ß×0 ú½>.,râÄ ÙL_<Óжí4Ά‰÷·( ÖÖVétºt»]VVÒ+æ—/µðÝpÃMìݳ›þfÇqDþ@ÍÍ7½‚K—.rÏ=w©jW>PÇOcçŽ]¤™Å8Jصsý¾äB´Z-’$¢®Ávl2j’áW£Ù–—•lã$ÙÍB³m¹©´¡,JÏ—®iPWãQ,uÇFC½&IDŸWÕ5Iœª,gp\WåÔdz0Mñ˜:"‹è6…cã0CÆcÙüÇ2KB7H•Ýk8ŠÄDƒ"N•÷þøyü±ÇxÅ+^ÉÖ­óÌÎÌPÕ5ð¡Õ ¹éU¯à_ýè?ç£ò¿Ù¿?†®‘d)?øC?À×}ã×c»6qœ°c×Ο¿€e8ŽÃ”ªjŽ?)Tœ8Vv2o¼Õu Ã`zv–ÏÝ}/<ü Û¶òÀ}°xi‰oþæoä¿ýæ¯qýu×Ò Û|ó;ßÁÑŽqáÂy²,ãÈÕ‡ù¶oÿ6žzâiNž<>¥+Ù—ƒiêÄq‚ãÚè†l¢sõù<‚c«JtÍhæve%ŸÁ²ªEæRJnöĶ™ÃþÍ0Û¡äcç¹ÌËC Ëb<è+P…Žëz.5Ïrj5M!iÛ–Éh4T«ÍôÌ´ÐÉûýf»]eC꺾W¨mqmÒjµ±5nX]åìÙ³ìÙ³‡ÍÍ ¢q,ÝÜp(>sMø‹†a í]u.uåR—qsèÐAyè‘÷{îèÛ³gïýÁàÿjuh:Að·ß"w:,]¸pÍ Ç_×î´Y]Yâ}ïû6Ž9Â÷ßsðð!öﻊ —.1Õâþûîæ™çŸkÖû‚ÄWÙ#ʃ;™we*°üÈ‘kÙ·ï*LÓd0òôÓO±¶¶‚ç¶yÓW¼•믿‘];wà.£Ñˆþ`ÀñãÇší˜aäiÎÌÌ4{öîazfš™™^ùÊY^¹“§ž|†ûîûðßIèûl¬oÐêøa+Ä2m¾éÞÉ™OsöÜµÉ†ÞægÏžáê#WS×$1¾ç³¾¶Žç8¬®,cÍ8 G#|Ï%l¤IªžÞ½Í>YšÑê’g)Ž#‡Wm€V »o8”Ð)Ç §ëJ ¨UÊ1R × ˆâ˜è¡kÝ©.ÃÁ€4ËiµZÄQ$¿K%ïe]‹ÆÐR ‡¢±¿c;”UÉôô i’rþÜ9¶mÛÆX[[§×ï1õejJ碩Y¦ŽNªç8NJ¦Ëï1=#~ýÇŸ~êg¶-Ü™—Ù—Txü½†ùßa›ìy>>öä»Oœ=Ckf ßùÎïüvî¾û^Ž=Î÷}ßw³º¾. †µuî¿ÿþ¦¢¼l±“Õÿ„g'¶19¯¿þ&öí»Š,˸pá>ú(PqË«îàÖ[oaÏU{¨ªŠ޽ÀÒò›ë›Œ†CJþS•%y.xvË”71lÉú¦odïU{9pp?¯|åÍÜ{ï}ÜsÏÝll® é–é`š"™šš¢ÝnséÒ¢hÌÞ½ûÔreƒº’'Ô[Þünºá¾ðè£EÁÖ…­´ZíFÓ8=5Åw|ÇøÉŸú·ê÷”êðÄÉãìÝ»ÀõØ\ß`a~žÍMâ8&C†Ã++ËlߺñhŒçy*ì'Ãñ\ʼtªJ£·ÙÇPúBÓŽ4ÉÐô SUJº!RŽ~¿hx¾«|¦¥×ÒÞ÷p,‹\q’x,ƒt*lÛ°,9èT Uç9$©T;†¦Sd'˜È‚LàÕm£×xä».½ ‘ÍLO¡iý0G|þ¾û™›A×4"eÃ;xè GçWùWIӌٹYÇå‘¿ÀÅ Ø2?'öÆMʼàßûüÐç¾Ï?€ÜvÛ­LÏNs×g?ÇÖ­[¹táO=ù4ïú–wñUoÿªË‹·¼à—~ñ—ÙØX畯zÿãƒÿ“×½áµ¼î ¯çþûä>òÇÜxãõ<óìs\¸p‘o¼]—ÙˆvÖrmŠL„ô­V@¦Z]M—–iZ˜¶ÌÉjE¬®ªº €·,‘?Eã?ðqC7(òŒåÅ%¥?´ÛòK“„LI}ß#O3²²Âr•`»6£áXB³4ÝÚµn$CÓ4‰°­iRîl[f̶Ò`nnnÒét †$•L¨Æ²åÞYZZæØÑcŽÈ[¦Åá#‡¹ûsw¿þÔ©Sß|ÍuG>ò“{h|ë×=y–~ÙW‘gXüñÿþø¿zæÔ‰}ãј[o½…ŸüÉŸà_þ‹Cš¤ÜqÇíŒÆ‚yÿ£q÷çïù"ÈiÍœpbäƒØ€4M8yòÏ>ûíV—oùÖoåMo~­v‹G¿ð(=ôgÏ<¹¦3;;#³0Eäp‡îÔó óTuÍp4äܹs<þè£,^º„ë¸ìÙ³›k®9Âî]{X\\fuu ÛqÃ×up¹¹y\×eee‰(бm‡,“àw?hóÎw¼]Ó8}ú4¦a°cçN¶.Ìá¸6^ ‚ç#‡¯f4ñ Ï7K˜4•”¹mÛ·§rS¦Aš¦†NosÏõÃÔ"¡ÉŠRlTš†m[¸® ç£(!MuóHüdš$B£V9ÇkkDq"9 Eæ86ãñør\‚jÅã8jðïy^P–ša({VܤøÕ•x‡SRpÙœN6ƒEY)˜‚e[¸ŽC¥TUÝXé ÝÀ0äg¶mKÅW Ý$Ë3tu°˜†‰í8x®Ëââ"Ï<ý펴e_xä |þÞÏ ÕÇõÃÕµ5.ž?Ï­·ÞÂõ7ÞÀÛ¾æ«yË[ßÄ‘«³xá"øá?äïüž|â þ÷Ç?Îx4æê#×°°užçžz†ñÏþ5¿ó»â=ï}×ß|ÿâÇþ%qóº7¼Ž];wpöüî¼óu<ûô³<ùôÓâ'V"ûº®p\Wè.™ˆ¤+õzY¦ šØL ]*+Ë4Ñ5!ÐáFº'4­‰ÚÌó‚v»E4‹ÍͶI󂼨ð}Ý” ±¡›Š¢­ÆOY.ÔžºU ãq$Ÿ]T–Âàéºï¹Œ£WÁls:IX,ËJ-¶…ã¸D‘ÌÒ«²RÙ˺bS–diN'ÌÍÍ(¥ÄDQ$dq[¬zòû+!9ºúý5µ!wˆã˜ÅK‹{öîÚóE^RäUYÿ½_Æ7¼ù+Iãô˾´ºf}yõàŸþå_þçaš0öùGÿè‡Ø³{?ó3ÿ×½þµt»]5T+ù³ÿ)këëªE¼ hJHÕlg‹¢`çÎÝ}šÏ~泬,/¡:¾òâfYÊúú:«««39Óï÷u^ULOO³gÏ.tÃàØñc¶c3X]Ycs³GUI²µÀ K¨½55£ñˆµµ5–––X[[caaýö£éð…GáÌ™³´;®Úwdcmƒã'Ž17·Àöí ²ÔÛÜtÓͤiÆéÓ'YXØÆìÌÞò–· Q³º¶F»Ýfnn–n·#V+•-ì8Yš°sÇvÒ4ãáGiæqÑíLøäõq±¼¼,mªë2?¿€c‹VÌud~w¥ <Ïs¨+å6)dYQÖ—ã>“8Á²„]”9U- ªîœT‰2RH’„ #…º2Ìš¤Ô@»Ý´LlÓ¢?`3ÓÓÄI"lÁº&MSÁK9.…¢§¤YÑ Ô %—…Ø–M¬lcYž @µWãºRM•%Y.ú¾~¿§Ü"".ó’,ϱmÇ–­þTwšsÏóÐÃðØ£qï½÷ñ™Ï~Ûu [!›ë<÷Ü󌣈N§ÃÙÏpü…£|õÛ߯ÁÃxêñ'yï{ßÍó/¼ÀÝwÇu9uê¯Íkض{'‹.ñ§ÿûãØ®­š!Õ=ê¦vM7§Pðüº¦SWò¨Ul‚´%.Öu…“)›ë:ÔÊíiÛÒ $Ê·«©å‹©,ƒB«çŒXгX× ž¬®*¢X¶³AP)-¦€ODK˜¥JÏZW”UÕäñdyN§ÓQ‰ð15QQU’ c[¶¨R‰‚X[Û`Ç®íÌÎβ¹±Áp8&Ï3Q ¡›BòÖ4MªEU—©¥j†hºÎéÓ/Þ~øàºž›R×*ÚÖøû»¶oý[õÕÛ·meéã«_9Èòœ;n»•……yþò“ŸæàýØŽM¡ 'îè±£5íJ;Û$±L`)¦iqðà!lÛæøñãœ;{†];öòîw¿×uøô_~ŠÅ¥EºÝy‘±¼´ŒnAHøXªJ†b|G6p¦aâX²õ,Jɯxòɧ|Ÿƒ‡ðê[^Å“O<ÃG>ò¾éßÄ‘#Gx绾‰8IxñÅÙ³g{÷^EœÄŒG#ÞøÆ¯`qq‰_<ËW¾õ-˜†ÁÆÆ&Q±sÇN!=ç9E–7›jÏsð\ñjNu»/ÙÈWUÅù 瘞™Ås†ƒ!V‡v§C’Š˜¼¦jPš¦†>ÃÑX9 2üÀ'ÏrÒ,#KEPlÙ-¯Å°?¤(%p~0„>žá0F A§Ûi„Þ­Ð'ŽSj4Æc™%¹ž'sž,Çñ\ZíãqÔ¸ªªfffšª¬T›l“ižç w±|Ó´)ÊÛ6 ü Ù ž‡åH~ó¤‚ ¯!\÷ƒÝ©®^ † ]åa¦%ÔAˆfètÃZ-BôáhLYILÀöí;¨+Éœn·Û2WKS~ÿ÷ÿSÓ]êºf4³kÏ.ž{áþÅýKØOY,/¯ðÐÑëmrààN?Áÿúðÿâ{ÿÑ0Žcú½>¦kaê:yšc»N3ÛÕƒÁq\×Â4ue b·ëÚ¦N§B*+Š\é =oBÁÐ ò¬@›ƒ£®…\dÙù“Dµ»=ëú>†¦+`…㈾×Rù,†©ÓôÑt]aEOZ"Kù£]×!S ¼0U}¦–s2Þ²ÔA캪Ðuƒv« µÆøìŽ=ÆÍ7ßÌÜ–96{=Š<£?Ø$IcœÌ“ÁèܺÃ4 |‡ZEšîÝ»‡“§NÍ<óìs?ùŽoü†º¾±ñ÷?3üŽ÷¾WBj¾Œÿs<—a¯·õ·~çC¿¶Ôß4ò,á‡ø‡iµÚü÷ÿþ›¼îõ¯Åµ…‰véÒy>õ©¿$V]¿Òe¢KH’×ìÙs{öìåÒ¥K<÷Ü3t;3¼÷}ï£Ûípÿý÷Ñô±‡¥KK”UÅ–ù-øÊ§\V\®¡„> ÛæYXX ÛíÊ [×ÊT~Yà]Õ/¾x†²¨xõ-¯bii™G¿ð(ssslݶÀÖ­ <ûìsD㈛nº ]ºÝÛ¶nã‰'Ãu}æfçˆâ„0𙚞ƶl\ÇQñ‘Ò*ºŽe Õù³Ÿý,>þØgy8pP*È$#lµ û,/¯0G˜†ÁÂÂ6²,wY+¶œ¡$I¥  zÊ0-Š\B’$QÚëZÑ“%ÅÍ0u? ÀÓ±më)°€Rå–¤*¤PUãóV¾îId©«$7U%XµñhDYKû%¼Á qýÔ•T1UUa»ÜP'£jš¡€­¶eK.sY©˜S«üކ#b5€o·ZëY éU)’Fš¦ä¥$ÛY†Q…­  WÊ‹Ïõ$„~jЧŸy†§Ÿ~?ôùÔgîbyi™éé)â(¢;5͉'¹úðaÎ]8Ï#<*Aé¦E^Öu]*Mœy*3;]AU5åšÄEÞä[×J˜]*¡ešÔ•TÙžïbÙ¦àü5™ùJ)˜ø*r@î-?¤êWKª®Ä·œ&òßSc÷P×t,5— °hV…H³U.ŠišùäsQ$ a`™I’`+U†mO2»#ªªfmuN§Í–ùy’$f8‘¦™(34£Ù!ÔUnX†‰e  =*C,Óä™gž}õÁý~×uÜ^–f"=ú{ºŒï}ÿ·cèú—uÍÎLsêØñ¯øðGÿä=iY2=5ÃÏþìÏðçþ—|á ñ¶·}• _]—£GŸãá/CIˆS43'ølË£HÍÅjøvºmÊ¢$ŠR4&‡EŽiIµdÎn™bÛG†i(Qº#!DºÈkµ†ëyX–´÷U]ãØéz®rlè´Z!Ý)Œ†cEJ ˆç¹ŒFcxð!^<ý"°Ùï¡&çĮa’g…lr󜲒–O+±ÄY–EVÈC|B§¶*ß´ õ@¹Y­i2÷SšÂ²”C]ÓE ÝnwDêU ñùŸúü½×åeÁµ×^Ãw}×øµÿö¸ŽËµ×^K’%hZͽ÷ÜÍÅÅKWäO(Ö—7Ç sàÀ~¦¦¦8vì(½Þoxý›xÍënçá‡f}}¸ta‘;vàØ6š.œÀV;d~~ž$Nè÷l¬o2éõ{¬®¬±±±Ép0$Žb¢XLJ$l…ô{=ªªÄõ\†Ã!Î_äŽ×ÜÎÊÊ—.^âàCìØ±‹ç/rìè1n»í¦§§¤ú Úa›û|]ƒ;wR%¦%€VÓT¬¸+œžçqìèQ~÷w?Äp8DSU²ÞäShìÞ½—º‹œëº¬®®0Ž[!{÷î!n-n±àµ(ÔRcÒªP×Ô*@É0 Ð…€]U%íN‹,Í/ÿ¹ª"ÏEVU¥j§]ªªhä9qœ¨CN˜‰n›$‘ 4âTÚ[S׈¢˜,—ÇB M6†iÒïõ¨UŠ`]Õ „Ö´L<ßÇе&Nf€&Žë±¹±!aIžKÉÖÛVËžF¬Ÿfñ9YD˜2>I“Tœ4“™œa¨oÅp0¤¬$û9ÏrŠ"kZ=aj*7Zí¶Ì¡UäfEvUíjÅ”UEU*zÔ‘B1&ÅebâØµ8B\§Y@øž+­¯¼Újã*ÒPU×R•U%£ádâ$Viw1çΛð òËŠ0AuMOO333Çúú:—.]`ËìV^ûº×°¶¶ÎúÚ:u]séÒ%vîÚ‰iZŠ s 3LMu9sæ,KËK¬¯¯cš[¶laª;%Oön— 0 ƒå¥eNž8űcÇql‡ëo¼ ‰£”0MîÃ?Ìm·ßÊ… øÌgî" ÞøÆ;‰£˜Ž£Ý逡sñÒ% ]ãº#WsòÔ ––™ž™fc}ƒógÏÒïõŽÆ$i¢Úˆ”n·Ë#|“gNcÚVcÏšT‡k"Dµ ?i/Çј^¯GUJe©!U]š¥T•©}OžøRñ ­X“ bª¢€  d<Š”ôÄ&‰ŠRRõRõsÚ¶£rzíÆ0‰¨ ÂË2ÙÜèažïS”%¾ç5ûV»M»Ý%MÆ£¡¤‘…í:M dÚŽã0;;CY 5]“JÔ2)•ºÈeþ™e9£Ñ?ðñ_‰ò5´ž)}šÖÉfº" [”EI0W‡eSÕ¥ð†’¬äh5h²èŠ"Ae™Šâ<'ÛŽ­¶»®ç†¶íG1®ë035M·Ó¥¬*%xŽp\[rj,K6ã†Nøèš.PU¹ “W¿“¦ƒm›ŒÇ1Ž+‹¸ÑhÔ,¤tC µeâû¡8‘Fc†ãš:´£hLžIõg¨ÈÜ~@’¦XŽTu®ë’gB¯ö<CiN]טJé¡é}J-]V’&Š•™áû‚î*«Š,• ~]WBÄ™|_ÃÀ4ÐP–5‹‹Kt»5êÐ$±òeYÖdZe!¯gQ’¥)I’G1Û¶mÃõ½îÙsg¿Ë±ò4§È‹¿—K7-›/çêt§Xߨ¼óÄ‹göx¶¥Ü|óÍœ={Žñh̶mÛHYÇŸ}ñ K«+—Áú/£XO¶©33Ó†ÁÙ³g¸ó oÀ|Ž=Šnè,-.²eËÂ0À0„ä<¿0O–d<ñÄSTuÅû9tè n›4K”Wؤ®E›UW:n‡N·ÍÚúO=ó4Ežsýu×177 ˆ¼fiq‰S§Nsëm·òܳÏqþÜ:ÀáÇyü±ÇÙÜØ`Ðë1q=—n‡Ãã=F’ÆìÛ¿8I8{î,kkëŒFc†Ã1eQqòÔiâqÕ*B{I2ÛÊò²ØÔÒ„4ÍØ¶meY1I2±Bå©hÓ‚VHœ$ÄI,•§ïQեЭ°M‹ª¬)Ê Ý4Ð /¯ªZnvÛ”_(r)´ãª(¨J©~4jZAˆiŠ\Ã4$³¥,K’D–;–iÒiuÐ “8Šˆ%%ÏrLÓ¦R± U)QuUãØ.Žo‹ °()ó\&²uM‘ £ÏЄ,퇡lTUµ<ŠÆ*YZMS Ó ¨j•¢“¥©ÌkM“ (‹‚ápèØŽ#@ ]‚©LÃÄóE\nXò èöµ. FÃ!êùÂæfOÑZòJ4]Ðùõd~—çÄiÎ8pl–eX†ŒFQLšÊ"¢R¶»4ÏÈs «¨I’Œ4Ë•H^‚òPJ·ÛQUS¸¤©´'r¯ ð›*Tª5ãx,¿«iªV8eumU*sUuU¦\UX/RšÉ†y}P¢wÔuÁ`DžI´‚§ä6“.r©:!ËÛ¶d(///qñâ%f¦§™›%B cŽ%-s]I®Ìä@,+atVU 5ìÝ»—sÎÿ«º®ffg§iµÂ¿—Kw=ÙþM×–ùyž|ꩯñÜYÐuÜÀçÀý<÷Ü èºxWUçº,/]T|¸Ë[d¡ÒÔŠ*[Ðjµ™šš¡ßï³¶¶ÂÎ{¹æš«Y]Ya<²´¸Œ¦ÌÍnè¤m053E¿7äô©3ìÝ»‡m[·Çò$©ª’N§ËÜì 3ÓÓÌ+$|ØòãP' ZtZ]Nœ8A’&\{Ý5¸ž ζ.ðÂóGq—……y>ýéÏPW5×^ gÏœã¹çŸ—,ß²`0Ðétغ°µõ5>õ©OÑï¸õ¶Ûèv§¸páËË«ôûC4Ýäé'Ÿâ‰ÇŸÀ¶Ô@ûŠÜØ =g82 ¬Ô÷=l[25Š"'Ž"åc1*5*#7wB+;”Ià{ø­f—².ñ<§‰(«Z±ó¤…“s¡g—Ù9Ë/Iša˜†Ô¥ Û*U LZí²”OÐê4xzôÈ2Éö¨¨H³X4«iŠã9LMOaè:ÃÁˆþ`ºhëVW×Hâ„™é)Ð$0ORFÃq3ËÊÕòAÓ¤2t¢ö¿È'pŒÓ01tˆ’ˆ$ޱ ù™J5K“„Í^¿9 Â°E–‰~5-2Õ‘ˆde’O⸮PÄ•¿;I’$CÚP-UD~”çe^PWÜnYy–‘çš.i‡EQ’Ä)¶i*wA”$TÔxž‹çŠb¢,jÒ,J‘®Ñê´Èó²ÝmSäUi–1 $¯D‚Ф"¬JÙpÛ¶%™/–­‚è/g’ûžØ\ËBXˆM8Y^àº.ƒáßó•šAHÙ¦©“¦‰¤4ª*¯¬*?ÀSÔ  éûâØ(r¦§»Êì.¿ÛT·Kc³ñ—ªb3Õ,(‰3‘!˜"¾-ËR=4"µe®q=CÏk+°m“(ª1MºK+XPkµÌì<]Íz“$Á´-Šª€l’8¨ÓjµU¨PÚ´áy^b˜²…Œ†=Ê¢ ;=ƒçºjž®)Q°l™+å¤)ò‚LÑm[n ×qI‹L!¤dÞ™åbÌÓœN»ºÖØ5]c0ÑiµA“yižeÄã «ÛÁul†ƒ¡ª†]â<¥.+²\º Ï´”«”í¼nÆ)~4³°BµÐºëPÕ%žåȶ¿ÎéNu)óBå<ƒ¡ ÅÇ0Ež“©¹aÁp4BSúÍÐ÷ÔÁRQ«0ŽcL'««Ll‰‹—ŽïûÔ:$qŠç8$YЦ‰ö±¬*¢$!³L¦§»ôûCÆ£×qd¼`jÊo/`ÑhŒ¡ DÂuÙ›=‹µµuÖÖÖ×m®aˆXݶe6&::ŽÒ–Ö•ÀžÇűff¦9sáÜ7¹æÈ¯Oˆò«««¼õ}oá[Þÿ¾¿=šP³tþº SÃñNŸ;{Ëæp ˆÀÛ·o+\Y2;;CQ”x®ÇÆÆ:_tÀ¾•ïyBß †lݶד9ñãǺ°EÖݺÁh4¢ª*:@®,J–m2ÙÜÜäðáƒ\ýµ˜j>•¥™’ÈFpÇÎèºÆsÏ=϶mÛ˜›UsŸ^ÀÆÆ&û÷ âüùó*ãµ&‰Úí®ç°´¸DšgŒ¢˜µµuÆãH}"¢hÜèÄ~ÿÃâèÑçŽ6xø PRS)ÒöÛßþµlllðßÿû_’²W–"w=—q3üÊÊ*q„ÈEæ©·Ñ#Kä³ùÝÅ>Ê ]×)òœ4“*ım4C§ª'¡R2”—öO$9¢7£8„i5àØT¥JpStß—yÑp8$/$ÁϲåçH󌼨(KAHùa@šˆ…,ð¥²1 ~÷{}Æ‘èù<×Å0¤“˜l#…g™â8B Ï ™AÚŠzdÙR鎇#|ßÇRAWaà+-fAY”Jå¹ÊÎ7 ¬+ºÝ6eYc‘yB=Js™Ãi𯿯¦ mjSV²íNâDÉZœ¦Çl¬o4ÔlqÕhÊâ^£0°,׊u]J•Úi· ÚJ-.™Á`€©TuÅÆÚº,”›Æu]4õ £HYØ$ÉMS~ãR%óåDãD $¿E7 ò¼hÜ$–m1Ô=&&ùºè«ºfs³G'º,áÒ$¥×ë¡© gG¹Áâ8&W’¥²*É‹´qÚø¾Ëx4b¨fïsss„a(@ÚR¹RåÀ¢nz…vµ(2Æã1{¯ºŠ$ËÞzúôéÛt Ο?ÇÞ½{xË[ÞUE2ŽˆGã/ûÒuÓâ¯»Ü `8;O?óÜÛtK 0==Mo³‡iJŠ—®iø¾×„6Mnò‰ùÚV/褭œ€!A¨¶y–aŽåH^°m* ¨Áêò*a2;7§2R$Žt}uk®¾š…­ ".EXm¶c7ñ‰íNëò­È9î»vïÂqmjÄG ínǵˆ gzfš4M8þ<Ûvlga~•µ†Ã½^Ÿ^¿/ÛK«½rf5ùÈðÛ¿óÛ¬÷6”Œ!g×®ÝBqþ_§ßï7¹Ç“ò¼ ‰å¦Ÿ$±Mô&¡LR¢ÕÍdS6AܦV‹Réé¤EiuÚ"œŽâ$¡Ès<×o]ŠÓÁ2i·;X¶M^æJRQ+y¼žUUÓïõÑÔØÄõt]ù…¦‹ÝL×5Õ‚ÅòÐ Bæç·bÛ6u]1Ž"ÅHLð}ŸV«%Än5‡ðOy£u¦§»8 0 «0®"/04×s¨©ÕM,Ú½8‰±-eô=¼U^ÝRâ2˲¤†ró¥B`¡¶Å²4 “¬ÈÐ 8MDÔlØhÊw\kò½ËB‚›’4£, tCÃ4¤}ŽÔˆb8RÕaèc™RÕ•Ì|ÛYj+]” Ö:IrÔq<ñhŒ®‰}®ªkÐDí¹¾~”2OS·p¨ÐhµBiWM!:E‰Pãà T$™1uUÓétDb¥(C†i0d6„M´®ˆËuâ$¡ªÇj¤aÎÀP3q­Öƒ–¼Ï…,`Ú6gΞcsc“n·C„aˆeYäyÚžÓ<%ŽEéÎ(a&ËRº6aòâ¹sï©t¬,¹íöÛBŸñH¼Ò›KG1ÝE©ñ⩯yöØÑù %îC7hµBú}a‹¹Ž«ôD:ëëÍ¡0yá&”–‰G¹Ýn+£÷˜v«çy¬­­³¼¼Dœ¤ìܹË´q1ŠÇIÌüÂÑÒé:­°ÍÙ3giµ[ÌÌÌG‰ª"LÂ0dfz×q±l[9 ¤‚˜Û2Ëfo“¥Åæ1tƒ©©.+«+\ºp‘VØÆs=’$¥ßë%ëšãºÄqÌÒÒ² ð½f`?ym]S0 Áù'‰Ìý&?kr#äyž©E€@LK–Ôƒá°ÁY¶nZ$i,•eQQU"õ°-‹$ŠÉóR  –mÙb ,*Å}´dskU[ißó0PEJ¿·ÙüÎiI+ ɳLà¶®àÎ õ:ŽG‘Ài]õõÍÆ—; °‡v§-7cœ4q§¶g+ž¥_eyÖ¼Ç%›Ê‡m«MjYdyÊ̬BLÅ©²ïÉb(SÉq®‚T…¼£8Æ4M¢±ކ%m-ha SœRÕUãÞ*JÙŠÖe…¦ «u-ÀÓ4ˆÙ°aˆãzd™ Î¤Qá:.-e!ô›QÈh4¦Ýé´Û½5Ž"6û}\×£ÝjaèuYIPUʱ*EQT*Ô^T‰Ê>Ö ƒ,Ë鬯¯3Õ騖¾¦Ý–{$M9X]‡V«-3é$QÂkqÆdY.À[Ë" Z¸®Cúèšš©f)Ë++ضÃÌÌ A*®£Tiš’æ9yY(aº° ¢D Ý«ŠÝ»v³ººö}çÏßyýõ7päê#¬¯o`û)ÝóþªËõl¶ÌN³´´tûù¥E±$iò†{®O¿?h2L²<%Ï2¢Ie¨½t^(¥·ƒãúbER¹¿Ý©.v‡ª¬X[Û@×ufg§/§¨¡Ñê°0¿UžYÎúÚ:a²cçN ]ÇóÄùÑnµ1uI"ët¦plGÜ º&sUµEŽeÉn™UYaZ¶ªÈ4Mcuu•¢(ˆ£„(ŠhµZTUÅ`Ð'Žcâ8b8(UÿåùI¥gY–8ŠÇqXXXàôéÓôûý&ªô2?ˆ†b#^M¢¬°L«3ÔjN&øøŒÑhD'ŒGc5ó‹ÑtCZé¢Ä¶ûT—%£ÑÏõiµ[”uE‘‰¹¿ÝŠñÄ©# y꺮RÕjåH‚º¦±ÔG‘H7 ^Óu’8Æ6ͦÒk·Û²”¨¤mÅÁ¡éšâåÉv9Ͳ'VVr“V¥Ì„5t…ó²0mK6°eE ¹D×t [wlZ­6ižázžÈv†Cfgg [mªº¤Õ [Qˆu4’Š­¬+,Û¦ÓéJfržcêz"¯©÷9Tâé¢(d>¦iTµ¨ª\¾gQ–Íl ¥‰Ôë¯*+…½—ù­íH@¼¼_Qã¹.ÝnÓ2ð<©’“$U¹>±ül–Øþ6Ö7i œ4y‘3 ©Õ½'r¢„Áp„®›xžG·ÛmGÛ±ðÃ@¢t Ë4p—©)‹lözdi*³NUí™´Øz¦„!º®‘DòÖ ]e˜K%W”²Ü [Aã_ÞØØ ßïã"¿i·Z ©,óFØ]V%e-¬ºmÌÔô–e‹+ËßqõÕG„²®œX5ü­.3Ó¿VRcžxâ©oEs*UW$Œáp$XŸ,òlƱpÅ_‚#äyA·;ÅÜÜ,¶*Ó˪‚²Â±d»Å‘ªZ‚<*Kya“Ó°ñ<¿YXœ~ñ4333\µG–)¾Œ~ ›Ä²DÓttšIK)oÌÍÏQæ‹—.IÞl-LÏs¨¤i‰î®¶dþX¸®eYÍüb’î7 ½¿&ÏQ¤4ÛóµµUISUòå׸nœ(Y.Á<¥JHs•8MSuhÔjp/óÙR%óiÔâgÍrÆ#Ñ$fyªn@? ¤µ”±’¦2G³,ƒ².åƒl˜ª=—Š6%ÍÔ|IÓ„€] ”³( Ò,Ás<|ß#Šc67写™x­®³9ì“§)u®ïÒõ»”yEš¥âˆ°aíy!d¡ äwHSËÆ²¥Uum—4ƒá`HàËTê%EZ¨ øÇqpl¡F•Ä^ŽFclË¦Õ Éò”ÞFO!µj…G+ÉŠ‰¸Û‘À-S#‹3Ð4jUÍÖuÅìÌŒd±¤¥0 • Q×@7uªZ|Ä”¤yPy®Ü;y*~a]L“ÍÍ>a+ Ë ÛÂTŸ³$Iñ<V»®Ã ? Ï |ßÁPúÊ8™¥‚Ü;ŽTTØ®K‹ÕµÑ{.yžÇ•,QÂßV‹Ë2qWîO5vGho ÿбmʺb¤¾÷õ5±êº†eÛj†Ÿ‘ejëoÙ*DJXY&ïy¯×“ÊÐ÷é[VÓ*‹í/E×5ÒÔhà“{ Ï Zm“½W]Å?ñc/œÍ}û櫪ª-]ÿÊZÓ–®¼¿Œ ÿ¯ü—¶m“W¥{ìÔÉk4CofXºaJ ‘O×Á±mIRS%íÄe1™NOMcë&V‹$ÏÉ !3Û¶-FEÝh!‰úÀô{}i-SüÄ®C†AKª25/›T¢²þÏ•pSf.‚žæENU—eÎh<Ý“c«ªh¤.¡*[–´âêkSQ””å¨1UúžD”N¬uõK6“Ðz Vª®Ð^9OÕš?[•• Æ=‡šŠ<Ë›ÃÐ4mÊrÔø}'Þ\ÝÐé ÆM’ü¼uã=µ,‹ª®‰!Ÿõd+®l8M¥ýs0L“ÍÍMX×—º®£›e®T¥ ÙýP’ꊢ¢.ËÆÑ`;Žú¼h`ÊŒ©Ûx-JÖD‰È8 Û´H DZ-ΪùmÛôú}匊Û6 ¨¥êe–VæQÉÆº.é¶;M·l)…ÚS5i”Ð lË–Ñ…rDQ,Iƒ¶äŸž' ¥²$/ 2å–q\—$IpA…¼Ež« I¬P¶3y ”¹€k ÃbœÄ¾K•‹(Ýq Í ¨ä¡X×PV%£Q„纄-“8ŠÉã4±æÙj1Éq.³ƒ±úL{¾„7M²R M£T ƒ¢”ažçh5T¥j¦º—ûý¾díä"þ·,‹Zù»ei¦‰·=S¬IåùÇ‘ ÚÔ–×u-ŠLDù¦)ªÛvGC666صk7SÓSŒÆ#¢hÌÚZJ’Hž‘mYTUMYˆ *¯i¬‡ÃÁ€ù-[Ð ½õùÏßÿæW¾âŒ†CÒ4ùÏó³*¿üX=bþª‹ªâÒù ¯=sþüÇ“uö©T9E‘+PgÙ’ UÚ6ÑÅêƒ? ÈR© ÑtthnÙ6†n6Ø q®ÁuEÔÙn· |iÿÄB$o®nˆˆÙ0¤¤¯ß Õ>Ù¶¥B¾M4Às},K懆n( ‰‡eËM¨©ö>W’ž,“!±aêMe5™éJúñ×=x&ÝÂùÒíúKÛj®\;Õ¥¹:DåD“HFò" f-Ix†)âÜáp$âb]º¦7¿Kø˜†§'ÜÃÉÓ:Ër¢HH"ežãžl`ËJ¬J¡õ{}ÒX°ÿŽe ¬,±-›$×K¨PK£¡B4­VH†¬­® ÎJ70D¢¨ ªªh~W±€ Ä6KSŠR¬iUU+R³ëyânÒÀ4tlS4pš!PÐ m9p匢XUñ¢« ‚éé)¶ÎÇ «kë #0 Z¯Jê²T³@Øìm2Ž"ÊR¶¼º)^iMbeÉslÁÛ &¿]Ãü†¤4 ÉË¢‘€…jûíyív›$ŽÙÜÜTZSéx4Mœ4O²‘çyjQ&v΢,š¥ÌËê&Ó{B²FŠ$S)€‡ úÓ$¥* 4…=CH2¶)Qêws]˲ÇTUI˜¦¤-ê†Î´:4'ÝQ¥?“qTQ˜–®(ç¥R(Ô¬¯¯³´´Øì&Ô'i•ð›g™¸QŠB€‚µÜ‹¥Š¯Ø»w/'NžäìÙsôFCz›½(«JbH¿ÌKO3ñq~©Ë4 ž}湯8wéŽí49ÁeUª¯T;Žc“Ä)Ãá˜/¥qÔ {Z^^lfEšò0Jx¹<Ñj<×'ðCt]—ù’šÕ˜¦,Gꪖ'+2+ô= 0 |ßõp×q”¿²n¤ “0lÏq0 ‹q“¥‰BÖ‹?u²É“8GM- L4 Æ£!u]4÷‰)ýJàÂ$èJ.^Ò*_™¹¬˜Íÿå"E7Dø;YM¤G2×QY¸y©6z²Åt-«!'çyI¿×M£UÑJEÇÂØË3â$§FóÚôû}²¢„ Ë¢øbDÓ LËld/®ãÅ£F¶S”•´ù†‰i ‚h’Œ—¤±ˆ>ÕF‰’±ø/¸,ÅMD§®i$ãCá¯êª¢(åa433†F¢ªñ0 H³C÷Ð ^¿O„Ô5Dq,ª ×¥,ª†éX×5e%šá¢ÈÉõŒáhL h6I*ñYša¨ÅS0Àq„üže‰@] qIUe¥^§¶tJ^–ú(ŠŠ4—ƒ×Ðt2Ó@72Ì,¦, :ݦcñ…Çãoz#®ã’eÙû»íÖç­/3Twl‡/u~@U”Þ¹óçï(ê Ï÷Åø_Ó¤—éº&ÙÊ:4ù^ÞøéšFGr8© ÉvðˆÆ‘PPŠœ²®”fl,:°V‹ùùyªªbccCþLU2·e 33ÓŒ†¢-+òRØma Ú`ù{®ƒ©´b¨Š-Ír5hÒd®·ººBG8¶£þ¼<õZ­ÖKôo“þruwå¬ðÊCïÊvX»¢ ü«ÿñ=×ñÔ¢ÃhÂíuD†¦²EÎs1ƾ´ÎƒÑ¨¡DUÑl9e¶)³¯8N0t¡¼Ô@’¤ ‰•Ì0ôfÈ_äy#ž¤à†TÖI𑯉òþÚ¸ŽDF†­€²–÷`fvÛ±ÔBe˲i…-ZíÝ©ŽitÇLÔ¤Z˜{%‰lî'9 ¦²þÕU…¦ÓTï†bAÚ¶%UI­a¹.Aè³®È&ŽJ=äò<#ŠcúÉûð=_ˆH¦`µ@ñ'}Ï÷i‡!A0HâHtžeE§Ýk–„­0 c9ÙtЦfòóùMЕF-ì¿F]“çUu¨*!9GC…|—Y“¡ë*%Íl$FãшYÞØ¶­Dƃþ€L ÖK5«FGrf¢1º®ƒÁÑ(‚Z(?†iÇ)¶m«ô8ùZÐ 1,“ ”Îgs³'Ç&Jb’Dt€3³³²Œ¢VÙÇ.A ‡`œ&Ø–ÃÌô4Ež£kíVj©‚å=•ƒ±Ö Žã~ Ñ(’ƒR½ï–ibZ–$ÒÙ|5Ù27A`…&Iš6Õše™¹h§¦:M–¶ØõFe²%×5éÌD?*$)(,5ÖU£&[]C# [j#¢ý$‰Õ6]9©ò\fJ51è©{eÎ(‹ jMÙ6ù?awÃÆ«ïû>ív§1hZMUE&ASY¦„Ø‚€ËóœÇ1<üY^°¶¶Ñln¾žZ¬˜Ó¥;žÇ—ºfgçX\]=x~ñ’ÊsØvll×f}}Mh²52Ä­ËÒš+ZHÃýùsgÉ3 šñ\‡(±¸¸H+l±sÇêZœÓÓÓaH4–Iåé86y&vœ²(ÈÓÝÔŽF¸SWdY!ÄhÇfÅJ7¥‘f‰X§L ¹–„®Œ"¯˜™ž¥Ýn7‘‰išÑ [ÌÍÍÐôè5$ž—Wxu3ÿÓ´¿ºMþ«ªBý m¨çzjÁ ³Ôáp ôdÏ•³aÊ' °,1ÿºAèËh"KsFÃדªD*ßsÙºmAâA77Ñ5Mf®®G«Åc†ã!AÊœ¯¬•}-¤R˜øáh¬2]„>b»6ãX˜ŠòAÍД:Ʋ-:6q<¦Tö®þ`U%»0 l‡Êk¨Ø‚TAD¥M¢ª †$q,c4ÇkŽ+êI$ŒmYJÒƒ³MÛ6Ñ õõæ@ò\·É‰ÆUQJo/s‚‹* IÜk·%z@FRU ² åâ  nMKW+ •eÙ{¢Ó’¹«Àl ü@BÚ[-jôût‰wT4—ŒªªÑi}‚Ù¯êŠ ðÑtµµu!å%ãdLQ–Ä‘,ƒ&[y)\bÊ¢`0(ËdÙhI¥ ÇÇf¿ßàæŠ\4޶í&±|_Ó¤Ûí*H­8ò"Ø.5u)²¦04ÕZWõ¡›eŠ ’R xU)ѳ¥ûês]U¥ì ªš4+šÔļPTòZZp˲غ}+G_8ÆòÒ%ú½M¢ñøNCòÿ¦KW ¸/ºtËàôÙ³¯Ž#LÝTCk ¯ŠŠ^¯ëÚ*ò±gBQ5Ü—TB‚bƱ×.]´,¶l™'¯2Î;×°ÏÐ4:Ý–e1G„¡°áWœYžqðÐA¨5.^ºÄ±ãÇ)ËB(ºü|“nÇŽã{$IL¯×ãü…óØŽÅÖm[1t™nll0 źT †hc}˲™ššfmmÑhø²YŸvEE¨ýÞå¯)’lÝhªçv»-Ud]cYqÓév˜™™ÂQ;Q$fÛ´$[YdN‘lÓ3™±Ø–©>Xú½¶ºFYWª‚°p<ÁËÇi†úü6Uz­Põ™Z*É/¦»]ÊJÍ Mƒñ8RZÒ’h,Y+–e‘&™X P5Žå6ä“FE‰h;‰\Ï¥Ýn£ëG9Ž1 K|Õ†ÜpU)7g§ÛŸj•tÝ”ìCǶŠR:‹ ð”þP¼·Aà§²¼ÑoQŒG#ú½†aâz’5mÝnÏ HÔfØV"xGYKGã‘,]W€³†Þ ñ=ÏÃQ„ï0ä¤r¡Ñ@ tZê„ä^*ÛY¥*× )Þ÷<ÐÎ0ÉÌIc‘õL:3Ã0±l“8Jp™‡#4]YJY´E1èÄT·K‰ ffv†ª”m­lp&l˜:¾'ê²*±S™"™Ã2IôÐj”õQÉrº]‚0d<ްL[uƒ}yüǵ¥‚¥&Í2V–—¥r§V¶]G©â†b.À—ò²„Nw’xÌî=»Y\^âä‰S¤iÊ⥥o)‹<Ð ]š„¿æÒ-%r¼òò}Ÿ$Š;G}³¦‹ãÄVòªàÂ… e‰iŒ£ˆTÁ*g®à#NZIJ¨hw»x0ãÊBðJ3³3†ÅÒÒ"««k„aÈÖ­ èºÎúúUUrèðAÂ0deyY¬=†Èd®½ö’$e8²¸¸ÈÚêŠT¿,¥r°-‹‹—.±¶¾ÁÅ‹—ý‡!ŽbÒ,%Ž"ǦÓî(¡¸èý\Ïc÷Þ]TuÅùóç}啈´I[óW!Ó&‡åWŒõË—º à©+špœ(ŠˆF1ÓÓ3Š,óTSjmll6Ìq=Ù4Z¶‰i ¬;Ùì:*Ý®FW>çª*ÉË‚ÍÞ&Z­aºÚ¢›¦E!N”}s‚ФYN­ª÷4:«pdYžËkWÊ8#SËÇq0M‹~o ž`M§×“Yžï{²-N$lËq,SŒq" TO¡ÐÆ£†®aÛ&3³3´Û-º ®ç9ƒÁ€^¯§Ì SÝnƒÇ“Y^E4Žèt»Ú£*KÂPì·†ê:®’(YÊ $àŽhËV[‰Á=×o2P$!Ïc<Š”ÙAoæòºa¨®ÓA×ä½1ôËlÍv»Møb(+Õ HLpQMëŸf9Iš‰‹Fר¾}ËKË\ºp‰JÓX[Y½% %–VéF¿Ôe>¥‚Š®lí¶.ÌóØ£_xÅ(‰0•€Y× Š¼P _/1™™ž–HϺ&/ æv±k×.NŸ9ó’C"V…#G®æô™YYYáàÁƒlݶ@à…<þØãÜpãMÜxÓõ¬­­ræÌY4McÇŽô6{ì;°Ÿ8Y^Z!Ùð [¶Ìb™&oxã8}úElG4‡ãñ˜µ&MOɨʺ$ ä`cuƒÚ„­A€È6]Ót4m 4¡i¬­­‘e9­V ]×D8¬°ãq¤EUS©Pø¢(¥ÃÙ¶nW– ã×Σ¡ë$©,<\[Ði­°…†ÆêÚ:ç1G"jV¯¯íÚÍf·×ë¡£I$n(·Æ2Í™Þ2#‚øÁÑzVuoÛP ü!Šcµ$²”¸¹Â±¤%v\GÜ/Y†ã:æU׌ÇPÄú%®eQ*²¹çú 2(_&¤œTpw–j¡³<£Ýi3Ä1$iJ” %êT—‡¨®fÇŽíˆÞ4”ÊÀÒ,4CS.!Ë4Y\^T”rAz%±¤ó9Ž ¤jSo*@Šƒ‘$ŒÇ#•=ViÏ èõúͬp²;°,ð›ŽEU£ì²••<ûÜs\wÃõôƒ«ÖVW·jº±Xþ5ö<ÝÐ ®¼,â.j^8~âú$Ë”\S¼³Š,Í…LSÃòò 333 Š<­lF[æ¶\Ñ2^>06Ö×h·ÛìÛ³sçγ¸¸ÄÜÜGŽaõxðÁû ‡\}ÍŠ¢`yy…4K™šêb×^{-žç2ŽÆœ9}Š¥K‹aÀ ß£Ónqäê#\}õ5\sÍÕÜxÓ ÜtóMì?°³\UJ…¸„Š\ h®ç4®.M×E·ª(×Â0Å‘Zº„IJmYˆ5¶&Í3¢HüÜ™®‰gcs],µ~€ïTµÖe-šà²µ®ëM–·ç¹´[-™ù§yÃpG˜¡Lböü™T홪©.;²lË"lµ8vâ$ë««DÑØ,Êêíæß°U6~é¿ü;vïn®ƒ×\Mwjêšßúßý©ÓçÏÓnw„ŠQk Y+‘|È·nÛÊòÒ ûöíÃ÷ED 5Ÿ¿ïó ±eòD)ÊšmÛ¶²sçNÖ×6¨Ê’;·c;/\ä¹ ´:ÓÜyçkÙÜÜäÌ‹g¨ëšùù-d™TÛ·meccƒ ç/’e™Dĉ´G®ly¢Å‰Ð‹‡ƒ!ƒÑ€õµUN;AMÍÕ×\M¨²Bƒ>iš1 9pp?×\s çΜáû?/É|¦©0÷ZÓO¿/n¿œí%‡fØjsãõ7ÊV¬”ÁÊÊ*‡Äó}Ò8Qí¡‰nJxxU_Þ k*ˆGœ:››J~c‹)¿ªq\›4‘øL”×Õ2M|_™RT[` “{ǽQ?ÛÓÓÓ$iÊh8¶ÍffÛ£qDšf yÜ󽦽1 S|×yªì[…ïVD㘪–·Ö ,D\›¤ UQR׎máúž:`e3mX¦n(ÉIŽ¡ë²ðS …¢(UržŽEaZTêw»\ŠiHzœÌ5•¾.ϱMÁ†Eq"ZU«¤9‡ZQ|Æã1¦!Z:I¼Tšïû”eÉp4B×t\×Sq­~4‹ Ý ‡Øæ{OQËbF×é Hܪn˜ ÕJKtk«Ôr$•™³¦I®ÌD™g™Â×™ÍCWY&mþ$j`‚ϳT¦r–J¡T+íñ8'Žå«í¸ÄBäy‰c»ŒÇ#Úí6Y–K¦IU¾d†,X3²§m›M!&ìÓH¨š®‚­Ì¦ZŒƒ&`ÇåÉúÚì§ÝnÑnµ³……ùhˆðûK]ÆÞÿ~Åç‹„Ïñç¾û÷ÿà#_5Î<×ÃÐ^*8WUE¿ßcÛöí,//†-fçf‰¢˜…­ <þØ£ ‡CÕîhÊѰ0¿ÀÌÔ´@óÏuh…-@çì…3¤QÊλ¸þúk8wö‹‹‹$iÊUWíe<c˜&»vî¤,KFã1.^Ä´,Ö76ÈSá)¦iF–älln²´´L’Fœ>}Š~¿ÏÜ–y>‚eš Ã&`{iq‰-[æ¹ý¶Ûˆ¢ˆ{ï½›•Õ4åѼ, ŸÌ«+æ…ÚK°g_ …ö×Í·mÛÎŽí»ÄX¯ili÷îÝ‚ÔÏr•Q!Ž‘L¹Dª²bªÓV`šc©ViOµ¤"M$OXSÛÈIûlêºø³ó žÞUÎi—,Ûi ¡Žçá¨M¨VKŽrš¦Œ†CÂVH^ȇÜó¥•tmG‘2ª²;WYá9®êúäÆGLš¤”EÖ̃êZ–.®c©9S&PË”•¢Å¦!8±$F7L u(:®+¶Æº"NþK]«Å….„î¢lÀŽã\fæYšá8®lzŽ.M„Á麶š÷‰üh2Ë•eÒq!ÈbÛ›¸JWp\†¦333-"ìºn€ V{*ïS’f*ÎTSóGCèÈûBáx.q’(˜0,…ã(ѳ®'KNË¥Èx4F«E"dÙ¥úYûj™ˆêt ]t»‚Ñ“Ž¥Ö쀆l÷$ ZĉHá4]FYž‹>°|Ôª¸ x8aèò}«²š{7ùç2£¼ŒŠR•è( KÇ¡‰h_îUõÙÒ Îž;ËìÌ4»wí&l…{vîÞýºiF†µ¿ü2~øè%¥âôÌ4Ÿ¿ûžïüÈÇþä•–+Ö<Ïóöd‹*¤šœMöìÞ­f]){öìe4±}ÛVNŸ>ÉÙsçšÃpBƒv—¹¹9i«+ÉÈð|îÔƒÞÓgO')GaëÖ.]Zds}ƒA¿Ï–-sÍ ìêkŽàº2€N’„3gα¾¶NšçMBßúÆ‹‹‹hšÎÌô ;wîâСC²¾FTH¦Ãâ¥%fgæ¸å¶[0 ûî½—ç^x¶™åɇ´n¨ÓÑïå ñ¥R›/µ`¹Roxù,”?{Ýu×ãùâ?6MÙïØ±]rJÒL¹6ÆøžG–åÒ²š&Žë¥²=œÜôUU r©(Ä®¾,T '1¦iáz² žPã$Å4t `r…>°-KÀeM ^-S ©V¤EŠ£˜h¡ë:ívKˆ3º,„ê²’ÖL¯eI;iš–¢<'—“漢8ŠÅ?oè⼨„OE žc‹Ø¿,q=[…Ôk ×Ï´%u®R,š†Z8jNfǘ†€ÐÀ¶4]"-ëJX•uUc:£ñ¸hø¾c3•]Ë0 ¢8Á4%¾±®¬7/¨©°l“$´}YUhšÄ8N,`i–A%7W’¦Í¡lš†Ê¶°›ÁhÔ0.˲DÓ¥U™dYd©øËºnòX²,SÕJQ€*\×St™UiºÆ8м@(¡Dˆš¦AYTØ®#-ŠÏg[&q«=[a¸â8"/*•Å"ö3]Óp\ÉA±ÕŒTÓ&D9˜×"M%p^ï%%sEGA]¶³¶ã |B1«ºF×$Ã;R¾ì°õÿ1ö§±ž¦éyv=Ë»ÿ—³VÚ»»–Þ·áôŒ8IkD™”DÉJ ›¥°9†í ±Œ|K¾$ù`(@€|Œ"Ž-‰– ‘%‘œá 5œÑÌô2ÝÕUÝ]]{õ¿¾û³äÃ}¿Ï9ER‰ÐYÓ]Uçüß÷yîåº~×8ÃøÓQDóPïG{—¥?*En"çQ±ç:I( ¹i‰4D0 =ç-Ök"F£àÂJªì¤Vìq§çáððˆm™cÊGNÓ£kH§8øÞ×ë5¶67áqt|Ìy?ö$„@ßuȲœ—­kâ„¿çh_À®ërÍÑT OÝ5<æñ4³¥Í0°X,I Ò4 v%8.Ñ’$Uš<^ÈxÛ.„ÄþÁöööpñÂEŒG£­½Ë—þÎ02ú•áŸýÚ7pøôŸ>Ãâè>ûü«ÿí÷ÏþæÁü “_hˆ*¹EiÒÜõ†ZÒë7®ÃX‹õ²Ä­[7QU®½p 'LJ¸s÷nàò >þ7¸zå 2‚MûÔ‚Ž'cŒÇ<|ð·?ý‹Ùo½õÞ}çmt‰Á½/îc:ÝÀ{_{{çÏ…yÑ~„Ï>ÿœÍÞÀÓ'OñôéS´m‡ù|†õºÄº\áλ¸óéÌf'¸qã¾ño`ïâîyÿìŸ}ïðSf»M0N ”dï°GI¤/y3îÃìt˜þqËm_=Ãbž³›o84¯^}¯Üz]O7ý Ýâlœu0–ßAdÚbJ!8®2AÕÖLµ¦–B"Ž45û>[®0iɉ I XçCà¹÷€Š4Loéû’ S<ƒJ×e ïEõö]ÃÆyï4£Þ)s¤GS×ð|y&™mÎzM2Ž)é;Ú4|FÊ)q–ØŒÆP.0å`Ð 4Ž"TUƒ¼ ¡t]×X¯+ŠoHbdip˜:ùŸéÐŽã˜8•üsð„éò¦#«œÒ}]¯¨åO’ŒSõèvà¹ÈÜ÷šd zöAO&#z~8ÓD ‰uIº[8-5¼s°Ö£tèp;ÖÏeYFW‘Z)¨ˆ¬™Z©°èR’#JÇr½F¤ÉÞ×pÀUS×4¿”T!ZOÚ=ç<-X²]Óa±\ ÏNõ£Zk¬W+HMúªªà½ãœ– £Ñ³ùIÈ_ö\å)%Â(ÂÑrÄØ`à°Ö ©;.°¨Z[­–§3DOq§QD‹žªZsŸägH2”؈’qsqD™ÔGGG¨ëo¾ù&âHãüî¹kýi]V0]÷Ü?êoý‡ÿÒúèçÿÞ?ø'ÿä«*Ò(ŠJEBA0¿ÏZ’q(M!ÕÖ9œáÂ… èºãÑ›[hš/¾ø">ÿì.žíïŸY¤ÐÍÞ¶._¹nk-ªº†JHL&Slïl£ªk|ñÅ]üô§ïc½ZáÅ_Â;ï¼¢È1™ŒQª²Â矎/¾¼Ãƒ}Á»wðäÉ#!1žL1‘$iÈpðÎnŒÁ|>·–9C;á‹,‹ ’$áÈQjÓŒéHð{æëÂÞeìí]›aiôu­‰-'mìæ‹9’$B'˜/hi$øÏ’Œ“RZ#Ò ñ(FÝ6ð°pH9_¦ªJDqŒ(IàÐÙ*JQV Ûü; ""• àäè›S³%=lRÂz­­­psCHlooÒÆµ·w=Ew À[ c·–6—UÝЋÂáXiFžTòõ’Ö/Š#t}¾3ˆ”Fš‰&V Óé'ó%VëQ¤IûŘLǨÊÅh%‰bžæ)ú†.€(б±¹r]†¥Yž¢­4mçÃ!–ïHšáááé£1¿»®Ãz]âüùs˜Íf8î<²,Ãl6Ã;#»wn‡ê0¸7àQ­K\¸°‡4MÙ}!!¼ç6ŒàEZiL77°½³ )?z„OnßÆÇ·?Á§·?Åþ³}%qîܶ77±¹µ‰íìí£ê¤*1;9ÆO~ò3üáþßýîwqçîm”e…ét#à†ƒoGIvŽÃCB€;óïÄáðËó£Ñy~JüÚi¢S÷ÏU…ï¾û G4@7¥àXÄŽÓÉÆ4ˆ•!€ºlÅš«ŠJéà·ÎpÜê²z¿ëz´}OZ:&ÍD‘Q”]Gmº–ÔVBàtnÕ$iÄÕ…^EX®Ö\aÄ´8Ñ´Ð,ƒ9‡xyOv¥4꺆é âH£·†¢x«¸.׌k#ôUË.Š,ËyÝC‚¾Oýæìˆ(ò i‘å9UkÆ… ¿’mG?ÿ®i¨šÔ mCõ(¢¶.I)g»nÀ ŒÇ#)a *úž(é*Òˆt gmt÷ül î‰$1™L ¤Àd2¶áÉt$jt±‘ÈM†áÌ"ä ‘m†Ê¢ÇOhD‘B$á×A¼w(F#wîvvˆN-”D×wX.—89™áððÇÇÇ!…-ŠcìîìâܹóVM!åÍñC¬A&°\.$)¶wv‘¦²,Ãx<Áx<¡˜F­Ïlí™’[„ðpk{Ìç³  óžæ2ßúå_EG¨ªšEä´µ‹”&«W¤!%m{¾YÓ$%ÖaxÀA.@À úl‰B’$ÐQ ÛÌû° 'd»‡à--¹E¨=íûÇÇ§ÙÆ“ ²P5 µo£ñ%ÛÓ$¡[Ÿ÷”ÒL.II<ˤÃð;cË”±Ö9š¹ 2÷·mƒ<+âÙ‘MŽÈE”?Å:`Á»¦Åd2BœPÈSS5&' /7ŠbLÃvE–­a¨¤D×tP ÞºÎ@*ZÓŒ0Ãrµdù ù~Gcæîµ-²$e¤T”#:iIAU†÷ôÜÅqÄ@ZnÄ ms‡ã! >Š"V*PWR7 ¤ Ïz¹.!@#ÉÔ°þPJÅyÉäH L¦SD±Û`ç-VkJ®ì] •“‰½é‘dYˆô$¹SBÒœžÒÿÈ6G¨ýÑdÄ|Hrì 3K!EÀãùŠ¢ùbÛv¼<#ÝkÅБF×·¼¨ÔÁæÛ´Mhe‡ïsÐ U…<óãHˆ¹«‚ÕÔ1%÷úõ—0±³½ótsgû;Öñ¼™ÿÑZR9ž'?züËÇ''ˆÙÈ '& ¬3è;ËÚÂBPXK×´SJ,+°^.PWkôÁ÷ðüåßú-!Q®+üµÿá_ÃøC*Ï¥:³Œèñþû?Ãd<Á¹]Ãx H‰ñdL®†Šä irFë($(¥)666y6GÛÚa;Ä©bò;~qÍih;†ÞËåGGtnncssë¹HÐa8<—O]%ò9P¹«<ÏQ]XÀ[o¾ÍÍMÌNNèÁ®[Jë“MG‹”ÞPˬ¤„c_7é=R¥PYšGcŒGSÖlIÛB2½ÂÃXôÆÐË>çä·åüèÕz‚¥,äv‘Z"M%QV5YÐ2Š)õ²,AÛö° ?C ÉàW„÷=Ñf†hN!(ªî:´ ¥¢Ñ8!Æ|±DňrM¿—zëÐòD±O¹ë{˜¦…ézdI@Bz’İßu¹¬1Ê ”Um{Y†ª¡K§ëé²Íòãé˜ƒÉ ë©Ê‘JÁ;ŠöŒØ_›¤ vw·I(®pPÕ4o£˜ôsü LFš¶§X†¦Gi*ŒŠÓV«%žk–%e0K!¸Ú"C¬#ŠOuà9Âû gªYRDÕ4É©ÖëÆXìîîpè¼A×Y”ëšðW>‚ ZI,—s|ñøê Ï;Ó8F'ØÝÝÅdlÙÓçQä!ÿëåšì$ŠMÑÔtÉi­Q×-"Î\î{r·ô¦ƒ±=!­ø n×”ï¢bùbNeIø¯(¦øÓ÷ðyétBFÓÐr)IprBÒŠaµqÁ a¿ „€‚†ù!í»¾c—‰&»œ$»±!-¤¤—<ÖÚ®CVФ­*äYcz<=ØGš$ˆÓËÅi–ñ¦»&9TBžÿ¾í0føj×¶ˆ’˜ƒ(¤k2 *iÓÝ´ê¦Æd<&ùKYÂU=æó%&ã „Vó?~„§Ož`µZbµZÁù?e¥ÑÆwwwqþÂE\ºp >R脇РÎô€±´KàYk×[r°A†Ú¦A¬Ô†AƒNÚ€ÇqŒ4Ni¬HЯ"’~TýRwckk+00‡Å¸XñÖBzOí²¢‚(‰cD:‚Sttxˆ(‰±X­ÍtÝžòÙP¤€úÿãÿ)mP­þÉ?ý§ÿÅû?ÿx”äâ(F–眡ÃK/Y˜LoIÏäQ1‚5=‹W+)QŒFx¶¿›×o Ô/|õðøá<|ôèŒÏB¡¬K,æs\¾|%¤| ÎØ"!!L×£iÚ°ž—lô>Ë$f™çêð0;l£–q6›aÿV+JÞ»páöö.†rüàŸ%X•àY¢õ 7$í“Âb1çÌ”Á“ìÇ ~ýÛ¿Á€ÏÛúHbÑw”±,%IjuSJ«šš–Iš*­è‚Ñ­µ”ƒ[×ÐqL¹ÏMHSìi’$xhI€íý moxkd mëyE:´A"AÐÉ"v²#FQ¶¢RÕ$Ë2BÝK áEðJÓÚ÷,åY(úvL¡Ÿišð|V“/IX¢Ñõûª#¤1ÙØ”’ÈGê¦pqD­{S7èÚ=Kƒò¬8µ òa­½hi’PKX19ŽcjuYæ°¤ðÞB I²"!у4ËÐ65/ÉH_YUb­à=áýËzÖ.¶]âC×k‚ yÙ(…·Ë½1èŒ žc¯L(úû]ºx :¢h€(ÒA׫”Ľû_âý÷†Ï?¿‹År޶møBüã*ˆ˜Dìx||Œ¾Äb±D‘g˜NÆ$àoD\¬×%”T\åö  (’€«6Ê‹I8Šƒ¶ÔD®&r¾äaºªàS59•¸ebG†ÅÚÕCD˜„ׂ§”R”*ÈÎ4MñìÙê¦Å{ïý´Tzww÷7ww>‚pqõþûÿÒþèçxéúu\¼p_|þ^~ù~û?þmüïÿ‹ÿ<,.¨l¦ªíáÃûRâÝwßÅd2Æz½†÷2 ˜-Ó™‹"þhpnêp]Gê¶í`"ŽcL§SŠMI e™þ<,Sþâ)Q!r… =Ú#ZK)0›ÿ©á×¾ö‹xååWðÉÇ?)k̳!ŽšÔ,˜­Èo $HL=N¨é»Ð2 Ȳœ ¥,Á1–†a—J)@8ıÆb>‡1›Ó Gw=’4æhÆ©ÿÁxzF¯´ÖQ_WPJ ‰3¤q ©5œ³¨[Z\ xøÕj…Ä&¨¼Gž¥ØÜšÒgÐw0Ád<¢P¥”æÐMmHF£¼s(KJH¤`2ªp{ã(ö5¥–U±s†/¢»À:‹q|rBÎ’(^øˆmrëÕ*dãÌБb2/µÀmÓ 9)h.!8#¨ÊÓ —RŠÖõ=ú––2BRµýèñ#´-A\ßÔ7žŒQcìîžÃÆtYž¡mš@¦¿;Ai‡¬”4K •†å‹šH6Gj-²Lakk_~ù~ü¯Œ££œ;wˆò{±··G€–$EPÕ5NŽñéÛ¸{÷RY†8Vð^³ŒÅáç·?AÕ4xó­·9›R,»®CY­il$<²ÂÑÑ ”’ˆ<œ÷ôþz¥LMôê<ÏÑÔ¬¥68Ï3ú}¬…ä*rÐÿRYO~fKK+ÇpüïYcIx­ 57žŒq|t„ý§û8¿{Mݼ7À.B…ü¿üÿ yÏîÜùŸüãïüÓo4l¥óÖC &„°)zµX’2Ïùb?«§%„Ž5ÿ0sì{è ‚¢’è¹'LJX.—¡Â×^{ßøÅoâáƒûD“•Ì;ái[XU%µð’Xr}K­c–&äÎqž4…Z!ލõµ¦ç8Ͻ!1pÛ4¼Q£Ã6‰9<¥H³Œ–S ðƒŽ5aäù÷LÌ»®Ö˜Œh&YÕ=Èš–LŽÛ{ÀS‡GGçá(ÿ–á§µ@K!×Z¤L,2EoSÅLj“}ß2Gp6"·JÓt¡MwìUõÎcº1% {^û§»ŽìzRS{¬¤bP„žgpTm9ÔMÇÐ ‚D ±”Yš¢kI`Ð|<Ïsz½CšRdC>*°Z.ñôÙc|úém<|xÇ'ÇXqèÕ(-«2ŒhöŸ=ÃñÉ„ô˜Ž§”×’gè -¡´"€©íwÏáéÆD| ŒÆ”R(Æ#Ìç'øÎw¾ƒÅr†¯¼û¾úÕ÷ðâµ—hÃï=´¦Í:¼G–¥Èó [ÛÛ¸rù.\¸ˆº"pÄ¡yì2˜šºÄ…  •âiÒ¤öˆÚ(IŽ))ÎFIé÷–„У¬#àmÇ!Úwøý†œöŒ)ô}ßñˆÊ±ú¡ªª3•û)Ï0Ž"h..’8 Ý”<Â…ó{xó­·k}~çÜî©£¨§T?õ·þƒ¿‰É|ðá_ÿÇÿü¿{«5„+J“’l:Œ¢—J…¼Íÿ÷hTðR…¼qD3…ÇOcwgåz…Ù|×^} ¦ë°p„o~ó›RàÃ?äG2”‘fG«ÕÇÇÇØØØÄæÆ&eär°öé¡åÃÖ˜¼&̇ßSkzY‡R¸ïM–N7 ÁK»?i³¾á >ýIq—=ŽN™wR†9åµk/á7þüob±Xàð`Ÿé½¥à&)%‡ØlII‰ª\Ã:ƒ("ßlœÄ›(WpüëqLL¾7,=¡ˆFpå°%MS&­Øž¢C… Ôýd‚uY¢*+ìní¢mZ8~QzkåYZQR¥ÅaaÞoNI6©Þ²•·ÖÖ’ín µHÚ8Q•1ÌP£(Ìc½'!®u½¡–{ÀƱ&iU[H8¦ø°•gc-rÎ'¦Y¥ƒÖ ó̓阀¿Ls÷Ô…Elå|üä)¦“ Þxãu(©òí­Íÿ·öû–4·ê?ú›“)~øÃþÇÿ⻿Uéiš0)˜¶¦Î:EAÑMÛ¢mh.qv5íœå y‰º®‘%Ú¶Álv‚K—®àÑÃnÞº…ªªpxtŒ_üÆ7 „ÄÇÿü9îá »©ªOž<…ÀÖöÒ8åd±çþlïøpQ¡"±áÐ$8+ý7RH>Ô2w‡›ç¬&hùÜ‚f8sX.æ8>:@Ûug¤Bt¾ðÂuüù?÷èûÏž¡È2(‘FÏ^JÝ º®% ‘RÈrÒ³ÛÃ6›‘Rôýs.Çðw¤Cj¡c–¡Ö m]ÃÂ"ŠiVðÕ÷=ùŠy •$ œs˜ÍfØÜÜD–¥89™Ñƒ¤ç“Õ‰ÚT•”ë2TÙ:ŠIŠe š–Cä5Q^¤TMƈcŠ8­ê†Ü%iÂù*šñOd¤¤DßmÚyOL¾4¥y µ(Š‚´dåÓR!J¢=f8‚à%Ö€ç2Æ íy³·• »\2†ÓFú·@°±YN‰rYFg³üèÇ?ÂÝ»·ƒP;Ï \ºt%ì÷ö.bccQ” m›0v¡y$¸ËÕ Ÿ>Aš¥¸x~Zj”UšA$¢Ù°Vd·+F-)’?þñq÷îüò·~»;»(×%FEŽ<ËáçM¹g±¼e­gž…ŽмÀr¹Æ›×1›Í(èŠÇÎ9Ÿc<š`wgëõ£ñ˜G” ÷Í÷½£IxœÄóÉÌÊá¦XW áÊòê½ Î­¦iв>–(§N¨º®it""­L—b 4b­¡ãˆ—SÀáÁ²<Å«¯¾Œ8Šp~ïüdyþ¡áå”ú[ó…TGÅ?ÿÿâÿøG?ýi% r¶ï(¥‘±ðUiEëx&ŸÐ©YG§3á³ MÞ3]d>ŸÁƒ½ qûömL76påêÌçs,gKü…ßø À‡}²³„›¾ï°¿ÿŒ³Z L76ÂC}:o<å%‚çœÃ!ö§U|§óJñÇbòƒÒЖZ²¦©°X̱XÌÇ´<8<ÜÇz½âèLñÜAxý¥[ø·ý7Ñ´5<ü’ )štbÆÐ2„– ¼ 8Ù¼ˆ7˜¥)ªuÉHxyÆ8ïˆÊÉhZGœ#¨h€;”ô€YjËw´MõdhÏómG~PÁÕVš¦X­éö¶ÞÂ;p[N¡Nň~úÞ„Ä9ɸ2­ègÐ6- ŠQï¢H…k¤t¨Â5Ïsh›(™Ð#^L /B(g«ZßQÊ]–ç”Ë"%ÏÖº¾Çj½†3.lmIL‹±$Ž'ô\×UM®43´•O³4PÄ'ˆƒµ†Nê0Ób±Àþápt´ØÜÜÁ­›/ãõ×ßÂÛo¾—_¾…ÝsçpaoW®\à /¼ˆ+—¯B*åbðl%ÉZ‹§OŸ`<a2R‹Ú›ð™ÚKÓ÷´éåŠñùçøÞ÷¾‡·Þz —.\Àz½F–ñØ(¢üpaNÆcH!ÑÔ Wd’©GuEX³ÙlØÝÝÆ;wÂcxwº¦Áõ—n@Ç1º¶ ïÅ3ЂeÈ[OÆX.–èúžfwZ@ bê(oFJ®§í3i-C]ië=@Ú–f̤â t$5”j­,ΖXÌçèÚ¯¾ú Š"ÇÆtc9žnüáPÿÉoÿ6ðú?úßù_tûSDVMùƒ•‰-f| ¡BPÒCáA¨v‰éÆ:ÓÁ;rB>Ä|>ç­ô˲ mµõ‚“îˆDó­oý ê¦Æý_B ÒmÌNÐtƒGi3Ècú®3XÀ¡3?¨4_!¯‡ae?e+Kª ÝéA—$ 9†&AP[‹¾oÃHc˜KêˆltBxTM…$ICÎÅÀ3ôŽ6ÎÅ G’d"1ýÜ£Hn_Ш¡¬j¤ cÈQYâ˜üÎ庢T?Ù T-ioØ5/g4ç} ³T)éÏuÖÀyÿ—eI3Ö¾gØ)µäÓñÞm:åÌ•(ŠÐÔ !ËÆ8g±®J§¬!§Íú¶cˆ¨b”EžyN[qÐ’ä?ø>ŽE1^~ùU¼ýö»¸rù*EVDUw–R%èïÖÖ.]¼Œ­­TUų9’³³‡ Ž"ì]¸ÈoÇ2•8̸ã8F^äH²ÿøÿ$Y‚W^~4&ün’[È[‹¶ëHhß¶Y tìA¡1ݘâλØÜÚ„s‡‡ò3¦v¶w°¹¹‰ÃÃCv´8Ž(Moðô;OyÒJÑűý•´Žä¬qÖ3Ò‹Õ Þ³ç^Áô=ú¾ ”A×¶·É\<)M8/Þ"K%‘2 H*Ê›^,–xûí·°³½ƒÉÔÛ;÷'’‚ú?ÿ_ÿ/˜Ÿœ|óïþÝÿ×_{ðì k…4"p^༠‘ÁH.¹…Ô‘í¥eï±w> ß³4…ÖûÏö1*F(F>þøçØÚÞÆ… ±*Kã—¾ñ \¾|?úñOçyC[Ëßlßw888ÀÁÁ>È‹I–yÄ0;„ÀCm‰Ðî 4™³šÃá,L’UUáÞ½/°X,˜Žá˜„«sïB›J‹²N¾õÍ_Ã{_ý:Êz/¿ü"x«ONŽÑu-FÅYšÑ/ˆý.yö6è e‰JÓ¨B‹ù ãф¸«u˜›VuM/±Ža½!ä“Phû–É6ÄÉSœr×ó>Nb(-)̇·¯•Æ"p¤+U‘&­Óµ)÷ƒ¹„Žs{£–‰Ò<Ár± Ôê8‰±æðŸ("[•³Ô*GQ ï-´ŽB^å˜ 4X.WŒZW´uâè£n1*ж Ö«’`¡–tŽËåŠSÕêš„áY’"M TÅIh™ ç“$üìöÆ åwÒE’üåû?ø>ž={‚8NðÆoáÅhQA[îŽ"¬Ç”€P2DÓv]‡Éd‚½s{hÚã1V«ErŸÏg¸°wÇ&`PAèÖÆZìììàΧwðñíqóæ-Øz$&ׇ3UUcÍ!`ëuEŸ³  AGèzƒuYÂâ4ZŽÞ,Ë5–ËÆ“1?zº/Á̶m°³½4˃hZ+…–a ÆèˆÞ™,¥±Y×dY›üáý£ÑšTUÉ>~ï9Ó¹GÛ6hš.Àu¼ͧ¹ªã‹4‰ã0;Lb: u¤‘ÄD-_,æxã×°³µ…,M㽋þK©”õŸÿ§ÿ[<øü‹_ù¯þëÿú/ÍçdmŠbÈÓöÑŸj|¢(bÏ%m†™šw> }z€gE†Å|ŽÑx ëž=ÁdB²–?ú“É—.]BÛ¶8>>ÁK× ºúôÉSpÛ,Â?ƒ^Œˆ×û888DÛ4„ëâ–@1åvÈŒv$YúøñcÌf'a¶3TƒÇóèèkÎ!Û :Õ²í¬n FÅ¿õ[×oÜijƒ§ØöQc<áèð«å ›[[¬Ø?­H›]šUÙ;O·*gVD:Âr¹„Ò$,5–Ú¼<§yYYVÈ2šÝšÞÀ8šùš¿CÛÕÌ»##>0š†äšq`›››pÞcÉù×$ˆÐ[tD1Íöâ8BQŒà<NCÅé2$²ôé÷Ô÷=𦿶’ÓËå2lc…!6²ï ¤”ŽH{¦i°Þ´´-®«mßa4ž Kh‰3™LGd骪áy¯ë–ĹJ+xAØyLJÁéøÇA¬o#K"‘‡À´h’Íd¬§RàîgŸáöí¡u„W_{çvö8Œ(FGÐ ¹#®¼’$ !MÖtÍ.‡¶u¹\àüùshÛŽ1ÿÃ%0ÇÞù½ èÚq’`:Ý@ÇÐJã»ßû.„._¾Ì„#r×4mËàZç”ÖØÜØÄh4ÂÆÆeYâÉã'(Ë*"Gù´;¬ÖKDI„'Ÿ ÂôAä>Ø[›¦Áå+W1¯sÈc¡ªU³!€ô ƒ®ÈsB†ÇÉ|,‡ãBŠþ,sšU.•¼h ¢ï#Bš¦¸{÷.÷Y€=Çl6ÇjµÄr¹Ä|>Ãáá>?ˆg]'î¹¥Ìà(¾¾ò•÷ð—ëßAžç8<؃úQQàÑ£Gx¶¿Ý]ªH9 hš·msÚ:¦®C{ïœ#w®0ÎðÁÌô•¦f|m³I§­ "š¥*¡Ch‰O”’ª'€B¥éNTßó™›ásÓ‘biÇiưe®ŸƒC¤4Lߣ¬k8GáJmÓrç‹‚¤X”^¦°Z­`­ ZÑg%?i;´}Köšgy ­"Ìæ ¤iŠQž‘‚¡´¤êä¼EU•èûMC`W!ésü!JEVCênhA”f)zGd-­”2äB¯×%667°.×øîw¿ ç,nÝzö.Q¡ïiYÅ1’$∠EH5x;KéqœRV%·ÈIº½½Ç‡vy]–ØØØÀ¥‹—‰>EH“”>W­P7nßþ˜L&A{—f9âˆ\EãéãÑëõ GLJ8:>—÷îáÁƒØÞÙÆ•«—Ùßl¸:ìH½`)6¶ª+6ÌÎxýéùÚÙÝÅöÖÊ5,º¾ éy–í¯iš’ó…[ã¶éXïÙ†"DpFÊG2P´Ë@}BX(°’«Yâ± RŽhm VÆÝâӧϰ¹±7ßxyžÃsgv|ü½ã£#ho šªÚ«ë¤½J’8àÃif㡌 °1”1azÒ€%I²z žÉ¿ê1*F¨ë e¹ÆùÝ=8°zoï"&“)þåïþsÌNæøú׿†®ëpÿþ}œœã/þ…¿„×oâïýý¿‡O?ýø9[ÝϱÖ`¾8Á|q‚û÷ …_Š¢4Ü8Ž`¹²~˜qZ?ç(!º2E6¿ìƒœÇ…_ÛÝ9_û·¾×_ÄÁÁ!êºEœhd6…5·?½r]âò•˘L&PR£®KšÍEIh·”’T¥©ˆ@Ÿ] )T8µÖHÀ9ƒ$ÍQ®WèZƒ$9Íá<ŠÑMGmae*()‘D ýc=ÖmEáám„uUR©§õÖÖ’8Âj½F]·˜Œ'0΄Юs;çÐv\¹hbNÆck1HàÞ4˜N§”ØÖuœÃ81IùÈIcµ*¡ƒ{”;^ıƪn¼ à…R ñŒ1˜LFÈ’ÕºDYרÞÞAÝÔ8™ñLwù|Ææˆ4*ÆFØÞ&鈃‡ë:˜ÞòÖºE–&ˆ ïÈi4ÏP7æ‹%îܽ c:"£O6Ðq…'¤8•6y‹ªnaæ´Öl“Ó:B\$¤uT†ý»;»¸}çcL&\¼xÝ€;wïàܹ=(Á8‚Å:ëHH|rø‘mÛ~Û6@ìqòôMÓ`ccƒºOG?ÏÝÝ]¼óÎ;xçÝwpïÞ}ݦè®CÏ´í®¥‘Êòh ÇΧaô4\Œóãd·^fwšàŸ·ç?g‰Ýmh-QUĤ Ã!Š3hv³Ñ¢ÑÔ”ÁBÝ—dQÔE𲄊7m çȽbuGÒKÄ*eg‘EÄöÄÎ\­Ö°†:ÞØÍ|\ ïzèå|‰GŸ\oºŽÐžh.i’уÜv°ž†¢ð¦7Œ¢Û´m[öÔF0ÖRD€¬wAM¿±±9ޱª×8¿{Â{<;Àî9Ýí]üô'?Æ|>Ã7ù›ØÝÝÅÑÑ1~øÃáÆëøÛûoãG?ú#üý¿ÿ÷ñôéã0O<›47,Y†¹JÛ6hÛ''ǃ©Y<<pCËfŽÞ³-Nœ‘çøp`æÙ_ÿú/â·ß”>Fßu(ë2`„>úù˜Ï–xíµ×±wá<¹F<‚4Þ£ë[x°Ô‘5‹|›D˜éúeUa<ц®¦4ÁbÌ•¤qQÓ¶¨›¶'LÝuØÛ$ëÛáÑ!~öáØÞÞÆåK—`̽1ˆ±^—ؘNðèÑir||Œù|Ž /¢š— Ñ%þ5<T!«Dppx¼@%¸¸·‡ÕzsçÏ!Ïs¼ÿÓ¼|ëò¢À÷ÿàxôä1²,GS7hØCìr1àÙJ]4@Y•8™ÍIOÊû‚$Ó œµ(Ûît®ï8Þg…B tm „¾3¤Õ§ÚÞAßj×ïœ SÒׂ  á5)JœlŒ»£÷k±XQ71êÅë/¼øéÿÓßþmüëýÓÿÍ?ÿîïíAQDî %ˆog a¼£ù‰d¦XÃV¼8âBLKFó 1€CøõÆÆ&Œ%Rð”ma³ù œ1ØÞÚÄÉÉ1î~~Rjò&+Eó¶Õ/^{_ÿÚ×pëåW°X,p||°ç "ÈlN+=Zìç<ïŸkOq_â Žë´žnláë_ûEü[¿öm¼õæ›8>>ÂÁá>Úžˆ,mÛàËðÃõCxïñî»ïÒÂcM(ô³:-Í#ˆ!Ô(Ò1›Ò-ßÞd%Ò¼­Ê5kë¸edÆ[YS„$1ú(:Ôƒ(8RІ¿eal¤hX·5 7 ²Õ”xÇÌÁ<ËyVæÂv—Ýu]¡®ªàÄÈx{M"ꊃx†V|4‘XkB_A ëɾ6¤ÖÇ#ö³Ò @°µ[K¢Ú4K‘g9PmCV2ï0žŒpt|„÷?øf³ÙŸˆaøÓ¾Qÿ° 8<|¥"Œ'c$Q‚,Í ´ y›%Ƥ`{ê¨áððŸ~“ÉvwvI`zI5”–¬º _ò… ç¡cOnŠ÷öº¾ÇhLhº$!YUS7è aÒöŸí‡Íx]×Á00P[’˜æ«Ælmlq|Æcììl3º J^U~ýÛ—/_ÆzUâðèÿòwÿ%~ù[¿Œ½ó{øàçQ•dMPH´=]UU£dY×z½Æz½fËìi5´ÀÅh ifrDª¢ÿÝÁ#áìêìN3dªŠ¢œsèZZd!i¼`¬EÛÔÁSÞ¶ G‡È@¡²¶ Õj¤5â8TtéØC³q„DúóÅÎX¼óÎ[HÓyš];wqïÿá…Xéùlž^z0°CQ°¸’^}kB €é»/+Xãïà`aû!°§‹KuØÜÜ&ѫ鱻wJ+,çs´m‹óça­Å|ï»xôèÞzó-looÃ;‡Ÿü1Ò4Á›o¼‰7^÷¾¼‡>x?øÁ÷1ŸÏ¡uz¸‰çBšÎÞdÞÿɈÏpºÅFuzé¥xýµ7qùÊHvQ<|ø}ߢëh¦Q7 ?zŒ'ãܹó¸yóF£šºF%áÏtÎAÈ!HË2R}ÄaÜÒSŒ¢â‡b9 1M×CHA$b¶=yÄZ¡mkj7£˜)Â=Í'И†ªÃ(âÀtš¯¬ÖK².Æ ¤§ÙVÛÓÁTä9Ë%²4‡P4Ïg°¶c ¾Ñžû¾§œçØúå $0ï@HÔM‹8Öœˆ¨Ðuk,—]oHRÔQÌ¥ä–FGDDîÚ^E–c]:@Ò%òðá}|ùðËÓÌhŒÇSÅ£bLÖ-Gôb9Ç|1 4j!èçR7->üùX®xíµ7Ñt ᱄À¨Èaz^~âdvÌ)Š£ðlGŠ"0…P2BšÅÈó _Ü»‡§Ïž¢ï{¼òÊ-\¼t Ož<Ãt2 úHª˜,zÓCGuUñ…qúÜÒs.Nõ—JÂØžù¢$IQʵ2°–.ßüð_aº±‰¦nðÉ'ã×¾õ«¸zå*nzJ(Ü{|MÕ`TŒ²Q;P£lgY½@ã³Äöa®NvD€ž×u¹Âd2…VUSÑ!Oß·¶í `Ùu½em Š;åù)œƒŽ4LcBÊä°Mt½Tø¸3g€ãΔæ³ÖXx­á=í=’8Â|±Æ¢,q^J”ëuôèÞ—¯Bˆ':Ëòb¹\åÞ8ˆHQÿm-¬qäcT’y ´I|Šýp ²t!MRÀ ’DÕèúŽmQé ­ÄÖæš¶Áj½@1™ N,çs7À0œspRÁ ðsH­cÛvHÓöx†ã£#Øk/ 3=â8ÚJÓúɃ‡Ó²®Ïó9x3™„éiˆÀ*Ë‹‚,M1%qYçÐqŽ…Àˆ .ž}3E–ÃYƒÅr ©$¶ww±^®°˜ÍQ×5&SŸ}þ)<ø¯¾þ ®¿x—.]BÓ´xôð1VœIqåÊ%\½z ¿ò+¿ŠgÏža6›ãèèÏž=ÅbIVUU§?(öÐrŽ4I±»sç÷.`ssišbggJ)ìïà`ÿ€Ëxš»-3XÓãÑÓ§xôè –‹9мÀko½Ý]Š6•œ--…BÛ5t±ç´,KL¦ÄIÄ3ÃPš¡Ðv–h;YNÛpk V«5·×oM)”¥,@iiM‹¦m0(µõ†ä´±ðp°0­AQ䈣Œ4|1ƒÈ´F”Òx¤m:bƼcQnò´Àb¹‚dHFÛÑ¿7P5gkTU‰Q1‚Ö1Ë967¦h[Ã[g ¥)Ë£®I*“$„)S‚ØŒíQ×’8FÛÓ1ñàá—á Œ¢W®\Åŋ넕š–]Ö2ÂxD©u“é/^Â¥‹—ñÉmj“D`èÓ'OðÑÏ?¯½Iï,KQB¢®;t]ÒéQTh3}txˆš7ŸÅh„ÇOžàÊ•+xöôŽOPUòŒÞVë% ©îû=Ã'üEÝ÷Ï™ʲD¹^c:ݤ4ÁºA—˜LN­pM–"bM „?|ï}ýëxóÍ7ñ{¿÷û˜/fØÜÜÀ…‹{xû· u„{÷îQ¬§5œ±MaWmÛòØãì2ñt¹8(L„@5‹"¢+I!±µ¹…uY†Ž³n*¦"ÆxjëãˆÂÄêºFΠŠa䥕…}¥ìg÷¤âyFèyE _gѳrc:Áã'qxpˆU¹Æx2†Šã—ŠÉz²¹‘ÕeIÁìÎCq©Út5ŠˆW4è—áÉ„`°§€ÚÒMYŒ ¬W% I¥ Pk <¤Œ‘ç#$IŠÙb†²i9â8²ªptp„Qža<™ é:¼ÿþøìÎ]ìí]ÀÕ«×pñâ%ì^ßEUUøü³/°Z­°³³ƒ<ÏqãÆ ¼õæ›0–LåÖYäYв¬¸Jêi=›¨ºJR(©±^/Q75ÜÀ-ͬéQU+b¶˜ãèè‡GGÐJãÅ_ÂÕ«W˜äÓ@+²¹Qœ#R¨ð¡ Ê4㈠4 N…Âx2FSÕœ;C/Åb±@Q”a›$‡]¯Ñz‡ÉhжkàÅðBö¨›†‘NŽ[¥E¾’¬Ï’BÂÀô-; <çˆÐB§w–ü¸ì"j»¦3B‘`›·€C΄³i–¢k;HAp…ª®‘$yš¡¬öšæóUH€ë:ƒñ„ò¯…’(&Á4ã™7T–KÜp_~ùWg9^|ñElomC‚œPÒÑ ¶µ9%bNš’X’¦M_x£Q>|I’ ,K>ÿü3äiŽËW®A)ª¬¡„Äöö6š¶æÖ–ØyÐ4ä¼*Fö.ìáæÍ[Þã'?ý)¾üòn\¿Ží-<{ö ›Û»(×kxçÑ›ŽaäÃÊÄéÏ̾©-lÛ6pu¤±^Wè»>Œ¥´Ž‚“‰¤U<ÀÛï|_{ïkxøøvv¶qåÊe{xÌNŽñ駇bbôÖð¡ß³F´áÖóyÛêP±E.ÿA_¬5£êª¥ˆ×XóAåYdO²)HûºZ­ƒ>Iâ`:‚G›¾£1gµt]‡¦i$ pæL:]¤8~¦zž·*Þ*{t\=›Þ¢éZäEÎ?³î[ùtòÒw>¹=ªÛŽ¥$t`¥ m‹»Î"M4›­Ù ëTBm/Yf,´–hêš ÍëUI¾@O–.Á¢O!èplZ €Éó ç"£Te é=¶77Ñ6 Öe…æðˆôd£Ö;ܹ{÷¾¼‡Ýs»¸tnø .\Ü HÿgOžªÈ@ æfƒèµ,¨ëãñ¦#™PœÄÁú±1E]WX.f˜/—8Ø?ÀáñNNfX¯WRâò¥K¸|å ¦“1à%º¶ ÒŸªª ´¤A‰0® Qyþ~ üyXårb`&JÖ–âd2åM¿…`K×·ä. 4UçÛ c«ÊÖ³o\%ðÖ£­[¤yÆNŒ]ÓB(z W«'ìQX¼¤°RðÖÁõd©Šã$,pèó§ù%=àŠB´ÑͲ4Hc&ã ?'=ºÎÀñåé§ º¶§Ù{®Ôd Ò¬Ë5'®qÿË{Á-téÒäyA³6ö×êDòÁѳ~¯§<¡©k¤ü9§I†—^¼Ž‡àæÍ[pÎáä„s_Üû Û;;ÈòmD:‚V]ß„( c†¸ p¤,9H¶··Ñ÷=nßþ‘ÖxéÅqëæ-lL&øäÓÛ˜/–8:™cTÈ ¢Š{çaÝpøÎíváÀ^ö!b Ihß¶ Æ£1^|áE<|ôóÅ[›[¼¤£L²\cgg{öðþûïc¹^ÑâòäŽÉS–+4 ¦'+^]7Áiu /þÔ‘ñL™N¤$<è¿KSÂv­Öë@ƒ§ÈcùWqþ±T"8ÃV«/cÉ XûðóèÝ¨Š‚î9Ô^X¬Dž—¾ëàyàW+zW«5ÖËÕ/zë }r|œ¶¦Ý$Gu„|ržÑ9ZS殳 y ç€ô ߈s Û eÂzèX#Ž4ÚÖsO€áŽÄçŒGc¬–+”ë5â4ÅF£o:4uƒƒ1³ ¥”8Ø?ÄãÇ‘¥`s²É"î—._†”ÔbÆGP»3\YšBi…ét !$ªª¢Ã[”ÕeYbµ\b±XâààóùuÛ<üîÎ9looáò•Ëaq à8××4—ô uWc<£íØÚ¦–KÒÌ©8 bv%iÁÐ }òÞ#ÏéB–ÕjE[݈õÖ™€ŽªëŠˆB]ߨ(…×J$ìZh!ÁÄkïX#Ñ „ÊfàJJA•2yÑ-¼uPI ¥([ð¨ë*UŒÆ÷ؘN ŒàÄÚ6TmÒÌÇaÅyÀóƕɬµHs"7mƒÑxL4›®C–DÐ:¢å‰sp^Ð\¯£xÉÝÝs(ŠŒ–|*àPÕ&£QRZccsI–´ÓÍ)*¬ïMÉ&޳cTU‰7®ãþè± ?yÈ­3¤?ÌSÞ„v¡ekbþUU‰‡ ©÷@YטcŒòyQàÙÁµµ°TM Aƒ~kCìe×uÏ:á0Œ´Û±|fµ^aTŒ˜¼Mí°Ø íƒ@Ù{à§ïÿŒЂº¸AŒl8=RH78Jaìp {¶ÁÕÁn:´É5¦È L§›(×ëÀˆcu´m¬½g£t«ª"Í/U–ð¹ÓsJm¿íi¬#†Í´uaž>ØO;VÂÎYKK½!’Â3æèü¤vHâÎËÅÓѦï6Vóù–~õ7âúïý?Á×;mm’˜K^èÇA™læ ^›f>:R c$j×—nXÓúÛ…¸ÏÁt?Ì•–˜N7°snÇGÇX-—°Þa4óp¾Æb1'Ñj±Dâd1ÃãgO ¤ÄG?ÿi‘3ñcr<·FßõdÏ;#«iÛ«Õóù GGGhš†×úäaÍóçÏÇåË—qþüùà“¦´4…¦iÈjÇZ'E°†Lêër)4âX£m"ǨèÌ‹” ©k(9ÄZ†$€«Ë©„RGÁ>8ÜŽ1 õÖÀHçyv$ɬ£¶?=hu¡nZ´¦†Ô„ã‡&âˈȤ ÃHªYžA)Š©Œ4…@I¥à­GYÕè m uÅä:©4Š£5g{ïç)º¾gR·B’PH”é{è(&™¬#ÄZßõ8<:ŒÇtùI!èße/jÊI×QŠ]GHøY¹ýåm,–s\{áEÒ°ñåsíÚ5|ðáØÝÝÁîî.éÏxòä ÎÛC–f$Æã1œµˆ¹­üéR:nQOåRløèèŸ~z»çvpéâEèX£k{,— EsKÜ4 IŠ µÇC%vök4!KIÐßõ=½k-YÑöÎïáÙ³§¨Ù™dmZÛSê;¡Ú"MÆ+ „H©D´)®‡ÀvªF#t9µ ²Zcgw››8::b Ù@…T‚c ” ´Ÿ!ÊsØG ¶–€ ”r¨Ðw(ϸ½ºn8äPqÞŸ½(<%*ö.àõàýðW 2ËEb"÷¬–+ô}¸ÈPÕ­°}?ÕóÙìRÓ¶”f8ê¼…ð”!…B[·HÒ,X_ä™~}ÐèYû|¤¦äÛÂZƒ¦"KT'œšÕ’-YÕèCŠœÆ¹ÝlL§8>>Aß·¨ê Iš ×9 · gÑjM•à@bnêëÕ>âÛÂ>Ç?´,Ĥ0)¶­g1ÿÖ:ÞÆåØÜÜÂÕ+W‘ç9¤ Îú€E"䙂V*ÕL+¡¨kѳ ~TŒ‚'¹ï-¤0º¾<$¢( ¦,¥ FHj=Ú¦E/LÈ«Íò Ö:”Ë5”–È‹o«;À{DšLñV8ogͦ< å—Ä:&1:Ô]ÛÁô’4&gKÛ`¹ZñÚ’ë¤ï‰A—gp áôð,R à#&d{Ä”œç)š–>ÓA&3&MȺá–K¡¬×D>ÊR<|ø`RaÌÂh–BÂ9ëH>äi$i†Í-ªZ~ô£Á{‡éÆ&ŽóÂô|ÓéóÅ£Ñ8†eY¢¬Jœ?wëuE‹¯ÉÓÉ3ŽÐú4ÂZ§)Áil ¥…1ìùe©×)$ùt AT º®mÆ¿>ø…'ÚtºårÅpÙ„bu½‡·Žô„ÌŒ9Wº®+(çI‡\Ó70½ÅdB¦‚åbIÕe’ 3š?K ) u¡}"I¬UágyºÈR§`–Ó$“ÞQò¡V|iE°ÆÔ^xx`Gï?{vQª–»#Êrñx ­bk¡ci +/•FÆL±Žs[ó,'|SYҋͨ,Bƒ[(©¹•ä“ÚÐp´mZ^á{¤ÓQq `¯ßxËå ‡‡a#L›%‰„Ëä®'+Í_žg!øy⡵ÁÏ<|¸ƒÝ锆!0™Œ‘e&ã)®\¹ÌX,ÉÃwÁ?hih IÐY×5iË"" Ü`!äøÒ"/ ”FÛ•´Qe´É•}n²+Ãa'%1ËrMìCM„áï<È †T4" µT½ò4k¡O3cH›–e9|íèÒ“1oº•à0öˆ17øi# ÉÚ<%œ´èû )b¡6-“®{ŠHbøX³ŸÖ˜uUa:"‰#ÎÎ`¬EY–Ÿãâ…‹¸võ*‰År…Ž+°²¦%_Qä899 Q Ç'ì?}‚­>tŠÑçÏŸÇ—_Þ 3Ã,„…gè%qíÚ5H)ñÙçŸÑh@Ÿí’<ËVèr'm¨Aß·¡;;§ÛØØ„R«åI”PÂag`MѨÀ믽ïÿàû ïHùp£\ò¡8±a/0 Ò†¿ÇYéL×µX,fô™Kz¶‡¸Üákww››[\©Ð45yÝydF‘$ªËŠÚ|VBPWè9œL¡åw³m;¤YFZXkN+MC•Œ6ä!îùV‰Ñ#ŸðhŒb*sÝT˜L¦X¯Éwš¤)GmƒéÆQ<¥Ía1¤íIËÙuP’@ ‚ó«½÷PRïs]•<$'HB–¥èú:Vè[Ç3®Ó¥H×uì ’p®CEtàpL£uJG(ÆSTeZÔF¤dªöjµ‚Ö%Ö5q§Sú|zǶ7 Ó4ð8µpUe…º®0O)WÇSžÆr9{@À1M û¨Œ|K™/ >øèCÄQ„_ý•_EǸóÙ]diXÇèmÏ2Ïk @2æ+ ‡áº,a¬A‘!(Ï#"‰YÓ4¯éB•8« jáÑ£‡ÜÂSFLÏ¢yš¯ŸLJóëšðN Ïì@^Ú;Ëãê¾…Ì”ŠP7²„*¨›7oáÉÓ'¸s÷._ºªnk] yý 1g-m‚+JŠ!Ð:âMûú9¾(-#¼ùæ;ÐZáøø[[[„ “ô¾y‚XÄh›QDz×–©ã:Џ“$jöÆt\UÇX¯+$q‚8Ñhê†Î”¶„ŽÚ¶ÖZŠ{` ^0N820Èp“´ BŠv–¥k™Í±µ¹EË¢¾MGã|µ^¼¡¡€ž„ýðš4°y°Ý4-½„Æ2IÈòôt€))ëvÈ6’f4Î:˜žÚ¼,ËxþEsÇ¡ìíºžômŠ‚¦Ó4A1Êø¦å‡seYQŽBÓÒptX:ËQœâÕ4 ³Í<£¢’ €Ñ¸à‚¤+}oX£¨ø×$š¦Fž$¢(ÁºZCI‰¢¡iZß#V,x"*%,ÃJ­¥Œxj³¥Ô¨ª5’4EšÐá—2ðë:.ë%V«â˜0ïðÒeɃš¦õ΢ë åé ´Vë5ÉàXǰ± º¸ÎTH4½ÜjÊ:‡ª,‘å#úÜØ¿Ý´ /B š–$6Yž¢ïXÏÖ¶ˆ”Í{ò4%ñˆAصãјB‘ºR‚C—È>Øw²4å‹’éÉ‘>ÍqéO5“C –uÊZxá‘ä:Óᓟü×®RE¶³»‹­í-|zûSäÙ(ÌòêYM—–Ö¨›*Ðn‹&À)×Ú°£b4IðøñY–áòåËxôè!â89“b¡”.ƒ^z÷œ_úìBÖ¢=õ½åÀûgC͇¯é&ò¢@×vÆôŒ÷¤úðÞ£¬J@o½õf³`w÷\ÑMRk\Ø8ËR¥$ïZ´ ©B´Ò(«2¼sà ^ý lmm…ŸæýéåËVOëŠ-”\=ÎL™„#œF£ee Ð4A†%q'£4B[“Ÿ.OY¥ƒ­sÎ{/‚ßy`…žy}f$0Äýv|7U¦ëÞÒU]_pg¼ºC^p¤#”e ¡XôÊ-bã¼–ôçy(ªgˆ^k…µž1_çw°J"‰“ð6ÆpF-8T;e­ÆX­Ö˜nŒÑ4–7¶Þ{œ?¿ c8ÏCx–Ø ÀTJRÔЬ84ˆ’“ñˆ„äuƒ®o±\µÐJÃ9jigå&I‚,§0À#J"ô%@½[‘/ØQ+¾±±ÁáEM¤%ƒGšåh"4gYFbâ¶æ á«Õèˆ(mÀ[ ÌꪬhÁÂÕY¨ã#¶t‡Ïqའh‚à)?¼Qñ-˜L–Œ§÷ŒsJ°Ú¿c|µ4p”«áœE×v¨û…¢åIY•l¡J9/¥ÆþÁ!â$ x&ÒÚÄ¡âÚ¾Eš¤ˆtDö<–™”å*„¥(Š4û3äY†Ï?¿ƒ“Ù çvÏa¾\âÙ³g ܹsŸ|ú)¶6·H \WX­VØØØ!Gm{ʶ$_½ ›ÝõšFF–s»µÖxáÚKxôèaøì”.*t<§Ð`ÃX¶°g홃³¢ë&]«pˆ 3ø›7oQÇ'PB¡dÞáx4ÆÁÑ!´’è»E1Â{ï} ßùÎwðìÙSœ?¿Çbh’™±`Î=Ÿ]Dï E˜Þbµ^… l XÀ«¯¾ŽW_} Ïž=…ÒÂK8ëÿ‘æ¦ ;EÑ€œ§ÏÝJÐyÒ4m@µ•UEŸ…7!_©g&BšÓbm ìGX¿êºè-gãÀ”$MÙ`ëÚŽÀÆ2„@¢k +'dÆîºžâ3åivý9‚7Öƒt…nozòQcMpjH%Q–%º® ÂÖ¾ïPÕ5š¦F]Õ(«-ƒ$­sôbpˆ’TLªæ ki“ë¬ ‡±Tm›¾c7=t´Il7QB’  'tRÓ¶R£(² ejÚ-·S‘Ž‚otÀ6 f¹ –)ú¹’ñ~H9Ú²4ÉB©¨‡€Ž£ÐÖ{jïƒç¼%`‚ýÊZÂ.ãPW5¢$B¬#8NØÆZ‘PŸèÕ]×cÄáJÎ{tmU¹D×´˜N§ð öêøø‹Åœ£– ‚Åv-¾øâst}wÞz·nÜÄrµD'xë­·QU%Öë·ÚKt}‡«W®`4c>Ÿs.°eqŨ*.•½½ ØÙ:‡®3”®567·PV%ŽÈÚÈ>Y:è†ÿ»bßÈ<AúŠc‡‹1Ú–pcß;´ÜÓé~á«ï§%ËILÆSœÌgÈÒŒò†»]Ûbsc öÎãÞ—_`>?AÅô 0#ph}Èîav#«>šºEÍÏÑ@n<û¯¿þ^~ùUÊš/çâ(Âz]ò²†" ‚LrYV‰†Å=òœr5š¦á[vØJ žÙ54;:2ã"ªHº®GÛ50Ö`”äB¡mÌéëXmî $…@[5ðˆÊR˜ÍfX¯×k`µ\Ò â k.<{/›šAŸ1EPŽÇÌMÔ¼´±€ §0à,P7%§ƒ‘ëd×Rš˜GÛ5ˆtŒ4ÏÑ5 ºÎ Ës˜¾Ãº¤C_kض)Žc8ëΠ™¦íº 0•<›äÎzxáo®7KoÚ–È4KÑõ-”ÔA´Lcb”£®ôtž¡mkdY ËÈ+šk¬–%66¦HRÂJ¥)…yï±.Ùm <4= mÓ‘þÌSå:_.‘g9FãQÈ»ª)zX0»^c4ᥗ^‚Ž4öö‘¥).^¼„ãã#Ÿ`oo[[Û¨«Z+a6;®:˜N…ÔgU-qóÅN2¬ñx Àã…^ÀÇ÷QVklmmÁZÁ]NÈ8CŒ€÷¤'wg@"€#(†e†2´¤_ûÚס•ÆÉñ Æ£1¬3¬>„”Óéf )¥áœ9&qX¯É¡Ejaý™–ý½÷¾Ž[·náÙ³}Xc0¹2±¹E³n){ŒÇ”Jض )FÒ]ÇñqLóØhÀžE|0Ó÷J\KÊà‰tÄÿÖ"Òp«-e̳U ï;~6† 9-µ”€è;ŠƒÈ²”2¸!h[^5pÆqü€Ë´‡PCN„b$EXZ˜b%,¬ñp®Dß¼ÈÃd­¤Ô.­ •f‚É©zžì_"„^ IÿnÛµèZjyz¶ ˆ°}Œ"‰4aý\ßÃ;(º&Œ}D3ͪlèP`oµ5$.mmD¤ñÓR‘Ï´©±X­P7 ‹£æépªë:lj‡yÄ©Á ”=C”IÓãñÓé&Æã1ÆãмàyNE~HdyA/ZK4¥(¬k,QÒqš‚ÒœIáúžœs6‚JBÈ}O/óp!e)U|m×BXÞÄEäqn[H³mGC]V°Îa<‡C™XƒÖƒdD Q•½;åHFIÄŽ‘QBÛ>)-… ÷„ “‚Ŀλ@9êúŽžÜØcUµHx{Ý›ã|ŒÞö¨ªuYC2(a ’x¶è 9Ó E§OŸR…Ô4XÌçxã·0_ÌðÅ_`{{“ÉóÅ mÛ¢iê@4‚à‡Ð­³ú¾a“?TûIš`ɇôÅ —ðoü2þà~ËåÓé&U>Ö„ÅÅð4ør‰;(ù> ÄLŒoÛ6ÌóàÆÍ›xé…—pxttj+[òîö-FÅ‹ÅYZ@*º.‘²õT@à—ù[xøà>þä#̘¶CÑ(´‰Ô¦ŸVVƒ¶•¾öö.âÕW_Åîî.NŽibÎçsêàx, •Fšêa˜ÍIN[ŒãeUòÅ…½Ñ©5êš$3ñ°|5&¶HÒ]{ZX Ë¥ËçéI¬‚>À#guû3ŒVæWQ$ª¦ŸA]•pÓ)„‘®«JJ%!`aý ix¢X£k lOûi¥Y£ä‰YHóBzñt¤xFhÉv¥a8g9„÷-’8År±B>JÇͪ,ý¹Iœ>)é-¤ ›JßÓKFªXg>Æ'Ÿ|„ù|†étìltøµ0F@ë8€ƒãhH¾¤ÚÐÍPKú&Þxã Ì di†,ËqxtÈè7 ïhI¶µ¹E‘§u áIgkCßѨéÅ—^ÄÞ…ó8::ÀgŸ}ŽÙìäÌŒôO'8eYŽÑhŒk×®áæ­[X-—¸ÿÒ4A–e!•n<‡ç0‰)H M-­¡Cø–à‹l¹Z*$ >Œõ\ [HŽ;ò †A]5Ô2C^1 gèN.ÄðºzpZ¤,]âPB >C(Æ4͞Ǔa¡§ÜT«H ÒAÊðà${:¼”Ö¬3‹‰«Ò4CU‘žÉɳ4ÚÊ ‹ n¡-ò€ó’æemÓ„RœD”Ì”iÃmë :^åÆ j ù'“”Ú¾Án–¤ œ·¬`§›&ËÉ8_Ìqrr‚ÅbŽª*Cˆwß÷ááf$ÃPvˆ]! Eœñ>ž¶Rg·d7*QU%ö÷Ÿb4c{›2"ö.\€³©G"Þ2¹€£çJ4û,TïxÞHÐòÉC[¼H'PR†t9­4š®fì¾äàœ.ÌN)@ª§lF‘îŒfU«õ m× ŽS(©0SVòæÆÁ’8xr)@Ê ®iCllÕ‚T "“,c¡ÑÙàf›$Àºì ŽYp´]­ëQ”°Ÿª¶ñxB[àºæÍ¡çê|X8N1_`]®%/@*‰ÝÝ]ÔuùbNr0>l†ŽÆä+´Ùu<[¦Jc4qÔ¥çÖ횦AYU0ÖáÝwßÅæÖ&~ðýïáèèãñ4¨†ßÓyÖJ0ØO†æ”§³vxë­·ñÊ˯#ŽH{i{®f­Á¨ÑB†³@”"¯ÿh4Âz½B]Sú]œ-²ª*L'\¹| —.]Á|¾À|6ÃÑñ!¡ðÛy‘cTè:";wÓÉMÓ`~r‚Ù|Ž®mÃBiss ›[X̤דŠà]ÑhŒ."÷ZUU„íR’/ ú÷¼vhš‚Y˜:K°Z®CîÒ~ðÞ#Š5œ%Oþ  檃èý¹ù&WˆBŠ ¢£âM2-…º¾GÓuP‘¤Bß÷½³»#ëu x<¤´ ëZ¥=…éXA¬³hqÈ¡•Pš*©¦¡69ðëŒA]÷„O:9" ŽO])§-ÌóØçémÃŒçlÁ8üÚY:öp`ƒêÕj‰Õj‰Çâèø—/_¡ÛÔ(¡(ž•¢(â%B‹$I‘Äœ§¶6NbH#ÑYš³LÆSrpxÏÁN h†Òu Œ¤QÍ'in³•äKÄë‹yC\Õ676¡¤DY•!}ÌšRS%Xšïñ|p°sE\Xrö½GšR•[Vk–ÆtPŠ—‘ ¬ c0èøïÇšº‚’šé#‚š.gFØÜÜB]?&·‰µ$:@ f³ŽŽ±½½àððMÃyÑ|é ªO*¢Œ›ç¨ç¨²Hn•r4]Co1BYUèZ¢Î\½r ѯhüèG?ÄjµÕmÒ%”rá÷%BÿÜE<|iá—~é›xéÅ—0Ÿ/ƒW1µ:I2 ”7=¦“)¬é0ŸWOÆ¡ Sš6æ–q^‹È±an¶»{ WšËôŽ4 6&S*pxÓÐ{>È»’$E–åèØ©Çº–b`ó"犛.²Zs– -{Aiƒ:ö !´ -)$òïF×vð©£6YëHXcá¤ZAE1Ÿ²‚†ãÖ#IcòŒÆJjXcQW5ò" 7ëàýíúÚ+°wȲŒ¨* Ý,庤4v!Åm×¢7/5èÁijŠ—¤(G‹²lƒž¨(r®v¶š¦ Œr5hƒ{ttˆÙl†ù|”ýƒøtX­?OâgñN±æg-Qÿ}¿”’!˜Û{’Ü¿Ož<ÆÕ«/àÊÕ+ðÒ¡ë;ÓØ|ÍÂljá„ å’³ž¶«mƒ$&`@ªRjSúŽª A/»’DuñÞQèº%‘4,=LB d:¥6¥ï‘§yðç)…0Yc)™Î‘µÏ‹ª"D™†‰Â¦ïÐ4”„¥E4[&á1µÚZ*ÊÉ}Ó¥1Ú¾Cš§P,h†—PŠ^†¶o!•¢—¹m±µµ'O‡ÞiŒ‚GÛöX­u6™L±¿¿ÜQBÐg1lq´ü ³°Ö2%¼?“‡ãE®\¹rMà=4UÄ|ÔüÔ ÊêY–ãÛö×ñlŸ}v''Ç“øÿï+ISܼq ¯¾ú*FÅUIaî­å¤9ùReUŸZR›š¤˜¯Vˆ¨J3ÈŠÎY¬WKâ[¶”"(@έÅb‰ ´]‹Ù|†,I‘e)V«æó96·¶8@M‡œ£Á‹ß¶D%ïÚ.ÌI)/¦åôûÝÝs˜L&ü|RE·¹I–¸““åNNNh\Ô÷èû/¾ð"._ºŒýƒ}ì?CÝT8<šð;ËÑØÜ ׈ 2«”Þ/‘rýb>GÎzÛ¾§Õ`µÕšZ_ÏÉíùÝ-Zº¶ƒ³ëNEó fBrÆ’áà)šýÇQ4»q!IÉÉÕ÷¤jèû.Ä%DÑÐÙ™@Ö\ê `8Óàé "2!‘f9iw•"T—€@o-¬#J2Í­hlz‚Ò:ø½whû™Ê`-ý%âX±ÄÁK¨TÙ¸ ñ°ÎÁõ&€5³4ƒæP$Ï~â8æ8OK¶ƒ [½“ãY€A¶]˸pààøûû¨ëUµÚÁw|òô¼Q}Љ…ð§é&nݼ…sç÷0O°³³$MÔ*°Z­ÃP{Ø@“çÒb¾˜á`ÿ÷|‰OnB¼?8®Pèçwxx€Åb+W®áêÕ«  .MsÙ†\tnhŒŠØìîiyåz(©Q®J$:B>*Ù¥®+k‘¥Y„’R@xE*~в\.‘e,‚é:ºœâ˜[¨J’,J鈪4)Iƒº®1MØvF®šªªÂE2XEÓ4Å¥K—ðÙgwÃË4¼IîNT-œB†9#ÂVñÔ Lókþ»‹/ÁY: “(ac+DIŒ²¬A^v’-,+_P5¬4Kd$»$ó é+ŠMÝA)ПնHÒ1´¦ƒe·B1Ρ=çÎ3¤¤ï‡0lîAF‹8ŠÑºž·ë-â,¢âÂ:h§ÂXaÐ)ö¼¼Šã$н‡¹ñÀˆ†¨ï3¼A)Ò=‹1kISEqÈ)×}ÛÙt”¡^ö1OñŸ1»ZxxD: UY¢è Fè[‹®mä騊ðHTñyÈHŸ<ÒE+ÍsGk, ªj !GáÐuŽei®P}Ð –›ÇOáà`Ÿ„Ò+@ÃRJp  #¼Ï$rÐÞ@ijÅ9:`IÁât¤u3DÁYËFj„VTIE™]K¶ª®Cå=UÖ"N4œ7¡¢pÎAE¤vO’Œ†®œôÕt ¤±A.¤-êpP r2±¶¶QD?͹*CÅ µÀÃGpxx€¾oÑ4]؈*ûÓüãSz°”§•áöö9¼þúëø3æë¸xñŸ‘|Zýù°Üª€óç/àí·ßÆëo¼Q‘ãèððÅèÚ‰ødzªº†á£?ÓV ¡0. Ä)-’8Áx2Áh4B^ؘn`wwQáÖ­›xë­7ð“Ÿü?øÁ÷±¿ÿŒ¤óù >þø¼úê«T!2ÁXj%Loà}G4mÓÒÏb± m_”„-[ÝT Ì)Û xàû•©Uï, Ú¹zŒÃÕ45Ò4 <Æ$‰QW â$‚ây/4åÄ´}‹HÇœÝ1Dƒ‚b: âp†µƒ„Ë‹œ_¢%67¶èï¨èÀÔü€'IŒ¾%+¡][c÷Ü9|õ«_ÇüC”eÉùÊ.Ì|I‚"–8õÆ`2™À;–­W–]RHx%ùïžBGŽóahŽ'¥B1*¨¥1R9Žïì'ï9õ¬‡íh‘cŒ…’1ÛiЯ4=¸Mk˜VN›dk-éó˜wI£¥5„1PŒN;88ĵk/À;ý“?Âlv‚MH©Ù[ÞÍÞ)ʆ¥Døw|ÖðõÕ¯¾‡éd‹å]ßÁ®¶·v‹åQ!gÀF¤i˜/¼$‡ëØ ¼qHÓð@U•Èòœg«T¯×k$L—ì¨PR¢eÊtš¦˜N6Ð÷}@hE1=OE‘"¯_ Ë2¬Wk|ùÅ8™Ïþàé3M¢ý²ïQ–äÉ~üø1.^¼„­­mäyN…BOŸ—e é‰INÒ©y–‡Ž,MSTUEø>OE}ס©i¶)¤DÏUß`²*ÿÓCðÔg ,Ö:ïÃHDs':P(€ô¥q‘QWì­+#FJ ôÆ…Ò4åÍpS7ðž’Ý<‡@)¥™ïFÔà¡Ü´‚rGâ(¦¤+æ‘ 'R*R– ªB ôØì•7ÍZi¦(S{0D R¤& b÷žáèè¨ÌƆƒð”wªæˆ!.ãüù €÷¸}û6#õu˜R žÓ€V‚:”õZZ°Ö°Lc k;öôjÔu‰;wîp"ŸÇO~ò\¼x {{ç¡õWðé§Ÿ¢®Ë†sûÓOðnö.oNkˆ˜>‹ᜅŠBô8˜ßI’Cƒm"•Ó¥4lý"£751+ã„4žæ¸Ž±Né %aô“$‰bô] HZjH) Ñ÷è-eRh­1OPÕ„{ʳŒ<ÓÃ!ÉšKb;Xçù×Þ´€èÅÆtJ‹„Ž©ËžYƒ†¶íà޲O?ÂK7^Âtc‚ï~÷÷0ŸÏEŠbÄÎdjˆàZ9eö™ Þ€ÉdН|å0Oñäñc@ZEH“U]‘—Ç9=G=™4—y–a<¡¬J¤1ÍF»¾‡ôû€·÷̨ÂFyÈö–x|EžÃzD×uÇÔZV5ª’Þ·¦%)Z–ù}oo1À zßÃ:ƒ$Á9¯%§ÊÅxðà>î}qU½f;`Š­ímL'Ó€$ë;‹u¹ÂÉÉ1Ö$5º½÷¨ªŸ}vy^àæÍ[¤´à™1†è7Æ jjžmÓœÞóù ¬BÄD* gèI³}g<ÒJ7ýÜDЊÒÜ߇\åàûäm’²T†( ‚r8,¤o:ŒÇ :ÖN—ËuY9}ØÎAë„ÑØŠ—!ILâhË/€³IÊ3? tm‡ªô”E,zr!$—È"èèâ8‚±=„—ðaÝoo­ ¶»b”C«û(Š:¢ÖøÉÓÇ8<<@Ût@ýÇ¿œc¿5@%{ØÎÖNØFíì솭õñÑ º®Ãöö-G$Ùæ(4‡8„¸*+PpˆgÀE­O¾– {ðÞv’æ˜N70›ÍñóŸÿGGçpþü¼ñÆ›xøðK<{öŒA |üñǸqãFEªÁXÆÐáÙéA(…³wÎ"ŠihŸ…ä¼Aî1´Ê™æì%§”ÂË/¿Š×^{Þ''Ç”ãlðévm ­46§p4;g°ˆTŠžK¥Ñt 6¦,+€##º¾ƒ3=ÏÛ=îÞ¹‹ÅrÁh³&嫪Äü ¯½ö:.^¸DJgC®„@]ÕHRrLÑü¹GÓ%…€ŒNG=ÏEVÀÀ[ŒãˆUÃBEŸúY­ðÀßRrâ§ Ÿ9U“„#$I ¤€‡ÓÛ»;YÊ#¶ø€ËK­4•ª¶‡sÄìkÛI’² ½ƒéåt´M¹Qüë!#‰SH-‘D)yt9ÑJ)òïZ¦ÿ.— ¤YŠùÉy‘cºIžÏ¦­pt|„#v *ûÓ›ÿTå?´EÃRh:ÝÂåK—±{n—ã-ªº¢åˆRxôˆ2eÿÌŸùEŠ-UŠ$Æò–×RØyCºÂÅbÁr:쎡µÆåË—±±±‰ãã#,—ó`ñZ.躗.]R_|ñ9NNN°»{{{— uŒGŽŽ ´Æ;o¿Í·âåÍQ‡*‘˜\q ÷üP º=C[x!‰”UÅäѳ Ã"¨”TP‘b!s-¼ôèM‹,ÏI ê€,ÉQVkdyHFiF²ÒÑHÔm ’1Œ·€&'mä5’˜\;Ötmç îª$=üZE¼Qg¨±€ì${Ì9Ù×^yO?ƒ0_ΞÓõ=ïòÐ4&¹|»»»ØÝ>uYb¹˜…Cl\E1†±Ëu‰$Š 8Kº7‘8eo !QդǬr½!°Iœ K„³ÙIX²Õ5ùª³4' ªŽpdDppØØØ„é{c2 ëȘçV«5¬³F¨Ö14zÓÑRDI(Ã9 ªKEŽ/ï}‰»wïxé¥x÷Ýw±½µKc“¶ESÕHß¹R)äyŽ—_~×®¾ˆ;w?Å£GÐ÷-f³äôžÜ¾ý Ò4ùsç‚!ÞÓ¥ÐõƦ%à€Q‘Ã1Ór ÑG¼¬bX•ÒÁC:f"ÊÓ¨ôi7xJ¹ËSç UÉ÷^m)I)žÄôlõÖîÎAh0¥„ãoˆâˆÄ–}(Bh4¡²hYA6Çî‡(J`X¥'qX’„þÝDqcº01Æ\A Ö8 oih[U²”òQŽ œœsKæÏ„lŸ]š¸çŒçÅhŒkW_ÀÅKYØÚcµ\Seå^ ÊÙÝÙ%®›wˆu çHRÅÆéÓé„}¦åºÄþþ>F“¼sxüø1{W—ØÙÙFžg8Æh4ÂåË—1›àððˆ·‹˜L& ½4xüø’8Á…K‘¥“Vˆµ˜¬q89™aTˆ£˜‚±PŒ§ÁÙCþÔ,ÄYz¶Ðe âÑÓMÛõ=2¶CcP#ËjŒÇ…(Yª€4C2¥bS×Ro:¸š*ÇÇT!E*# ˜—(òzÓb>_`<*HÞ’³ ÖPÖå šIç‘ÒHÆc¬ª Þ[HM[Æ$NP«õŠ Zc<ãæõ—) CÑ!œpTź.±\¬Ð·-²¢ -m_£oí)DÕóõ bXäy·±4¦ˆxŽm­#:³5˜/æ˜LÆPJâñ“Gøà£ŸaŸG ÿ¿¾ŽèÑÑŽŽ¦.\¸ˆ7o¢( (%1*FœóíC+/¸ *ŠÞ9,WK¬Öò¬Ë…ùlŽÛ·?s_y÷ðú¯£*ôÁt: `]Íâþ¶mÆ‹ÔUIžõkW¯Áy‹éÆ]×àþý{œ…­Ñõî~ö)òüdYÎ|€Þ9ÄIx‰MãXÖ¤á5…F¥YÓw^rj£à8“º#¶¥:à¼HV(UŠÓÖÛóaiŠHüáôq¦:§9Óµz4W“ñ„"¦È ¡Þt!ÐÆö4·±Î"Ò1gX˜Ž :¢\ÖÒ|† /R`]¶ Ù$m “$¡Y^ÏÔ®zÄ,ÍÐõʺD–¦HÒG'+ìpbœ ÃpÉ`Z& Yë/^{ ·^¾ ­IMUy&¥’ð~`ž ®Ü(»áéÓ'ôw>“E: i’ / ¬Wäà¸ÿå}Ücíà`ãJ)ììì Ër³UnL’6Ü¿ÿ%®^½)5ö÷Ÿa6;BžÈóUUÁ˜Ÿ<ÆæÖ"­á„£<^ðèHc+£V*¸vXd áC¶®V £¼€±MKAF¬-8æ‚æVÖ:$¬½“B"MbTUC?w­aL‹XGÈtNCß|¢3¤t!ZÞÔ ˆAš¾y’£ªK€ã ’˜Dæu_±ì¦Tô®¥yfE€H“MG™ÕDX@`2š²Ì§Ã1»=hIÓCxA›JÓ3 Cš¤°ÞÂØà¹TžSMt I£Ž¸Œÿ_Âþ+V×,?ïÄž•Þø…NU§ªN¥d7›lQH‰$ Œd#sgƒá‡‹ñ­ Ø€|mlÀ0yçý…7¯à‹ÿ­oІ <»êT½¿ý¾kýÃóüžqP•%΋œ2YŠT†iDÓv88܇ÀG„>úkñèáÙß?ÀÞreU§Ó:¬VäŸ_¯Wל0}ßáË/?GÓlñþã÷±¿¿ó«sTE Á1C?`±X·¢åƒÖ)¹š>ûì3´íß}¿ù›¿‰«Íþ(΀<ï1ïFkÉ*o!YÈ,µÆÄyÏóyÀ½{÷ðâÅ ¼ùæ›8;=E×·RBífƒ§O¾Æû|ƒÍ>y)X>KØ'˜à(ÎÏt¶[Zñ\p7ûŒö`ŒæC0p ì!œ©’+Í#@* €a˜ -‹c‘¥¤„6ƘNÏóMU–”ã¢CD²SB¦ë²¨!% ƒJ¾ä¸ÑIØ! * þ£¦+zjc°|ŒÌ8¬hFìU RsPz?Rèúf³ÁÙé)I^›à Äqçï¾û½õ&ÆqÄz½!uü5<×ueÄûÄ¿oú”˨¯8BÚ›ÍëÕJ+<{þ”SÅY&š”:•a¾œã¨,“Ù¶sk(¥0 #>ýôS¼óÎ;0æ>ž={ÆYµ%ˆ[4Í''/Q×à8Ù+ÐÓ£ék´ØÝn·lQÒ¨Ê ýØÁ;â )˜sHÕqŒs”ܾ"0€×:ïaŠ]Ot¥)G%7ò‚˜v1Œ=„¡o º®¡–oo}ÛR‘gèZJ=#0/UpýУë»Êdx©<àáPä%Æ¡ƒ2Dµ‘BP"œa9:gÙIUÔȲ&Ó<À¨«=\­.á¼Ål6ƒãLŽÍvMÑ­RRC†€¶ki“žL”öPÆ@Zò8“±J•œÒÔú5MKqË=ØiÂÿç_ÿqšÿ<ÄÇoà`ï‹Å‚¤6QÎ&€Ívããc|þÙçxöü)£©h1prrŒ‹³süæo~wïß…÷eUa»iȱè*¡ e_W%泜c26ë .//1«xÿÝ÷ÐuGVTìØ!}îdßô¶Ý‚‚ß´†v¢Ú¾Åþþ!ŽðüÅ3<|ã |üñ/¡Ù™e­Å‹—/póÖm&À+Œ£O. %©¥Ž ‰·µn‚ íxDGäË!gtvŒ¼<ÑÜîÈETpœ’|Nxi˜ˆ(9+ÛA° j^×Ѳªt§þÇÿÃÿ¨úø“ÿ§¿üäcŒÎ"/JÒMEE·§`#kÉ*ùeÓDw“é”Ó+¥¢pfh¥SîB`ó¾fŸîŽÕG9$à6I9M#; j˜ÌàäägçgÜö‰k€…ײœãþ·Þ~COCÜH§HK÷°aó ã4×HÖ˜ xs寱‰2CžÑŽixk Åĵ(r®i(N•#mÌ»®cšŠÀÅÅ9‹%4‡pÅ!ÅÔð¶m0›Í1›U´eì˜ÛÍšBŸ8æ3FBF-šãÛo²–[œ€ˆ  ÿ,^ezðZ”4K³nLpÕ²(Óh£( @Ò¶èÑž7uúH‚÷œuTi»Œ&ñx–9%©E˜GέU–$bk–Ô‚‡â~—Ók'H%Ð÷-=—°›W!(e0Ž´V˜Ï˜†  –©¦à4q¾_,³Ù ]Û’kÇ4‚©É]ß±Rá?üž<ùŠLÕ ßÿþïà·ûwpãðV¢¦ç8‰nà{†÷éÀÌ2’ÎPÎÏÀYØ—ç¸yã&;s,ki,S”WY çUmß@I‰¯¾þ /ž?Å7¿ñ-Ü»{-¨´2©šrΥ iš$¡š,åMvàÍ-ewmårççç4s4Íö5-¯÷´šx\¥91ôÊ{Ê>Éx{ï-1=·ÿàª^^³7F"N|ß®ï›E´6ÈM£U"ïç&#þÇcл§ñÁïc±·DYT¸qxø™úŸý'ÿ >úåÇÿñ_üüg™»S7ÎübÙî¼g›PHªf­4¦q@–›d|ϲŒ‡” C4oAÈ_¸ÂRõ™qZž÷þšYœØvÏž?Åv³Jó»¨#üËáûïˆ7Þ|ˆ¦ipµºÂjµ‚TóéñhABTþ¿ÑØõôÑa'ÚŽòŒHYÖÌô ³ éÀ3&Çr¹‡,ËHáhl@{{{B Ï xo‰ÌÚ­Ífýý=(¥Ð¶Mˆ)%Yåo±··O˜¤iHDqm4E4jÅD—,YàÀºAÚözÆVQdAÉÐ$Gà?SòA<Kq<–‹½4Îóý0P`OˆÛ×ÝxÁºD';qà”D7tìiw<vÎ#+rÞ$ŽÌè ³ù‚µŸä'xó >°ºÈ²L¿Ví]…¨$Ußu9£ç—‡÷´ ¤ Mgðm·Åb¾L/*µôŽÛü2ͨ¼')—çVtɆ:_Ìðù—_à—¿ü9‡¾àw÷wñøíÇèÚ>-†@ UY2|4@ðœÎã­·Þ¦ö²888ÄÕÕešã¿z…££#Ôu¶£˜ÕÌähÚ-QÊ Ãб½Íâ£>‚ï½ÿ>4gNCPg!%í6Û6Õ ä€Ñ i™PV‚‡‰™´ô°nÄ8Mh›yžãììôV|ð‡IcJåN-ºpEúÙ‹(Nr¨"%?¾K€b:\#Ô7Êfˆr¥5½ ZIh©¹ø K5*LŠ"G×uØ[îáÍ7¢¨*ìÍ—XÌg¿ÔöÝ,÷WJë™´]È‹Š#: §ÉÂh>9«!~ãŠ\$ÀÛ7‹œoò8€•B¢i;d™Nh§˜46 ‰¬JtAc+Ò7ñìù3¬®®þ~ëõƒ°*gx÷Ý÷ðÆ›ˆM§,CYøø£qûÎ2{sR”ÝÄ0ošÃ(H§Žs¥DŠB%W A$€¾ïÒïd™®ë(òÒ6‰O˜,¹ f3ÎAÁmLÓÄÝ$)899ÁíÛwÐ÷ÿwɳjŒÇùù)..naoï†a`´“I‡ÞÐ(Šž“ª¼‚0HèúÀ’©0xZäL–Zg·Š”ë IÜä”L9Ùô}À]ÛÁò‚¢È Œü™E‘³·¤P’šyQ`6›aì‰h² UUÓs;ŽÐY†YF³; °–lW]ߢÍZe”ÈF€)FUH2í—EAÃA ‰•*1L=?_=¼s( Ò±®ÖW(B<Ë¡ ½¤³ªÆåú RÐ,I)E/ã4b1_Âña8d3ÛŸÍ!ÁdüçÆá!~ÿ÷ÿʢīãSäY–^ÌÀ¹‹¡Uë#Q½1xóÍG°“£çå7küèG?t}‡Ï>û¿ñ›3Ìf5¼óFfN„ú2`½Yãèð0ÑrÞzëí4EÜ ¤Dßq¹\ , ãüü‚ç䊴¶g"uM7ŽŽ …€õýàQU%^½S¥u=jxÛlÑ÷fó9¶› ¾(úÆNe]p0Wޤ˜˜•¯åÞD½aHÞòˆ ï'îiü&ù@äcxH•%{žã9¦i‰ëÙìTÞ~ãßÛ?ø27Ñ%²,»TE°|LÉ”5¢´¤ (N£¡X‘0Æ‹g J˜‘$:‡¡Ó4a³ÞpT(“q2:Í£Ô$‚ ví±g0f‰7=ƒ÷‰<)Ë2¬×+|ó›ßÀ?øoý}>@¦ôïFw8טÌí&U«qŽxn¹„q¦J³ÏÀK&Ê| ¸ÕŒçY?`2²/_‘6ÍUUãþ½\Ù„4?==Ár¹ÃÑ Qˆ Ïž=ÅÅÅe2öCÐaÐ6 –#ñ6|èéÀœ¿¬Ya2¢£ô~>Æ0ŒØl·$T,Ú®…uÆaÂÁÁêùŒ¶€ÅbA˪~Dßu`ü½V˜Ík¼|õþç?ÂÅÕ9t&QW”üXV €÷ßßüÖ7Ñv ÇnP·£µ†6>ìv4)îìÃ?ct*yÚ£k{µ*d:GY”pÖb§T´(.ƢƨÚ"ý94ÒŠc8zg¡Á`^†7€„ç±:/²³å‚$MJ!/Š—rìïí}U9)rxÖDe¬IU–Öäj T©ûíD "È„}·v"‚‰`r…iKl4Ạ€¶éáœGUÓ@ºë:Á  (J%qqqŽf»I‹j]wK“²¬ðàþC, i+Un0´í¹4¹Árùùb6§yŸÆÑ‡¥%ikáþuÊÃ%œ<±ú”à®m8uDÍÄ#´œÁA'·³ÔreÁe ü9=?M¢×c#¶éÖí;xç·9•â©u˜€ pëÖm|üñÇø§ÿô"Ï)ÃØ{ÎEZ¥æJà@lq-¢2$mâõ™ªçj)Äù_¤ DËR\Åÿ¿ižuìííãáÃ7R5§”f-Í8¦©‡1³šn±““c´]—hÄ”S!àQÃ3—h"e”I³Àq¨Êç6óˆ#„€¢(™NR°h–´¤B’s€àu9ã¤CQR AYÖJÚ¹å&‡Ñq(ŸeÄÕúг°É yY7M„ë;"y‘6ÂRRLÅ Y^ á-5ñ²Â$Ö¤§É'ñýÏ?Y‹žù´Å<çž’^6Z÷©ß£i¶°Îbÿ`BKXKm¬É4F;bÛ®1M#¾øâsn÷qÿþ}¬W+Œ-…˜FËd’xàÖÍ›TYq95qüçüXKŒCMÿGÞJ‡áf³ÁÅŤVä8qݺ®EÓn‰2Íôhê~¢Ž2+ ž|ý>þø#ܼyïð>îÝ¿‡ífƒÏ>ý ——é°QJ#Ë ¾~ò5'd’JÄY‹q x‡xÉ^ÏŠüd'ô±';bèŒc$˜“’¤®+t}ŸŒ‘%àuyF‡é8RŽòëð•$v’#( 6Í]‘ó±­Jb±¿¤åÍd!pxóÆW²'”Eu:ŸÏÓœÏóöÕ±ÈvÇ4p¥!,å*Cˆt«ïá-ù_µ¢ŠH³F*A¨®Å¶Ù¦ “T Ã@Z´š!mÛ¥…Âv³IŠòøÇD³º^àöMZß÷CO,>ÊïH9Èd~ïÝÇxôÖ£äÉ‹ªâ¢t†;]ó:Æj‹Å¨ôÐÚô‚Æ™j–åÐ:Kísžgɦ³~I˜¥C²k;þ÷ŽŽnb±X¦‘„dâK]ט&‹Õê J)Ìfs¬V«þÔLN1¼%[¯×h›B’ŒªF¢³@J—ZFÃÓfb'©Šóì ØlIð,„ ò¶¤ -0umüˆtK²˜ùb‰à]šYyGqðt°Úk§1íO^7ùñ!§„©»»eNÇ•ZQÌstü²Ñ ¥(J¬×+\^^âàà JŽŽX|>áÁý‡øä“_rZ˜J1˜óù———0¦!ú3..Îqxx€<Ï)è§§Š¢ÈsLÞAc'Dw¼Ø  j5¢h¸,bµH)!‰ˆ-AFc…Š–J£ïÚÄZ¤í<áa]@ž—Jbâ@§®oXA !AseØN (ý€"ÙE1(cš,Í3¥åì–R ªDð@Û4˜Õsôªgè.iÖÎÎOðìù×É'; =êºF]×0&CYV(K »oûž3z$‘Ë‹œkTYµ#Uê>PFÈÄó²ÙlŽÏ>ÿ5_vûûŽ^Б«r‘°–ÄîEY * ¼|õOž|¢È±X,pv~ʪ¬½‹úݾGVÐ%¼Zo1Ÿ/’ïêê ëõóÅ<)0hÉDr¦è˜qÖa˜&d,B7’|øï¿ÿîܹƒ?ÿñ1ŸÏPqÕŸg9ÆiÂv»ÁË—/Ñwî߀·ß~DšÇËKª®@¼JÀóåÒF8.B*TU‰õÚ"`çèªê’©øp^ÐÅo-šfËŠÚ–¾–¤`èûd¸ˆ»ŠÈ'ðÞB›*-7¥¤0¹Ìd1 hÞÞÁ(# CË-;ÙsÝl¶0Ɯݹ} ™ÖèFÀa<9¯°mÚ¢C8¥q¤ÌÝÀZ hÆÔu=ªY‰¢Ìé%’±m¡)Ï 4M“´~FH†*Êu=ƒ¤½»¶sçªmb2õ ܺuÊ(ôÛ8„A® r—Xç¸<¦ò=n…L×1Iæ#_KNÛiE*ùicEÒªRÁýŒg”"™Î¥p<Úèù{¿÷{899Áøgxã78A.#zŒ¯°·ˆóóS@ÑrµZa¹\Bk¦i1Ÿ“=mµ^¥8Â5… ÿ·‘™1¹z"ˆG×wðÖCñh#Jƒ¦iäyT@×w(´¡ï Øé`‹¢€òŠ1e ¹D&²( ®Æ´möp‰ºã=’NOªè¸hQV3r•‡ÀÒ™¤XHÑ®H-nÛlÓÅ#µÄÅÙ5_¡i¶éçøWýU%p°¿£Ã#òÛ…Y›´qÞ{EIÔhk¡ Íb|¢³DRÎl6‡Ôš"+xžé섾kqtó&¤ÎÏ/ðäÉt]‹ƒƒìííáî»X.÷ðìÙÓ”™=òÉz ãI<üêøµtÛlqrvŠšg¶‘À˜=I0YŠbè‡2Sø9œÏçÈ‹˽%‹¾úò+r‡±³I²!a6Ÿãý÷?À£#|ýäk´ EI4 üµ éÒõÏ9¶â´¥E_ðDÌïú¹! ]Ûu(ËmKÊ‹¾R*á8N©ú³ŒL‹žâ¨-ô~—sª:MÒõFWX Ÿ§¹BðŽ©\t–]ꀀùryvtxDÛÐ@Õ¡uÚëk„hÅÎ…€<#"u?Œ˜WsŒ–òŽ¥Ð$QðBÑ2AIÅ· Ié÷+tm‹Ù¼F.3›¢v¯a~žx-¾“²; ž?†ßÿýßÇ·¾õ-ü“ò¿ÁÁÁ>ÉLÆ]G€Ûa±pˆõúЉ;÷O]ÏÒö5ŠFW«ö1ŽCÚ¾ ¥à­'á¬R;7 œ€ðÁqûF@Q€l•³zFÃí¶AžxFxçé¥ë=òLÓÜ4vÒÈeUcdAì0ŽP#‰z½ 0™JŸUbC? HŠ”Jb–רl74 ”ù„„QBС­8b¯z™ ɨëÖ›5¾üê+œ¼zÅÕïÿÿ¿ú¾Ã‹çOñêå âî½{˜Ïç4Þa¹X욦¡ð$¹×Æ 3Ã8ܘÕ}%€qèQWÃýâ‹ÏX¾àÛßþ6¦iÄþáâ7~ã7ð­o~ ¿úõ¯P–Ñö]Úº:OöËHm¹ÎìôÞaG8ç“ *ÎÁ‰*Cˇ¡ï1M¬s°]‹ù|õzƒ?þPRa³Y§MíÑ#ܼyRJœŸŸá£‰ÀŽŽüå´ë…u¤•µÑRÇÕŸ`_p-^!xEä8N¨«š*¿‘3–8a“—’cèû,«‚†Ók^¥I1ψ‚Ó <_¢tySÅqöŠe7QYWÐu¼R©õ8Ž—zy°åbùó£7ž­È”KB”"/DвçØ@ªò†¡O‡¥uFkÌê ‹Åççg„¬’db§@rrc-+..ÎqëÖmà ˜¬ƒPã0B¹8ËÔÉMãx>7±è:b¦¦É£®*Ê`GT5UvWWW<›4èû]×á`ÿÓ4Âzh^à<gêjŽÉ²N2ÐlYfv°i14LJ•C®k1iƒ<'1¬Re]Ã;;ðÂsÛ9¥§ DBZ"•Øl6øä“_Ë(6ÚààèûÈ8`i½Záôô$]×+þÓ³l·Üð7oÜJsʘ¢µDÛÒÈFsÌ­ÔëÍÃZi¦ X¶´ ðòøš¶CQäxã7°\,ñüÙ3üÅŒañ½ï}o¿ý6NÏNñàÁC¬W¬7kÌf5“Á'V?D(ítíR¦¥ˆu> ’64šèzTUÍÀ‰€õzåÞ’4k!@ì§Ÿ~в¬°·¿‡År‰åØ‘5â‹Ï¿`©À4‘RDH‘¥["xØ0a³ÙòœŽEûA$ýïÞÁ>¤èû¤-OH> ª*A¸D²¡€nš¨CºZ±W=¤¤F:è= Åbÿè(‹%ªw"L¦QU´V8;;‡T ‹ù<1Wó,û\kµÑ—çþèæ_Ìf³«-×(ncâfÔ²ŸP “+–^„ rEŸðS€Ö&§lסŸ`¸/ïúóÙ AzÀ"Fh6U ËrÔuÍ€J寅¸LCyõêU”š«A’(9Ð÷¢MùÑþǼ3Ž@¥Ì‹ YN/;*å’ÄÞ{‘ôQä‡äÄ/Žw$[›ÚÙ™3(*Kôç ½è1›ÏqóæÍd9Š[xÇš=ç<ªªÆååEÊ<¦\‘ ËåNOOß­ë:l¶À{p+ ŒÃ€"áèkM¾Z““¦-¢ÒÆqb³A"¥åE­b^dðØn×è^–臎‰iGƒ…éåš‚Eד3"Ï ŒÓ@!vĬž£k[(­°\›O@!ËrÎ1TA8ŸPü£0ŸÍ“ó¼õ¦ÏKãòò üËtQÖõ~ð!>¸ÃÃC”=/JÒ,°ëz<{þ }ôËd™‹?‡®ïx+,p÷î]ž¡F>/*X»Iike^‘¼i˜È²Æ#„¨9t’€¡Û¦Åb1Çr¹D‘ÚîâòÚh|øqûöm<}ú?þññ÷ÿÝÇããO~‰iœ’Š"Rä/ã©è¦÷…úk:¬"BàæÍøúëÎ/ÎqãÖMwxÏ8=›:£Ó“S~Ⱦæ=ÃÎŬ"ym±gTx»mÐ4du Ž·ºÖ’\j>[ð2‹F!Ó8±\IApd§àX×à)àL¸F1„Îsô…ut(_w†Å­2‰¬5ù¬ã®BkòXóLÑ$ÎÒ<n´ÖO@ Á{ìí?YÌæxê_¤í°CHøzÏÞÕ´XYN­y]_ÎRŽXa:ëa !%ù–ðÈË vòX]­QײÜ@+‡®íp~~žlw;Y QEž<ùÛíe1Ãr¹DΦª"*vYæ¶„NÛâÀÛïÀÛ5c êY…ív‹‹‹ Î’ÕIåþ: L6=ˆdC ! °H9/2Þf©kK“]Q–]ºžÕ˜/æ t®Ýr×ÉËóÅåÅ[õDRá/—KÞ [ä9ÍUú®çô@úq(­‘çD;Jò!K‰a( Á>QIIm†gLD@±, ¦Š=/Š´1ÜÙªHŒ=Œ¥“1;ÐyJÝ‹—'X Ìæ3 ýÈ™C"çyAŸ…™a…U[ã0%|äÖMœ"bŠÛ{ï¾oû7pãðí¶ÁÆo`4¹ö ܽûÛø­ï~?ùÉOð¯þøÑ¶ Ô³øôÓOà½ÅÛo¿ɞخïRœfÆCü¾ë0›ÏܺÙ†Z)¸É¢ãŸ™TOž<ÅÍ£#¼ýÎ;xûñ;4›ï:ü‹?ú#\^\ààà—«KŽ@5 f+šŸÒ.^±> -ªOÀÞn%‚@?´(«÷<À¯õ1šmƒòèðZÜLôù L±£cK&u.‚ãèðð|zO ɹ"ý°M£&²ÇΠ ͺ#ĤzePUUbXK›ï<Ë0Ž„Ö4[8Ox6ëh®>MäM&vbx­H‚ˆ_o ðˆ‰a)˜ÎT9«L(¯½*KÂb¹¿ütyxýü«'¸<9ƒôþÙÍ7ðñ矱d„๒1¹ (M•¶k’¨Ãd”×iÀÙ6d¡*«œ3)=Î ïìH‰TEAáÐÚh”e‰aèyÃ)_c½eY‘ZÎo~ó›¸8¿Àf»!Á¯u¸º¼LÇ,'{•–%c¢Û$çv3VmÑbVÕ¦q¸ö‹k`Ò/.ǃßiœæóyj“Õc+«Ž<¦)ð?“5Vã{OáB‚~8³Ù<ù“¥¤íòòŠ­¼ç¼á-éɨ•¡?ÓšvK-ð8Aeô Ò€»ã@ÙÚ(X ˜@â¼tÖa=šmÇX( ,Khe¨Ò›Ò}M´20•AÓléß½– ?Š ¤´q!¥@Y×)ÎsVÍàEORƈLȦq¤L⪪ifª Øð«_}ŒiPV5~ç{ßÇ;ï<†wWW+vü°n²ÌyyæÑvmÂèÿÍ¿ñ7ðàÁCüóþÏ!¤`®$ §?ÿü3äyÛ·ïÂ3¼¡Ù6"D²ë6Ûmòb{Ö)NÖóeA-êÈñ mßÁ:ÒÁþòW¿Äf³wîÜÁÃq|üŠÊy‘ò:hl@ 82$ˆh¿J„¥œi@QNÓu=¹^f¤ö¸uóž>y‚óó³ä™'«ÿ+™Šw#瘄Ð÷´x‰3ïIÑÐ4Tå ›í&-µhà ©R:8ØG°X.QTIAˆäT—ïžS…ŠÿÛ,È®ëgû’Ô(.zâ& £ä ¶à1ó5.¤ˆÉæQ×·&Í5£š_JªŒ##Nð ê ÃSïŒQ¼Œ"û]ðä$Y,2pÔº ý€¬È‘Õ$>uÞsÀhJ~?Τ†0ÀNu5£Lå`'ú¬}ð0Ya ­yƒ¹„žÇqC¨u–¨"ãDN‡€](™PUe"-s!PyÂÑ Źj¥®>f³ž={†Ï?ÿï½÷ªºFpëÖ-”e…Ó³SX68aÚ'¸ª$²Ç!u‘ D[r…Yk1ŽŠ2G‘ÈòW«+d™ÁÍ›7ñõ×_cÉCA)# U¥³5.#âr‘–LCúç‘KHd%ÚØWUÍsæ!U’Áìííc¹ÜÇéÙYÒæÒQ³‘€f¡o–­›R\°ãùaä5 )Rwù…!¸×:7úK6J0Ê?PÎt^ Z $û©åŸQFóË““S: ó<‡~u÷ÎäYF›\=Zk†prQÑÜ*´äAV!yJ«ªD&á¼½óÈ ƒa™‹H Ï †qÂêê GG7x«¥jê®H!¨¤ÀÁþ‹9~ö³ŸáóÏ¿D×vi3=Ÿ/]ƒ°F‚IÛ¶Ô"ƒ*©àŠ"ÃÞþeNAé—«©ë•â3–ÝvrÉC³Éu@Y”,îd.g7Ä/fñÆm*ýÏŸK­Jœ7’ã' ªˆg¸Ùl Òçî½IŸIÜ6wm‹ÅrI:ªˆ¹¤Í§1mÙ*S³)ÞóbÉñÌ„©¢®0öœ%ï²µ;ˆlžQ59ðßN™¡t7¡=‚ Èd†¡ïà‚‡–Š6ÖBq~KH›B “)87ÑöÔ.™,ƒãCÔhƒõfMúz–†úJKœœœâéÓ¯|øްÞlv.Ú$¥TÜ4$Ǥ€ ”ï»^o1«KìíáÑ›oâüòßûí߯ó‡ÿŒ7È[œâÍ7ßD³m8´Êqr£ÅjµÂÞÞ>ö÷ðìÙSl·k4Íóùœ,‹~ägXàåËWäw''Ç(ò‚ªü¾K„&z>v`àà„’œ ´N/ÃdYŽd¥eâ"Vu‰<'álßwìÐ, åEKÔ†Áî™x3³Å"Íl<û=‹¢Äl6'Œ?·êÃ00 !c× Uá—WWôRhcrd†’ç&ž IA"ç2+ÐumŠ,µv"ÄÖHÛø¶íhFã” ìáõLœÉ¡´ö2å¼ÃØŽlM3€ @ -3Δ äÄi°(+\·]*#-#ÍéDò;0l· ºçL’G-á—_~AVÌ[wqûÖ4Û µv™bû‚QÊ"„€0J+(AÚÓ,Ï0𶇠ï>~ò§§hÛ¾™–+ÏŸ?ÅÁÁ!ŒÑhÚ†ñk4ôwÖáôäu=ƒ1Äìºöµ¤?g=<¨½yó&´6xòä)Ô.›Á<ØÒ¼Nh’§Ðl׸¸8OVÏø8Íf3ìïïha´²Œ¾/)%<(^ƒ"Þ}÷=üìg?Áéé nÞ¼k©í¦ñŽcú4`màn¼á¥91]WØl¶(Ë Þ[æ"¹Pàƒ>Äb±ÄÙÙkiñ•›#&³;R5ø.LÈT‰<§ÏXk…<7°–æÌ12•åQ‰áOµ‰&7PʰEÁdŒ’0ZqNG×Qu½˜S–Q^ä0&û\*õ+k-´T ’*ªoí-—EUUØl·˜X£S䇶žQK¬¤âŒ”]4h&rtm—œ´åÛåµN,”œ¦ vrÈJÆ8ñÚÞZ‹«««kÃÑ1Ûè ‹ù}ßãêêŠ(&R¥ßCBLPR\ï0»A±÷ŽCà)ùo¨Ò'"mG=^¼­’çX댳[<“]’Ûh£1_,0ö#¡¬ ”O“EU•I·(%ÍA<´c ¾k§CÚÜñ㢯8ZÓþ2$ftA¡\ã0&4U\vÍêšâN-á ”Œ¼F‹²,©ý$S軆â9YÓayáe†•eTéf&CÛu$`öß4È´†Q“R†>Ù ¦ª1«fÉÕäœc€)“vø3މ82K—CY–(Š"YIbõ€<é]GÐX(Cn šJHŒÁ ú¾‡wZKtÝ€,3Èszqìäp~q…‡ÞÀŸýù0›Õ×´ˆ=NN^áîÝûÄŽä……bÔ›É4qûö-<}ú„HÓõœçÀtÈ‹°¿·ç.//h!!„LLa|½£ƒS)ÂçG/ö«W/1Ž=ÏÓ‡4{ïÝ÷™ Û-e“¬7+@ÌÐwŸMDò> ï¼ýöÛBà§?ý 8g);%C&Ñ™| <âœÅÄñä}¦YdÛ68;;Mï¦åúýûpëÖm\œ_&íaŒJA‡€íf‹ª®`½|@]ÖŒ2“PRs˜œVãHóÄq;!Ë—À[dmØwOÕ?½"$»éz’£ÚH µÝEQB ­Ô¯›fËé}9ᡎŸ<ËídñèÑ#Þ#Í­†aH‚ØÒ {Šå)­óét~bø$Rû<¹O¦ÉrTÐv=Æ!nËGD\œø4/4†H,%3#~KòûzÞbd`EoÂúcOíÝ0L°vLÊë²HѦ¡q¦i8ª’(Ö±$×,cY­64Ë©µX¯ ç_W5...Y¿(®QÀ·KŽÅ±&1ó¢¶1/r²¥‰k¶@rÓ|t'å 0ô)mñšfK² ï1tìäøÆæY\p˜Ø’gã×â¼s”‡|rÄXkYîdÒ†‘ü±T­yçhþÊV³œcCÛ®¡…KQðP\åÓ€®ïIÚÄBHú·˜DH.0‚HÖ/_¾€sGG71ŸÏ°ÝnHBa ŒÒhÛÁ“¨œr2HGIò$-;O ïÁTí›ÍeY!ÏHOyïÞ½t ^]]A0F-xâ8ލJ‘ìãáÃ7l6kå’é™Þ=õä!<•s9˜ÊSä.//p~~J0\Ö†:g‘gxÔ³9¥²ôh8ªÂ;v3­ñàþ|ûÛ¿‰Íf¯¾ú"ۆàÑu-V«¶›-š¦Áf³FÓ´hÛŽgý ¾¸8gëŽ}Ù|ð!-†¼cuÁVó‚º;M‰J‹Y‘²Œ€i˜èAØ´¶íøY)_:.#Cˆ HÞ'H ŒàEçu‹iœK^nÑסQTÅ©Öôêà<„’xúäIhºo¼ù&þìÏþ Rz&|-OZ™¶‚‰/´j÷P ‡uèÊÁ™ÎÃ>ö4ËÀ8õ4;ÊH—xl6kGG‡Üú¹”-j $´ÉèF±ã`“ˆZ* mr&âRºLËÉ5Ú\ Íš­J((-x!ø@ýõz®k! Ë3Cr€ªžAA¥ÀïÎ/Ι÷§’ Ž–8gggÉÑ·‚qÃZ–°)F3Ä$胃œŸŸíä¼}Sj§ý”R0‹“ô\ZB‘i…~`8ÇWk­ cŸÜ8FëkòY=Cß¹Y*-) Ú¼ƒÍfƒÌ˜´•–’¬OëD‘à*bž‚»ûž®‚Ü9¡IÒ…äDM¦™²ÖY: ûÞ²Ep Äç˜pSA  ¨Bœ&‡ÑZVéB:Ø?$W„@B˵Œ{SÌ)ìúGG7pùÙ%²l‡ËjšÓ8@å&æ=f¬]Gúß¿ÿï¼óŸ}ökl6›ty8áSçãM¥PÉeDË—> ú§‰†üëÍ_}õ%ËŽ"ˆ«Â÷?Ä|>ÇÉé ¤–ÈLŽó³SäyÅbq±·¿‡““cÔuE ,G™ç>ÄþþþôOÿOŸ~ºžc±X²é@q;’{…*ù«ÕÚv›¢A£Þ~û1¿ó˜@‘H$%Ëòhda'—#Þ“kŒ8MßaVÏØ*jÑ6Í4‰€4_O³,Ž·5J¶‹<£ idØRLìËqœ µâ!3‹Ž+¶*êízùþ^>ާ_};wî!zš ^ÃÏtBÛi„tØ…@ùZi(Eƒn©$rÆ"IHHCö)4tF·•¯¯XÔé½GËŠþ8#ÐZAK•DÅF“89pÕEaô’É·Ø¡²¤@ +òµ n ñuðPì¦8BÚfŸ¿Jmz‚Pz®°Ý6¸q8<<@×Q»¢ m嚦áta°ÒâöíÛèû>AeÉÛêxÃÚâòÒ³W”lj€– óÅPJ$ÿs|”Ò©mŽFôhJ7ÚP²”¨òš^2êÂS5J‚p‰ÌY¾ïÍÈ‚X,Ø_m‹²È1iÍ3—ŽDÉr’EI†xïÍß&É6Û †„dì«CoÃ$ÁaK‚u•qY@ÿÜ&d%ÄeUðMÛ"ÂÞ)å>ýôS|ùÅØ?<į¦_¡ÈKNJê=z„¯¿þëõ%ý<PÕ5D4Š/µ2ìÒßñ:cü,}f9®./Y€ï°X,ðþûàäôMÓ ,KÚ"WDËƉ*dpðÞ#+2xï°Ýlquq…ýƒ=ü­¿õøõ¯Å |$)Š"O(£„\/ã8¦Öù:Bïððo>z û{hÚÍv‹Ù|Ži¤Üž¨GÕüüH&$E›6šäxæóš…õœ•ì<á¿úž-³¯„Ä0’æË&V„‚¼ôC?À˜,ñG#ca6[’µ’Ÿß²(ž)E¨/=v°¿‡`ž>}Š¿þÎ[”ѱZÓŒ(/XçãÆ*…¹d9œcÛ‘tFs) … LКn ?88ë!t\™Nr£@ñ¶í ¥IKÜÎj¥0ŸÏÅQ£‚·¡–Sâ ‡õhjÏåÄ-ºLYÊÊfè-?|´u:??ÃÙÙ ãû ßnê Aáää$iµh1á’®p‚â² þgàY‡ñúÃУm;,æKUA¬9ŒÓˆ—¯^àüü4éì$ÏEcû9ˆtPÐzxh¥1°<¦(v'yN¹ÊÁä%¥GBù{nu¼w(ŠÛmË’<Íqnrm~©:qK§´B&3t-Ñch[IÉk1)‘$Ä7”*΂Id,Qˆ…à¬m¦l6kþ9INVÌ`Œ&°jAYk'H­$0«g8?=ÃçŸ~†>ü™ÉRŽÇÓ¯¿† ޲h qzvŒw¿‡´Ò©Žº¸<ìä ™|ܘv]ÏckG\^ž¿F„ùó÷pãæm¼zõ’Ó)&#˳”ýY’á óÙŒ:)G—³µŽ–¢Öáøøf³9Þ}÷]¼ûî»X¯78?'÷S–zoM–áUUa6›¡®I¦5ŽÏ=‚Ï#D!Ë©2³£@‡™€ÉŠk À•#)Œ2)$>^¨$§±I{ì=xô&­ µd_¶ä÷’6ôÎEQ$ÏlV#7y–BŠGvWéq`ûJJ\]®!¤ÂÝû÷ñÅW_A 2õ;7A©,µŽÑé-ñ2K1J îùiK$„ gÀ0¦ðøäaexìb±à-ì.™.J^b•ÛÄ(· ’ n  3MaE‚âI}dkÍÈü‰[j§‡vÀ8LIìœeyÊv–’ðA„êÊÓf8„;ĹbýÙN7(Ó¡Ežà”³‰©Î]Ûãòò «Õž={–€Qû‰9áš 0j¿â<…„ª. “sÐÒ$h¦å näå€ëW>y NòQp-È«lxl1 m!1N{›5K<òœ2ιN´ÒüÏh.ä8Œ©hi#•b¯1UßEQrÞÅŽÔ­ŒNV¿ë$!HÑ2193ðV¦0,k-êz†—/_à«§Oз–Ë=¼:>ƃû÷ñÞûïaoZÓ¥à…‡½vÁá5Q4$‹©sR§ÊÌYjù6ë5îܽ‡?øƒ¿‹?ú£ŽçÏŸàèè&f³y’Ÿ87¥ç)Z ®º!/øç(p~v©$Ûé0ÞA-µä‘Q™etðÅ8VÇF"øú_Qö”eyrdvÅd&Ã4ð!e‡'ÇÇX,ø;çïâÉ“'øÅ/~†³³œñ€?Û‘’¼wxpý/¥4>øàC|ûÛ߆µ_õ5ƒ9æ3ÒNŽ#],‹ùÉiÆåAóùŒ€¼´¨çôÙ5Û`+çÐØ?8H3ÿ¨ä°–F\BH²Èöo5GÀú”åL…„I•ûl6C³%¹OlR (‹(™ÍfØ¬× 6·ÓฟK?ëp-"8­)oI+ƒÂ¤oeÂ|ÁÜÔ*k.bô·UY¢®kš%—ÅWEžÓû @à§o2“Ã{àÖí›X.Øn·Ì¥£!rt%8F‹Eq-¸|YF¹¶Ã@ ïåöz‚‡Æ”«Ìäè¦Y¦ÑõÖë-îÜ \ êäC¤8¿8GÛµ€(«,ißK,Iu¼Û¥÷Ń8VSY–AåýÐÁHÐÅr£þˆÂ•TezYû¾C?Y& ÔuÌáà4°ëEÔ]Ò.S A¨!ágÉ¡ùˆ€L7)þR»Fÿ;ì ×H²NÍf3˜,GÛR RY•h›ŽÃ”EÞqýz°VßõЙAQ)‹XkΓ¾l¾˜“,ìœ “›0õäX™ÏçéR”Š´DXæÖ#3Œ^ÊrÒ„ª²$²1¿MCÛ[ÁÆy’gW—¤BY–£,KtÝ9oË$ïBÀI0NØŽ æó9ö÷÷ð£?ûªºÆÂ:$8;‚èRÀ{ ºÌº®%:v~ݘæ6ò²I*®x¶Í—°Ò©uË2â4žžœa½Þàí·ß»wp~v†—/^àå«—X­V†¿|èR¥sÿÞ=<~ü.êÙ EQðe…¢Èyž l›†—#š|½ÃÈóÂ]Ë9ŒìëÕCMmãÄ‹ÙÄÃ]Ðå1}7`¶ ÏñÐ÷(«=oÜ ‘§%`ê˜bÕHç.½÷üæ=b(Ž€€Ñ9 å…”(L†~èI’fâ!q<‰=é“á4P ¢ÿJ hc -üòœŒû’µVúÃ0Q m¨MžÏÈ2ógmÛ`dh®Ž3k-ò²Ä¯>ýß}Œ7ÞxÏŸ½ 9€" Ê4M(+ZåãH-ÏúžÊÑ¢ÜmÏ"Me6+ ¤DÛÒ -¡A™Ó8bµZ£,K,—K<}ú”¾){áwãË/¿Ä/~þ e™àZ“nIIšcX¦#Ó¦9ÀCÈÀÒ…À¼t`Ú‰$™Éq°€®ïÐutdÞÁZn½84ªëzܾ}‡‡G8??§*ˆà•”É0g[Ñ­B$ï1®1騭DÚØG}W;$’È_jÝb„€@UVé{ÓJ£oG˜Ü@Hš'–eÉ[ÏCß#/rž ÎÆÜ*Z„1°N+Vÿ‚óp Ðz£5šn˰LÍZCJ;Œßw×÷)ßdÆòŽàC²÷,pBÂ3Ђèè´ØŠÝÈœƒ,ä­qqq޾PU6qþ✰( ¬®.QÏæØßßÇÁÁœwXoÖ»à v:Ŷw”ý­”ÆjµJmè.¬\`1_¤?#Ë2ºyï Ü0¢ª(Þ4Bfëµ°_|ñ´6¸sç6îÞ½‹qœR+LóÁÎ9ì`oûí„ã—¯prrB°S^,¶mŸ„òuU'@„wž3O[F‹ºLa8·„œ0žSCÈó ¾®ùòT]Kdl“ñìÙŒèºÎi €}Ëôlj&Ú=-i¢ê€äjúJ£‹,ËQ乇  «à'8»ûZÇ5 »ÞDªS]CÚLFð­ù2V$&gÉ À «‰™ UE6×ÑŽðð0Yöç|_裣}ܾu„å’*€çÏ_ ï{<~ü.~ò?Á0tDº¥ÉÔµ—rGLFaæä+ÌPè¤#´ *]×A* 8‡~$<B€6>鑨Mó\!æxùò%nß¾ƒ·Þz ««ú~€s¤}RR§¬)”$%9™cÈGš9ŒÉá¦À•¤I/Y–ç4»D@U×èú>=¤Ã0B) ßÑð¼i¶PZâáve»¸f““!¸‡×Ìï‘Ig¬B²s±ÈÜÇC0¼n·»^!ÆÍ;=äW¦ xGÈÉ8ÃN#„å¿—iH¬VkbÓ\(zl—‹Ɖ‚艂,áåna£•„ ~rp½8ŸÍa'r9Œã\Ú(ç<4 ñÐÕØl¶ü9SK? ”)„ä“„'@Dð<[ã4Á®Ã7pzz‚¦iP×5Ê²à „üîmÛb½Ùàðð_}õ²RÖŸãocÞtY–R¦qÖÐ3¾Ÿ EQ°“¤‡å"M–¢Cª²¤4ÇÉA@"/h|a奊Nf”¨]/ (®X‘Ê€—QeU ,òôŒï–ŽyQ`FîXKÒåÌhJ½òa§!Ðÿ¯ÿêiË›™×”Gòù—_âý÷c¹\âüìŒaŠ.m9£ü„,8‹­i¶¦½G‘—iÉKä–(¿ÈŒIú;²‘Îêàà³ùœ_F™ZˆÂj[ša1‡´k>U=&$Y×Ú6@Š&3†}OZ)ë&+g“#îÞÝ{wPä^¼x‰‹‹sz0‹õ~…BJA´ŽA•q™Ãò)iúø/Z0çÛ)7N0:GžåF b:·Œf·@Úù“‘Nñ³É²Aâœ/…ÐsFmðTUäEN2¡®…àYÜ8L¨êUY¢m;ò2{Ú2&j²ÜÅ"Ppûg}ª¶…Ü(HwG|Ê{÷îáìü,]1ŸfÉ2H˜© S–h©–ó(ÀÚ]0™`h±·ڶëW/˜h”¡m»´\:::b;×îÏ"ˆ^²›#Ë2ÒñŽšMÃò 9ø%!¹ÉPä9ù÷Ùs,ÅyjMZÐ8ÇV’À0%SBßõä¹Õ*1Ú¦Ãb9OÊ fW˜Õ£»ÆRÕR{‘emu‘s–Hf…®áÑÌÐC+…²(Ñ÷#œ («T ]ÛsvzɺUË]ަ¥ ÿlÃãÂ/ÎüwïÁNƦ•FÅn%%´Ò˜&‡<§QŽ&(c`Åv»R’Pw&G€€1úSœŽÃÞ9ýî·>`±ïˆ_~ý%²¬ÀËW¯ð?ÀãÇïàÅó眔G_,øÜrÍ>S™*øÀî B€eŠ@¨Ó8¢,JLÅSÆ% Í‹œ¢[aooJ*ŒŽf%]×àüü‡‡G¸sçU®ˆcŸÄͱâ²ÌQóx]ú3YúïÎÇâèæ r‹8áÀ3µóÅîïáââÁ,÷ö pqq‰5»!ŒÊà0N# Sm¢\@²à9FŒÖè·O3ÁJ~ÃÈ$ËY$–ÛäˆNw׈5µ#À±±( ª i‡DÖE¶È~d•Ô<çr鳉°OrÇ (Kº¬&;t”¡Ã8R¤$G! àŠËù@dôŒ¶ƒÃÈ·. ˜-5W;ÖcÄŠ9ö S¥ôÃÀ”0²}ÂþÁ!qqqNñœì=QÃ4‘¥0DØØ&ÒÅKZY‡q¤*úÕ«èºûûû&Ïá½{÷ñàþœžÁÚ‰ÈKÓ„<§ù÷héÏÙ¥e å“+¤LzKÇ0ݲ,!¤H–Smôkù"‹åœÖÀLOMn«øóÊž™˜ñçÜûfÊ8"“&›õËåÎ{\^^Ak…›·n`½^¡k[Ôuª®0²eT2m(¶ð)ï„!¼‘ôÍÑäãiHàDàEµÚÛvË¡q9®Ú²Ü¤|r¥%¼£}BÌ(2|h_]­0 =?Ÿà,BŽå–K…*/iƒ²*YWÈ–FÏÐZv¸\­6hº™ÖΣšÏ¨š/Ë™ŒñÙÐEž±eJ’•NIôA:½õþäOþ~ÃðxåS»V•llY½Oú/šÓL¨`ŒIŠIÆŠÁ“1D©»¢¨Â;÷nco¹‡®k¹'ÃwÛR¸Îáá!Úme"°Ä‡œŠ£ÁAGc¹|RÖ7› ªº‚Ò õ¬Fžçú!‰Ëª"¯£ŠÈŽD ÷ I9rn¿&FHhìÄÒT=x8¶Gd{¼…–{ ÌçsœŸŸ£m·˜¦!¹3bôž×}É×Q^HÙ½³E.ýˆ<çÌZÿ³g5‚ ±Ès’RŒ¥áE©ý]ªÞ&7²äßÔC©ÙéÒ÷}Z$Å¥Ñm$ëÁ¨z†œ¬c‹(øÌèDÎ)ŠŽÓÞ”ÒɪçÉÖ:t]‡wß}?ú³âääîܹ—F4ñ¹"f^œñÅÅ”çN†*7>›quu¾ïpxx}ß&˜†T ¿‡À.$  iZTU‰¦Ùr&O ëmò¾ÆJ–BÒéÙ‹ó»,Ë1ô=¶Ûk\Dþâ¡Óu=Ú¦ÃþÁÎÃŽD¦¥g]׳ȜÜ,Ä2äÄÉœ{û{h›Û=§q„ZhÎîé Tåõcp>Š#IiœcE‚@ßõÉë;D¢Žƒº"m4„$4‚@frV"kÓ0ÒhGIi0ðBvè©`±Ö&ß~̽‰ÚZ)(>¤ª*²pFi–"׋Ñ&©, †ž¤k‹YMQÈÞÝð19‡Ûäøÿæó9ž?y&ÿìŸ~„ÑNí„—/qÿþ}Ìg ¬˜è=0 Œ!×É0ŽI‚@V%•D±y^$Q-©ë †¡‡bßpY–Ì™ Ü®<{ú wïÝÅ›G8>9Fžë4ƒ†]×c>_ ¬sHLƒƒõfóðÖѶ[ éÎú)e°t]‡Íf²,RYUX.)¤=ËLÒ>ÅöÊY -%îÞ¹³ós¬7äG­Ê— uí`ŒBž×öTª´.XŸH3=c2ܼqRQPü4±;$ø„/Ûm“ýõØ‹]¦É %½üàåJ\nå9=ì>q1mÏóòÂÃd®·†žtb¬§kš† „Ñv [ùzÖáŬð˜Ä&•À0LdÉãvߘ ñ¿gvp`Ð#¢ÝcH¼µTYÇðxg=$‚ÁÂ;‹ºZàÛ¿ñøó?ÿ3œžžàððgep&£žF>P"pÃZ°F•*àõzç,o¢ëšDÅ€ï}÷{¸{÷.ž<ùY–#3sl¶ë´Œ)‹’CéÇ\Öµ ––„wË B©4*®Ü<<¶ëYN­pßõèúltë:Sä½5;¾¦`$U”‚„@Áùb­$¶¾E×÷ÈaørùRÈg€~èç”ÒØn7°“CQô\ðZ²;+WDbЇb–4M)ʪ„¬à'‚=Ó¼Úº‰„ítÃÖyIe­ Æ{ž?rÈYßqÈ”âHm²”‚5¬š šÇT´£‹ƒÇ#Šæµ§§g|Ñ–¤AæBGiõ²mÚÔU€ü­ï}ßÿ½¿·Þ~¤"ö òìïïãÎ[t Iq~Ì´£¹Hœqí‚ËCáJé$Ü-ëdG L¡YŽGßõ˜ì„ËÕ u]áÁýÈ´FQä¾óßÂf³Â«WÏù3QèöeKQ «ê¾ï°Z]¡ièR;88Àv»Æz½JŸõ[ÞÆ£·ÞÆåÕ%Ê–ÄØðñûû{Ø›/è¢4E‘C žM"`»Ý’›I“³kš&¾Pò"GÛth·£iûj-yìµaÖ_ÑK©{Ô± 89>e¹idëm¶žF‡×8N˜,E%cД_C?šG6$Í¢ì¢OÑ22G¢?>>ÃyN‹ÈÀ3Qo9èËRšád'tÝ€<+  ÛIÁ=áWR*•–ŒDªêx¨Òv>:½¢ZB½=›ã°rÀ1¿0Î9â!§5¬3cvp}ý÷ –1³WˆS¾¼X]]áââwîÞÆl>ÇÕÕò<ÇfF`‘ª(ó4S eÉη葈©å¤ØNH~ä¼ÈIÅpO!h6µ(rl.6(«’Ô£a‚·˜î=aÛ4ìÕ¼ÂùÙ„ÏX(Ī‚//¯Rëx!D$•`žŸ}ö9V«Kd¹IY¶ô9…k¢ß×7Ë1m:HI± #W@ÓdatF7ó4¥€úq EÍÔzÒ6•ªÁªªà,éÁæ Šôìú‹åC7¼f´Î1Z4²øý8¢ªˆŒbZZ×s cŸr4¬§‹“ªÓÐm„DÓ) \*‡Éíó¤Æ0L8==Åí[·ñ;ßÿkøáÿOž|‰££[ÀnÓç»e ¾5âììôµêà›ßü6¾ùÍoáââ‚ í´=¶œG¹3¼ É­ù‘¤tðImAI€Œ³]×C© .ÏoûT Ov¤± ÓŠèB`;CŒ­uØßßç%–Dðä:š¦1$SRÍò’­ÊгªcfÆÕÕ«è‚F€ U]¡(*€óÒ'k‘iC„ëéòã4»Àzb3û¤‚ljæ¡R$x¯Ô’£j E¡˜ÆÝ'ÀÇu—Õîù÷ÜUTeÉÙË"ʵ‚l¥Þ„‘6ÈëÕ‡‡¨ë {ûûÜ•¨ëú+Ï˰ô^™,ƒÉ2ÚÄI‚ccðüÙK\\\âÃo|ˆÿ÷¿ü—mè1 #l -Î=²¬ÜͬMR NNìäð|€_“¶Ðe]Á®íqòê~ãŸáßý»Ÿ}ö)¾øâsœûû(ŠŠa;W BBð÷}÷šåîw~çûxøÆ›¸¸¸à¹¥Âåå%‡KÅ„6ÒãŒd‡<Ë¡´ÀГ{§È3„@›Xråð€ž•ûQÆÐ=”–T L·sÁ›õE™§¡¾Ž]JŽ·…$>æÍ)SІq ì E™ÆÐ‹ß¶-e%Æa@@@Y©¢›¦)ñ$#ÆëòâUUá;ßù.2AÀ—°˜ã3bj`ŒË½Î# ÏÕ'Ÿ–E–³„J§nkÇüäyzn°í;\®®R'5+*, šWKõ+ \b€Ž*w)¦q@Ûµœõ:ÇG¿ü%îݽ‹÷ßOŸ€×Ã<ËPd£ph[549?;‚Çûï€?þã?†Ö´÷ÎÓ —é¿Þ[¨Eðí§¯ÁI%ÏöTbìù°³çÁ3°@ T‰2/./ñàÁ}ܽ{ÏŸ¿@]×(Š ——ç¸uëlYðøBºÅÍš€”ÿü=üàßüÓ0A(¥MjM2I‡\Û4xüøäy†ããcÎ.±Xµ£ÈHÌZ–%²Š[ø®CÛ¶4˜f QNØp&sHÆ’í„RH¨LÁ9ªš²Œ6ªó¹IÔj¯è ÝlÖ°“Mÿ~¸Ò¡âS‹¬Ic–‹}BaY: D/ìzµ!×MÅR ­aGÇ„Ã. Ò–A±–žd—óÞÃNv´ý˜¾g©$¶Û5B ­¶ÒëÕ"ÉcÈ%¤‘gE‚Xhž¶ö9' R‹ao©L0H‘@«B°‡6xÒÅñ–1~H³Jˆ­ÕŠX†·o߯íÛ·y OÿÅ|Á[w‡årà#öŸ%y‹÷ôB­×ëT•ŽÃ€ÌPPÈFfÑs#¸âSRqh—KÑ¢FÓœ+€ò}pŽªN!éç™g&1a´#‚£j¸8ëÐØZL£…ÉUxžªÀùœèæ¡?…‰$.R‚p8ã9KŽ ÞaA?WÛÑF˜½Ó ×ò¡%™K 0ÎR£ &И8ƒ‚©ô;‚”•]U%Í9RÔ° z)Õ±i¶©œ&Ÿ "\))’‹¤Èùó—<Çž¦q'äw´ðZ¯×˜Ïç(8` ‚òwŠ¢ø"„‡"‰ ¨Ø^&Ùð-1MÔž~ñÅ×(Ë7nÜDQV¼%#-—·;aœ§dYŽ ®k,¤ìû!U žè1à\H‰<Ëùöî±m¶xöô)ʢĽû÷PäS„ãåå%|ðØn·$íðä¢èº¿ý;¿…»wî`½Þ¢úä< {½ü»žçÇ\æ€h›BIäuIàGm YKæFbüa¥a½bÌW ‘´TÔ"ÇJ0®þép“¯-¢€4Ïs!Ï©ý ˜Ñ)ñÛbUxý+Šm;Ð\¬ÒL³0Ùά<Ä:#ÇHßpÖa¾¨‘DEvÖ¡mº´Øˆ·û8ŽS)hæØuʺDYI$}×£zt]—ÖNiÀo-‰ãi&DzPÏ´QóiLç=$‡—[æ)f:KÉqëH/&U^Yž'¡5é é+ŠmÛâ‹/¾ÄñÉ ÎÎÎ0Œ#Ê¢Hmþ0ôxñâNÏNqµºbx(±c%9›Í¸8¦ÚÉ’‹Hè俎m\ßSÊÞ0Œؽc¸j¥¶U¢o{d:§ˆ„<ƒÒ ççç;µÁd‘ä'V’ƒ‹Ø£5-K¤"wý=• ^* °Z­© bõFÊ8÷ž¢a­G=«è>X˜\#/è’TJ“:Ynà¼Móe$FÛcœ¦XÑÜÞY‡mtT¼ÄŠô” ŸmE¤LgGda^çÆ¥1ò,G¦ $g2IÅ ¿s§HEq°—W+ŒÓ„²(€à±X.PÏç¨g3Ìæ³?`Ýìµ_RRA*€ÍzMÎ “¡(+<{þ Ú<~üUY ÈrÞX…TáI·¦ ùÅH3†qQ–UÚl½ÑšRଳ°üÃÒÀdòÜ ËHk齃ó$E’Bcš&v£°6.*øùˆî‹œX°Ím²”uÑ4+ý ¨€ä9µþÚPFJ]ÏH Ý@ª‚@Õ¢SKI#Þúm6–é$Ìö‰‡Qh]K1;¯ª`ͯâgã02¥\ýø´èŠã„‘õZ+ÊÏL·¥–[ìè¯7Z¥×d K-JË$´Î2ƒa }-‚µ•B€çƒŒ±SEY°Èxçûî¸â&´˜„³T@h£©Mæù\¬ªc,cnE<8–…Ô¹ø°Ër†žÃÔ ªµÞѨ'ÏéPwÞ¦Kßy‡žßk­4„$*€Î ‚éÒÞn6‹˜ŠÖ€`¿I€‘ÌdÉÁÕu_t2‘}âòP²¥5Š­¶ iFB@7 ض1뙞ÿº®QVʬ€F­^ey.µ&ßöµ_rËåRú@ò¥u É1Úà‹/¾Â›7qtó& §Ü)% !8mLÒ…ë¨|²gñáÊ%Êg êŒ~0»ƒÅsNóf³Á'}ï?~³Y…º.±¿w899Æf½Æ4N¸¸¸ÀáÑÞÿð]©_žžÂYUÑMÌË!™þ½JÃiS5Ò4ä.Ü"‚”âÌ1|¯VWŒ€ê^ƒ·î˜z>mE%gß¾sRÓ¦7¹ÜD‰Ý˜¼¿R‘ØA$ dQP¨Ó¶!´Q–ÓÕ¶}šÛ—ŽS­5꺢Cv¢Qƒ’ Û¦%Ÿ³§­iÛ4hš Iˆ -ʆ~`M¨`T™å`Ú%ªY‡¶£%B”ˆ ý@ú¶‚°¼È¸:1ér˜Õ5À/·RЍä†è*ØTp÷1¥å]ðd« ,oÑšd0ñÚ‘—‰ÐÉdG%‘1Ã=¶Û†e6=ú–¸Ȗ"0«)R‚ºˆ®ï<ý}uµa‘xO[ù¢dbÇΕ€º1ƒ¸Ù¶Dš:*.8 1ŽaªªD×õ°“‡›h¤C eÁ5H­Í΄@FQñüÏ@¢iÒE霅ð2‰¥S삘‹¶á(æó2“³k;¯jŠôBP¤î8ìàÍ,ó¬öØA’ ت´&¿´”Ôþóÿ›´ÅžÒEãʼÄ|>‡Fäeù|‰z6{f´†bbûõ_2†èH!}ÒL  dí„ã\]\ ïz¼ýîcdU#%´ ¼þõŒÚØF÷Æ”( q–CÿM—†ß±å‹/zÌ28¿¸ÀËW/pãÆ-Ü¿ÿõl†££#ܼq ðå—_@p‹i­KBÓHšÑ:cÑ«LÛXo=ßh"U…ëÕy‘#3Y ¦ºB@ÅQ²á‚Jý"/¨]°#| a³Ñ»mš-ªªF×õÈLÆÒ®ZX+'…ÀÅÅ%®®®Ðum5_Ç]ŸVU‰-gÓ™ÅÖÄÑÃÅ¿ˆ3½ž¶–$3ŠV?²’”,„½Že;é?ôTåf˜Æã4ad™‹€Lí>]€EAÑYN­‰—3J5Þοv\¥YöwS€W”hQóÁ³³è»ŽdM‹ÐõìDù5Téd˜ÝOËžŒ¿/ÁÖ4›x…ô}71ü–ZzÅz6„Ź (AÒtªk4œØÖŽL—ö°ü^ãˆq˜àlÀ8úy‘¡®+нudOk¶[t]!ªªN3f;ÑÜÕ9¾ï¸ƒ"”›\‚ÌŽãˆÍzK!‰Ø|1#h.ÿŒ2¶Ëy?¥ 6:4-wÆhˆ Ùªy&ï žœ1 †Gbч0«kJ¨ãg@JIÚ>Iû‡ˆã1±;rÎó ivX’c2U×UÍ6B«Hƒâ5U™-úÛ–‘gÄ×ÌóR+TUýîmÐ6 e]û%ÿÿ·ÿÿÕÿõ?Ç¿ùWÿúY^äìÕ¼ÍÓè»›Õ ›õßy³Å U]¥\]‘@¦\ùL6-b…ãc\güPò¼`¢ŠKƒy i‡ŸþúS”U…îCJ‰££#ÜðJt]‹§OŸ¢(s¼zùð’ìf!ÀYŸZ^£8RÔ;ʼ`zHÛ5œr&°X,vUEÄêfÛ ça°çgtË …Ùl–¸t¦—PY–ãøøï¿÷>þá¿ÿïcèF¸É"™¿DÄÇ»¼¼âS—Rö"{G½FbìÀ[7oÑçÈävÛ ®+, ÖÔN+Mü»ŒEÙq‰•¶ü’f}í•¢à&­&cȈìâð«q¢YdÆh©X1)^HñÈ;ý,•Ô˜Ík:È·[&±(ôR¦Œ³.U¼ŽaeAÄóˆ”RÀy²TUu ªp(ؾß-´˜·YU5¼£p÷Ý@2UñlŽ*:¬ÇD¨ÎL;YtýÀ z„Œßʱº\'™ h ¯Ið>MÎN‡>Í#ç0†OQ%Ot“ëÄ,Ë v¤|i6òJÏf3ÚÀçE:€°Å +ve$³±Øl׉-( ±e$ûšE¾u]qê1¹‰! Ã,ge$xF0Ù³Gö\ ¬VW0º¾gy}ŸÓ@fY–©Ò,ŠÐœ#O¸³/_¼Ä¯~õk\\^b>')Æí;·ñÖ[ðîãwqûÖ-ܼuÕ¼¤Œg¹‹åÆÐ¦~'Š‘´äïºWW+^ÖЂÉè ãh©òÔRÚ(†=Pš³´ÜÚlÖœe£’ôF"fYú~=ý;Q§WUò,Ã8Œ;f¤§Ã¹ë©Z¦­£g¤]rÓhÓ%Þ¶Ã2˜2m ¤ fàéÉirÒd|Ñ’øØcG’u „ŠŠã00`–^î¼(à¬ã÷‰6¡´gfˆ,®¤‚yaPÏ«ÔN:çÓÏ(RY2CN™ª¢™fÓ6ØÛ߇T mßp§AºÓ¦éhÁÀGú:hûîmŒ«Ð‰iYV4 §Q@»%#Íl 7—e†Ðc=K°´à¥OïÃ8’Ì+c"Ñ8ŽL×’\-~q>.[ìÍ• çØhž¹3²›²yœã¸B Ù<‡a@YPøSày{PÕõ¯”V§ÓdáØÇ}ý—¼wëîß¾‰û·oy­´–3„€fÛâòò‹ùŸ}ú„’xóÍG¨+ ‡×R‚4Ä2}Øyž¥­”2mRwx›0Oq>¶3‚“¬m[@4Í?øÁñõW_ãî½û¸{ï>ª²B=›¡®çéúÙÏJZ@çðòÕKÎNvi#»Z­ ´Âï@‚]EÕŠÖ&-(Kƒnê¦ÙbÛlqxx„àõzCrNÞ‹‰ZÓdiO6%äEŽ£G¤Ýsçç®°Z­Ñu ”ׂäS0ÇŽ,]ò¢À£7ß¶iÒLn(³“"æ0ç4º€i¤ -˲4·ŒIÎ{¬VkœCk2Üo6œžžÁYiд ÎÏα·ÜÃ[o?Úi%Aò©².uF) xŠ›Õ×Åì]×ÁÚ1aŸˆÂ¢(šVg)¨(3ä7¥ìâÝ…Àä1źÁˆ ¸åòÈhØß÷f³šSöÈ£žé2. ¿9¨¬È 8Ž~ÐF¥J+>Ã0¢ÙvD:rÆi§}ŒÚÀÕÕ›õ–À lÙŒ±«Þ{šÁr‹Lš@ç)Ú˜ ›í†Hà-L²œœ+]ÛQJ£%©1®®.áÉ–ìdÉ‘B[i%e²,KŽ_:S׳‘çÚqÆ>ÑÂM¸dÙÌ‹laÚ "PðX´$jMìK"Ÿg(‹*ÑycɨR ùêSÚ¡”&+P´RÈòœªCŽ ‘ ±øàÆkN6ŒÊ-ªš»–óŒ(gõOK^ÂäyþoýÒ÷ܧ­¤6¶,óa¼ÆÖ‹áAÑ ¥,ß|¿üè#Ìš+ï0õ.°¸YIö$îxeäÈ2 giÛ.¡è£§™<•`ì|IZ®qÂl6Çóç/ðãÿþÑ?ú‡(«]ßáÑ0=ž>{Šq t²Ÿÿü§øÆ7¾­3œžž¡,‹Ô¦žœœb1_âöÝÛp“ÇÞòãÄî æ "~(ƒ¡ !puu…aØWìø¡¢6µâ[™©”aBªÿ][ ¥ÄÙÙËr,..ÎÑ4m"}Ó‚„Åg_l@Š¿ùÆ#Ô³.Ï/$ tíäatHUo rŸ&r€8O8£q1_R¶ñfµáÜç ÷îÝã8ÚÀYGLÇ_ýêWxñòîÝ»½å>.ÎÏɱSäX­hÌpçîm, áøøǯŽQUKXçHÖÀ ?m4º¢c•RØnÌæ$ì¦ G²m‹–QtRÅg*ͼ¼‰}Qi™6h›†ôªyžroš¶ƒQ†½>u³ºBß°~‚ɳDN¡Î›¡i  RsMd˜Æ¹æ8N0Ú Ë †žÆ_cÛÑØ .Æ‘ºˆ‰qxJ)¬×뜕äj×ì¼yV S„4¥çZ‹Àm¼F7ö¸¼ºJ r±”e‰Y=ÃfÓ 3„ý*‹ü§ÿvåî/õ¿þOÿSܺwwÜ÷ÿúOþäòé—_î߸yUY²ô‚g i€÷=ÂùÙ)¶mƒ®k1=t<#ºNg&‘%ÍãÆ1.V¨už‰Ùɉ€ý¼Zk4›7oÝÀ·¾ù!D $V×õØlÖÉÆcí„óó ìíícÆIÖ9æz\]­XLK…¢,R&®É вDnŠ”v÷Î]¸àðÙgŸáøø†¡Çl6Ç|¾@]ͨÒP$JvÞ&‰Qž‘€x³Ùââ✵…ÄÎkš6ý>‚›# PøQÔ_Žc›7oãáƒ7±ºº¢¡>?0UUò¬4ƒ–t0XGÞTÉàN)»H+GÂæŒÝu=ÃÙÙ)þôOÿEQàÆ›øò«/ ¥Â»ßÅ88;=Cžç8;;G–e˜Íj|þùçX­W@6› ^¼|‰Ï>ûo½õ&-»š ŒÑ‰L‚‚GY• e! JS‚â8ììdžy‰Þ;ØÉsÔ·4l}Ô†¸‡‘&Øë¬”Ä8‘0ZifŒšÿž¡ÞÑ‹àYçFåÀ:K S¢Ÿ—2’Eù@^dðŽ–1y™‚>†uqr£w!mI5ʇ¾gúqÛ¦%Gƒ‰¨*&r\*ä/ÃØmm”ýmSDB´KvMOßGÎópç1 ´Äˆ9Ö‚‘øÞ…$kÛi[iáC±%UãL Š´rú ÍlãÈ‹¤>¼¹×m×°fP0d䨕6¾æ8!ø¯AYT¨Š³ù UY’FQJdÆP”ƒ{›®ÇÙÙiòùo7[8ëpëÖm<~çm o¾ùnáöÛÿGmÌGñ}ûË¿ôf³¡v¢,a¤³°œëá¼EÔ&UYàù³¸{ç.Þ}÷]<þUUQx¹ ÜS2˜æƒr~'vœD0`1jß“¹Èsá0– $ÅLþéŸþo¾ñ¾ùßÀè&´}‹aìñõ“'8=9áÿ~‡>úo¼ñnß¾•¬z³Ù UUàG?úžß~†Åb‰å’~åy¡h£¨rIéuJáàðòJàųgØl¶ ±Õ,å`tF-…³Øy"¯Ökl·t[m·[œžžÀ{—Ú‹(|Æ~!$ö÷÷h±Ô†u=Çûï½a= ·-$´¢ÖÜz!‚ð‚õz,p¯êò^_Ãä$ ¾8¿Ä«WÇðÞáÃ>„É2<þœò%êÛí_|ù%g7&\]]Á{àîÝ»X­Wøø£O ´ÄþÞ_|NÚϫˌ6¸sï6çá°LG)xG{LÉ‘”ÎyxGsªeœ³†äX{Hs"ªâtåzFüoÖäEÁ–7…šß⬔Z3Ë$g—|ÔRFçè‡ÚP`•ÉðÑwC²%‚EÓÃ^…”PF¢k[“‘ˆ=3JB š‹£™Ö=ÁZÚ”Îçstm‹¡§nc6ŸQµ,\?¢GÏvÉ‚ÞA€TÁCK ÃBr`¼£Ct`ŒáýÇ8H”U"äX¯W¨Ê Ã8‘1!/¨êT¤i{úšUF]ƒcj<ÿyИF‡ háH‚ý–:&!ÓbÉKÒ –eIß'«ÛòùÉÏ „Àr6‡’rˆ©hÑs£ûb’—÷çggx÷·qxx€®oicé&g!…âÙH€”À8ö¼± A¥J\hc(ÍŽ?¤(ÖÖ¼ÒwÎ!X’Ê|ùåø/þ‹ÿÿ£ÿè?Ļ߯åù9¦qÂfÓ`»Ù’>Ë“ßùóÏ?ðþûïãââ]×ãþýØßßÇz½Æéé)^£ª*”œ&9ºT Á^T‹gOŸáüü,e¶Ò-*yè¬ITp®F¦k°]o“hùììWW—éö I¶A"^ZdX,,¬Ù?mðï|&£6IˆÝ–/€„y^`)C¢*ÊTHDxÒè8ØÊû “Ù½A‘©mÛ‘PµïPTtxÍÇ é:üê׿Nü¹¸"¤ÃÚ)‘Ý£Ò$Ò˜LNÚά 3ªòIuBóXljã0áòò’­½HQÁ+,—{†…Éa”Âr¹wqptôQ‹ü•‡á·¾÷[Rbo?øéO›‰å.×Ó±œ2I¡ÃÃC\\]¢éZ¼÷.4eQb(žS ¥©¤ö^&›ÑuQh ¸™&›TìÓdÙsúéÓ—ç=mÚ?ýÙ_àÿù_âÿþ|ã[ß@Ó´¸sû6V«+¬V+ !ŤTxúôªªÆ½{w1Ž#NOO‘e91Ïržíá=¬s”覜µ89>Å4ØÛÛÃéÅy’KÄÑ:"õöüÂŽã”ÂçW«Kœs.­NYÓ|d“|¦ªj,3 ÄÍf›è¿ï¿ÿ!´68;=c§†Æ”¡® &Û<ϳ:Ýy–£ë‰.âUÚ‚¡¼!xíqu¹Âêjç-‡n Iˆk[:üçó9Æ‘ÄÈÄ#ÕÀááÚ¶Åf³ÁlV#c«Åb6€4›hœPä%à‰£7«çPš²Ž ‡yg¥0ôëO‰Î“ÃÒ”‚|Ç"@);9(ééŸO”ÝQ–%š-ý\–‹½Dl ”ã“å4@°å+Þ\S·¢Ðl)£…„êÞ˜ÆíALA nú=ãÆ$‡šƒ¥gB‚’ÜÐ6 UÀŽ6¨ÖÆåE ý K½‚ðèÒpj%1d"¥‚ß?;zŽq¹Xb½¹Bßw˜/4ïžUæ>A@R´nP2@j¥1h×cO—‚ÊøÂ‘ŠÉöªªLϵ”Šù*q³œ|ÔÓ4‰rµ#éDIÄ ÇÈ5Zø´6WùÀ4X¬7"ðsõt‘•yŽƒƒý`ÎòÆd? Þµ#ëÿÊÃP1úÉæ‹y/l7[ܺy3ùR1€Ì)ëaÃÙÅ%ob¹¤8¶ka{ ;Z§ŠRJ )&Žÿ‹mb´íL“%(@¢ÛÊäÆ?¬³YÍjþô/ÿeYâ¿ÿþIüºººâ9‡‡sJ?Ž>ûìS´mƒo}뛸{÷~ýëÏðâÅ dY†½ý=Ž$ C8Y{j @ƒízo™œ]\¤¯ÛZ‹Q ¸~·$²¬óZ]­pµZQ¨º,í@Z(E)Ö‡‡‡0&Ãå冡O ¥ï~çw°¿ˆ—¯^°hÕcè{ÔuÍ-z‡Õj›æF@ÀvÓ¢ª ÖAj ã€Ù¬J#Žx—¦@ÛµxúôU]ì %­ÕôZX7z¢ƒƒxÆ(EÆûDûYoptt”ÄÉ<à…G`}D®Ã„²Ê¡ Ñ´-'à)ÞRN‹R„üw,†.Ë‚ø|ÞAHEYÑ„ÌWW+¶*Ú²³¬ƒ¬m–äA&×\‘d¬}latÆþm£s²Òad2‚£RxS`Í(I¨¦5]æeU¤”6£Å%Ù{uµNh\–,÷”¥ÒõhÖ-ʲ¤| Ï8 ’f›)›ÛÍ:zÍÀ<ÄÍfË6B²(IÔÜì@ÉaY벂X•¤¢ Ã©žÕ´©gʾåøÞi¢ð2ÃzÛ®£¼%‰¶=ÚSC0Šªœ¡ë^þå°©ˆ;¨JsB*|Àê‘".¢3-ˈŒeñR•àå¦÷°¬öܴ̋ ä9CE ¢ÊÏ—ûœ­}EÛh“ÁdÙ§ÖÚÄÖü+(ó»¿ã/qyvŽ“ããÿîO~ñ‹÷ ­#i#i•’”3\Uh¶ŽŽ!øe¦˜¿ÀܹxÆÜÚè}CàDØdµÚ†´êU\´ÄàyÃ3›/¿ø ã8â¯ÿõßÅÞr‰ó³3L΢at<ÝTÔl· š¦Å[o½o|ø ܾ} ™Éðå_bµ"ËÖ0 ‰ø-Xë¢yaÑl·Øl6T]î¥YW´·EäÉÉ ./wR‡h°§¡3=mZáöí;Bàôô4qñ¼÷xïýñðÁCœž²I²p˜†Ú“›0Œ²]W5êYˆÁ§Ö)IR”q˜Òì0‚Ö› Ž_âòòM³M™Ò»1€L• ”⺠iþíSÛí†ÝE…YÕ5͇.‚wüBú ?‹èZ²Ü.Y¶æžD^&„¾ç?•â" U)FR±´,J´M“ýÄ! І^l¥$y¬ùç“ï” [tøR —ܵÈÖ² F£ª)0)‹ \¾ìMF>qg)0Éh I!Ö×€.µÆeYB ízKÑ­–!!>ƒšSëhC¹ BÐç–g‹ãék°Î¢iÚ÷”g2«g‰øc²ìš•f³Q§G9>XŽ(0²V0ò.ÇqD‘å(K"i÷,¦·–¾ç<º¾'3‚à\¤a ëwP1oœÎ““!bÆ4 YðÍHÁg…b}áùÅ%ÎÎθ€qì.ÐuŽñøñ»Ô5Þº‰{÷ïâÑoüŸÊ¢ü‘›,E*ÿ¿Ôÿêþ¿ÀÑÍ›xëñ;¸¼¼øƒÿæ_üÑw…Ö¸uû6H…H`ƒ8d%˜e‡º,qãÆ ¼zùÃ@S;É÷yó/”…¤QtI¢l—4‡q¹Á17vGÌgsÖ$Rý‹Ï¿‚µíw¿ùb—/_ÂNCß³»%ÃGl6[¼zõ J)<~üßùîwðÁûïÑܳë\€›,º¶ÅÕ%­ê›f‹Íf‹ív­4æ‹%È/Ûµ-...p||ŒÓÓSŒÃÀ•¬ãêtA=88Ä­[·qxxo¼ñ&–Ë9.//ñâÅËkcÀ»ï~ˆÛ7oãøå œ·$#êú] ÇúL­4ö÷`­EÛ´),É:‚lŽÓÈz/‚©ãÀÁãž>ý çIÓ?Bb¹4ྡxýW4áÓ!ã-!šXÃGÑŸÔ–{¦ rj£þKóö8x ª*FËÓÞZËfZ4u)¯9P<¦uÔ"$9Hüú†~€TyQ pK/øÐÆ‘é2@ßSÅWWy\ÕNêCóE`ä¶WH‘|ÞYneÉ9FÍG>ŸˆKC—bR‚b*KÉŽ-s4#ŠŽÎ ·‹ùvrˆÑ71‡Ú»€¢¤ð(ÒHƸÏÞ%;¹äZ"Ç ‘¨-GF¼~Ü¢·mçé’B¡ih£’¼«ïZ°0ÝŽ–ῤmÕ<ÿžØY½Ø™ÉS: $£‹×q\Hd+r›d¨ë9ªªDžìÇÎh!%bkNçúmpyy‰ËË‹dÉzCßãÍ7ÞĽ{÷±Ý6¸ï.Þzô6îÝ»û¿tÎÄÄÄ¿r›|çÁ=„…ºqûÖi–eè‡ã0¢®Ê¤ûSJò¢#¤AçW_ßüÎoâá±Ù6èû† ›¾‡uÀ8ît=tÀ+ ca¨Øàn©ÿí´ ‰!;Áoª½çá,þ³ÿû†ÕÕ ÿ½üßÁßû{øÏþQ»_&£{\ª<þ ÛmƒçÏ_àí·ßÆw¿ûüí?øÛøwþ/¿ø_|ú9†qÄ‹çÏN;¡íZ[ô®Çæê*©è£-O %+dÆý$˱¿¿Ä¶k±\îýiû¯`˲ü¼û–Ûö¸k2+Mùêê®ö¾° "=„Ñ8¢†FÁ!'$NMH!r&óª—‘F =èmô"NL3 JdP$A€pØ6U]mªË»¬´×œs¶_k¯¥‡ÿ­s‹CÝ@+7¢QÝY™÷ܽ×ú›ïû}¨ëðݸ²*ñƯã[ßzmÛ¤Ãøèó/à駞ăûÐõͧ:‚ÔU…¼ÈàìÌ>\vߢmI‹V/j¦þ ö½¹<€\eQÕùòì š)Ê(‡˜‰y^`¹\²»†`|Ñõ-,gž¶vE¼c‡EQ”PÊ'rŽs6ù 8BR E޾c”›hšÚ°ï˜_NÉžáC¤£f÷‹Ÿ=²Â0xBb±X ï(ܼ¨Kìw-¬"}àìò¢ÀþáŽá²´4ˆ)kã4aè,—K à%¶—{hCX·yt˜€÷Ô=t]Ïh)“gɾ¨ŒÄ0LÈrÉ›l°y¨Ê’Åñ ­ ‡Æ œ^;IΉÕj™×M³‡ñó ú :‚}8ëà1ó¡˜qj…Ö‚KpÌâ ÔnGË8NX­–4 š­³Y²§ÚiÆ<ÐÊÀ’Y†t,WK¸™pbyA$n"&P"jÇ‘ó_Ü „9- ½ø²¥Xˆ‹¬) ÎHªr©(H*RØèÇ»-å\Óå'áýÈR* )5nܸA²%QU5²ÜÜQYö}ç 1}ç­wˆú{±E°óƒÕj…G»ÜLl2)yr…@^dÈuž–+Î\\\âÖíÇñÞ{ï£ëZ Ÿo/ù éö6,m’n}›ÚpÅäj/â!#éàˆÛXaê‘ç9~í×~ spø+ù/á/þGÿ!þù?ÿPZâââ>„µ"ÅPn·çxñÅ-öÍççç¸}û6>õ©Oàé§ŸÂÇ_xÛË-Þÿ}¼þú›X,*¬Öè;"P‹QT%J†áúÙááÙä CjŸ·íprz Èòí,ßÿýßçŠ9c{¢Æ¿ø¬W+¼ÿþD`) ´2é’0eQ%ÿrß8>>†T`MeÛºèeeôT×¢ëhØw[Þ%ðøã·ñÄ­'p|z;MÈ‹<Ù¨ìd±Ýmq~q‹³3ô}‡9Pþt^dúggg8>:†µ–ÛžeYÑ‹é=Ϫ4#ò›kŒFß ˜æY.¸…ž¡„fìüœ:’X¥I©0 v´!7‘”¨Ê™1wÂìg÷! óÿhfø_üçÿ,–K\{ì18çNÿÕ/ÿò_¹ÿè Ç''899N­.Íe˜«ÉM0êtrzkG4ûÖYLaŒ¢˜Rðøà.E¡ºËÕ«Åe‘ãæã·àf¢þ.êyÆh¬²<…µ]Ç˺¥O®ã#}?ó3ëõ÷ÜÇ«¯¾Šöÿýgxï½÷ ¥N´áõjƒŸúS_EžçÔj†)"†n´½ iÓ8Œ}úï²Ìp¬æÄú2É>è,As§iÄÇpyyIz.©R[Pä¾ø¥/ã'â+xö™g‘%ÁXgò[ÇšS ¯Š›Õ&ˉÛç¼sLÙæq†°ÙñöèØxQ™’ÑQáœC]“°Ö²UI;DÖhBÍOvDßœiC-¹2dýëûbE£‰’ó¢#DÙÐ ¼ýœˆÂ$øL—³KŸYdêe®í’ÅOÉ2‘ø‹ÚPÞw„”¨u²L ž;K!9i*•ÙÓöz¹\a'ÅnFl‰îJ)ªÒ§‰ðô{I»Ül)¬*X˜· DÇ‘*ÏîÊʉ›ò8Ï']c_9òL •ÜQã0Á˜Ù¬ÕTð3çi ZœÅâ%¢¸âAm•ñÏ+Š eU£È ,ªŠ¼æEÁºÚDrbÁöv»ÅååM³ãYíáφGë >õ©OA]H|ìcųÏ>û/ʺü'äò à—~ðà>çA4ú~·Z­Ó¬dè'½X&ÓúB+Ejrç-ܹ£ÍÏ<ó,>¸óVÚ?øq}îÀ9Ï©j1”›†§´ iÜFœ9Í/bôâÁÁ’å...x-nð;¿û;¸wÿþã¿þ×ð—þòÿ·¿_ÿµ_Çw¿÷]ÖN øY!ÏéEjÛßûÞwQoâwÞ†”Ï<û4¾ô…/âøøËÕ O?õ ¤’¤“ê ãÄ–7ºÑLfh‘Ä[Ü®ïñòw^Æóþ^úöKøÁ~ª¶XMûØ'ðÌ3O£ïÜùàœu(‹Šå¬5©ÕÒLx™,…\9ka2ƒ¶m¨m ãh±Ù¬±Ýî0M=¤¢Òƒ°Ý^&awÜhߺu ú«e]£¹Üâ¢é1# °Œ'ËXS$ʪ‚ugg(‹ÇG”y†Ë²Æ;ï“Ç6x,KŒ#¾§§×©&3†|âžfgã8R zš+æ7Ò…E¸äœšA:G‡¤Ÿ=»I_ÐÓl±Y¯Ðö¼'pèÌa’r9Ô‹*ÙLj3²Ã"Ç8ŒzZ~,WKœ³;ˆŒþRhtm¼È°Z-±gM« ´6¨« R’w¸Ùíy1$’ÖvöÚUJas\ÁXó³K ìQV%´¡îK Å1ž– >A<ÆÉ!Ó Hà–²Ìaꌌ´|¡j¤;Ñ‘™õ)üÓÿÏ?Å/ÿë_Á£‡xë(¸>Â<ÏxxþžQô‹Ågçȳ›£#øibo»BQæiè?2ô"VŽmÛ£®+xOir¤åÔ0ü¿ïÙ Rp{8z€.Bɺ„‹­oähJ¶„–Ž£ØŽÖ4Ô®ëL¥|ߪ,)`«ï).t¦Œggì¶ òüN5ñå($YóbÆsÀÉ„Á:8?» ñ@˜Ñõ.å>Ïs@P3s‹²äŠfjS7b¶yA‡LßX.knEGt}‹²(6Ì\…R¬*)4¸=©bü¬ÃÐQ‘ ¤ÄÑåhGµ6& œ#°7ãåL.Íõ©r 4×v4³Ž3CÍ"Ä<˜¯À[eªpË¢„V™10Y–2‘‰¸ÎüC;¡i[t)Û¡Ý{õzÀâ˺F]U(Šâ{‚Y èaØò’aF­Ûª®¸"Q”%i«Šƒ §åApÀr¹Àör‹wÞ½ƒ[·Çý{wÑ÷k =•åZxÌŒ "i‚¢ÐõÉBi“òUÁ[fB¨Ç zÏ^ÅŒ1ã9Hí"’Šò˜ó„(Z.—¸Üžãý£„ï|ç»ø÷ÿýÿ)þúßøkø³¿ðsøå_úüæoþÞyï¤ñ¦QêA«ÒDívxåßÃú+Æ-FË,—¼ðÂÇprr@àí·ÞE×udÄï¸Ùa·Ýc¹ZDwªtö»&eÓÔuÉÄfÁ’˜ÝnGY¢(ñÖÛo¢iöº_xáüäOþÎÎÏÒ¬1ôÄH¬jF›y‰ããct]‡­ÝR n-6GÌaƽ”Џ½ÜÂ(‰¦ÛÓÈa¹‚T”ãÜ…ËUt€9£È„ŒÀ òÁã”Ü–7u]b·ÛÃkª(1Œ#ãÌrh#±Ý’h›æ—#&†‹ ÌaF¿ï‰|"iŽ*‘Z¼%› M(·¦ÅsÏ>ƒç>ú‘7®]¿þ´ļ0„ýÎko¤{µ\UYôƘڃôOׯŸBñF9H`ž‰è—#ÞÏðpSwî|€§žz÷îÞÅ4hÛ³³È´Â`)" Ò± <)Y6 “˜4†ëXëR»9l´pQIà‰ÀÔV‘¦‹PYž³F¨lÿ¥_ú—øÝßû|ßýùŸÃ/üÂÏãg~æ§ñ½ïý/½ôm¼øâ7±Ý^àªe1"ƒÒC)"‰7$2}ÿ!ÍQHaopíÚcxæ™gpûömò«Ú ï¾ûîÜù€9PAÎq¢…º®1ôF7¢}2­ï›=D,+d™DUרïv ˜¦™·ñ:%”ív[ŒãpEª¢ðå/ý0 Ù7,ÒÎ1 S:Œ¢€šï68ÚÍ‘œúq@]ÕX®V§ EUÂNu½À~¿MY+Á 㘀„PÜ¢Nã)4t¦í4‘3Ë iÙz -5Í~¬£ !ÅÀs"CÛ‰Pa +ð@ q¶stșܼ”kûÁÏï©8‡c„÷…ÊaŒ8„”ÕΈÈÚÐú ožáÛ É)E Æ›3Ï AXQË¢¤¥Lבn¢÷Gs,BÛw¨ª… 9¦1Ä`´n²^ÀcQ/*¢Šg†=|Ðpœ–Å@H¡>dy#¬¿ƒÒš‘] ŠzÎY;ÐóIÄj/Tœ Ià[ZÔä:§˜Úq@ž¬^PМCóɽ ôL†‚fi^D|Ž*VZ#8i‚&7³S§K¡U±ª†GGÇi‹¬•Ær¹dpÌ„¢®P”Ö«õ¿ÖưP]üámòÓÏ$†'ÇÇÃ˯¼ò>B8õÁÁº ——;¬7 dÙQ°XãÍ ²,CÛµX,xã·ðØxüÉÇÑ4 Ö› | ØÆ%‚„Pvr(MޚÓ(/ˆuJeYR Ø«ªÒß%†ÒÇ3UFdÚ¦<9#ÍÖ¯üʿƷ_úŽŽ6øÂ—>O|üãøØÇ>Н}íçñæ›oâÝ÷ÞÃwîàîÝ{¼t°TCŒÅbÍfƒ““l6GPJâhs„®ïpvöwïÞg™E`iÎ7¿H¢òEµ@×wPR#Ëù-} Ÿon°R æîQuܵ-É-d@`ÊË8XŒ£¥,[ï1Ž}àÀ—¾ô%ܸqßÿÞ÷ɬ?Ï臑0Ꙇµݾõ>ý\gO‹‘9F©K¬–Kгœ-ªªBÓ4̠˰Z®11Ù\ŠÀ»•¨Ð÷}ÂGi]¤eŽÒüB@*#%º¶ƒ© ./¶¨j jö-ÍçÜ„àivXVy­¹2k›ÊØÉééFiÌRrå30ðÖ0Œ\4s8Æû~D–Q~È4Z”e`Ë5±|e±X@®ïgÔö ÉBi´]Kã!!è“Ó3Ð4 Œ6X­V\=”…Hy(YNÚLáòz k]ú¬§Ña&ž±z,–Kš£†ˆÅ¢03ý\Àά“ ¥æ¬…4 ——)ûš–Œ<‹WHñ‘¨>€˜¥‚Ìd2RhMï¥VdáÇáC1­à@,F)9’ð~äCžg¢¤ÓâiFÛvØïINcí”æü1JƒªPr§l6k”%YPó’ð_Y–½œþ8ÇÀCèƒÇ¢®–Eަiù%._U‘’TåŠýÇtJ9µ¬}×áÅo½„¯þéŸÄý»÷8©¾‚sF+ï1û7LÔŽPƒ’\%=¤t¬yq63 !eø*ªâhuŽ„‹€ˆ’C”Ñ-Îß|„¼ú êz'Ÿ|Ÿüä'ðÄ“ãöíÛ Õ¶-./·Øn/Ñv[’ »ˆ\LõEQ’úß9‘p’†Eï3‡¿SëÙu¤«ÕFaöŒˆ/½çø‹ŽR Mç.9ؾªJ8ë±ß7I‡›e¤ñ#÷Ƙœ_*x> EO°ÓŒ¾™6îx–#XšC }Ž*À@˜’GOr_ù?Nœ5=aìFJä¢R‚ãg¥RLçh_Ò|0VÒÑ•ñ•’Þϼ(‘i‚·–Eà=fAìDYÏmÛ`·ß%Ÿ|„ÅVÉ«úžœ5Àj¹„6”¡s||„££ ²<ûAä°þ‘‡¡¼²aÑÆàøÚéeYüÂù¶Án»ÇÑfM¸îÉ¡™Ú$$ж#¤’ȳZ<óìÓøÎËßÅsÏ=ƒçž{Ûí—÷Ã@x£¸‹ͨ1´–b #ù–Ü*!¥£Ñ­7SÒoU£Ý(¦ßÑžS†ç9#µÔò #/C) ¼÷þ»xëí·e‹š²™Ëšù‡®]• ò¨Î±á"ÅÂÚ m× ¿gâŒàÙYF‡½Ñ†BÐç°Ý]Â9rUP£â¹¡Àz½æ|Ú‘C¯i ,%Ys8>9¢aù@È(£3x4M‹ª¬È»=Ós5æy†)4¼›9ßy†ÔYF"fë&dΰʧÏï±ëÁ[ÎBV ^ÁZZ¥´ÔÂ,!Ï3ª€gÉÓÜÌŽ“³˜Æ Ež¥ê[ "ÏPå%µ4Р–«ëz¬VhÔ6-=Ș9@ˆffÊvÎ(BviMËÚXRl2Šw½8ßA ““SžuEÛÙÓD3¬,S˜ÆZå:*Ë [­#T•§¸Òª"åDÀ79 ¼ªºdT[ϳiÊÌ!ðlNñ2‰ænv€¤n†±žçeš_2CÒך˜E"%À‰z‡?§YãžZ{%#Θ2=CJJäYÁónŸâ[—KqÁ'¦¢Ö®ª JJÌRRfNNî®ä³ŽQBüÈZJZÄŒ±Î0›@ÑLGLþaíŒ<רë%†ü}eìÉ. 1Ól1{¡ë±ßï1 Fþþ´&jÖfs„år‹‹ ôý)ŽŽ±^¯“Ëíx³AY•¿Bè£ ê< §áÀ÷êÚy–¯—4ǰ3 q˪¢cô©Ý¡jErŽmwû-NNðúëoâµ7ÞÄ3Ï>ƒ‹ËK,—œgì=\×bf¹B¬—ÐûfŸБ@%0tx"mn'®h¦)¤ÙÐÈNŒ¸„‰ËŽ˜2G-jžªÏ˜ëÿ;:p꺆’Ê žãÔAh1Ú†üŸ5ªªJÿÞí–|“«åý0@©ëåmßažεèàìœ^Â}³Çz½N@…²¬ %%¶ IzŘŸÜ÷ÊŠÅ£›Òf³ÚDϾš8vëÖ-L3ٮ⠒å™î ÒxâŸÀ²P¦t`¹èE°q)y£„²ª šº¿@˜BR,Vë”ù’ñ÷žå‹|É;,Š)Ù3ÅF3¬tv3WR¢¸öØ5"¹xáq¾ PÕÏ=·x„ôjÛZx*‚8¡ÏQB› dF:ZkÉ]•¬«¶—Ût1MÎ’.2Ð<˶Ta’ãÄò<›ò’˲À0NØm÷‰çжmÂriM³6b#J ýˆ¢Ì!f ‰aêaÇ ™É!‚ÀìÉV8{Èj96ƒ*tjŸ…`̾V«%†q`f%ýëÅm×9äés¸dPZ!9Ú¶CQÐᥴ>hóŒÂÁ¼G€L6K:E²¥ÆgKkºªÉƒœÑç‘—¤Ò€e(ÎÈGÊ>Þï1Ž=s+ªc„È< ÖÚ R(lŽÖXÔº–œi׎OgÙ;vá~ØÃÐûßËÚ¹ÑÍ’WÞÎ:\^^ø@“ô ª ]¢È ؙĴ±•!¹‹DUÖø½ßý=ÜúŸÿyܺy“•ò=G€ÒÁ6‡xÅê6ÈW•âQ—D߸K£xèEÐ@´=E›[ …aõzÓRJžA Í2"—¢?ÆiJ€b£<-jh»%¥¤ð(F8 e7ÓÌ'CÀ¾ÙCI<æ¹£Ù÷ ƒ^2#ÎCãüÈY‡i´Èsƒ9xL“cÀý™d1 ¼¤ª·Z’0ùùŠ\ƒÀ RJŒ  ,… ˆL<Ïak¶„¤qÆìe"F¢ Ýú$ó0Æ`²6½xvœ $6I!(6V0è èQºR/jÎ’W½ïG€ýë&c ÛÐu Ê“¥ïÙ±£¡i[ïQ/7w~~©–>EH,ÊÂèð14›lö-*Y²lif+!- æàXA¢^Ô軞Wžšzö O£Ãî²A^j@‚¹z#{¥éÿ/Š<%îõìY,–lç4YDÚkhC?×¶m/±\Õp3mÛ//· +-’4ˆ´…¯i'‹¶ë¡„‚Éu"Éìv{ØÉq‘qX]6EQa,¼§âD+ÊQŽæÀ)…wAŸ‘Ò šÿÎóK,K†AÐ 9Ë3œ£¬*4|åyŽÍñɓǮ£k»N|rý1įk7oáèäôѲªP9,+ø‡~D× ¬ƒ£Áh,[‰É–³F£®+opÿþC|÷;ßÇSÏ<ƒº®°X,“§4Ó "U}ž’DÛpi1·ÅàêÓ§ &ÓPðùˆi’ÖŒroW‡žgŒšE£‡U¼äü šI©¤u‹žu–:GÂî,Ë“‡5J~ªªJå6dìéõ@ álEQ!3òŒ*;­ ö °åM)áá0ƒ,Q³#,Ù4ŽÉQ`'Z:ÕË E•Ñ÷9ûC~p€S@’Ä‚t„ãD p#·™ËÅàìi©%œ÷>°—õœT‘܃£6)¸‰6–s ŸP€D,'ƒ'f iöhsk¸]¥Ø„<µc1ræ ¨Z”¨Ù5äÝžgËˬç/}:ö{"H¯×+¢õÔqIËY‡-H€)ò³#‚;Åù*Œn‚s!u'œ! QV522‹ƒé41ùr²SR/P²ãüð9Œ#ܘ ú¾3CéŒoפ¸9Ð;ôº¶Ã~·…sƒUÊ (ˆæMïQø >`½^¡ªi”7#»}Šã“kï[ç¾ÞÿP›d³¿†¶A™ç¯ž^»6FÄÿn·''‰¤í,- ŽOŽ(Pš+¼¾íàfÂŒk¥q|r‚¾íð›¿þ›8:9ÆÉÉ1Š¢ÀfCŒ?e4´’!pÚUp±m ¶yJQ‚:ˆ¤‹Cî¸ Þ#ÏòäÇ3ë‹„ècŒKètœ9Ló»9ñé(Èœ(HpS¤ VœoXF@¡éZ…Ü !mF£Ètœ´™ý»®Cf Ë%Ú®#I ãÑ)MÎ3È‚„åõ‚’Ôš=‰\û¾Ç8X­W< ‘æ†Ã0R6t¯ ÂoqÞ°œ=ↆ†çóL[mëÍ”Åö•tœïÂÀ#»2²­µXÔ5´"?ªÉ [½¸*s͵¸²£²,©‚ë:þYPJv±Õ¢LŒÃE]‘«Ç“›HÎHìǹX,L 7V«5&K#))ØhÀx¨šCÇ…¤K‡gŸ(ð–Ù›³™¢M³Qž¿#Ïs´M ­Èf6MÖq[ØÈL†7ÁYž±µ/ Z³<(ãÔ¸¸­ÍYJÖ¶l8Pypjù1Ú†ªH“è5jµÖÉÝ‘åÕ½'¢µ1)“Xq‹„¸µ&ljMËÑØÞæy‘€¿YžœBSzŸwt Nã„®§çŸô‰3]zEŽŽ3¤Ž˜™HÖÁ~t¼Yc¹\R¥€ã£cäyö½Ù9? =Ü4þP_º,ëyj‹åÃk''o—yþ10 œôbR{¬p~vÎ~˜rìgÚš …ªªpóöM|ç;ßÅ'>ùq<ýÌ3Ømw˜gÕj `»ÛÂN=¤×)Vìw‹š=Û420¥J>uÁ¶ÚY£©ó°–›Jä™`èìÀ³vÅ|ÑÚŒÃÄȯÆQÔ^SbQÔõYF€“åÈÊÒè”çì½ÇÈJ‡¶m“¯zIç©´Ä0N8::†IÅ í7œ³X-–X¯V<ÿ¡L†ÍѤÀo¶¼þaé}³ÿ×Èòâíõzõ±ìüMÛ`ö›Í*ЋW–tDF[ÌøÍó eI·ûoÿöïâñ¿x?þ8Þ|ë-TÕÓdÉ®âÀ?z“‰ŸGp™0^JINmCB¯}j±R‹™ľïÒ!ž9ƒ&gá?4 ‘Rp釡£*‹ó ãL“l[‚Ë$©˜½KäçaèQUU"wÌÎÃKâ1v]KÉ_y¦m¸ºö»=¤Ð(Ë ýÐ'Á¶1‡|e"ÉŒlí*а¿Xó¸¢( l·;,K¬×rÑ8‹íngžz íÐóöÏÓ°“£| ÛnöéÆÏ ~Î!VaHU§-¬¢t!V…±B%ÐÍqú±ã LÂŽ³›‘rCÙ-»ýõ¢‚w»ÝÓÀœC£Ðv Öë5Y  Ü÷Mƒ¢ÈÑì{Ptµ‡ÖD„šf‰“!U†Ð“ˆhâBJxÎL˜YÔ t}›„ëã8$Ím4lÄ®ˆ"(¬*3Ež#×92E3Â,Ï8ì‹À¯v&›â8Žh›»Ýž—LR®ë ãHŒº®ˆ×(èÏî‡à”Ãõf“œŸŸC ›£ã¯gy–~ž?ÔaØìvúaž‘çÙƒÕzCƒÒ¾Ã‰(Kò`TAPŽ¢HDFCYëPך¦M^Û7nà·ßÂïýÞïãOõ«¸¸<§ÈÖSÐO=X s:ÓxŽý<< J (EóÄ(¦Ž™4\¦òšd9s‚'“aø¶…–’õ`±º!÷uSr Ä?/2¡°ª*®Z 3ÿ17V‘û}“¤CYfHðëhÐgº¹CYU(؆´Ýîé†Î4†!ðàµ7šÿÃ0 È ´]‹ÉY,êd„"¤~]ÕØn/ÜÇóyŽ.*LrLi¦Y,oÖ’^,3†pìñþE‚$6B 5Ÿ oýD sŠU¼ÖyA²£ 2£a§EFI~äëL0ר^ì¡”Ärµ@]ÓfÓòKß¶ ò¼ hKfòQX“€s2w^ôϸry&äõÎØúÙ¤êÈY—>_)T¦à&ÐGp­VÊØïš”@¨U– ƒ#U@¥KÙä9„¤$@?{®øè¹v~Æ~·CYX¯ÖhÚ–mrS±Á¢hƒ}» °\,RûI ž9ÅNÉvÇGÞuk'`&î(ålé œg؆H°a’É-жì4sKÏ Ñ)$SGFV?Ïï›MªÁî¢ÈQ• ‡¿YNÑŸ6 =†‰0jm×až-ü„ÑmÛáøä=€µ–³_, ¸0cì lZ–ìdÓò'RjfI02!øµŽœ@ªÆ•œ§1$ͦµsÿHªO®‰"/ж|˜Ñ=+®$ L®¸¢˜£ïeA/Òj‰¢ÈñèÑ#ŒÃ”|®ÚŒÓë,êšBä3“.SЊÌÒ Ìãà°X.‰‚̛ҮP´x Fa,| 6}£¹F72.ŽàRÑ¢II‰\çP:VMš=º4_>>&„Ô‘s»¶CqLUX‘—Lòȱßí?Tu»3r ˜†²>'Üxå¯àñÛ·ñÔÓO¢ëÛDÐõæ3]ß`sú€Éy–*S ™†q›,¥£Ñ’cJhÞwr{;¦¾¸„ñÞ!ËèhU‡£”ÊÒÆ:’7hI“A(rYĖܳó#2­µy” •Â4‘䥪*˜Ì`¿ÛÑÜ)"ž%1ã0$Ù‚””¬¦•‚åT4çвDÛ¶¼Ù. 5IƒÚ¶K›³ó3<ûì³Øí¶xÿý÷pçÎûøú7¾ŽŸø‰ŸÄ‹/¾ÇyÇÞùDq fª åÌéj´|pŠ|©ŠeFQÀmŒÆåù†‘d~>HŽOpíÚ5Œãˆív‹Ìä©&j.™‚´p‡ÓH9È!x8ƺK%8fÒÃ:’[œðÅZ«äŒ‘R²3ˆôUU ëiÉ’å&u7B(Œó@•VVÓëé%"©KÀ#šÌäÈMA„—àX£Ir®qìi ?„DwÚï1ÑaÓõ0:àÓÁF›v•RëV«%†/rüäEF¹Ës€›¼ ˜éÒʳC`G ?ã@ióLϺ³«;Ù‘1ú’"V'*ʲD–ehšuU“bÀZ¦HL“E7t'ŠŒ óiœaÈ™!˨6†Œ ZÁ0™<†ÉÐwvû=š¶Á0R¬p”¿-—ktmc4CBdÒ !°ßïÉeÈ‹ eQQK½£ØÓÓhe¾×¶ ~B}ß´¸úµ¿ÜÂMÓG›õÅr¹BYÐ,b{y‰³Gç˜]`9Ç8ŒÉW\U„'B‰H·PLqÓFvË9|ã[ßÄh-nܼ‰,3¨ªÇÇǨë™)Ò\…4M’hDòÜH`&7L`Ï"+þ­K‚c¥®‚Kô:\Á$‘mÛðK¥´éüQsZòBSåéR|il_³,Ãr± `³Gôu]‡¡'©BQ䉤-EL\S4Ãñ…VèÙ] xQdŒN¥ý<;4M‹¦i0#/HŠÑµ^xáãé‡ý›¿ùxë­·ðÙÏ~Þ:Bs-|HÞ8ѦŽÌ?ä›Çv¦ÜÙÙRJ¡§Ö}röû}º|´Îpãæ´eγœüÐ ÈóŒå†ñò膆F*ƒÖú¡ãD;Ò}†™¼æã@nŠH®ŽŽ¥x9ØVòž(ÇŠ:Ž]¸Fr”g h\L¥, ŒSÏYÑDžôaØ;^r6¯@YU‘^F&AÇÀõ‰Ã“¦iJI1æ'«$kÈóŒH7,û¡ìiÊB‘üp~ÿ{¯Aà _øb:ÿñ?þáƒîáË_ú– ÆÛ™¶ÔJbD5K1â ˜*ˆ££™UY”˜†ûýÛí%š}ÃÚDú™]¿~J¨5pÀÐåå%U³YvEŽr48r½lÖ›D¼ö³Gfr&²vˆÌ´Ï4ëîò´1Žü¿økH“eÈŠâUp}ßc»Ûb»Û²Ó*°ˆÝ`Q×èûU½HÊl»µÎ²¸ÄjµFQ”i‡°Z/±Z¯B4Qö£|I; ¸ú5u0ÏXÔ‹w‹UÐÞ{´]—´šã8ÀÍ3 xö ˆ[ˆ[øËÐ÷ ZšeRGÞd:ÍT­µ0šÃÎP.ë±½l!hc C…(Šýð¤Áõ³'úŽRè‡ ß Ïl•@˜¦Hi‹1RkÅ[™Š Á¯7ñI}º ãì>fD%åg(Ë*ÑÜ [˲$ÀÄ<£í:õ´ÜcÛ^¤Nd0ku½äs%Oïo”¸=xð“PW®_{ O<ñ$Ž6ìv{4»->ó©OãùçŸÿ¿„—¨¢ž¤/õ¿ùÿ Ó¯|…`§©zõ×ÿÊ£‹slw[‚XJI†"§ÄÌÉkÑ«­óL4áCÖ3º5{&¯dŠ$ÎÎðä“O’~©kÓš^)E3ë’6ÎìbàÏU*ÍÕ8Ãxè…0ó!(¯ˆ¶étœ ­•‚`%µ¸žµd‡›0B#bØOD8ËH#}©ŠÁTqÛõ«-:E;æ 8´úQ÷8Å™5†éeŽ•câÕAð¶:gðeÅþå¸a›¦ÛË-òÜàÏÿùÿ>Àƒû÷ÛËK|÷»ßÁùÅ€Õjõ†D¬Çë –‹£–hƒÞ´ îÞ¿‡‡îãââ »Ý.hNhð¹Ï}dû†½ÍSrÅÍ£áÍ»s3ÏÙfV ¿R–œµ˜y,•@žå躖ºø+e%]2}7ðEå „ª ¶ƒOUIäÖÐJa¹ZÁ9K›X^\EÑþ4N\ÅóÌrš’8ZÁD$;šÒ Ó8’Ûe¹À<L–2‰‹² ýè0pÀ;ŽÚ(o§±yâéß=ôLF2•˜\Gb“`±Æh,køŸf}R*NÚ›ùò éïðÿÏ)h2Æ{Fr|”ÍÅKR ªª%ò,OQšå|‰RJã»ý]ÛÙˆßÑ(<¿ÜnaŒÁÉÉiúyÒß›þìý~ËË-”X-Vxìú <þø-äE‰GgI…?õS?å?ò±çÿ·€Ø‘ Ýüh_ÿ.³ŠŸgäYöÁ²^¢®j,ªö]GŠ¡ÿëELè»›ÍÖŽè»y^’γ&7ñrÁ!Œ@‘eœ¶µ©Ža÷_~_üÒp}pÿî}v…,Ü‚àªÃ€‰+É´“CªLéÐI(É("±Õhwh'ŽˆÕù†mÊ`QJhèb…G¿.W¥‡Ê,(8W£JsÂXJ¾µcGNžLØ€®ë±Ùll·4Ÿ«>¿!(¿ºë:ŒL Œ=3Æ Ù·øúïEQãoüõÿÿôŸþüê¯þëtñ¼þÚ«xýµW‘çV«Šœ¾I­ã0`œhè}¨æ¢!^œår…O~êÓ0Êàþýû$óáÏ)39F;^ñš“THÊn ðÈ2Z&Xž¢–ž¢)•Ì?!0»¯iö¼Íµ @R.å‚DÕ}OíT€G]/Ð÷<åꪄÈ,j"AO£%Ÿ°öІ²bÆa` °ƒÇÌbf•âG£½ÓÏž´€£ãÅJ‰®Ð6QqB?Ó¥<J ÒdŠÄ×<‡œ9b·ÈKŒ#ræÑS©ˆ™eœ¤‹èBÇ!À{£3záyÙu5O£M–Õ<ÏX;1ØØÁÚ9½ 98½ò"‡ÖŠ,Kóø¨1¤r²¥Ž#%úõäG†TÄ`µZQEëfŸ *Kš™:—…Ã00²Ì¡Ðe^`±¬“,ÍÚ §Ç'ØoÞPZ¿ï|€úlxɲ>:úýÃ,ËP׋;ëÕjÊM–å&ÃÖ7nx;“žK@ /(‰tqŠøt¹á“¶Î!x,£wZß iZ\»vŠGá?xÏ}ä#)!Ï{Šõw{¶oQ9KEŠz¤™Ù!J” Š @Æ@Zÿ3ËP›Tî !Ð6 U<`>ˆšç´°ˆ‹)iyApgj'<“{•^øÐïY¯Wh»îCD—É’[D+2ø3ŽãÈR¥Täy:H VBK~è™›§h0Ž)Ê``´˜s4,_oÖÐ2ÃïþÎïbü…¿ð¿À'>ùIü?þëÿš}Û´UÇ>ø#šXiÎkyìúM|ù+_Á8Žxû­·iNè,ò,ç* fÅSÀÏŽDúÞ£íÚƒ=Ø+Y36mX­õ0š<Øv¢e ~©²?¨ è%Šù!1s¸iö°Ö¡*+LÓˆy>p)í4¡m÷КÓè®Ð€É2™ÓÓSÌ~BßÓ–Ùsn°ãü4Õ·eˆ0Qˆ š6¶×œá ÏN­¨7Ì2ÏâkIÎ+ç‹‚+Õƒ”²Er<Í󌮟8…²µ¥TÁI——À GËQ Óä uÜ®û´à"p1dQ0oŒJyÇѨƒÎ¢Û‹bD‰Ê´Û“°:Î×#ïp½^C)‡ïb±XâèhÃsTŸº¯8›¦ ÎNe‰¼ªpt´Ažü󨈈õ1àJ¶ÓûKýÝ¿ý·áCøÐ×ì=ʪlïÝùà?zí7®·]‡}»‡c¿jYVB±7CQ–€/3H|«µÆÌ2 Ç(!ˆ˜Ñ=lDvK‰Õz‡b± /ó~ßÖO€·ÎS 6>Ä$AµJÉ\±E¡QqcIÿŒæ´©¦–U)’ûhö*'q+G›^ µABW‰äzQ‘ÛDª|üÄZhÊ21éf˜¨9‹!Q‹Ocö…sô}[GÀº\ðœÇ@i™@Žl7SËõv¤£às)$ÞïÞ~û-|õ«_Å/þ⟃”¯¿þF:¨#ê,‚pé¥Wæ­‡àú““S|ùË?O~ò¸¸8ǃû8„~oÓìJ@~Û¦my–èÒ­N™È Í~©$5%œ#ÍŠisI‡kà¹ožç4;æC™f›9¹J& µZmÓ`öt8xGŸuð3„--† µ šÿ™›gŽ­IÊEK6ÅÛу¤ªï{eÇ¥)¢`¦X]ú ?B}ѤK$"xÁš¿aìa§‰=Ö ã8$}mœoÚɦlžXQgY– 1>3ú¡ã{YyŠù¤Ïã@(Š¡ïW‰QD÷éÓÁHôðв„ÑÔ¶VÓ?3<žÐ4Ú¶GÓPº÷‚Ç ”a"Bܼy+-Ç4/4Àš×óós‚0(…õzƒë×®áæÍX®Ö†ggøÌ'?‰Ïé ÿef²oLãˆàýü¥þîßú;1Â"}Í3 5·ÛíŸzù»/¦ì›ÃH[-%ȾU”0ñãý@%zÌ?Õl ·v‚ɲ´Éó MÛ ç°¦,3¨ë÷ï=ÀõÇÃf³F×µ˜Í5”¡\];Ù¤ŒBÌ(Æ^bÇù= Šg…>É?hÃ’-IJG•¸RÊ÷ðÐ2“‡”›DÖnúØZøtðŪ5þ}â\ó ôI#9Ž#]6\ņ@¡S´ôQ´T½wm¥4*ÆAEÄ-u,òœ,‘À2Mä¾¼¼Ä׿þMd&Ã/þâŸÃ/þâ/bµÚ ï{"CsJH ÇÃWž—xüñ'ð¹Ï}_ùÊOÀwßyç.Hž‚æªÞó`Z’¨<Ï3ž¥‘\döùê?ó «¤¤jÆS¹\.hKÞòãhÄP/+N»#燳tø”UÁÒ¯—IQú2"Î’”Ô _Õ.õ·Ö!Å*Þjöc£:I6Ç :0$‚ZP"Ð( b²ÖŽ˜çP[“›Ò(À: gé„籋Sv$ Š àdÁ#ÉÚŒ rC\ÃàEº$ˆ£éƒg6 K4¡xñNÓ”ÞúŒLâxÆwÊ9²ß*5…zA^ó8Ï´æP´»·º®Ã~·G×whÛ†ÿHï¬1§§×0 =v»=nÞ¼õz͈»%-À8×Ú wïÞ%ÉMYbU/qã±ÇðØc7g9¶Û-fëð“?ñxâ©'þóaĈÒõK¯Ö«gɸX-qrròýE½@^äÈt%É,>NY ˜w×¶-²"ãàªBÆfÞ”uº&;RdñmŽà¬ƒ¤«ª BH|û¥oãK_únÞx ï¿ÿN±db³‡åárQd©©dþ’Fé€Ù²¼!“W.‡a±sSª:£Ã³K¤`4Oi&h2²*9ËÖà±\.Ѷ]‚VDÙ€bPn´&’ÐTpeH5Ñq·z¤sÇhµ¤FJ Z­–(Ë mÛpÊXH4’:DéP€ÄÄ<¡Úþå/ý ¼ôíñ•¯|ŸûÜçðÅ/~MÓàÍ7ßÄÅÅy¢ãV´ÆbQ£ªkäYŽ÷à•ïÿÛÝ%´6(ÊûØ y’ª”)#òd’A.¥„ ýì)FrÕàY{0ö¢ Í·†—!e™ ÐÖz”³âæÍÙ9êEM‹ ©¹£±ìš frnEŽ‘gdÓ41Gs¦LÉTŽ6*ƒuм``,mÄû¾‡$š¦1Íi¡¼qšÇåI¡¤B™U¼ð¢ в€²$³Z,ÆMÓ&òzÛv0ZSåi2Ì3uÃHŒÎÀË4c¨Òžú~âò–‚.‘æ£mæ…iôb}Æ<[X;'¥Yg‰W`2ú3 £ü ·Ç–!­mÛ¢iZ cÏH®pá±\. „Ä~·ÇÑÑÊ’ÜDeYòÅæe¹¼¼LÕ"-š*e‰ÅbAgOÓâèhƒã“ãwì0}Ç2ðóKÿAT;MX¯Vo®—+òV–™µ= j¥¢¡¬µ3fOHö|E³BÌ‚­DÔªî·M†R*\…Å¢ÆÙÙO!õ›Íû]ƒ—¿ý|ö3ŸÆéé >|Ëíèr¹¤ ¥ýžv†"ÝR”c!0[ŸhÄ!ÌpŽ9!%ó )€ÝÏvF’Ò\ ¢š¦™7å*Qªgçοߣ( TuIG.— l·;ÒýqÐ=Íóò©%] á…±†òh)„¤@ó4%ròz½B×7Øï÷X.WÐF£ë»´Ìqnf?.eŒã?exé…-˜ì=XŠà,r²aýÒ/ý2~íW·nÝÄ'>ù ܾõ8ž~úªàÙ;Žv—[ܽsçççØïw)Ë:ÏsH,5./·€ _2e8M„Ð÷6Q¬£ôh‡”˜eÃ]ˆÛ7"ËfµJi–b9”EÕjE”ꡇ÷3–‹9üÌ?·(¼§–?Š·),KsÆ3a¦öûF˜LcFž]B·ù)mVÝìÖ«Ò˜‚y–B!Ï$Ú¶MÄöišP9†ÁqæP Ï ø0“ç”MBU¢všàbà®§($WÏä@r 1ì³_ RQŠ_îFHJ Þ{&Ù4«‰*CPšE•¬Ÿ‘ΓgÁ”=]ÛÝ}7`²#†¡‡µ3+((˜­®k”å]Û%k¹BY–IºånZi ,~w<êÉò½·Qfv||Š®kqqyŽ“M–‹%'^Š„I»¼Ü¢m[x7#/ hC—Òz½¢±`7n<†åjù¯‚¤VøãþÒÍåößù_´b‡åbñíÓk§¯eYþ|YÖÈÌÖö Eè9ܙ憓Ò<ëÒì<ó,nb)Ë•ƒ!¬WkÌÎá#¨L? 3ÇÇG¸óÁ]Ô‹%nݼ‰ÙZìöTÝi.Þì[ªZ”¢[G É‚d‘üÉ1–#2­âu4‚K®ë‰ &¾Í%ëô,oI½X,I,ÍAö±}¶–Æã8¡i[j7tÔF’¯ï +ýúcxôè,ÝçyÆ3ÒÖNÈóË¢H¼Å¨ÿ’‚<¢ÞýàqS>$›0Æ`´c‚˜ÎóŒ £¬ˆX|ã±Ç°kv˜0Ñœ” -R(Œ#] fI€„àiv<(¢‘Qb«‰Á§‰ëŒÀ2}g!Œ"kp m6{‚Ì6¡´d RCJÚÆ¶ûeY%JJ]-èòt3ºŽÀ™V¨júß´ûõ’ZÈdØî·89>Nþqc$L¦&ZdÁdð.Æ4Pî·>mKzE …a¡•FƹÌqÉ<µñÓèP”Y"#EK^UW˜Ç`úÒ42R’ ½Š·=/Žª²D×·˜g­(:• ÆÁbœeëf¸ÁC @åŠ+^Ãî™uUQÐÕ0 i÷WÀ$ñr¢î§ï{ά!¶äÕíñÄ!^9PU ’òHú;+ŽÐÍ‹y–¡ï4µÆcO˜»aSkL"m‰££c„àÑ4;”åeY§x_©dq A±ªÎÒ2GÒÂqQըʂß=A„")qýÚ ”V/Î1kâ{fÜ:ý»~e‰õzýr–™ç˲dat‡à 1D_‰ÕrЀ»ä-_ ^k’ú‡(%DºžÒ6σ¡ï!d,Ç œ^;Å÷¾ÿ L–áæã·1½ó/Th±&ý}béżH%?É –© y&û~à8U Ód©ì.WAQ¤J-µ1YÓZÎ ŽðQz@6¯”ôƱŸÖ!Ïe²ãÅŸ‘s3o‰/d >·\¹’ðÞCjI0\4ï‰ZE dÓ´i›“ú¶Û¨ÆJRìö;ÞìKTUä“Òá—Ü4Cr,B| ¬£vpöcßóFW@*‘´•E‘aÉ!¡´âŒjšJ%a•Õ4- w³ÓDV­"/ÓgM®‡-óÏ!)ôÝCÂd‰¤ý‚‚ Öë5fO¶E­ -F 9¡„”)Å0=ÙÏEa’–ì¹u̽„ˆå™9‹1ª”†,HܽZ­8ªþ—C?BK¢6õül‹ XÚÁy 7[fe:¾4Kް˜gÒº^u~øYÀލ®´bÿ´àÖ›¾?7;haØ’J•(³ÛrõÇ31º!J¹„ F A¾o¦ÂP¡AìKm ²"'a{ß³-³AÛ¶”à ›4Q8::Âb±ÄÜ€ÄÑÑ–K‚öÆ®†Bïט¦‘FaÃÀ³A‘ç(‹EUQT-{ø—Ë%nܸùž€xÅ10úûKý­¿ùŸ¦­á¿ýeŒAß´?ñÚ¯ÿm“ÚŽÍv$ê‰?ÜœUñ³›©Ü–„@¢Xщ7t´9 v´ë;ŒÓÃ|ɰØ÷ß»ƒë7Ãz½Â4Iß'…€ÎX/Ȥ˜ø²’hÈ%”ÑVOÃYjñ´¹I ¥•dôcÒïÅé µsdÍš“\¡ï{ÎF.¯>Èa yöh-iäh^¥/Ja¤É5C¾L¢}D„=mYÉ¡[yk-êªJU¢’17ƒB½ëj,'б RO­¢¬ˆ^bÈÁâ&O1"ðܪˆåzèâÓšÇ&ìÖt¡åYÁbõ‰æÉ3[•RØïw´L*È’VVEòÞÆxUBsiÐŽÉJi@„DÙŽ'¥4ƉHÛÎ:HvÎÄN"ËrøØ(“¶ˆ(¦Ó3_”%xžPrîŠÌŠð]`É Å{RÖµàf‘6ð‘2Û~ËFL¡Æ!‘½Sð•§¹]^t]‡i´”(¨s†¼úÒ ³wWŠÁ¹5.ÅÜ%?q¤6Åï3Z!i)ˆDð1L*«-EµJÔ (¬Îy!³ßî±Ûï8Ì©Où7’é¾BxlŽŽ°Ùœ ml/.°X®p||œ8¥‘Ò!+û]‹»wï¢mNp̰¨—8Úlpíú)NNN!…À½û÷°Ù¬ð³?÷³ÿ|½Ùü÷Ž+Ì?îׇr“ÿí_Ã8âèøø›'›c|pï…§km  e{?#÷qiÎÒá˜kJ)«ª Zä&çZ@¶ma¼ÆñÑ1îÞ½‡ýn²(жBÀ7¿ñuü©Ÿü)\»vwïÞ£ !e ]Ó ï9JM0ÏÙ*!P¾G`›OÜNŽÈ¦˜Dz`fn푤‘Œ^:xÀT—ÏRZþl±Æ¬i²is†² ’5¦Àz½âí$îBІ“ªnt£R”¥H™(-Rî…1ã0±t‡^€LÌ ™èûŽÄò”ye$ú‘´lÓ`1Œ=Ëš}‹à=Ê2Ç8Z%a4µÑ¹¡dWR‘.u´ “à&Ë™RÑ|ëâü‚r¼PÉó »"dµZó{™~¤U/}Ê¡¹ÉOâr€¤J”KâgGP7R»ÌÝOßup6CU3èÔ"Irb.t¬éb£ËF*eI­”Ï’SÚ!ÏyWïRÐöz–DÁÞí·I‘ÝC?Òaë2R.àgÚ"—E¦ÙÓ"ƒ[à¢(©ßM ’%í¤bYWÌ÷WŒ–çè!E~ÆË€R…è÷'î&GZhMÒ¶LšòAzy¹EÓì¹(˜>_œ•³X,q||Šišpq~–’ã|¹È Lœ#ÑÃУëºÉaòyQ ÈsÖÙ 8&KoޱÙýVUÕÊ ÿcµÉyUþÁecf°>=þÍããã •1ËÖσ0ø~š! B µm—ªC­7²!UQ™áE‡êªBÓv‰ g9Ž6ŒÓˆ®§YÀ88Þlpÿî=|ó›ßÂW~â+¸qã1xº¸”ájÚüŒµsÖÛÄæË² mÛ§êsä&‡QoatAó¶iBQ”Y9B‚~æëõšPoÛ Hþ)?Ú ë]òC H"w˪v€¸…ôy¸TÁ()Ñ4 „ ±ŒÉ˜±çi«;Œ^!`-‹%”’I[ëì”dYÖÙ(QV‡8[Á¹>3‚ÉÛ> #fOfQD‘ÉÅß÷{•;d&#²’¥ù5éI;Î/X©A]Ë~¿'¹Ò˜çFQLCß÷Ü%ðA“ˆîÑ>«D@&(…䨪ª‘å Ve²VÔ~æ÷ûûýMÓÀډʇebtaŸ\ƒ÷g˜f‡Û·GÉX¯ãã5¥6-´R¨ÊggçxtöäRÎC劜LE “QÖŒ”CÓxâ‰'PùËvI†÷'9 ‹,ûƒC¥PTù»'GGo­Ÿ!úlŽ^õP0RÃz—fmU¹Àìd;!ÀØE–Ã(…ª.QVüÕ´-$€'¿õŽ”â×Û¾ùcë ÓaHÉr•^ÞÓÓÓ×Ë¢x¦ëût¨AºåeCÆ9Ã@·ŒÉÉÖZÈšì´±¤p§ÑŽW¼ˆ3ëìš$ %m_D€)õÉObžg\ž_0ê? ÈØ <Ú}ƒ!LW<ËùL‘Bš'JÞˆkmØ>’õ)ŠQM¾U—` ôgýy²‹E"-“„(ðÍM wö T6âöû®Çæhƒ7Þx/½ôMàøø„[<§Á{‘Üî{v¯E¼ªktm‹‹‹ ÎÒ˜ô3ÏJ\¿v Ï>û,žyúÔuÃúÇwÞ{M³GÓ¶8;?Gßw$fnH^× x?cstD¼B#8#›Rؤ )K`5Apç·ìOŸ)áë¢ÀÙÎyQ`¹Ð©š#ìœO…¨kø™.ÎŽå^“¥Î¥dU€”*£9òÄÒ›8š©8+™fœ>Å1xO4k) BPIDMÕ)ÃE$EU2g[ªãÍràKÉ:BZxF´a”?YQ†zÓ4œ%4ðŒ3¤ó". ‰\¿@Ó4ØíwÈóëÕ Yž¡*k”U‰¡ïѵ=Öë5òœÜ$çÜm›²(ÊÔ®/êéfûC?àxs„Óӓߟ}ÎÎø“þÒsøCJK®vn?ñø¯,Ë_¸ÜnÓ¶×Y ;Z˜,ç0JPžC–š§)"ÒÒ<\`·¥Áù¢®a–†­T4·X,k0L=R&tm3FìÜ~â6Þ}ï=”e‰çŸÿf7Ãï¶PNÂ`œ&, (¡š=†!`žGAŽXI©àS¼!eîRNm–6a*áýÁ‡©MkÜÒ9¦3Çÿ­àpsïêºfoåœd2óì0{—r{OOOñSê‹È³¯¿ö½‡÷iÞ¢ ŒÉà…Àä,FGhøÉRj^×vœ›Ã͘€a>T[û¦A½¨8âpÂj½ä­)°Z­HSÖ“„‡f69G>vý1¼úÚëPRâÙ§Ÿ‚Àör ¯=Œð³BU%/C¯8g9K«þ,cÁt˜*HJ™^ÔÅW¥Û8ãZr„+ÀÃY©í’2–8ò›Æh¹\bžg¼÷îû8>9ÂÙù†¡Cžoð3?ógP–Þ}ï=¼ùÆi©Àÿ¬yvxç·’Õ/ÆeQá¯þå¿‚Ÿú©ŸÂƒ‡gØí¶hšo·qÄÃGis.‹5»qÏ=û,>øàn ÞÙ¬7øÎw¿ƒ·ß~;-oŒÉÒ€pMÄÇ “°\,¡5 ð•–ð˜f¢¤ÄÃ\¤vš’䊴MƳ¥9¬ÔIôC‡"§0&¡˜Æd 7%¡Hƒì\{²iŽçìœ;$wJ¬ž$ -‰Ž3‹!§E×w¨r"}Ç »ó.mñãH%3Þ“”‡žm¼€›,Ëj(éNðâá{8^Þr‡†‰Ç2>CÚ¶K”ŸøÌÆÐ3c à%1SÈù)aÔ2£QÔzÆ¥”d¶'Ñiè`ÈõµÝîx3¥çèþòÞ>`s¼Æññ1†aÄååÓd±¨k,+,Kž“îV”ð0ð³‡-vû]Â| Ž-yüS9KºfþÜÇŸ¸ë7®ÿK­drWý‰C?ÿáååØ÷(òìÎc'×ðzöVšCqŽ6u̧>¥zEáïjµD/¿ü=H!ñøSOÐMÙ¶ÑXj•Ä®mè‰Ô"Ð E2x4$¸jG_ñĦx)‹Ãð›Ñ€†R‡ô<ú½>Ùež' ¬âCј,If¦ÉâÝwßÁ믽ÆCyƒ¾ïñõ¯O>ù8H}€6&ë©5×(Š÷ïßKÁå1z@J…¿ö×þc|ö3ŸÅ–7ó±µ¢±ƒÄc7®ÃNggçhúÿæ7~o¿óvÛ-†qÂÀŸ9ñä$NNN¯¼¬±ß_8=½†ÓÓk! ï)à;7ÌÓã*­:ª6Ä¡]“’Ø}DæÔ4JXË x$7 ØmàgÊ$Ž£hIÑQQÒD²-$$¾.Uz‘FŽá4&ƒ˜-ú~â=#qsìœãÙ-Üì°XÔ\ýÓfz±X¤¹ržeôùx²úÁË´­õ<Ìó ¨%ÎtŽÉ軞ÛuB˜±š ñ“ ;k£6>fŠÇÖøj,YûôŸã,—>/4“ÜIò{­`¤¤ELF›^„€]Ó`·ÛcûD¶‰25ÚN3›p½ÄÑáû÷|°cp|r‚££#j»µÂ²>Âä,mÝsƒÙ\\^ ÚÔ™e™¡,$er¦¬Û‰™JáÚõkïîvû_ŽÅŸø0¬‹?‚]'°\-ß¾}ûö÷ô7ä'´Ñü½Ó0Ê1ÞÈ&Á±ÑYÊT]­Wp¬ËX÷g2j}£EH •”êy^`³Ù œ œŸS¶†-'X7²ýK‘ôÄÍxù{ßGÀÍ7ˆöºÛÁsÞ®Äj±LðÑ®ë(¬ˆåW„‡‡`ú惑€@ÉbÎ& ®H˜£ð9"ÂâVšæÎ ‰¢c$‰Ki¶ãS\Âb±D–¼öÚk”&€ïÿUôÉ](l\QòÑ4{4ÍþJ& µô_û…?‡Ÿý¹ŸÅý{÷‰æ"fÔU…¢ÌÙ8;;Ç;ヒï¿ú}üàû¯àüÑfg1‡€¬,Pä7,“¨ë㨰9:â- Ln ¹PØívØíö˜¦ãØc²3göˆôï$ðFŽ££S(¥i$ÃØr¹ÆÑÑ19‚1ë²@?tJB ‰ý¶ÁdGt]›.¤OJ2#Úb×=ÿsðhö n^»†ç?öÑ7{üú¦ýñ†ÞýѧêØ8Úl¾yrr‚]ß0|Q±ÂŸ¾)(Í6‡L“~«ëiÖã¬E×S»S%‘<Æë ™êš}ˈ"Ê &ÑjŽ1P 6m8dYŽªªHgçV+š»½úƒ×BÀÓO?…€€vß0µÄc2QR´6Œ„ïüAsö°ZÄœ[ܺŒ)y4Iɬ«ÜÄØFG"´”4ûqÜ ÇpîXÑ„@sÞ~çM<ñÄÓ899Æ~×°œ*ðá“¥v~æŒòž ¶žh»&i;£4IÍ™H6†7Ôo{¥Tðnƶ¥í4¡ç ´MÃíšõn—)¦!†õ=µñ4Ž$‘—2‘A*q¸ð®,Y¡Í«Bà‹.c‚K? p%4\‘Wða¦Å Ó§µÈò y^b&²÷i™héd± ìµiiÔu=¤ÈY¢ti©@fŒNq´±RUJðûS2ÖK¥6;.³,GžTµÛa{¹EÛµLæ¦Ê‘rá$AæUUàää´Vèº6!ìêºÂÉÉ1 ÚÏ–Þ¥Ñn,øÖÓlx·Û£iš4bRJ“ÔFSÚcž(ÊZ)tœ©rëæ \{ìú× ¬«Ïa8ÿ½vß÷X­¿uûöm¼óÁtj£am®F;!ø*P+â¼…§ä¨d(¤v®(s„³WÓͼŠ(à0j7;4û†ùi*i •T899BÀk¯¾„€gŸ{»í–ÖJbv.•ê3 š¦å©=#MUÌaö|Ë"½8D:ñiB•NÿŽ8{¡¥á¦hfC”øwˆ‡Ú<»äA®ónݺIqýòZ$Ëö{2Ý«yN”à‹‹óDòÖ&Ãr±Ä4NÈN‹´}ì‡ b ŠP)…Bk‰¶m 8ßÖ9 ?Ïھà ŸüþüŸÿе.//1N^|é%üöoÿ6ŒÑ¨ªEÚjîv[87ãÚµkøÒ—¾Œ¶mð?üÿÞ?ŽªZ ïZ‚*”Ôºº™roM¦i»Ìá`ô™2`TäY;9HÙJ!;òý¢íLHº&;¡®+”e‘R KVÊ\'9LÕ Ö4OÚpú¹MíâÈÐŒYŒnö\q¦ø4¤o0g’%=-£Žž/ÃY< †Uµ@–gmO#š~®fï1ö#§KÆ%\Æ ›ìžñ2<Ì'C‚Œd=Ÿd-óAy¸¨£„¤`tî¶[ŒÓÈ-˜  %µÈÂhŸ2}ªeK^‡¢(píÚ5s±çH[©H#lgvMÌC³³3LãÄŸ;‰¯‰tD¢ó²,Ò(mG(©ðÑ~´ÇáÿÝ]ø£ö?üaøCT†ý=½Ž³³‡ð~ÆåöÏ>û ¦qD? ôýi¢;÷ý@B÷“h£q÷Þ}J8„€6çjŸøøÇñ‰O|=„ÖÏâÞwñþá?ày–7'méÀˆšÒ¶~ögÿ,nÞ¼…ÿî¿û‡xúég‘å8ëõ&UTyža·Ûc³Y#39>zÈZO"»ŒÃ¥ ÜØÊ8¦Ò#•„ÕÎ9²fyÒÚ CÄå}™*‚ ŒQLR"Í_¬¬&;Qü€Ö˜ýŒ¾£˜ŠÌ°ŠÂû4ö‰Ûðƒø0 dB®å¢`¶ g¦ðˆc'‚HpV5ZCÚTǃž:’lYKßkô#ÜR‡ù±R RŠÄÊ,Šâ`dà¯Ø’’ט”Ûsìw Ú¶!%H`¢;CÝ“âMV`µ:BQhš=Ú¶ãM¶ÆjµÂññ hµ!1z™ÁÏBJ˜*ã Ø£ozŒ¥F‰Á¤é]1&£ €ÿüGáx½ÆG>úÑúYº†>‘ZFÎ…è:šÑÖ‹:µ¢”1ë B@n2h“Ѧ¹épëÖM¼ùö›8»8C–—üR" å%‡•e‰LçøÁ+¯àóŸÿ<îß»‡ßøÍ_Ç /|’õf‹BÒ I({ðHB`½Yáöã·1Ž#>¸sÛÝU^B÷!éHÙ*Imr†tL?éû+¡ƒ?<Ò¬µ¬¤ê²ªJ¬EçQÛ3ª‡~àg;§CA û†Ñ¥‘•Qu2{›–ñR¾Êè}Éÿþ˜ÍÇqö¨u„}¸ÔåÐÜy`|HïN–)^ðA¨¢lFp8Ú¡“‰mõÙÙ../àÜÄcϱ´2("Ða¹ÜÐEc鹆Zl6K¬×+¹‚TN¹6ÖZ8ïQ2ß`‰x¹ÝÂ:z'%j3%q}U•¨ÊÞϘ8KçääÇ'G¿ 1™â_êïÿ½¿ÇÛÊ?üKÓñì«?xõÏ]îviVÞRÍþdÒÍ3Eì•R'WIt„Ä„³do’Üj­ µ"ÌÞ•–èú–ðYYmšÄ14™‚÷ï=ÀÐõ¸qó1dYž³žñTJÒW” Å(L!ÄHQ¤Â·^üÞ}÷dY‘Ú«ø’ˆ `2ƒÕzƒããceÝn‹gŸ{ßûÞ÷Ðu]Ò5 ±û2“ÃYõz‰"ÏQÕ5´"hìb±ÄbYsä¥Ç4އ m>c”h„eD¼}Ô Æ ®•snGÔ|J¾fÞÐKE™àó9†IÑvÛ->û™Oã ŸÿÜaÇéþÌEÖãKk¥¨S3̧§§ÿìñ[·þ/o½÷î•.p›Y§`JØrIó¤¤DÐT9EÌN–ÓËïØ›ž«(¦âæ:ƒÝžt*M‰k];`k.¡ô1/:¨-2R…Þ{ÿ}ŒãˆOæS899ÁÅåBè1³Øh Ãy¯BJrÐ(›f|±JŒ þ<Ïìzˆ’¤öÈ•’,ÞÚWõÜB©b›EþeÏ •‰ýΚ³(ÀÀCqGÎ#éú¿ú¯õõWqrzŠÿæü?ñÅ/~Ÿÿügñ›ÿæ7д{üÙŸûdY†ín›f|'Ç'Ø^\b·£Å‚mu™tœç9>¸{ççX,–ðÞãƒÞ‡R&Q‘9m²ãEG>YÃù»TéÞxì&^}íl6G|€Ñ…°^¯°Ýí‰9ØŒ¸{÷g‰Ì8ÚãÙçžÃéÉ)îÝ¿›àñ%ŽùÈqİ^¯Ðw=v»]º\©3QLrvP|‘ôk‚”ö€ó‚s­a¦Ú÷]’ZiM³Æaî“Ø™ä$[ª”Öó… f™*»xˆWeIËó É‹A"áÚ³Wؤø’°„¤hˆ"õÀAiã01îM¤ùŸ1eYPñÀBð¸1þ䈋­þÅÅ%v»]RZPìP wNJ&ü(i°Z¡ªh<Ôñ磋’qYHn¡àÁttò»Žþhš=aâøïT´ÒJBðóTUUŠ±í‡žÌÏ>û-•™o7»>…ÞÿX*Ãÿìïÿ}¤ëéùò! ^.Ïïݹû_ãë“`ÝŒ Èm5G­[ O—9^` ‚H\À<Ë!*ºB®„H»˜ZiN*³ÎÂN4W¡‘ýÄñ‡®dÒM™Láâì g8½vŠåjEKÐÎÍ|ÿ)MÃpÁ¤ßãú­P$Sü¦NÒšx’=N}H´·ÇZ™Cú›R )Fr+$–8›aÎ]” Åà«—PZáÑÃ3|ðÁ=|ñ‹_À—¿üe|ûÛ/ãììyþ#xøð!¾ÿýW`´Áõk× ¡tq%LnðÝï~ÛË-ÑRº–SÿFŸãæÍ[„ÓFl·—hÚ=Ÿ‡-z"ý=Š¢Âññ 6Gk‚è²=ñÎ;xðà>–Ë “Å#÷‘ó”ž×}rM̳C³ßCó¢#Î[:¨*Ó¼kbîU¶c´LR%Hy `«_DË+Åø(ö À8Œ‰qððáCì÷;87a§”2©¹²¥w/ŽOÓ ¶ïz´ížÝ29V«%6›M ¤2JÁ°‹… &ÝÏnÆör‡Ý~‡EܳŸ‘ò!çäEYb³\âèøˆ³],...P¾öµŸÿç×oÜø'‘ÒôãúRÿÙÿþïýpÇf§¸o¾øÊ+?ø|?Žô‚Ïköƒ·4x‰[Ó(ý˜½Ç4Ò@6êëø¢"¨'oªé‡5Ѹ 7Ú pê–g§„¡Í‹„&o´ÑûÝ÷>Âæhƒ“ã#„9Z¯øád%-=‚꫇_lm£ ¬c¬bÔ FœWdJyˆ4%¦¢Hù'”q0âZ>‘bHãv2m2czÙ4aö3uíÅ/}ûÛxáã/àk_û, ÜçÅHÆNmב'ZgìÞPRàÁýûxôè!£ Èë7nâúõëp΢ªK¬×kX;1‚-K²¡X­•E‰õŠ ›Ë%Ù­ò,Çv·Åïýîïa²ŽŽŽRüh<¬ˆž§C—òƒ5I§”dQy›æÂ±µÌ󳣤Œ­‰Qv• zd™Á8i¬ÅËŽaÀ4Qé=\΄ñPŠÆ)´ý· Íf­K0ØØ)xO3pc2 ÓÏ~hºôRTÚx`ÔÛä¨IgŽÇg"Î(µ&Q(ËŠÛuDÿñâz˲,ØÊ×P[ÓðåDœT¤#$²68Yžãøø‹å"ÈÛvq$[ÝÑÑ癨Êеˆäy‚<γó«J£ŠGîc·Ûr¦6© k ‹¢Àj±ÀñÑ16GG´x}ÛáÙ'ŸÄWú«ÿç,Ï^¶“Åó—úÛó?…¦êËÓC3¿þæ›åÁ£3ÌÎÁ;6G#9ß&3[±HVâX.Àò®4Ûऔ<›á9 óc½É2 ã)ëÊ€-^ä/RbžC‚q˜Ü`èzÜ¿{Õ¢Æéµk𬑺0/•L)^q©5ÔÚÎé ŒíR´"Y{X¬$]!BÊ INDÆ!Ò¬2nVãácg‘Æe ÍÁß+[ÏÜ©•Æ·¾ñ"î߀¯~õ«øìg?Åb m4vÛ-Þ~çmŒvIJ^¦6êæÍ›xî#Ï¢®zc˜Ìà©gžNdn¥trt\\œóÒêp0Q +mó7ë#¬×k”e‰²(ñ»¿û{xÿýwPU5ªªÂ0L|èû+¡æSÚ°ãÂÕÉs«Q<^1 tŠôùŒý~F±¢ÔíØH)y ’åë°Õ%ï0sk"I!¥ŒkÔDHºB€ƒœÏ<ò _ºeQ6©)¢v•dT2Í[i–hÓ¡N\Cª ¥¼§¹¡VñûŠÕòÌ›à eQ"Ë¥‘,—%Š—9ÇÞn·;ù¤”Øíö„câLó–pcÃ%|~$Ú*?…a˜ØG™¥ö-nˆóp®çÿ_B§Š3ÊDHÊR[±ýÑTÖq`o­MÕM$i+-áÃz³Æ;o¿‹ÿÓù_áóŸÿ¾öïýÀûPØ»É –Ë%6›žzæ)<ýÌÓxñÅ—pv~Nz> yp›7o`·»Ä> Š–%1ȇªxjw¦iÂoýÖoãå—_„” §§×STB‘ä$çèÚªT e¼'ènfxSÜs ÓAËçÒ ˜Ø¼ï1_ùßEE€wÔµ4aN³^Bç³OšÑjä ±éâŠË产í9ܸuãµõfÓ«F_¡Jý˜¤5þGøNÖb½Yßyê©'eù­êktƒZ̳ÜŒ ÿ€yi1'Amô/G~ð0–Qkš¶÷]Û%×HÎ7;Þ8iZ"” ªš,Y·ö»$ñˆÕ‹$kñX†]×᯼Šfßâcyž°éÒ<^@+@‡Ás×uÉ_èH9T:>I¨¥6Ȳ»Ý–f˜¼9ŽÔš¸=¤ ö>¥íÅÊ'Ò]¢Ÿtb«W¤” Ã@ô mà‚À0tÈŠy™áµW_ï¿Õz…O~òxâÉÇñ‰O~ÎYt}i´ ÝùÁ+¯bèÜùà..ÎqÿÞ]Ôõu]aµ\#ËéEûÈóÏ£(J<|ðÐ]^úàš¦I Ù÷ß>€1ž}ö98G¶®¸=3Rj‹Õv%…”G¢IôûÆ…„µökíæ´A;T&LE`ÖuË‹Kx®:â3Ý/‡åÖ”6Ûþ<‰ƒÙ®G?;ÂßóàÓÒ0(Ä0´´uKHŒ!âxÔªÒìmF3ûæ´‰M>Ѩ‰èx­˜ü³d+Kð…¸U/¹â—*ã8âìì瘦óÒ¡$‡¯ Zœ„¸ÆÑñ 4ˆÝìX·:@JJЬø]«Š*-HfG-©I‘ñâÑÍ3¶Û.Î/HÃh]âFÎ)Á€ “a¹XÐi¦Øn·ÇñÑ nÞºùˬýñþÒ?jšÔìNNŽm½Zír¿ãjÞ0²~ŠN ÀK §‚õItL7x! —´$˜FÚ@›t>Ü6¤é³)¨ªjLÖòF«E>ÒËV9Àš2£ Œ‘d;ï¿ÿ&7â…},!æã‡+®´NWsRº®ã-¥å,g—*šTŽûsºý¯nûbE_¼<ÑìÕn÷ò¼d!ñrK›EŸæ@>Pæ/@›r3f?ñçë0ŒÞ}÷‚_­±Z­qã±ÇpóæMܼu«ÛI¬ü…ð9D×íÒœ.jH•ÒhÛO>ùªªÆ~ðƒ]©”BU‘ƒ¤®kôýfŠ1c#¦}ß§„Â8ϊˇ².9Bs`VŸIz¶ˆf‹šCª¢$Ï à¬mñg+9)UjÛcÕ+„€Òÿ½Ô -³#Mž³V±­”.}ß3iš*ÎSY3G%tq‚‘i¤YUê`§‹3P!$$·àJ‘Ð>êW>|„Ýî’=ù"‡¢m4u•6¨Ê«Õ †Ÿ]k)Ÿ§ïÛSY.—X­(Š UUa{¹…1† ȳ ³óTØ”9‡jy´}›.¶HRŠ ­4 ã)Û Ë6ˆ€¡ïñøGŸÇí[·~Ã1°öÇ~®7›é7TU…Ûß~åôôwîßKå8)ç=‚T€¡CÅ#ÀKòÄÒíç!…fJ m¡¦qÄÅùòHÂÑ:שõ3=EQÐVÑTV%oB{Te…ÉRŒ€÷T–çrî‡kE&©´T8»ÿßê|ôùç±Þ¬IÃØ™cvbpP! bÀä^Q{ü~â þj4bD;QøÒ^Z;Y"Ÿp~4E ÈiJÌ<ÃÔ!m¤Ë²H­xŒ 4=¢(š)ÎcZY˜¦ UM¤à‹ó tm÷ß½ïgœ^;Eàƒõñ'nã™gŸÅsÏ=‡'žxöç}?àÎ÷ñýï¿‚ËË ìw{ܽ÷#µT½Çìæ[xá…cst„ý¾ÁÜÁ£GD!ËŠž‚Å0 xøà!>ú±#|âãŸÀåå| êLÛv8;;cOña«g§T-“¬Ik"°DÆ1]×RuÉf™\é"ŠàÚišøeÏ!•"wˆV)^þ>t8õ]Ïqá ËÒ& PœÃQVÉ.;È­BЇXƒáç14½#‘â4£®1n…c²¹½$Ó¿Ù&ËšÂÝn³Gд ¹§¬IÒÊ¡¿oU.è 4d+x²®k8ù±ÆzµDYX,–¨ëûf¥#yÊS0Xž!ª¦åQÓth›–$CóÌÚMɲ'“º0êv4ʲFYR@W?–ðÆn½^·ëû”°ùc= ÅxÂNÎa}tô¯ž|üñG¯¼öúi¯Z] üÌ´UV šãÖbÈzÜ0Ç–)‚5H¸=ÃÍ3ÆaL²“Xöw->EY2PŠ^UBiº®¡Gý~“iô½XÛ$mî{#ó,f%$Ú¾Çw¾ó2žxòIܾ} ™ÉÑìö@ƒlq`³§ëå;Lf8GBfÔöÇŠ-Ê>Èš—A,á{ÉÒyìæÜÊ ©­#?,I†È«,’C6؇\ÜÀ†Xu+¥xkO‹©(Æh3BaßìÒ¬ïîý{øÆ7¾ `¹\âúõëX¯×8:Þà _üƒÿP%îœ%((oÀã û­7ßÂûwîð61ç¬ ‡>‡Ô _n·øæ7¿‰[·na½^ÃËËK\^^@ÚnÇV0¾Àô¹¹TÍÕu ç,Ç H8®Öⲉ^º,¹fRÊ!#ÿ£TdÇâO*xoDH£‘X!9]2“ œHVL—‚L«‰Õ#åÑ!ŸedMŒ%4C› %¿ ž,ÑŸžçÇgÒ¢†?E¹ØR$:ÍUbTÔF(Åýû÷1Ž}:œ#Å]BÂÅQ‡UµÄz½f ©Ç4hÚ&ù†ëz‰Íz“Z”8jñáþqbL’)˜(ö»=vû-æÙ2q›cy#(‚cD²èƒ§68†ËS†0å”PBŒÇŒ›×¶mQ””,Ñ÷4$ïÚa&1«P†Z>Z(¨ªÂ $<†iÄ;+¾ëñô³Oa}¼Üí1tDFF/ØìÈ¢'JÁHrB EÑ©‚6¢ÜVˆ6¤ …¡º|bðÅù™à¥AÛvi+G/‘J‘¥MÓ$û`UE½Þ•X$ˆ+iÎFv( ÖAˆ@zAnG¶ÛKlŽ6T¹M#òºHíлヌiz JRŒ©T§×N8·Å° WQÞ‰›±ßïñòËßI¤ð«—cܦö ÀºªI!ÀÅ»ï¾çÞL!FÎS0~~q£?Mçújž5e jXâ$$’Ljž äš±‹i´Sr] IcŠB¥1Gžç)⨫:µtÎMé ˆ>ö±(+J4Mép%晬4¶©˜c ®ÜgE¬Uš7ZŽÚôª*SÈW¬òâá5¨ñŸE¼ælc;YœŸáââ"ù×ãj ^€‚ ZŽÉ±Z­)Äž·øÃ0Ò–Ü:~Þs¬V¬×fkò;åiD£Ysª´‚ôHsKk-š}ƒ¶éR®8‰}z– W„yž£, fzJþy ô]›×NñÜG?ò/<ÝjøÿÇ/=Û]«Ó·NOO~ýääø/]ìwtŽ# DÎÑÖt‚{ç à•‡Ãµ¨å2Z' V4å ¾a"V@ (s:ø|@?’÷x¹Z¢ïLnDד汬Jò<ÂÀkB )oîœuxðàš¶Á³Ï<ƒ£õ¤Ô8;[>ì8fT”ütð<Ç2¶8³‡TŠÎ%Ö‚Î¥¢*5¶8T LÉ^ç1Ë™Â~Bj¹ã&š¶×H’’X"þ¼8 ˜j`©Íí6›uÊÙmx>f’S%Ë€q¡µÁ£‡çi«¥þÿÇÞ›]šÞå}×½=ËÙÞ¥÷eºgQK3 $08&!NˆM°ÀÆ8e\.ÇñöÁ”TŒ]N•ó!±«‰³ØNQeƒËe›`#60-l#!@I£ÑŒfïîw9ç<ë½åÃÿßÏi™E=‹Ä{#5Óýv¿ç=ç~þËuý®®£J¤(IŽ“£MǼ4Ûµ*¦˜ó¡ï³?ø\Á8벴奅wÚÐB¥k;zGgV{KŒã€¾ë¹¤¯Ú‘-ÀGEy¬"„b6«Ñ4[üú¯ý:¼q׏ޢ*±>YÓAÑÌtÙhš@Mx$ºÌºì®,Õ\Ä<'㦒‚ž Dà!Dòy[„ ³i=mB“–Ž6‰Ï'SåZ×´™M² ­ j*Ok-ú¡Ç|6CYVØlî@dÑnÞqüÐÃYšƒ)-²[†6û‘3€ ¤š¢M©‚ó)$_iî5må@5ìŒ\‹ÙÉw[–lòÏ~]J8l\“?`鵨%µ{OtìÚ¶ÉÁ4kK€$J‘E3]¦ÖW4‹¦JÊ3•Ge·u?Øyð{}´Ð†|]×ç1GªîhAÂ&(%át’‘䥇”l.™VsûöÝ¥K09ÁXëKî*‘µ½B³zA:Á £õ ºèØ]C²£½½=ÊàN™>ÖBr޲R Á±^XJš-DCˆº}×£í^<}½ªJH9¥ðÑvÜ ( ÞXçóÏàÑÇ}ïl1ÿã£ã—¥E=_.?ãß$¥D=›=qéÊå÷WUù–a¬²çR„{­nÉ D)¨btnŠ1d RjÁû±E°VR®-§CÆ;Iy¦Vò\£P§ÃZšá Bðèú®JBEé#¢ y%% ž;* †?ñŽ×<òº‡ppáÛ“5ìh1zÊö‘PDRHª4…„¬$·4:k¢(ÓØç #9 Rk;™ëÁ@¤Ç«à\€”ÉY;%òÖ“*“iÛ Eˆ‚í[Þw”ÑËW€²kbŒ8::º‡q—Hà©Z(+çxÆÉË2Á‘™B1^Š5‚ –.¯œdÇ?c!%8r4 ËG`;Z’på/}ßå˜U’·ŒYp8*€*mj‡ÐY‰Nž´…4«\e9æTjôýýÆ»³A„—/ùuùk%m_ªÎÓòŒˆ3_`ÿà0_ØiFHÑ­}F“íí­Àa zxqØ¡‘ÖGxëPÏ*©Y#Ë&çÑõ$æï†ýÐñÈà¸EQ’®W).ÄóÙœxàÜvŸžž¢Ô =ôàû÷÷ ÅËÓ"€^Ÿ®?«ßèÇõëWìÜÁá[Úvb«)A³8A|"z3Åi4_ ÉO‰,a¡6€tX$¯A ˆ‘‡ÒÄkáp ÍaÉïj(¢(è©ê,±ëaî‡ÞbëÚiMÏ1ˆ„©äÈB­1/ DÛq›Xâ…žGÓlñàÃáÒÅó°ÃˆÓí–æEž¨Ç^”åœæzÁAK1™DÝ÷Cžæ›¦@ò¤N-.;`åj®$¨Bw4zÓ%˜èÍ}ßc>_À˜ ]Gô‚Ý*‰lâù ›¸i ž–1NAC)&]ÎÃ8 ®J@ØH"úª\@j1‚?‰íg³Ã0²¾“¤/©LŽ zMh\1~ë’·¹!CF­ó%(DüTw(„’,í²b"Ä€¼t9ùâ™–]rB‚dÐ¥,ó÷¼*žá¾÷ÎgÙŸÛuy£JͼCËÊ?\‰†­8½VdA30,$OÁbÙ"–è7Rd×ÊnpÙÉÉÇÇGØl6<Ž ±C¾AézBF8ëóìo±˜OÄ(†,ï.8‹¢Äþþ>ö÷÷¨Je«`Q”ðÂCj…JJô½å9&ÑëÓVØ9Ÿ‘{mÓ p0ZjéÓÌS«”C"òÙl%ÎZé»oºõz\ºtéÇŽîܹ§ê¿ï—! »?ócÀ¥K—¾ÿò… ó¹_ *0£©Ò,ðò¦“Z%ð\k„ó1² 9(©Yv¢òܦk[”U‰0(Íþáè¹-¤Åµ% â*„@×öüÁ&`¥ª¨ ¡à#} ¥RnzÃ:ç0_̰ެñÁ|›‡ÄÕ«×°·Z¡ë:Œ=¥Ç¶}¹DQkâìYÖÓü¥ÏôÈ$Þ&á0y»é²üèw) B„)³…-Š©‚$mâu]s«-) Ð:‰$92“Av“Ó&¯ XLÞr§ ñž½§!¼KìIç5jJN­šáÐûĸL™RtÁi­`Ýté ”yRÐ9ŸÉà!ø¬3M¦)Y˜ðq„ø™NN¡>;&!7Øl2§ª>œf¯ZJOø°¤/Lé|]Û¢žÕùk&ŒÍ =èIv“ˆÒšß#d,P¹M®ª2 ©’–ReÏ‚.$âózMaÛ6pžÄ≹˜ˆGRòæ†],–X,–9‘l‘}?d¡¹R‹Å¤ý3‚ó•ӃÅ#$æ‹:nËå’• ôÚ6mƒ—n¿€®o¹ ÙB¨µFa4Á ²–œÉNß³Ç0gô ½á¹²®þÍ) é_¶Ëð³ýã8b>Ÿÿ‡o<ðô‡>úÑ­ia¤‚W*[× P¬ÚT©hó”DéòË\µÃÈteZFDÇ8 )d»"ƒzÚæj£¡u‰Í¦áEIA ïó3h  áfÄDŠ%W4M³Í8²Çÿ^zñ6~äa\¼pcAÙÐÁyôv„ò ÚH¸às*š”b‡h3Ùê¨E ùbÛm‘wì©rLñiv’¨Ê2oÔ¦ ­‹e…Ï s!8öü6|iŠ|ÙXë˜5gò×ÓJd PE:ŽÅ~²KB)‰¶Äð#%úyj#wÁª”Y2ÀÙ1WÇJ*(£v¤H.#³ÒÜ2Ù™©q²l(-"¬ÔÚqXTJÄÈ‘ y™0Ÿƒ¨ãJò¦{zPôþJ¯²ÒÑ{È#¹xã›;i¦9A;H/hLÍAO»‹‘¤Õ !æ‹<„0ŠZè6›-NNÈÉAÙÈÂm‚,$©LâXjm0Ÿ-°X,ú\½& Q„èõ_-—X.—¨«Jk".ùúL–uÀ €Ôªs3PæyÛÂzGóY;äyª:Id`x.ª9Çæ‹yv§…@’°Â\àú/y缿OxÿߦM>ý,+÷\†‹—/ýÀÁþÞ_]o6T! ©åj RBLj¨”ÐÞÃsHS.Ox¨¤×²P™œ~]ÊVV’ç;‘e3*¡ŠÇ•d v<勈Òêf³9¼àaŒð^䯆ýI‘¼'§'øÀ~ׯ_Ã#=„ý½¶Û&_\Î;ÂTð^æ uw;ž22s/½IŒ1èypZ£¥”F]KÖù%'DBÁ‡<" A3vª¿[íºže=]rЛ+b>ŸåœarÿP5¢MAl9!! KÃ2‘CÚi ¡ò,ÌZ›áégº»ÁuŽ UUs”¨ÍßOro¤v¬çsòm'Ñ6ø{ Q (K¢Ø8–ÀìØíºn€1š-šÈA`‰ÜBîÉÝŒÈö·eŽ0M3»$ K¾ô´I§ï‰¤'޹ÉÚ™ô~ ½Ÿ*"É+ïIJ¦ä=†bÎfò¡+¥Ñu-ë0OÐ3ä@1”@Õ6£àQ~¨•_Rà#’œ|ÓžžUFö›ÂÙkOUkˆÔ">æ‡-DI?—®Ý"xþÙ­†¾†<“4F£dó€”äØÑZAÒF.—Kº¨íŽOpãêUܺuë{}ˆP/ƒÐúžËpupðYÿ檮p]ßø×—Î_ø«Ï>ÿ”1,3ÑŠÂr‚÷éBä9B4âyË•ž¾ÉÚ”ð\š½ÁBмz·Ž„›uUú)«¶*K C¡=å,ÆaÄjµ‡Íö”PîýïżÎîˆx<ÙæÄŽŽÌç žyæY¬O7xÝÃáÂ…ó VïÇ‚ÅÀ÷<ÁbÊ>1¹­MËša…,¯)Êj'—*d•« ZŒŒù²KdÍ60ŠùûHÕ†÷’]*ÿ~ÇaN‹}‚'¹„w=É[ʲDðý0ðöOqŽGÈGº¬Ò2-H'¨²sƒæˆš'žž/ÁJù{ªÃÔ§-gØÉŒÎ8údqÓhd¢Y‡\%t?9>†–M±kD°cÇ2HCíDÅúL:WJäYäÄTYC™ZöT-ÑkX±&UOÛàT o„SŽtš §X^玎p|rÌÖDÅAÎBLqMŒÑ‹UU³~°ÊïEÚ“tfÒ¸(Ë%æóYÖ6Îg3ç¡4µÈÎð! ,j@иJß#-#§BĶiðÒí—0ŒC‘xÜpR@jz´1(‹uUqåó½±Ù¬qõÊ[qxxîç¶]ó²m‘'i oð>›# °··÷sׯ_;ýõÇß+Š0e ç§8ï% -B $JYf£~úá“Âa"ú*E¾F•ä0p6sÿ†q@UV4Od‘kdJôhéB6‘ƒ!ÈùÐu=œk eÄ 3Á´–Hf±…É[Ã:º¢ `¥|ð×pîð¯Ã-,ö!·[ØaÄà\nÁ(­^ð?d+VŠ( ªSn â4‹™›ç™yH™³l×£ÊÃdÌxÓ+òå@­·BÓ´ŒSù2! £d§†ÌÝÙlÎÿˆCÖ¦mKe‘½ãIþÉqˆYØä¿KZZP{,ó÷œ.0ú€‹Lz!²ËÈ’˜˜¥9åRÞ‹ìÚI(7ÂÆ¹ü0M#ZƸÌŸÝ(É·ž>Rª,¯Ù­Ô¨ƒˆ0å —eµ3—Dî\R%—n4äöuÚÖþóªÂÓC8éúÒ‚«iZa½>eøêÔv‹mÌÍ? $Q‰ˆoXÜcTÇéG³*,—ÔBÓf›ä."вRRÇc ¤Ä8³ùœùŠLSòœ(霞cû¼˜¡Ð8öSk2OÐâ„(UU¡®*ÊÐV Ûö–+¼þõ¯ÿëì]ÓÞwJÍtÚßÅvÆÆˆrÿôæƒ7¿goï/lÛ&g{ï !…€ˆ2R«‚C”2 x&AÓ†m@2‡Ì§7aˆ´5öa„Dæ¶YG4¿K•@˜ÚϤG#ÅÅX¯ÝЩFHÌæ³Üš&‹¸IØq¢ð¼‘ÔZãÊåKˆ>âÃþ0ðÐÍ›ðÚ ´ ¤‚ …㊆´Wž³9b,¥ áô`‡¼ Hx¤C^²$2Nš?¼SgýšaDUjIS›Oÿ®à¯¡òø!U9§†ãZ«º†ǼЙ„ÞØ ¢¶/xU”¨Ê CßçÕ$™éºi39²à\JJ½«Rb¾Ø”:„Mÿ{ùA™À‘+Ò !v¶dþÞµNñ bGŽ1½7ÒÅXewuŸüOxŒ >Pwú!$Hr•”´eYæ*rk(»&"fBKν–ÓJH‘áÇ'''8==F?Œ¬®ðPRgñ{‚3Äè!h½Ž²¨°\­(^SÊleLEBq%ÎÀb1Ǭžå¼)% mPš’‚šŽòuZ£ë‰ì=ôcÁêªf.∾±Ù•f‡{v“Œˆ¶Ç’)î³Ù e]S¥ÉK ã»G8xˆ7}á›Þ™ºÆ©ãy™.C÷»J¶]‡ÃÃß¾tñÂ_¸}÷ÆÂ`°®÷¬$]’Gð,+}¸œ Yc•»4H¦7©Ï/€(!½„t,‰QS4¢) \`çŠÖÙ'•àp€J’`ÔºN@Ôjr Ð0œÿLIâ^ç¼s¸zå*yäáÌçóøâ‹/>úá=þ§>òøG¯]¿ŠsçÏa»Þ`ìH/)B•3 ¤CÑÓ¶·,4‚ 0eÁs/Ÿ& Õµ‹“Ï’ 1mÓ÷Ÿ6Ÿ$ö-³ãeh«=›Õl…¹ ï˜Ä#8¶t6«!ØÖ6c+Zb,’ÜÅC©2·ŽÃ@‹ůsY‚çi}^8Œ£…U9#ÇÅÒÖvÌ—yªTI4^äU›BøÝÀ9bö]‡aìÑ ïh¼ Yó)ùâ(xŽ:›Õ´¸Œ££1[×¶¸ò†×ãÜùÃŒüz¹¦9Êïæž?÷£ݼqô±'Ÿ=Ç[‡ýý=\½zå×¼÷o¹sç.®^½Š‡~ä¯<þø‡ÿÉ'ŸyæÏl· .^¼ˆª®Ñ7ävPì³ô†ä6:h-K°Ç0:Â9cÇÎͶ±¤É¤ nj©“Ðy±XdVZÊ”²à‡b·äÐsªžMlI³µŠÛË©†´¼|¹¨µ€Ɖ8¤òüsºÄ'2v2ß'Ïnâî‚ š¦ÉU¥t®zn?…‘ÖÏOc ­á‡ƒã ×fy UP1_â)@,U-)|=Íàvµˆ‰D“4i~œ‚›$»ŠRu !r‚ŸÀÄe„ˆ¹"_¨½…=šÓ5šf‹ÍfÃ?CvȤE$ƒi§7 uÁaL+®ì¦ù{Ï;g1Œc†X”E‰sçÎsT“bļž‘K§ïIê2«Ðõ=º i¨¥ï»ÞsÅÙõÖ'k´]K9Fãd[ÔZBI !ISh6!™T0˜!`†q„V _ð_ðäþ¹s?~zº†ù QƒŸÕehÌï~CS/æ§<ðÀ¿^Îß´Þl!D—BÁOÃÕ¤‚Z1£¦ðø¤‰£6©È)q”Åy¦¤á½Íb ë„+ç,D¤7¦Ò*ëÁÚ¶…ac¸ÓŽôc1ð2‡Û!IFBxš i[,fsìííÀÿÒu´Òh¶´1»uëÖûàƒþ§Ÿ~ú/Þ~éÎ_ÓFcµ·GÖÀ¶CÏ›Vªn€à/ $he®ÔbˆyÉ”ÚõaòFº,Jªx!å/øIÊ“¶ÝiÃJ34d½bò£&J"K§ùÖ8ŽY0Nÿ,]t#‹ ÖJG›iáÓÜ÷q˲@Œ.kCéï¥x ò‡¸, (-Ñwôs2,¨Oã‹´åíº6?<ow/Zè!üÃ~GVC.¦ty'_wŒÈ¾ðÛ"FiAWðF3 ŸÙ¢¨4ƒ@&YLúÚ""·Êiá²k4èúÇÇÇÔ^²C(á¸bR¦Ëˆ‚©8‹Åóù‚ÍÚŒ9ŽÇHQ³J)ÌêËêºÆr¹dD»myN?Ÿ¢\Mx xÜ•¤?ÁžÓ{t) º¶Å¶Ù ïûü@£ª:ð…]ç Zjb-RlB…ª¬¡ qP»¾'"UUá‘G~7™%^þ£í0þ®¿H·ÞââÅ‹?qõòåo::9F×ôv€ð"Pö¬‚s@$´¼"|žÑ›GdÎ!­þmb"_%¥ñY£ªëš™…Ú€³0HÚƒ àGzg²–U‰ö—Ž£%Œ½¢üÚð1meêžÐ÷´Ô…Á|±ø§U]}o×õY6BÄÉÉ ö÷÷?ú¶·¿ý[îܾýñüÉÿõääXÕu½¿°¾˜˜m!¬…Óô&sn„g¾xÚÈIíâ9`&Ïn Ò9©[ÎEN•X2ú'½^Öl²–-Ußé ½Ï£¥‘½ÅI›—„á’‘_t¡E^4è,²5††ãpu­óV=YÜ’Ü'ÁYÓ6ß{Æ·õ4Ïç¹b#Û ¡¡>ˉ('†ýÎBæË%%ÏÑåO3e}Ö`¦JIkªÊb1èèÈšÁä7Nß?XÝ ø¢£LaÿL´&† KÎ8Ž!f¼›Ò )ò«m[l6´]ˈ:—l»ºH%‘«N £jj>«1›ÍîyàìÀš #» \¼x‘ÝJ!@DÁú>™]KFý q• ³º¤ ÷r³…·º¦CÛt’ZøšP}eYç9«J1̘ÍfD£â÷€µí¶Ác¯=|ä៶£}Ùg…öÿ>vCýs‡ßóÀ×ÿ÷<ñÄùª"˜—AF¨b*… #¼]ˆ‘rUHHÕÉ–:)v\àÍ4§Ë xGÙ²1*Óf1U;2,ôôlú!ÛáJÞÃ0'/m¬"_ÌBÍv‹åb‰å|Y]ëÈëÜŽÄ„6¢Gw°Z.¾ý±Çýö'žøø;ŽÿÏ0 ßXUʪB×µèšÖ[„0‰%·_ÔÚ'šà?jí¤Ð3ΛvŽ4zL[)K0;pÌ­g’4(ö œG.J+ ždÁxé±\.Ѷ¥2=%E•ŽãˆÅbÁC~‘eäC®²Ž±(HOZE‚„’à=æ ­äm†f¸J!*‘3{~ØE™¥9eYÀYPÅU¿”4WFŽYùûuÞfŸò$ƒ ¼Ý$Ú´IÇH¹Þ)Œ~7G$¹>ˆ#³HÌŸc#S´jBH%}P}vÅ$»˜äñ€µ6 v×ë £Ø«<“£ö•7Ú9²!f{]QX;mR²à9o† ð¬0›Ê%#Þ“m®ëöÍz4Í6oy­u˜/æ(¬åYaÌ[è]ÊRì÷„Êî¦=„Ð˜Íæ;À‘3„ Cm9-ÝÀs@ºHÁß“TAþÙK ']§Òr‚“(•þeQ´x Ü 2ÛäÅLDï´Á§± -HÈiUçË#ÅÆR¥Ï,ÄÑbd©ÉÃæ¨ë 1Hœ?eEdù)ê€(ôJH ]Ïä©€²®A4o×Q>Oð§› Sfœõè\Ó“t]‡mKsN"ŽGÖ”N1ZH‚¸²Èº®*‚¹g=Fç°Ýn1«j§f¤@ÓuYý;`Ïèé}xxðóJ©/n¶Í—Ä¿­i¶pVÏ2O®ÙnIÎÀÜè€@‘ŽðYÝ©d„D®€’_[æô±²ŸÅšyΗÈ×Þ»4ÿ47 ‰é'RŸ‡P&ÿLèCJV4£tÖ×åŒ Þ “­-ðˆc–=ˉâ’ÚÎ$Ë >@͘çrÉ“FË¥fÔ”—`§ÉÅ#•à žä5ÉiDî(zï%„r‡ló3½}rròŠ-Oh›<Ž÷í‹õ]‡K—/½ëÂ…ó_·iʲeëO;HM/%/>"6a×eáÀÝ ‚ ,8À§©Útüd ÁCÕ 2ÎzØÉ{L:{wE’—U‡ã)ž1(Qî«Õ ËÅ«Õê—h‹)?E›T`޳œýŽšÌ$W‡óÙì?Ìóÿb³©AˆÿÃÉzýß]Ô5_Œ]×bF(ËÔïH»É•,å<Ó¢Bª˜¹’Ÿðˆ‚/@ÀÀ³îOIÀK QR–…ó޳mÝt zôýzŽ/²äÊRìs}·q@Ö J©ˆ3YR$€çmwU•$—bp]”‘l1{“œ OU§y^•àJj¾ì¨e;®š„&:’ˆ${ÉT˜2a²èAHwn‡3Q|Ú4K(ƾ‰,á׋.Á““cl· çû{ýô~—Üf‹ ´ u†!Ìf3ÜÚ’éÀó(Äç •¤HJ¢ÀHTuEüAi %‰£…h»ûû{”L"ŠŠrRB ¥Q䊟,„" ¤¢*Ѧ1ë õ„w“S0<[ð¡ Ê¢dœ x4!  ¥B]UX,W9V8Ù1OONðàõpppðÝ/>÷BVB¼b—áý$AXçqpîð¼yóÿúÄSO¯4K-|ðäÙ ZJ¸ ÖŠ@¸¡”ë ˜Å—ž¢Q ª$TÔ(LĈ1g{X‹lÏ’Œã¢¿ÇHo& žA"úŒ+’’È5»Ô˜$Ð,‡è™h2ŸÍQE;›Ïßév‚Ì?•àÓ´ ò"âÓ«'ûVú ¡ñÍ/^øæ‡yø—n¿t÷­§§§hš-ú¡G×6‡-FcŒ°ž²`¼‚*À}F?Ak‘"3WܼýTB”ˆÁCšDP‰(PæK+·Œ;9ÃéÃH­qël–¢ä/ÏÀb ( ÚÎŽv„¨ƒk#Lv´„=]ÌÛÖD˜ÖÚp5é's(ëJL Š {Ý™õ¥‹:ý ÚüÄ|dÌWZx$ w$Ê´˜2´e¶á!Ëmb¤v‘‚Í;J"”©:ÕÓÜ1FD$=£ç‹›¶èu]ó"¡Èç]¦mCNüÅTÍ#ɽfõ,CQÎbìG˜¢Ø±7zœ;<‡¶#¦ V R wé`G‡1ZÆ´i´MGê€Ò`,šmß/‹nh¨óS ÛmCsÚlG4 Òð‚þCےƳٌMÓûÌ9‡ Îÿ`Ó6?uttôŠV…Ô&3‰ø~œ#ôÞêsµ\þÙM³%ïí0e‹i!Äü“TÕH‘gEŠõ6i "ÉÏV,ªnÜŸ¶›©‚麖dZÃÚ1jÌgK@DØ<'d¿ªœ"5#p‡T)…ÓK|[aô‹á·°. ABßÈôìOó.œ6¨Bâò•ËØÛÛÇr9þ¡oöÏ<ûÜŸ>¾{ô]Ûþ‰£“t}‡ív‹¾ësd§u­ƒŒ‚÷Üš†@•¡¦´Dð‘«sÒ¹ A ÉK ÉÿÈ1“ˆyfHz7zÑÊXp~rÌó×ä–1@BRVG¡'˜«˜‚’„²\4·äSûŸfÉŠ­äP§lÍÛ}!²£%©Òìpw1v ‘Ò:èÏ“4kaL mDLÛbÅ—µ©›ÍÛmƒmZ‚±×6m£ÓûzÒr\‚¤1NšwÒF[ òÛË¥?xÜà€˜ÆEGÐ,ç hm°\-)ç'ìï ¬ Ú¦ãy·! ±”¬Â˜R «+’,*ý|"HpgÇ1k’TiGlš º®%7[åŒQÕPæÐ3¥i¡F1 S&rUU˜³ü§iì¯öpãúo³Ý‡ ¾²—áýübI7vñÒÅyíÚÕ?ûÒÑ]²-C:6Lƒö4L¶¨à"üg/éÆŒ&…¿&”ìv<ï‰íNK ˆˆ²ª@éZ.¸Ìû£ä1ÅþQ‘sJ”&Q,‰€yx-•ú®ÓÓ5~+¸dšµum‹ƒÃC„O£¼‘°aW¯]Åá¹s˜ÍfpvÄvÓü«ùbñ¿í­Vï3R}_´î¯_¾téÏoÛæ/Ÿœœl¶l6[´-E/Zoó‚&0¢)dR@ˆŠ=Ü2ëÎ‡É Ð´é !ä<øÈXÒ— °S$ ?±¨Âw™˜=-"èÁ’ÞÎ*W{RÒÅ,]DÎË|y凜˜ÈÙi³-vÞcéý‘(/)CùÞE„Ø©,¦Í5íKùbãô‰È˜¤ Œˆ(|5%B ¦ípÛ¢mœŸ`© ÑŸÒa%¿uú;eá:/&ʲÊóÍ#¢§‡¿à‹3Åöñw]›#"ŠBb¹\ápR+¬öVè»Ñ#k‹9¶›ã0b8ß§§*¯( Œýhúù´m‡¢àˆÚŽrKªšÒùº¾‡ï{8o¡´Di¡â]ÈØ.Á….è"L\¡Èsm¸Ö¼4)LAª’@Tð!àôtk—.~°žÏ~êôdûŠ_„$­¹Ï¤ÎyÌ–‹óÐÍ›þèÇžx´g’µ.0_ç6SKCš7Ï <ÒaéK¼Ø€ÏåÿÈzÂ0õ)ˆ·ÎÆ(‘(Ì:‹³ž+µÈÉ?›c•Âv³…’ÐóùTeñ±–)Ù¿årÄ;ãàðpêÝ~;)û6¯]»Æþâ>,}g/÷ÀJë§ö–ûk¾˜ÿ­ýýƒ¯´Öþ÷]Û~ÍñÉIyzz‚ã =¥=;S¬sy(mYÌd  ‡çˆ!¹´Ì „ÄLo­´!dvŽ()àAN")è稕¦=³BiŠ¢ä‹MixÇ$ͬØYlezõ½Q˜ÓlRdLj’Œÿ T¨ xt¢412gó9uαö×1àððÜwum®kñj=}c÷ïxpíÚµ~þàðooš-æÃIGbb!xŵEdͲâŸÀ ¥$mJ¢( ½Ày¼‘ìv”jG¡×RQhG YJ¥Á0ŒðÁe„yÂ/!çèÓ<з- ²Ë«ÅâÇ$I~»c´Æz}Š£»wqñâE´mû;•ÑÖº‹ˆOLÃôßÞŠ~¤×íÝ{û{ï¾xñ‚ºÐœÿ³Ãhÿ›ãã“ÿüèî]yº]óS½ÇhG ýˆÑ‘Œ¼ß4#RîXã¦Eˆ‚®¡á(ùP‚Hôµ”àm1_´1FHn2¨ŠQ’ mXîvˆž7»L‚¦ çÀ RÁBg™ÛR‘pp‚ä'ðÜâ+òéÒìˆ :Uæ”Ü<šÿΚmf>¨‚þŠ í•*Ó¤MN ËX:Á—=ÀÏ<ÓìQ+ah D^MM’r‚Côˆ, RB!Às[–A±Ì$´MËÁô S¢žÍ°¿¿—éFÄÆ$õÅlFÜJ)çÏ´9:ÂZK”'wày­¬%\ŠÐÍ£ ú1ñ³hš6ƒl)¼‹f„EYçtCú<˜¬Ï”¬(ŒÉpˆTÄàж-ª¢À¹ýƒ5²øø•¾ _ô0 8¼pø½×¯_ýÛϽô" g5Yº<à¼äÅGˆðÑåÀ˜A‘£ 47‚"¥¢ÿ_а™(*Ô>vÎÐEcGÖ~±2Û˜rA;thFòG`µ·Çœ:ñø¶ù4ôN‚‚«ŽŽîâÊ•+Ÿî”õÓ0&—ÁH䯋â;ªùü;ö¯ß¼ùÀW7Móõ§''„ðO§ØvÔFý@dp·O¤jçi™‘H:o§iÆÀ99@‘\‚.L™`!#¥Ò|N*Ê`ðÁÃ(C¨ÄÒ“>/‘WTZbɘiBÉã‚Ï ð…FVɘ_ŠUHU§4";F ¦w–!tA+¾ èÁÌit!ìˆÃ'+^d219@ÖÌSŒ 0Êðœ1ÉcLþßÝÑHˆ\YE抷÷I$ïæ; $X¯Ê³zNáÞŠÞÃÜIYîfJ5 #V«¤’;ògã%ªªFß ÄÛ-Þªªä-3}·íÐÐ…8Zôù¥{ÞŒãÈ™Ï]׿y§R:?8èõN³QÈ4kÍK³Âl{ÕDcG´M‹™)ßã½{ü¥Û/áÕ:Z¼ ôXo-û{¹yóÆ»ãCåhGNSðŽEÃ"r‚ - ³óàzr>$&²éØs&Ó’1¥ <ÉTŽ(œpù)ðHkÅÕ„ÂÝ;wÈö4›Áh}ª¤þ÷ÖSÕô;£+Ÿ`»ÝìeïÿI²vÊi[àʪB$çÉ{!ä©@ø´æ€Æ(l·[œœœââÅ‹/;¦<}À’{dtn-´ú¡ó‡û?txxçü×t]ûŽvÛ~Ѧi¾üôôÛÍëͳJ|ÀI2‘æ‡ÁG¦·9òÕÇ{cF… ¿4ÚD%ŠˆPBC(®öxkHs2@™—/iÉ‚¶ÐS(˜bý`ŠÚ\ žý‰Á¾`¨…b™ ·©JämqºÜ¤NóÀégžh-ia“QÖZø@# g,ËÃ<ÿûª®0[ÌÐõÝ4+ɽwœ01ò‚‡+é¤ß¤ü—áf’¢a5§ê8 tm‡¢,!¤ÄØ(™ahÛeG‹åjpd@ª®»ŽT eMšÏ¡)ãgtDÍæmõ0ôPl×l»†­y1SÁó+ _nÓCDMKÉ4óåÄ>©$ª2E£R…N°j‘Eööö~Vå«WöíË3¬ì)Oö}oÿÒ/ù‹M³ý‡wÄíŽ74(#$µÁžueQL¹!uY²T¢,%¼W\)ÒF7ÍÁ:®¢0(3N)€.BS˜\qh%qtt !%Î$#y6½Q?ýË xê©§`Œ!ñ+ùÓ Þ“ã4Å®V{?ZV5Î_¼p#xÿ|o>==ýc'''tM‹ãõ)Ú¶ÃÈPï)îÑ{º ÜhY FPÞ¸CØN¯‹ä (IA¤2¼"]ÓRLÀÁË„! ©)¬{#ROš=ò¦?]*‹Ÿev…Ö<&¬–6fÚB³×<}Àñɯí£×ÀRÒáÈ ,ë<ãç<gdÖ ¥‡ ÏRs¼àrYCÙt[Xç0ô´)·#ùÄɬ²ù!éJÓâ2;Ç$‹¢æ˜Šù|ÎGODp8´ÍW._þ·×®^yj´–³£_¥Ë°Ù¬_¶Šeì;Ìóôè^_I¥þžT w•&ø#“OBy)Eù"¦t<0àtbõ ER˜ÀùÄšCŒf°£…ó”WW̰ɶ´˜Û2“î‰Tò4Çê‡õlFó¤è!…xŽ2‡?ýh­%¶Û >ô¡á‘GAi †Wø‡*øVvÎÁq•´~º*ËïZÎçßUÍê¿~áÂ…kÁ‡7v|GÓ4_Ñl›‡†qØß¬·XoÖ'éù‚pˆv?Å¥ÂØ3Aq " ãÖ$ëøœ÷$Áa-fZ Ô¼ùÍšÓ9ªà\mï<¡¯H ™£„yûœ¼Ã4&Ú‘L[eE1”"éyü⼇ëÔúÆ@sf‰– Mã0ô=YG7D@ ^ÊðüÖ»H<ÔÚ?Å2$o;ïŠ!æ©íî;¦Y3ñ¹,*ìííуZ—˜Õsªë Jlט¢ Ù,a ƒfÛ¢(K87¢kû<£”R ªKH%²C+»Jœƒæ±…6 COÌÃc7†žfÌ‘3x%§&&ú¹Riªs»¼+“#m(Å肤ueYe¸«s’G1ëÓ5n\¹ö}„6ë^q¡õ=ŸÝ —/¿|JΚý²ßÿÿK¾ômw~å—å;ý7>T?÷Üó8YŸ¢íºœßš}#\fô!°Ü#ñ #ˆä’Íò$¡ì/VBÀG‡ß É‹Ìj»†RŠÔfI™i"çáâJÊ÷~¦n"¦l6ܽ{„7@Ä«x’C#8k1ô¥*žUÊ<»?ÛÿqÖF ©ôìÛæ6m{±k»}ß½mÛvûýУïzô]#,ƒC©ò™ZìdKœHÚ^:Ö–ÍFÄi–†É¤$®çʳDhZh€Ç4÷KhA•RœæÉd/¤*·äü–r€‹çÎoguõ^¥Õã1àÎr>W˽…xéö±ïûeY–_þÜó/|åÇŸü¸}Ì!XvÙW¹½”@¼ðSk¼#æN²+0ÒÞ{›e2RË<æ9<8€Š)-`Ö°ƒËY‡‚s˜Sº`?¤Kµ(hÑ$±©Ê‚–l3M±¨àòê1ôYëÜÈz±0°Ú!Uß’ 8&0D~ˆ%T‚·RgVÀÔ%Qe© E@Ûn±m¶¨ªËåÞ¿ó" ^Í£.œ¥þ¬ïþ²/ÿ²ï><ØÿO¶MóûONN¿þÙçžÿ}/Þ¾»Gw±mÊ·eÏ¥S“S‰ÉÁ B‰ç+‚¶Ë‚Z!ßZ[¥á¼èi-r0üD3Q¸³Ý¢,+ÔU 7 («úÉÅbñoÓ†ì3=J)ô}‡Ž-Káw¼uß;ê@üÆ_·B¬”þÉÙbþ“³Õ ÑYÄè‹~ô_<ôý#C× ><ìBxó¦iºþÒ8Â{rÁ ão±$抒<Ï)f†üÃ4ÓuvÌ›Ü4§Ì0Óțȼwæ éAÈ)w)—9»E@?wÍyu¥Hð«õ(„x¿1ú=óÙü{«½g„ÀO ©Öõ¬ÂóϽ€º(±Z-qt|‚ëׯã±7=†®é®ß¹sçÏüêíüà?xиõ|ïxÚª]%vÒiö=²E2 :lû?#´–(tÕj…‹Ïc»í0ö¦ x‚ïz†éÆœ IZ@;t$”î'…@UV0ZÃŽ#) 82—ì­àTA‹ª¬ÑŒ[£(5ƒig*÷МZI­0=H•V(82—f"oßwÔb(,˜Ú]2y]*Aè½à¡´B³Ý!þ@ áÃO?õô«þ™Ð~´/û’ÐZëõº0ï½~xý½7|à[|ðæ¹í¶}cÓ5ßðôSŸüÊÓõú“ÓÓó·ïÜFÛuð!dboä!B°ÂCB‰môBd_320SW àó…:›Íû³6nG\º|9ÃOÏ;õðp/S~?»ªx@³Ý`VÏ0â5|â´¡vŽ$ÕÑ;„QHó õlö †¦(°×v*çÝ8^ûa© }S ùv;Ú/Ça> ½­»BXBˆýBáœC?Œ¤g žg»‚EËže:ŠÅÖDì¬Ad»:*ÊŸc ýr6ïÇq,!>xxx¸iÚö9ãv¾˜c6Ÿ¡ªg¸ýâmŒCO—Ž¡¥Ì‹Œ®ëpttŒùlþÌ›¿ø‹ÿεëüGßøè_þÅŸûùð¡Ç?‚jVC*…¡ïѵWª:çЀ5‚Öº ƒè‡žˆUQ¡*K…Æl¾€. œœlèŸQ»<:‹¡ U -U†5$Ä8Œd”"_Nš/˜Ôvo·-ÏRIJÖv[ÃHzA) Fß¡Ú É Ê_î,‚8Ž@ ÎRyV˜‘©¡€y‚K!`8ôI-ûÜiö¨!Þyr|ÌÀàWù2|…?s°ãˆ #ªœ÷wgóú=.ŸÏr¾@ðã8þñO<óÉ·nšæ‘f³ùÆõfƒ¦iÐ÷Öë5ú¾gä½Ct†¯ÑQËbsÈn'¥þbŽºª§Ì!qçÎó¨Ê çÏa»Ùb¹ZžÞ¼yãH°úÙKdŠbJÚ¯ŠÁè³ï¬“?Ü{‡è#g£µ^ý"”|Qj…²ª~n9›ÿ ØÞ~áE©Ç ‹ºÜÓeussrzÁhSØàcßÚ~¨‡a¨Çq4!„7ÔJaì{(µd½£ÂlV+1,Ëgµw|ðwîܾó‰—^zñäoxƒ?>>g‹!ZH¬ööðÌsÏBDÒ¸…à­ƒìžON!óñ·y˜ÙqÄíÛ·±^opëÖ­ÿ÷ío{ûþÈýðOüØÿ›G‹²D1_™Äî½ãÖ“–#quÃ8Pk]co³ÙœäEB¢¨ˆ&TòÃÙ{C(%1_̲&äåØˆù|޲$ŠùÀ‘¼vœâIû~„VZKŒã@£m)¸Ì¡×o´Á|¾Àf»Æ8öè‡óù’·ÄôuSAQdÜÌØ;`zx Ö&P®”TV‘,µ” -Ñm ýðÒ—¼ýíïœÏçYñ{æ2¼çÆÄd#WJÓ¶4c(‹ï[-ßwñÒE8ço|ò“ŸüŸOŽO¾z°ãüê•«‡Ã8àt½A×·Øn4›m&rÊ©GŒ!…Uk$QX.QÜ„ÀmÖ€®ípýëpÖ¢0¯{Ý­?~þÜ…—îë1r0ú«&žºÿ›±éCâCþöý@UCÐÙBpò®”ên0_-ég½¡¸H‘¤3LÄI3A+è“ÌÓ«ç3(©pxx€®ïqzrJË6ˆGPèWd§MQö4'ŽLÛI¦ñ™¿Q“Šá¥—^Âá¹sÏüÉ?õMUù#ïz×»¾Æ˜u=Câ0Pu¨†n€Í68­ ö–3ÌçsºÈ*Ò :ëà[ÒMÎj²²µml‚çñµ¢ã@¼À¡€Š¤e 8!•„s#”"ØÐ[De°mZÎ1F†íEÍz­ œ·9k¥ï;–k¹i%Sh›ÎUhÖfæð*šÉJA3áº`JMŽP`oùÝ£#œÛ?x÷bµìÇqütd¼ŸŸ—áoµx6–}?„/Ƨۦûóý8 Ýne? )%¿ôðð`aôÅ?Ôý[ú~¸Úµ-NOOp÷ø˜Tûðp1À(SÌfæ‹Å°.xhmð‰'ŸÄÞÞöKļ¿y±XüäñÉñ}ùžzê7†~^ž ¦ï1³nCº ìH‰Œ}ÆHÑÒEä÷BúZÎ:Iìô5’H{BŠáeÕuJ)±Y“åñëÿè×ý×1„ïûÁõ®o¨s”º@ô>E•÷$Û_íc¹Xqò!ØL¤im4œul;¸/‘£¼(Jçlf9Îf¶›-FëP&p.hfèÚf&¢KÐëŸà ãH¤ø¢0ˆ" éÖ™ËH§$ƒŠÆK,™\?‰fC^äÉÙ“¶øéÏLµ!Rg8Ž#®\ºüOìè(YOˆ³Ëð·ëÓ’½Ið!„ÓÓÓ_wnüuM ¿ßw#"âóοãÒå‹_pzrúHÛ¶_%”T‘?Œ1Ї&I f)â—~ù—a­ÅëoÝ‚Vê£7oÞüÖýý½ïl».k®îÏ*÷óùü½q§1ø€Íf‹w|ý;þøÇ>öļÿWõkööVDE c&XC³šªÁ=c®(’Öû€íf ©VË%|ðèûŽ\%Üv:G¸®ºžA€.¢€ÀsD‰ÑZ˜‚ }?dœÒ<׃"oñ8@*Àºt’ýä´…Ö-!8«C]לŸ3L_¡™³˜lÓÅ-óƒBiò] –<¦€1Šs§‰[˜l‡}×á`µ·^­–?;ö=”xmtMúså͘}¦2[ÔÇmÛÞ§Âã÷Ðeø›¯c&/)Í$<¼³/vMûxf¿•eñ:mÌ›Å8^PˆÃ0ˆ'Ÿx2\¹|©}ó[¾èƒÆèŸßl»n³>å\"ëÃîÏß2î0hÎ.ÅϵX^RVš¦Áèè²Ø¶[¼õmo}á¥_úßó=ßû-‹ƒ᨜Ï3Ú°¦loaì8&¡€æSØÞ¡f¬\ÓÐF»,JÌ5F;âôtMÎE~é²&—F×õ(‹EQa9Ÿ¤í"%æ £….;³ŒÑ°6`½^cVQ°û0 ,P§e‹Ü霒|&gÚQ–3‹dÝJ>l%%¤ÑlÕ+L £Y'ì"Wˆt!–eýcCOÙ*¯•£?¯Þ¹,x•jò¿†>&$>–ÈØã8‡€G{EQ¢ïÛ¬IâìÃvv¶äŒZ®V”#öÊ*ÿ çÞú¶·÷ûÞ÷³ßrçè(·‚}GN² €ë8’µ±*ªìA–Bb´#æB`µ\ÒåÅíiJ‡¤9+ù…%“¢ë-ŽîÞE=«á=å‘8çøBtùï­9y.ÍW© #@EÛµ GÑðð92m/FÄÅGð‡dÂí±¦-­B‰\ &‰Nmø@ôvÛ ®ë\»vå‡#@„î³Ëð•x+ïÌívJ)4MóšúAœ×Xo À;ã“S ³ú›@.^¼ø+7|ð]O?ýÉw, PSˆ´YNó7S¸ÑbÛ4(ŒÁÁÁº¶Ë°Œ‘;?:(¥³+JBR¯×¥FÛµLèéû-‹ÛIÂ#$Ðw=ç˜(vüäÎÙõB °jŽ]H1*·ÀŸj8P7‰¬S²P„6+Œ¡q¦¿ƒe[¥÷Ûõ—Ï_úÉÙl¦iÎ{vž³óÚ<ÞyfÕt- #á~³S•×®]ùõã;hqA ßÝÇLuh¶-ŒQ¸páºv@ˆU]£Ù6К$2J*´]‡Årm4¶ÛˆÀ80EAY>Læî‡žýÝÀ0öÐFA «&‰¿Ô’@‘ÁBLf)xN¸Õ¥–W tšJ " )3ã"³A!T—áåIQ”ù5KN¡¾ !péòÅÿϺ‘쌯¡svž³“ BIÀÙ¬F=_À9åbõÛ€t‰G}ãŸ?îo6MH ¡(q³Þb¹$3áð9Þ3 ¥AÛ4¨ë³yÍzƒªªò&w³^£a ]>H!°>Y£šUˆ@è‡.³1Dt®a_ŠÆ• ¾ST*’áÀpJ¡Ï9Öék¥9·ÌLí2MÅ9&JJ­aÙ•RrÅ`Š´œúUU}ÀŽö}Ï?÷ükn±xvž³Ãüq±\-qéê5´m‡OGt¿m,–‹Ÿ½yóæGßÿ+¿rK3…Z@ ÁÑ˲DÓ40LYoÛUUQk;T&Å ú`4š¦Áro®à¬GUÁfè¨E kGΖvä{<cÏÉ¥ ÔK&û\à-±ÎLr”p%ÉPåö%…ä PdÙOö$k !aÖ@ #SPÆ@0[Ô³0Hˆ~ãêµï^.—úþ57¤?» ÏÎÙáX…®>pÆܽsçÓ–|­0›ÕïwÞßš £ ŽŽ((L]SÆsäùŸÖë“5öö±Ùl˜0›×ˆ>¢ë;%!õÝè %"+Ä”–×v€Œú1« Ü@[,•îš²,rE¨” 3”5qSLBJÔL1BI‘£W‚¥©8£(Ò’¥(HlÍU¢bØnªBC$ÿÿb±øÁ²® ”|ͽ Î.ósv¸Å,ë†>cì?­«TËåò™¨Íh# ¬u¨8t¬ïÔ)êTëõš¯ÖC±Òa–à¨$R CëÐà¼|dú´åeF€ä6uŒ1“th3™F­™l~o¦4×…ùÂBR´¦Œë|/%ñ%W›”BÖÉäCŽˆààªp½^c¹X¼{µ·ü01Gqvž³óÚ»ÊY ]VÔ†£¬®péÒ¥Ÿ_.—œ«S 4 Ÿc&¼#÷v»Åb¾@Ûvˆ1 ,K FaBÀf½¥í³uØ4§ÛIsMë(¸}艴\,™;HN“Dm’&Í‘9”@˜"z™4Îzݦ¸ÝÀoš‚QþŠS Ó9Í 5eÃŒ)îɽNmßvxÃÃÿ‹ýƒ}¬×ëׄýîì2<;ggºiþ¥%Nš oçÄßä¬×kxßjµŽ #ì8Š~mÓvOk´mË‹º.±Ýl3wÇL(÷ÁÒÅg SÂˉz´õµ£ÃhGÊÿf7ˆµüý(ɱ®”1m­…04Ï > åP†•P{LYC‰Ø µk¦ß¡‘3® \+*£¡ùk§ " ‡ç\ÖAÎguBø'Ÿ|ŠýÛg•áÙ9;¯©3 #/\ ¶3„Ïj¨¯”Â|¾xq±˜?{ûöK ! Œ!wGY`tDø)%8ÁÑàîÑ1W±m-œw|- ìHr%5ìr´E×µŒ £I¢ë&`@„H9'ÒC=þä7–cȶ;` ´/B¾ ªK:ʤ¤¯¥AYÀ5½~¡¥/qttŒýåò‹ùâ…¶i^³î†³ËðìüžnëåE]ݳ øLË凇Ï<þ‘Û³Z-qóÆŸÿ…ŸÿÅ?€LžÖ8==ŬžA)Mé‚)4IIxëÑm²¨Á;ç,6›5ú¾£ÅIêY‰®'©1ZË{..’ÐÐHóò"m˜NÀSR’ÜgY‹\bg&˜Ý%ÜKÞ4§$¼È¹×š*‰æ]–E–æ(­à¢†;k?æG÷ƒO}üiNZ<» ÏÎÙyMœ´p1àèîÑg5'üÔã¬CU–ÿnõ-ý8B Š¢pÁSu$µ” ²Tp#Q«]9QqD ÁHlB"rGŒŽ(ÐF¾ŒAYe_0ò…˜Eh8&UÁ^œ„ÒRälÉ›bDš7B$ ÈäA–rÂýàÀ(‚5Û¤ m²ÔœûBÙDJkl7[¬–Ë<_Öhš–þŒ³Ëðìœ×ÆQR¢-¶'§TÔ܇ÏçÉñmÛ¢®g89°(*ìÕ%¶MƒHk|D×¢k˜BÑò7¼#½ŒÑD½°È¡í1§Ð ]â¿;Q¨UX§tBÉsÉe ÁCJCK¾sô¨¤9Ô\1rU(‘³¢”ûû“÷8Y#"´6ðãˆàš¦Eßvøšÿꫯ¾ùKÞÂÛóxvž³óê÷Çô_B+,—Ëû6Ë×Zc¾˜?[ÏêÍÝ»w–äÆ0Øl¶!`6›£(ÜÌö{û{pžæŠ!FŠõ”­ì8Ak ç´âŠ+øÑé½»'Ý1IgäNÆKÒ¦%MÚ§û„úW¨–Ún€ä7“k…ÿ9£¾ŒÒÄ_Ô:W‹Z³†Ñ9­NNNpñÂ<øàÃß|°ÚÿÄ¥óçþ.!ÎÎ.ósv^Íæ˜nà ÎÑ|ë>¶lB,‹Ó+W®üÒ³Ÿ|櫌1Ø6-G8lÏEgpë0Œ™í8¤Ìh © b²§Xˆ˜ç›i)²{R sKMå^¼‡Ö–»$Ý`ÒJ-îÑþ¥ÅJzí¤R‘b¢©2ÔÐ\*ž‘:ï)¾!RÖÑÑþâ/ÆÅK!¥üÃRé¿« Ï.ósv^í²PÀEÀH…¢0÷ýO(ë.œÿE!ÄWÓ–y™JÓ 0Ç.*ÇàÐuÄT’6³QÁSNOºð´6—š.>ä|!$Mú¢ÈÕ_ˆ€ª<¥jгͷÑlÓË.þZÀ¹›v$‚[k"ÛèŒú§–ÙÀœUÞmÓ , ÜzÃ-ŒÃ€ù¥KO]¸|ã}Y;» ÏÎÙùl«6)04«Šª„{™b)½õ¨ŠêãUU¢mC²ÖrJâÈr²,ÑuÄ2 ž"iKµŠ¢-ìxOÆwºÄÓlPJò{ïs;ë#º˜Hð)t |ÑMU`"Z…šÜ("_ˆ)NUH -)õÎE2ÐÕ˜‚ÚcÎ[xñ¥—ðÀµk¸õðÃðÞ£(ÌOÅàÃÙ6ùìœWíŒÃˆår‰‡oÝâËâå™[-—K8랟ÏçèÆc^bÜeö[®Öʲ¤-°žÒç’ìÇYš ’<Y˜ƒSµH^c!)3YðÅ…Ù ÄüPÈ ||RžPÄ´I¢_C)4o¬¡ˆ($m f†ÒcŒ­EÛ´xäËFYU˜Íæk¥Õ¶Ûæ¾lîÏ.ósv>«YUWç/\@Qèšöeû³º®ÅÁ¹Ã¿xéÒ ·ïÞ½\Õº¶ƒR Z+®Jxïቬ†_ÉóB‘5"ÿš\}z¥4Œ¡bŠ•L¾ö¤ÿÓ:Ï S%&šŸ5™‘(%„ˆˆð„ã’’‘ bª>!¸¢d}¡¤¹(IR#_C$*Ïz³Áj±ÀÃ=ë<´–¿¦”<¶£}M¿WÎ.óóy}‚Ø;8Àñúïÿ¿ò²K;–ËŰ\.ßc´þFe4úÙ{zÊ e›~O°0º„uJÓF Í¬äK&U€i««´Fàlf­ "'ë )°'X€õ†Üs%˜  Ó$ù ÅŠ„t• ïÏE)r0i ·ÉD¯ñ¼¥öÎC*‰Ízƒ®]Ź‹ç)‚t6ÿ~!Î.ósv^¥ªP ï{ôÞaô±Eh)UU½¯(ªoƳºÆÐ1"F›ÿü[\"ç'r4å‰h¾le†~O„”„Øw.-BTŽûLmvjESE5Á12%7éßÓ¢%e “£RLyÆÜk¥³`=e£^ºøHBñ±¥Êûuox„Ò@ôÐZýÛ~è)·üì2<;gç¾XkqþÂy\\̸šzùOY–¸rõò~ìcOüßÏ=û< S`µ·ˆˆíÖ³XZePBQ–ì¦íî.RŸÒ2Ïõ¤Œ÷äŠxö%" 9ÿ8-bR«e§`¨$« Üž“\Ì1Ü^Ç|1“»DA.AZ iŽ ì®¡µÆsÏ>‹½Õ®^¹‚f³Å¹ƒÃOjc>hûšžž]†gçóöxOÕΕë×PÕõ˶AþͪÑýý½~á›ÞôÃO=ýô×–u…"¬V{Œd¹ÚÓ¹mMÖ6­4]Wbji“:ÆÀ`9‘µX[(¸CN%‰²ýÎešªÇ$ºÆ4/˜?(ò¥›Ù…’âF #½”Ñ(‹BR~rQxÖ=*Ð¥ºm¼ñoÄl>‡íÌfõI)}ß÷¯ù÷ÌÙexv>/Ë®kñð#¯Ã7ob³^UõŠüÑ1FÔ³9¾æk¿öÛ>üø‡¿öŸü$´Ò@!å ÎZXgóì’f‚Š/8Bì“{Dq¥ˆ SÜÆ"Ò¯1Zø N¤™SŸÌóA%%"|LhÌUdŒ‘<9QæM³T JˆL¤[e%$û¤ƒ³ {•¸}çöV{¸qãF®ƒ ï³Ã;Œg—áÙ9;¯ô‰!ÂhƒÕr‰±ëá^áâÑí;ØÛßÏ×}íùÿôŸý³o]w´4Ôê ¥'ê4 à¬',¿JÀ´ÚžÚc. EdÀj€Œâå£P€]I3¢ç R&ÁÒ׋Óyª8‘«EHÂÓDHI­=·¥¡ÅIŒ'ÇǸ~ý\¸pÃ8¢2ÆV‹êgoáE<» ÏÎÙy¥s«ý–û+Ü=ºóŠ“•cŒèÇ_ôÖ·|Û74Í[~à]ïú“'› Š¢ %‚Ðð™@c •Ñ^Þ{xO-q`ÉŠ’!Fh¥áCú^_v"é ‰AÓµ†ˆôÄV9Na"c ™.@ Å‚jH¢€K©ø×)˜Â@ä’î]G‘¥Þ¼‰)œ¾œÏ¿_)ýl³Ù~N¼oÎ.óóyZÒ[½ò~Ø#ÖÛ-Þú¥oÿ&|ò§ßý3ý™çž§¶$ù! C„@¤êJ(-8¤Ä>!(¾}Úç6Û®àDÎHñž[g™îÄÉ—-!!!@_›Âà‘/B)x[“ß c¨¢e£ä¿›’$`ÅÉÉ ÎŸ?+W¯À:Ûûáçn¿ø"Æ×¸¾ðì2<;Ÿßw!/ DÒ”¼ÂGp£ÅÝÓ {ÓcãÆÍ~ø§êgþÏâÉ·Ý9>ž5‡´¹õ&À;Gm±¦`÷šíAE8N´Kâf‘i«1“¯“Ì…f„ÔN#Ñï´à`¢·Œç¢Ö8A#„”P‚áµÒ(ë’<ÈRA) k ]¾mÛáÑGCYUhÛ¥Ò͵ëW¿+U»g—áÙ9;¿‡äÙn·X,ïùÂ/ü‚·¿ù-_ô_>ýô'ÿØó/¾x£ï»ß×´ía×õèºmßg¶ s”rçCD€ 1žOFɼ$…!¡t |yJ+‰(IO„\2S5Êñb ŽiÁ¢Xà-(T"`+©r‹-„À8Œ€¼s¨ë0Ž#‚õ¸ñЃßùê¥;}×Îü¼Î.ósv^Ö ‘.«¦i°ÝnqýƵŸxôÑ7üĹÃC ÃXXçÞîìøuö ëúG×ÛÍWl·Û7Ž£-¶Mƒ~è1Ž#†aÄh-Œˆˆf€QP@{ ÔI›áÉ}’¶Õ‘‡ˆ€V ž—7Ø)œ%_‘B€&)fTS$¨ÑyKM¼C’àŸœàÀb¹€sׯ^}âu·^÷—„2ÐEüœùY]†gçì¼B—"PØ£i[xïGcÌûŠbö>ShûC?.¥×Fg/wm÷h×w¯ošö«š®{KÛ4hÛ£³)à­E€À`m&TSÅæ¡5{”cD䬓#´$àBd"¶’ JHn…%…¢À -{ž ³g@‡0Â{ ç/ 4êYýâ¹ ç¿Bv>ø×lÞÙexvÎÎkábL kÊHö´ Ž! :Ä€M¡Š—Eña%Õ¿+‹Ï_ÀµëWÿÈ8º¿xº>½yº^?ttt¼Øl·°ÖbÛ6èúÁyøH™ÉÞ()a­Å0ZXoYp²W:·½@&f+¦á¦@U•pU:g,û`=&ž~ú)ܼñ®=pCß=uõÊå/ 1¼è¬…QÅ«2¯=» ÏÎÙù<¹*C  °Ö¡ïxïàCü¡ /þÐÅK—pº^×MÓ|¡†›MÓžß´Í%kí¶Íö Û¶½Õ6maCäêŒæ£µ@ˆÈÿî%a';_a ˜²@=›AK…ÂG±ï{ï!ÅžÇÁážøØÇÞså⥯Rö)Bàsíœ]†gçì¼æ[lú¿Þ9ôCO:ƾë"ð‹Æ˜_4FaµDð’³]FkßÑl›¿Úý•¡ïoŒ£•€«çuÒÂK)u S†‰àê*E¡$æ³9¤TèûÆF‘Õ8>>T ¶þnaÌ_+Ëòsfs|vž³ó¹~x1#Åïh~ç=‚÷°ÖBeæ]®,Þå½—NYØYñº®¹1 ƒuÎo‹¢xç|1?ˆ!~¹Rò!­õaŒ¸dŒ¾eÊòua´Ö Î:æ.FLg»Ù"†ðËuUÿ©½ÕÞã‚+ÅÏåsvž³óyÕdÓ,29YBð!Ä8H)†²,¾#Z|x?Â: ;ŽO{>b@ Ã0 ( £Ïyïß\׳Þû)ñ&@~¹ÖæÑª*{8ÿWöW«ï„&zN²ú}N¿vñshÀyvÎÎÙ9;/×ùÿ_Ïè÷~ye´IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/yabause.icns000644 001750 001750 00000130543 12256006101 024254 0ustar00guillaumeguillaume000000 000000 icns±cics#Hüþ?þ?ü?øøÿþÿÿÿÿÿÿÿþàààüþ?þ?ü?øøÿþÿÿÿÿÿÿÿþàààis32\Ÿ ‡ê­|‚ -[o 1/ B5€ %â^AS I\B úðAg`I‚o¡Ì² lŒ£–:›°3qç€ -‡™|V¬Î Æãã€7t}[ ±á뺮<ÌKsعů­÷¾Ÿä!§´¼¼·éµ¡¡@•㳦§±¶ºäq‚’Ø;vN8Š€“Ž€„ ^  €  !åo& #"7# J>€ <™AK^ QgK ¤œIsjQ{z˜w™±£@=j£€ 2”§‡wT|;EŒ‹5€Šd>§¾O7£RÚffmQc]ViByýÿÿþ™mÿÿÿþ“úÿÿÿá"èöþÿÿûÙÿÿÿøÍÔ ðÿÿÿþÿÿüF}ð9#ûÿÿÿÿþ£RÆn ÃÕlþÿÿÿëd÷ÿþ¬ØØüÿÿÿºÈÿÿÿÞÅõ¨ êÿÿÿ¡¬ÿÿÿÆgöþ×ĵøÿÿÿþê¼âÔ;PÜûÿÿÿÿÿÿÝ9 /r§ñÿÿÿz ìèèíxICN#ÿàÿðÿ?ðþÿÿüÿÿøÿÿøÿÿðÿÿàÿÿÀÿÿÀ?ÿÿ€?ÿÿÿÿ0ÿÿüÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿü?ÿþÿüÿøøøøÿàÿðÿ?ðþÿÿüÿÿøÿÿøÿÿðÿÿàÿÿÀÿÿÀ?ÿÿ€?ÿÿÿÿ0ÿÿüÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿü?ÿþÿüÿøøøøil32bàƒŠ‚‚ƒƒÆéù‚„‚ FfúÙ××Ý …°Ùñ%é‡$, ¢‚ 2*"ˆ%/71ƒ8>5,#ˆ * #.8BK€)QI@6*‡‹ÖŽ6ALUU `\SI?‡yÔÃï1IT_i>Uog]R,‡Y×±Ü"J\gry0‚zpeK†KØ­×C anz†Œ“Žƒwg†3ÀÄ0ƒ;q~Œ˜£¡–‰{+Ïu!ªYØ ØÀƒ\Œš¦£—‰Q?烓…”¦¢Æ¦Þ„u‡’™˜rÒÈο¯†ú®´ †U‡‹‹…?…Òìãζ©¶à¦·E†Otz}}y:šáöëÓº«—†Ù£­é†Ghmpol4ÖÔáÜʵ©šƒñ«œÖ„@[`ba_31‘ÀÈź¯¤”U¾›§Ø’‘ƒP6NRTSREZÓ6„£²±­¥œŠãðפ›¥ÌßHAä³vAn‡–·ÖïîÖÎÌÀ„–£¡•ˆ•‘±Ö£››¦¹ÖàåçÓÒÐÎõ¤››œÆñ/™ƒƒ‚Ë|Ù·Š›¬Ìö¯¦„ÜÖÉ´¬¥Ÿ£¦ª²ÇÖìò©ÄŒéæõàäçêäßÓ r> “ì‚–‚éà‚Š‚‚ ‚ ƒâUˆ‚ „‚ ÿ­š™™ €  …  }™Æa5*‡ Ò +6AKT!€-[RI?2‡–Y?KV`_kg]SH‡y§8S_jtD^{rg\2‡¨–\› Tfs~†5†|pS†x™V•š ly†“™œ¡›ƒr†ÖrvÔƒA}‹™¦±¯£–ˆ0ª€b¼Ì™@˜`ƒe‹š§´²¤–YŸO-34-3ê'yJœ„” §¦}I8{|ZC4.g­XbɆ^‹”˜˜’EÜ.¿¬|G@1>Jfä†X€†Š‰…@]B§Ö¾‡OA7.™EV¤†Osy{{x:IBЦœtF@8.ªS:’n„ Gfjmli9i4\niNB=60íq9L™¶ƒQ90N8˜G9IƒžñºH 7h‚©ÂÜÙ´˜”Š-7=<6/3m0•E99Kk•¦´¼«ª¦‡kK99<ŠÔï4.--F€˜f=Š9[™àû7?ê„sµ—cWK‚l€<ÂÌÖßááèæÝÔÆ½\a»‚sÏÚäî÷õìâ×QÙA2`Ô_†&…„§Úåïù÷íâß(wd1ŠƒÌáéïîè¿ $ ff?%·™@K°†›Úàäãßmp}³f(# ‹1PȆ•ÑÖÙØÕi/*˜Î²r1#‡+>†ÇÌÎÍËd%*v—Š\'#”; E„нÁÃÂÀk 5@UO0%!Ò\3…ûHƒQ³¶··µkNƒâ!&%$!(¿‡.0pŒÚ•Ìì5’¶½ÂÉ×Ó«‰„{! H‚+2Vƒ–¦±¡¢ž’vW3!yÍŸ#L€î‡P"ŠE‹Úó¿w„ †mM?2"'.6?P~°ãêž}ŒŸ:ºª¦°¸ÁÂÇÅ»«”D’Y‚y=•R‚o8œÈl8mkmÿÿÿÿÿÿÿÿÿöâÿÿÿÿÿÿÿÿý¼ÿÿÿÿÿÿÿÿÿ£~ÿÿÿÿÿÿÿÿÿvîÿÿÿÿÿÿÿÿþ! +üÿÿÿÿÿÿÿÿÃ,ýÿÿÿÿÿÿÿÿÔ^˜ÖúÿÿõÒÿÿÿÿÿÿÿÿòrÿÿÿÿÿÿÿÿÿÿù¼nDXÿÿÿÿÿÿÿÿþ4¿ÿÿÿÿÿÿÿÿ÷ öÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿ’®ÿÿÿÿÿÿÿÿÊ~ÿÿÿÿÿÿÿÿý7ÿÿÿÿÿÿÿÿô ‰ûþÿÿÿÿÿÿÿÿÅéÿÿÿÿÿÿÿþ;øýáËÿÿÿÿÿÿÿÿ¾ÿÿÿÿÿÿÿÿ‡[ùÿõñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐ"ûÿö^2þÿÿÿÿÿÿÿÿÿÿÿÿÿö¯úø²zÿÿÿÿÿÿÿÿÿÿÿÿÿAYt=(úÿø-Æÿÿÿÿÿÿÿÿÿÿÿýôöûß1‚øÿóòÿÿÿÿÿÿÿÿÿÖŸøÿÿÿÿþ÷ ÖÿÿºIÿÿÿÿÿÿÿÿþ4úÿÿÿÿÿÿû·ðÿÿ¥*ÿÿÿÿÿÿÿÿû{÷ÿÿÿÿÿÿÿöúÿÿæ*ÿÿÿÿÿÿÿÿûœÿÿÿÿÿÿÿÿÿÞÿÿöjÿÿÿÿÿÿÿÿîmõÿÿÿÿÿÿÿìŸûÿÿúˆÿÿÿÿÿÿÿÿ뀭ûÿÿÿÿÿÿ÷’$ûÿÿÿøó¨aM=Eÿÿÿÿÿÿÿÿýüýúûþÿÿÿûß}øÿÿÿÿÿòòóöÿÿÿÿÿÿÿÿÿÿÿø@Üÿýþ£ ùýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöß31,ÎüùÿÿÿÿÿÿÿÿÿÿÿÿÿôAq¿áýÿÿÿÿÿÿÿÿÿÿáÿÿÿÿÿÿÿÿáÿÿÿÿÿÿÿÿáÿÿÿÿÿÿÿÿßich#Hÿÿÿ€ÿ?ÿÀÿþÿÀÿüÿàýÿüÿÿÿÿøÿÿÿÿðÿÿÿÿàÿÿÿÿàÿÿÿÿÀÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿþÿÿÿüÿÿÿøÿÿÿø?ÿÿÿð?ÿÿÿàÿÿÿÇðÿÿÿÏüÿÿÿŸþÿÿÿÿ?þÿÿÿÿ?ÿÿÿÿÿ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÿÿÿÿ?ÿÿÿÿÿÿþÿÿÿÿþÿÿÿÿü?ÿÿÿ÷ðÿÿÿàÀÿÿÿÀÿÿÿÿÿÿÿÿ?ÿ?ÿ?ÿ?ÿÿÿÿ€ÿ?ÿÀÿþÿÀÿüÿàýÿüÿÿÿÿøÿÿÿÿðÿÿÿÿàÿÿÿÿàÿÿÿÿÀÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿþÿÿÿüÿÿÿøÿÿÿø?ÿÿÿð?ÿÿÿàÿÿÿÇðÿÿÿÏüÿÿÿŸþÿÿÿÿ?þÿÿÿÿ?ÿÿÿÿÿ?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÿÿÿÿ?ÿÿÿÿÿÿþÿÿÿÿþÿÿÿÿü?ÿÿÿ÷ðÿÿÿàÀÿÿÿÀÿÿÿÿÿÿÿÿ?ÿ?ÿ?ÿ?ÿih32Gÿï…†…†…‡… ‚ ÌŸè¤yT8! „‰‚  7PÖƒ×ø$ Š V€×Ú*ß½ÜÌ€ €‹ &#­«U„ +'!  "(-3ˆ 33.)# "(/5:;‡ )@;50)# !(/5KS[bioj[vpjc\TLŒ¿ Õ¬ÍÛãOX`gov|H€2‚}wpiaY*X×¶Â×4ƒ/[dlt|ƒ‡?>?‰Š„}umeFŒÐ×ê×Q„Jfox€ˆ–š›—‘‰yp` ŒUÿÓ ÍÞŽ… aqzƒŒ”œ£¤ž–„{r„™´„#×¶¬×¹‡"r{„–Ÿ¨© —Ž…|>‚+´€ƒ…ÃjõÑœÁ׎ˆ Azƒ‹”œ¢¢•„`€8±ƒƒ›”‰ƒ„<Ü×»œÓ#‰ _‡•™™–‰x­ƒ™ÊÎŹ°ž…ƒ#®×§¢×¡Š {‚ˆŽ‰ƒ/1ƒ¡áãÝÑõ¬¢„…+!Ò›ª×&Š u{„‡‡…|ˆ‰ÚïôèÙɹ¯¦–ƒÇüÊ›ª×#Š ntx|}~|yu€óƒ¤áñöëÛʺ¯¦„”ãÅ›¡×ŒŠ glpsttspm€Zƒ±ÚæêãÖÇ·®¥‹ƒàÅ››Ñõ‰ _dgjkkjhd€Lƒ¬ÎÖÙÕË¿³«£›‹ƒÏ››µ×ˆ%W[^`bba_\"¦ƒšÀÆÈžµ¯§ ›…‘_ן›œÏÙ‹ˆOSUW€XVS€†ˆ±¶·¶³®©¢›–ƒ¿¬×¹››¢Î×jé†)GJLNOONM^\ƒí̃”­®­«§¢œš…ƒŒÓŸ››¡¿××;]ƒ?R_p¬ÈçòòñèÜÐ噃£¤¢Ÿ›—‡ƒ€Æ×È€›¥ÇÚÜÞàíÕåîïñóôõëÝϸ¥€› Ìà›ƒ„‹Ž‰‚‚U€²×Êž›œ¬¿ÇÍÓÙØÕÑÍÈľ®ƒ› Èퟀƒ‚ª+ƒ˜×б”›®Îîó ÀÚ‡“ê×É´Ÿ›´Èòù÷>Æ¡Þ×ÙÍ¿·°¨ŸŸ¢¦©­±¶ÅØëúûú×ò”‘íjòÜÞàâäæèêìîðòóðÆ™k=š aìœñ‡‘\rjd\P1¢†¢†£†ÿÍÿï … …  …†  … ‡… ‚ f’[+ òÝ͇ „ ‰ ‚ Ü𴘃™°  ‰ €  % =€™šÔUñsf€&!€ ‹ "(-*z/}ª„ 3.)#   ")/5;ˆ ;<60*$ ")07=CC‡ /ID>81*# (/7>DKP4† UQLE?81( "° .5=DKRX]„ S_YSLE>78«˜n;CKRY`fSƒ7lgaZSLD,`¥–l”c2HPX`gms-‚ utnhaYQBŒ¡”a’£`HU]emt{ud‚|ung_U»—TŠšÂYbjs{‚‰O€7Šƒ|tlc/õ™dx™ƒ4fow€ˆ”EDD•—‘‰ypN™zQ™ï„Rr{„•¤¨©¥ž–Ž…|j Œª³“A‰…l}†™¢ª±²¬£š‘ˆ}"„åï„„™eT™9‡%~‡‘𤭶¸®¥œ’‰E‚g>-€.Eß®;v™ˆ H†˜¡ª°±«£š‘i€=..8:61.-ms™l:”ω iŒ”œ£§§¤–ƒ<.F}|gLC;/.d2™ME™(Š ‡•šž›–4i.Q§¬‚bEA<..Í‘9Q™ˆŠ ˆ‘””’Žˆ"€/4žÆÑ¸•qLB>7.E³…9Q™„Š z€…ˆŠŠˆ… €.R§ÌÖ¾™tOC>:.3¡|9C™Š rw|€€|x€w.\—±ºªlIB>:2.Ÿ|99®Š joruvvuso€r.O|•‹vZEA=92.ÁŒ99c™yˆ%bfikmmlif%ê.9\joiYFB?;9.2ú™?9;ŒšÎˆ Y]`bccb`]€?.0DFIFDB@<97.Bå™i99E‹˜U†PSVXYYXWdUtèìÌ€.6€B@?<:9.-æ¹”?99Cs™šç“‚!HWcq‹£»ÕÞÝÛ˱œÄK.4==<;970.Xø™=€9I¡§¬±¾ÑïÞ·ÈÓ×ÛßâåÓ·œqK€9 ”½8..2431--]4™„>9;Vw†œª©¤ž—†}_=ƒ9BŽÓôX7€.-;ƒ"™Ž\”9_›Ùàòo›£†Ó¥™„`@9=j‘äðë)Èâ(™žŒui]P?@FMS[blНÕñôóÐyæ”¶²¦¬±¶»ÀÅÊÏÓØÝá༔j?š  ‹>I#a[\UM2 ¢† ¢† £†ÿÍÿï k…uft„ur…"x…y:Žb…y5‡?…ywƒ I4 í×ĶA:|„yTˆ\‚y z~‚XÄÕ ‡ƒˆœ€ƒzyk‰ o€yz„ˆŒ$6€ˆ‰½.º g‰…€{yyw‹#xyz€…ŠŽ’|l 6… 4—“І{y8 Az…Š”™Eˆ ’™•‹†€X a„Š•šž£œ‡ f¨¤Ÿš•‹w ~Ž”šŸ¤©­k† *±®©¥ š•"Ž œ*’™ž¤©®³µ&„ ¸´¯ª¥Ÿ™H¿™‡\R£©®´¹½—ƒ `¾º´¯©¤t’„WƒX}§­³¹¾ÃÈL‚ ÆÈÄ¿¹´®™ŒfƒJ€ ž±·½ÃÈο € ŸÓÎÉľ¸°.Œߦ†cs€œ–‡vjI"ƒ(~Ëëª-€ƒˆ|D”HÓÚëA€™OS†Ÿ“ˆqI&#Uƒàîè#¸‘ŒˆŽ{aSF8%&-4o¸üÿÿøªÝýÿÿýGþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠ»ÿþÿÿÿÿÿúi,ÿÿÿþº•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑ¿ÿÿÿÿÿÿÿÿþÿrÿÿÿÿƒØÿÿÿÿÿÿÿÿÿÿÿÿÿömÿÿÿÿÿÿÿÿÿÿþø)½ýÿÿÿcAÿÿÿÿÿÿÿÿÿÿÿÿÿ„òþÿÿÿÿÿÿÿÿÿÿÿ¦×ÿÿÿÿd>ÿÿÿÿÿÿÿÿÿÿÿÿÿ},ÿÿÿÿÿÿÿÿÿÿÿÿþßïÿÿÿÿŠ>ÿÿÿÿÿÿÿÿÿÿÿÿÿ}`ÿÿÿÿÿÿÿÿÿÿÿÿÿÿòÿÿÿýÝ>ÿÿÿÿÿÿÿÿÿÿÿÿÿ}dÿÿÿÿÿÿÿÿÿÿÿÿÿÿÇýÿÿÿÿg5ÿÿÿÿÿÿÿÿÿÿÿÿÿl1ÿÿÿÿÿÿÿÿÿÿÿÿþä›ÿÿÿÿýûT!ÿÿÿÿÿÿÿÿÿÿÿÿÿIöÿÿÿÿÿÿÿÿÿÿÿÿ­Pÿÿÿÿÿþü—!ÿÿÿÿÿÿÿÿÿÿÿÿÿÍÜûÖÿÿÿÿÿÿÿÿÿÿþü3Ðýÿÿÿÿÿÿý¯h)!ÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿþúÿÿÿÿÿÿÿÿÿÿ‚MÿÿÿÿÿÿÿÿÿÿÿÿñáËÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØÿþÿÿÿÿþþ{~ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÚÐÿÿÿþÂ)†ÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿã'JFSèÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃP ƒôÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿgI—äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿI8Us“‘—ÿÿÿÿÿÿÿÿÿÿÿÿÿI!ÿÿÿÿÿÿÿÿÿÿÿÿÿI!ÿÿÿÿÿÿÿÿÿÿÿÿÿI!ÿÿÿÿÿÿÿÿÿÿÿÿÿI!ÿÿÿÿÿÿÿÿÿÿÿÿÿIit32Gžÿÿÿÿÿÿÿÿÿÿÿ󗳕“–±•”—°••–¯•—–®”™”­”™“ – {¯› ã˵¡Œ{{Ñ… ‘› ŽHy!üâŠ×„  Ž ‰fí˜.ì‘×Gƒ Ÿ‹  „C‹y –×J‚  ‹ŸŠ  ‚ÌØ•×Öõ!"ƒ  ‰¡ˆ   ‚@×í!y4ÀC†  ‡£† !#"‚š‰×Ú¼%ÌŒ #! …¤„  "$&(‚!Ò„×ÖŒf(&$"  „¥ƒ !#%')++‚v×õc|²“,+)'%#! ‚§ !$&(*,./%‚ Ããyù—%/.,*(&$" €©  "$&(*-.024‚#Ƙ421/-+)&$"  ª  "$')+-/1357257531/-+)'%"  «  "%'),.02468:<'<:86420.,)'%"  ­  "%'),.03579;=?>›A?=;97531.,*'%"  ¯ "$'),.1357:<>@BC.š;DB@><:8531.,*'%"¯!$'),.1358:<>@BDFH™&HFECA?<:8631.,)'$"±!#&)+.1358:=?ACEGIK@— KKIGECA?=:8631.,)&$! ³ #%(+.0358:=?ADFHJLNP"—>PNLJHFDB?=:8630.+(&# ­s;‚"%'*-0258:=?BDFHKMOQSN•!USQOMKIFDB?=:8530-*(%¬ÇôÏ%‚"'),/247:ACFIKNPRUWY[]_J’a_][YWUSPNKIFDA>;9630$«ÛÖ×Ó­C‚(258:=@CFHKMPRUWZ\^`bd#Vdb`^\ZWUSPNKHFC@=;85/«c‚×Ï£¿Ì ‚ 169ADGILORTWZ\_acehjk8Žjljhfca_\ZWUROMJGDA>;"«¡‚×ÄŸÄ×|ƒ'=@CFHKNQTWY\^acfhjlnlUpnljhfda_\YWTQNLIFC@3ª²‚×ÁœÆ‚׃6ADGJMPSVY[^acfhkmoqsPŒ,usqomkhfca^\YVSPMJGD? ª‚׿›¹‚×¹… AFILORUXZ]`cehkmortvx Š pxvtrpmkhfc`^[XUROLIFªÙ×À›­×Öì‡GJMPSVY\_behjmortwy{h‰N}{ywtrpmjheb_]ZWTQNK/ª7ãל¤Ô×ˉ3LORUX[^adgjlortwy{}8ˆ €~|ywtromjgda^[XUROB©âñׯœžÎ×EŠDPSVY]`cfiknqtvy|~€‚{ †q„‚€~|ywtqolifc`]ZWTNª.×ÍŸ›Å×s‹PUX[^adgjmpsvy{~€ƒ…‡R†D‰‡…ƒ~|yvspmjgda^[XU%ªõ×Ò£›¹×æK(VY\_bfilorux{}€ƒ…ˆŠ‹„ŠŒŠˆ…ƒ€~{xurolifc`\Y=ªÀÜש›¥Ö€×ØŽ@Z]`dgjmpsvy|‚…ˆŠŒs>?@?q‘Šˆ…‚}zwtpmjgda]P©¿ö׸››È×ÏS^aehknrux{~„‡ŠŒ‘“•—˜—•”‘Ї„~{xurokheb]ª¤×Èœ›¯×'’.^bfilosvy|‚†‰ŒŽ‘”–˜š›œœ›š˜–”‘Œ‰†ƒ€|yvsplifc/ªðÛ€×Ó¢›Ï€×Ü“2cfjmptwz}„‡Š“–™›ž€Ÿ›™–“Ї„~zwtpmjgJª-×±››¸×’”,Mgjnqtx{~…ˆ‹Ž’•˜› ¢££¢ ž›˜•’‹ˆ…‚~{xtqnj_ ª´×È››¡Ô€×ìu•* `knqux{‚…‰Œ“–™œŸ¢¥¦¦¥¢ ™–“Œ‰†‚|xurnk«ü€×Ö¥››À×.—( knrux|ƒ†‰“—𡤧©©§¤¡žš—”Іƒ|yuro;1!~!=U„×¼›› Ö€× ™'DFGHIIJ€KL€KJIIHGFD#Vоéîîííììëë݆ƒ˜©ªª«€ª©¨§¦¤£¡Ÿƒ›ƒŽÔ€ €×Òž…›¥Ï‚×Ö1N˜?@ABCCDDEVs¬Éæ€óò€ñ ððïïîîíììæ’ƒ„–¤££¢¡ Ÿœƒ›Ž‚ƒ´‚¿ì€×Ö¥†›œµÔ€×ØÙÚÚÛ ºä‘5;<=>>?@WqŽ«Êèó†ô€ó€ò ñðâÓÆÀ½ÔììÝ‹‚ƒŽŸ€ ŸŸžœƒ›™Š‚ƒ™ƒ¬×̇›, ÅרØÙÚÛÛÜÝÞÞæN†ájl‚àqæ[ÚŸ˜(2;ENy‰›­¿Ñãðòòóó€ô‡õôôèÔÁ®ƒ›¾íìÑ„‚ƒ„‘œœ…›™Ž„‚ƒ‹{…)×É›,¥À×ÙÚÛÜÜÝÞßßàáââãäååæçèèéêêëììíîïïððñòòóóôôõõ‚ö÷÷öñÞʶ£‰›æíìÒŠƒƒ „Š’–˜››˜•‰„ƒ”†{Þ׹Л3¹ÍÛÜÝÝÞßààáâããäåææçèééêëììíîîïððñòòóôôõõöö÷÷ôæ×Ǹ¨œŒ›¨îííì®›“ƒ¬ˆ›Ö׺Œ›* ¶ÊÝÞßàááâãääåæççèéêêëìííîïððñòòóôôõòäÕÆ·¨œ‘›¦ãïîíëèÔƒ…‹µ‚×ÀœŽ›¤±¿ÊÓÛãäååæçèèéêëëìíîîíéáÙÒʺ²ª–›ª×ñðïîîD€›Ä‚‹ƒ„Þ¦3‚×È ”› £©«­¯±´²¯¬©¦£ Ÿ› ¦Òñòñðïïø½‚‚¤‚†ƒ¯.ÏV‚×Ó±Á› ¡Êóôóòñððòò† jÇBÓÔ OÍ8”•ƒ×ÅŸ¼› ž»ßöõõôóòññ}4©úÖ‚×ÖÀ£·› œ³Òõø÷öõõôóòW”¬]„×½¨²›Ÿ¹×ôúùøø÷öõõô=#¯‘•Ø„×Ϲ£«›±Éá€û úùùø÷÷öóG‡³G'„רÙν­ž£›£´Ìãøû úúùùø÷÷öÍÆ·Ÿ!‚× ØÙÙÚÛÛÎÀµ«ž˜›¦´ÃÒáðøø€ù€ú€ùøøñ¥a »ŸqØ×רØÙÚÛÜÜÝÞßßÚÌÈþ¹´®ªª€«€¬­­²¹ÁÈÏ×ßæïôôõõöö÷÷…øä—JÀ0–ÕØÙÚÚÛÜÝÝÞßàááâããäåææçèééêëììíîîïððñòòóóôôõõöö÷ó¾{W Æ6ðó8áÚÛÜÜÝÞßßàáââãäååæççèéêêëììíîîïððññòòóóôôõõæ±x@Ê)VäDÜÝÞßààáâããäååæçèèéêêëììíîîïïððñòèÂwQ)†Î¿¨Çb6ïããäåææçèèéêêëê×ŲŸŒydG"ŒØ jMMƒŠ»•–ä–ä–ä–ä–ä–ä–ä–ÿÿÿÿÿÿÿÿÿÿÿÿ—ÿÿÿÿÿÿÿÿÿÿÿ󖳕’ – ± ”  “— °• •– ¯• —– ® ” ˜ ”  ¬ “  ™’   — šžÙrXF7( ¶… ‘ ›  ŽÚ}y ͳ¡Š™#„ Ž ‰3"×§‘™éƒ  ž ‹ …Î ¼–™3‚  Š  ŸŠ !"‚š•™˜®Í΃ #! ‰ ¡ˆ  "$&'‚.™¨Î #`‡ (&$" ‡ £ †  #%')*)‚m‰™›Ê<@9=@><:8631/,*'%"  «"$'*,/1468:=?ACE#-ECA?=;8641/,*'%"­ !$'*,/1469;=?BDFHG ›JHFDB@=;9641/,*'$"¯!$'),/1469;>@BEGIKM5šCMKIGEB@><9741/,)'$!¯ #&),.1469;>@CEGJLNPR™+RPNLJHECA><9741/,)&# ± "%(+.1369;>ACFHJLOQSUH—UUSQOMJHFCA><9641.+(%#³$'*-0368;>ACFHKMORTVXZ'—EZXVTROMKHFCA>;9630-+(­Ð*‚),/258;>@CFHKMPRTWY[]X•%_][YWURPMKHFC@>;8520-'¬­“‚).147:=@CEHKMPRUWY\^`b;”\b`^\ZWUSPNKHFC@=:752/ ¬s¢™™€‚0369ADGJMORUWZ]_adfhjR’ljhfdb_]ZXURPMJGDA>;8*«R‚™“V'‚.:=@CFILORUWZ]_bdfikmo'`omkigdb_]ZWUROLIFC@=7«û‚™Fr‘‚ :?BEHKNQTWZ\_bdgiknprg;trpnligdb_]ZWTQNKHEB?«‚™ƒBw™™|‚ADGJMPSVY\_adgilnqsuw>Žvwusqnljgdb_\YVSPMJGD'«(‚™{?{™Yƒ,FILORUX[^adfilnqsvxzx^|zxvtqoligda^[XUROLI:ª4‚™v:~‚™ƒ>JNQTWZ]`cfilnqtvx{}XŒ1}{yvtqnlifc`]ZWTQNH ªu‚™s9i‚™9…JORVY\_behknqsvy{~€‚„$Š {„‚€~{yvtqnkheb_\YVSOª3š™t:V˜€™˜^‡#QTWZ^adgjmpsvx{~€ƒ…‡r‰V‰‡…ƒ€~{yvspmjgda^[WT5ª´¢™w:H”™F‰:UY\_bfilorux{}€ƒ…ˆŠŒ>ˆ$ŒŠˆ†ƒ€~{xurolifc_\YJ©q«™~:>Š™æŠMZ]adgjnqtwz}€ƒ…ˆŠˆ †|‘‹ˆ…ƒ€}zwtqnkgda^Xª×™‰?:{™¹Ð‹Z_beilorvy|‚…ˆŠ’”Z†K–”’‹ˆ…‚|yvsolifb_)ªd™‘F9i™£x-`cgjmqtwz~„‡Š’•—˜„–™—•’Ї„~{wtqnjgdDª`œ€™˜O9I˜™+ŽGdhkoruy|‚†‰Œ’•—šœ~DƒE|žœš—•’Œ‰†ƒ|yvrolhZª°™g99™I \ilpswz}„‡ŠŽ‘”—šœŸ¡£¥¦¥£¡Ÿœš—”‘Ž‹‡„~zwspmhª+™:9X™Ñ’.imqtx{~‚…‰Œ’–™œŸ¡¤¦¨©ªª©¨¦¤¢Ÿœ™–“Œ‰…‚{xtqn5ª8›€™’D9<€™œW“-8nrux|ƒ†Š‘”—šž¡¤¦©«­®®­«©§¤¡ž›—”‘Їƒ€|yurSªÖ™\99g™”,Urvy}€„‡‹Ž’•˜œŸ¢¦©¬®°±±°®¬©¦£Ÿœ™•’Ž‹‡„€}yvi ªë™99B”€™§°•* kvz}„ˆ‹’–™ ¤§ª®±³µµ³±®«§¤¡š–“Œˆ…}zu"«´€™—J99u™Œ—(#vz}…ˆŒ“–𡤍«¯²¶¸¸¶³¯¬¨¥¡žš—“Œˆ…~zBs¼ƒbfŠÅÉ™n99@—€™¾™'Bz~…ˆŒ“–šž¡¥¨¬¯³·ºº·³°¬©¥¡žš—“Œ‰…~aŒH0….2Mªº€™”?99b™s™&a}…ˆŒ“–𡤍«¯²µ¸¸µ²¯¬¨¥¡žš—“Œˆ…w‹Z/‹.1j?‰ë™k€9„€™¶›$v„ˆ‹’–™ ¤§ª­°³´´³°®ª§¤ ™–’‹ˆ„+‹w0.2ŠˆÌ€™•A99F™Aœ"+€„‡‹Ž‘•˜œŸ¢¥¨«®¯±±°®«©¦¢Ÿœ˜•’Ž‹‡„M‹?“.J…š˜€™n€9h€™˜æ!Lƒ†Š”—𠣦¨«¬­­¬«©¦¤¡žš—”‘Іm‰?5„. 2HJIGA>:6/„.;$„/™G€9Š€™Çžk…ˆŒ’•˜›ž¡£¦§© §¦¤¡žœ™–’Œ‰€‰/ƒ. Kfuog_UKEDCA;3ƒ.4¿ƒ®€™‚€9L™uŸ~‡Š“–™œž¡¢¤¥¦¦¥¤£¡žœ™—”‘ŽŠ‡4‰¥4‚.D‚Œˆ‚{sj_UIEDBA?;0‚.;¶™^€9c€™›q  1…ˆ‹Ž‘”—™›žŸ¡¢ ¡Ÿžœ™—”’Œ‰X‰?=‚.[šœš•‡~ti]RFDCB@?<2‚.K9€™–<€9u€™À¢ T†‰Œ’”–™šœž œ›™—”’ŒŠt‰b‚.t¦ªª§¢›’ˆ}qeYLEDBA?><3‚.€É€™€9‡€™û¢‡Š‘”•—™šš››šš™—–”’Ї‰0.b¬³·¸µ¯¦œ‘…yl_RFDCA@><;1.4?Л€™g9–€™g£ y…‡ŠŒŽ’”•–— –•”’‘ŒŠˆ…‰Q.I«·¿ÅÆÂº°¥™ŒqdVIECB@>=;:0.nM™M€9<™ß£w‚…‡‰‹’““””““’‘ŒŠ‡…ƒˆ.0”³¿ÊÒÔÎĸ¬ž‘ƒvhZLECB@?=;:6.1?n€™—:€9B™M£ u€‚„†ˆŠŒŽ ŽŒŠ‰‡…‚€ˆr.[©¶ÄÒ€Ö˾°¢”†xj\NEDB@?=<:91.° €™ˆ9G™ë£ s}ƒ…‡ˆŠ‹‹Œ ‹‹Šˆ‡…„‚}ˆ@.©·ÅÓ€ÖÍ¿±£•‡yk]OEDBA?=<:96.OÓ€™z9C™i£pz|~€‚„…†‡ˆˆ‰‰ˆˆ‡†…„‚€}z‡.A˜¦³ÀÍÖÖÔɼ¯¢”†xj\NEDBA?=<:99/€.3²€™k9<™Ù£ mwy{}€‚ƒ„„… „„ƒ‚€}{zx‡¹.[”¡­¹ÃÊÌÉÀ¶ªƒvhZMEDB@?=<:992.©€™d‚9•€™g£ jtvxz{}~€ƒ €~}|zxwu‡€.k™¤¯·¼¾¼¶­£—‹reWJECB@?=;:995.¤€™a‚9‡€™í£gqsuwxy{||}}~~}}||{zxwusr‡f.n…š£ª¯°®ª¢™„xl`SFDCA@>=;:996.ž€™^‚9q€™°£ enprsuvwxyyz yyxwvusrpn‡Q.m|†—¡¢¡—†|qeZMEDBA?><;€97.œ€™]‚9\™¢ bkmopqstuuƒv uutsrpomk‡P.er{„Š“”“‹„|sh^RGECB@?=<:€97.ª€™f‚9A˜€™¢ _hjkmnopqrrs rrqponmkjh‡`.Wgpw}‚…†…ƒ~xqh_UJEDCA@>=;:€96.¾€™pƒ9{€™žq¡ \efhiklmmnƒo nmmlkihge‡{.J\dkptwxwuqle^ULEDCB@?><;95.Ú€™zƒ9U™ì¡ Zbcefghijj„kjihgfecb‡«.=PW^cgijigd_YRJEDCBA@>=;:93.€™Œ„9‡™D W^`abdeffggh ggffedca`^…¥-€.4FKQUY[\\ZWRMFEDCBA@?=<;‚9/€.0¦™Dƒ9O™¡ˆŸ S[\^_`abccƒd ccba`_^][ˆ;.CEFHKMNNLIFEEDCBA@?><;:97.I=™[„9t™­ðžPXYZ\]^^_`a€`_^^]\[YXˆe.8DD„E DDCCBA@?>=;:‚92.•­€™y„9>‹™¥’MTVWXYZ[€\]€\[ZYXWVTˆŒ-€./@€CD CCBBA@??>=;:‚97‚.€€™—B„9E‚™+œ KQRTUVWWXXƒY XXWWVUTRQ€ Ž k óßÑð`.4AAƒB AA@@?>=<;:ƒ90.ZÝ™g…9L“‚™¼ëšGNOPQRSTT€UVV€UTTSRQPONM{©ÏÓÒÑÐÏÎÌË·2.8…@??>>=<;:ƒ93.1¿€¾€™=…9HŒƒ™Ùb˜ DJKMNOOPQQƒRQQPO\t“²ÒÛÚÙØ×ÖÕÔÓÒÐÏÍÌt‚.:ƒ?>>==<;;:ƒ95‚.m„™n†9?„‚™›Ãk •AGHIJKLMM‚N\tŒ¤½ÕààßßÞÞÝÜÛÚÙØ×ÕÔÓÑÐÎÆD‚.7ƒ=<<;;:„93‚.>ƒ§€™—I†9:b”™™šœžŸ¡£ÈN5‰‘>CEFGHHI\rФ½×áââ‚ãââááàßÞÝÜÙ¿£‰x¤Îϸ:‚.3;<<€;::„981‚.5σ噇<‡91A{™›œž ¢¤¦¨ª¬³4~îÁ|¢ì£‘ì5#+3ƒ9zÑÏ¥/‚./49::…983ƒ.0Œ…Ó™x‰9/IušŸ¡£¥§©«¬®°²´¶¸º¼¾¿ÁÃÅÇÉÊÌÎÐÑÓÕרÚÛÝßàáãäåæçèè‚éߺ”nH‰9ÅÑϦ4„. 1578998641„.3†š™iŠ93=i£¥§©«­¯±³µ·¹»¼¾ÀÂÄÆÈÊÌÍÏÑÓÕÖØÚÜÝßáâäåæèéêëåɬŽqS;Œ9PÓÒÐÎM5“.;?ˆÙ˜™kŒ9*Agˆ¨¬®°²´µ·¹»½¿ÁÃÅÇÉËÌÎÐÒÔÖØÙÛÝßáâäæàÆ©ŒpR;‘9 NÁÕÓÑÏ¿X-._‹7‚™t:Ž9H_xŠ›©·º¼¾ÀÂÄÆÇÉËÍÏÑÓÕÓ̾¯¡’ƒtdW–9UªÚØÖÔÓ€?C-‹.-MóÙ‚™A”9GQVY]afc^XSMHB:ž9 N£ÝÝÛÙ×ÕÜð‚7^9-….-x½çæäâàÞÜÛWW©g˜‚™˜uF·9 ;h¤éìêèæäâáß;¬¸ƒ™˜pO²9@t®æòðîìêèæäâ$ί¶ …™ŒiF«9=e“Âôõôòñïíëéçä2W³ у™šœžŒrW>£9Ii™Æïô óòðïíìêè±U·’΂™ ›Ÿ¡£¤ŽxeT>—9 :Mi†£ÀÞíîïð‚ñðïîíåœ\»’€™/›Ÿ¡£¥¦¨ª¬®¦Žˆ€wne\TTUVWWXYZZdrœª¹ÈÙâäåçèéêëìí‚îíÚFÀ9ðyÄ™œž ¢¤¦¨ª«­¯±³µ·¹»½¿ÀÂÄÆÈÊÌÍÏÑÓÔÖØÚÛÝÞàáãäåçèééêêëç¶xWÆ68f㥡£¥§©«¬®°²´¶¸º»½¿ÁÃÅÆÈÊÌÎÏÑÓÔÖØÙÛÜÞßàâãäåæÙ¨uA É*-fóÁ§ª«­¯±³µ·¸º¼¾ÀÁÃÅÇÈÊÌÎÏÑÒÔÖ×ÙÚÛÝÕ´“rP+† Î?á `õßÍ¿·¹»¼¾ÀÂÃÅÇÈÊÌ˽®Ÿ€p]E$Œ Ø”*^f} • ä • ä • ä • ä • ä • ä • ä•ÿÿÿÿÿÿÿÿÿÿÿÿ—ÿÿÿÿÿÿÿÿÿÿÿó–BA²$•B$’e–yE± q”yl “u•yw°O”yx•0–yZ¯"•y=—O–y'­i”y[—h”yzl¬@z“yp ™v’y{|~?– ¿S¤H1"ùïïh…{|{z‘y$›5yz|}€}Ž£6Oí¶ŸŠˆåƒd€}|zyCTŽy{|~€‚„X‰Ø¿•‘ˆÎƒ5„ƒ€~}{y` k‹y z{}€‚ƒ…†ˆ"„ší§–ˆ-‚ †…ƒ‚€}{zŠyrŸw‰y z|}‚„†‡‰Šr€‰•ˆ‡›¶·ƒ ZЉ‡†„ƒ~|z‰y*¡9ˆy z|~€ƒ…†ˆ‰‹ŒŽ;‚)ˆ•·ícå† 'Ž‹Šˆ†…ƒ€~|z‡yI¢X†yz|~€‚ƒ…‡‰ŠŒ‡ ‚a‰ˆ‰´û}ŒŠ‰‡…„‚€~|z…ye£ m„yz|~€‚„†‡‰‹ŒŽ‘“”W‚……ˆ§ùåM”“‘ŽŒ‹‰‡†„‚€~|zƒyt¥ x‚yz|~€‚„†ˆ‰‹’“•––‚Jˆ›ß‘¿“”–•”’‹Šˆ†„‚€~|z‚y0§>yz|~€‚„†ˆŠ‹‘’”–—™šu‚{í#–vš™—–”“‘ŒŠˆ†„‚€~|z€yP¨\yyz|~€‚„†ˆŠŒŽ‘“•–˜™›ž5‚™>ž›š˜–•“‘ŽŒŠˆ†„‚€~|zyi© py{}€‚„†ˆŠŒŽ‘“•—˜šœŸ – Ÿœš™—•“’ŽŒŠˆ†„‚€~{v«$z}ƒ…ˆŠŒŽ’“•—™›œž ¡£¤Sj¤£¡ žœ›™—•”’ŽŒŠˆ†„}7­Dƒ…‡‰‹‘“•—™›ž ¢£¥¦¢›,¨§¥¤¢ Ÿ›™—–”’ŽŒ‰‡…ƒZ­e‚…‡‰‹‘“•—™›Ÿ¡¢¤¦§©ªtš“ª©§¦¤¢¡Ÿ›™—–”‘‹‰‡…u ¯|†ˆ‹‘“•—™›Ÿ¡£¤¦¨ª«­®-™Y®­«ª¨¦¥£¡Ÿ›™—•“‘‹‰„!±,ˆŠŒŽ‘“•—™›Ÿ¡£¥§¨ª¬­¯°“—­°¯­¬ª©§¥£¡Ÿ›™—•“‘ŒŠD³QŒŽ’”—™›Ÿ¡£¥§©ª¬®°±³´M–‰´³±°®¬«©§¥£¡Ÿ›™—•’Ži¬E%‚s’”–˜šŸ¡£¥§©«­®°²³µ·«•F¸·µ´²°®­«©§¥£¡Ÿ›˜–”’…¬¥šƒ‚Š“•˜šœž £¥§©«­¯°²´¶·¹ºo”®º¹·¶´²±¯­«©§¥£¡žœš˜–’+¬-ˆˆr‚6•—™›ž ¢¤¦©«­¯±²´¶¸º»½¼$“x¾½»º¸¶´³±¯­«©§¤¢ žœ™—R¬ŒŠˆTƒ^˜›Ÿ¢¤¦¨ª¬®°²´¶¸º¼½¿À“’2ÁÁ¿½¼º¸¶µ³±¯­ª¨¦¤¢Ÿ›y«,‚ˆ>‚‚œž¡£¥¨ª¬®°²´¶¸º¼¾ÀÁÃÄD¨ÄÃÁÀ¾¼º¸·µ²°®¬ª¨¥£¡Ÿ“«ß‚ˆ{,]‚˜ ¢¥§©«®°²´¶¸º¼¾ÀÂÃÅDZeÈÇÅÄÂÀ¾¼º¸¶´²°®¬©§¥¢ 6«ÿ‚ˆp(bˆˆn‚A¡¤¦¨«­¯±´¶¸º¼¾ÀÂÄÆÇÉÊiŽÆËÉÇÆÄÂÀ¾¼º¸¶´²¯­«¨¦¤a«‚ˆf%gˆOƒl¥§ª¬®±³µ·º¼¾ÀÂÄÆÈÉËÍÇŒ›ÎÍËÊÈÆÄÂÀ¾¼º¸µ³±¯¬ª§‰ª‚ˆak‚ˆûƒ ©«­°²´·¹»½ÀÂÄÆÈÊÌÍÏÑŒNÒÑÏÍÌÊÈÆÄÂÀ¾»¹·µ²°®«£ªL‚ˆ^S‚ˆ…#¦¬¯±³¶¸º½¿ÁÃÆÈÊÌÎÏÑÓÔ9ŠÅÔÓÑÐÎÌÊÈÆÄÁ¿½»¸¶´±¯¬Cªó‰ˆ`>‡€ˆ‡7‡L­°²µ·º¼¾ÁÃÅÇÉÌÎÐÑÓÕ×´‰‡Ø×ÕÓÒÐÎÌÊÇÅÃÁ¾¼º·µ²°rªJˆc.‚ˆ"ˆz±³¶¸»½ÀÂÄÇÉËÍÏÑÓÕ×ÙÚ_ˆ7ÛÛÙ×ÖÔÒÐÍËÉÇÅÂÀ½»¹¶´™ ©˜ˆk $wˆÌŠ žµ·º¼¾ÁÃÆÈÊÍÏÑÓÕ×ÙÛÝІ¼ÞÝÛÙ×ÖÓÑÏÍËÈÆÄÁ¿¼º·±%ª¿ˆw$gˆ¤E‹+²¸»½ÀÂÅÇÉÌÎÐÓÕ×ÙÛÝßሆoâáßÝÛÙ×ÕÓÑÎÌÊÇÅÂÀ½»¸Qª<ˆ,Sˆ‘÷X¹¼¾ÁÃÆÈËÍÏÒÔ×ÙÛÝßáãâ-„"ßåãáßÝÛÙ×ÔÒÐÍËÈÆÃÁ¾¼‚ª‹€ˆ‡60‡ˆìމ¼¿ÂÄÇÉÌÎÑÓÖØÚÝßáãåç¹€deeddµèçåãáßÝÚØÖÓÑÎÌÉÇÄ¿©©ßœˆQnˆ$0¬ÀÂÅÈÊÍÏÒÔ×ÙÜÞàãåçéêìíîïïîíìëéçåãáÞÜÙ×ÕÒÏÍÊÈÅþ/ª ˆnAˆº’4¿ÃÆÈËÎÐÓÕØÚÝßâäæéëíîðñðîíëéçäâàÝÛØÖÓÐÎËÉÆÃ`ª¿Š€ˆ*!z€ˆŠÚ“-eÄÆÉÌÎÑÔÖÙÛÞàãåèêìïñòóôôóòñïíêèæãáÞÜÙÖÔÑÎÌÉÆ“ª¾ˆDPˆÿ”,–ÇÉÌÏÑÔ×ÙÜßáäæéìîðòôö÷÷öõóðîìéçäâßÜÚ×ÔÒÏÌÊ·ª´ˆn(ƒ€ˆ•b•*¹ÊÌÏÒÕ×ÚÝßâåçêìïòôöøùúøöôòïíêçåâßÝÚ×ÕÒÏÍÉ:ª €ˆ†0`ˆ`—(=ÉÍÏÒÕØÚÝàâåèêíðòõøúüüúøõóðíëèåãàÝÚØÕÒÐÍo½`C23FeÌ–ˆY&†€ˆª˜'qÍÏÒÕØÚÝàâåèëíðóõøûþþûøöóðíëèåãàÝÛØÕÒТŒLI$†'U™Œ¥€ˆ‚%Kˆ-™&£ÏÒÕ×ÚÝàâåèêíðòõ÷úüüúøõóðíëèåâàÝÚØÕÒÄ‹L.Œ6¿‰´ˆU€q€ˆ¢›$ÃÒÔ×ÚÝßâåçêìïñôöøùùøöôòïíêçåâßÝÚ×ÕÒF‹=Fˆµ€ˆƒ&-ˆœGÑÔ×ÙÜßáäæéëîðòôõ€öôòðîìéçäáßÜÚ×ÔŠˆ “%Ô…‡€ˆX€R€ˆ‡Z!}ÓÖÙÛÞàãåèêìîðòóôôóòðîìêèåãàÞÛÙÖ°‰ª„ 01/)$" „„ ˆ-€x€ˆ±ž ®ÕØÚÝßâäæèêìîïðññðïîìëéæäâßÝÚØÍ#‰™„ 5P^WND9-'&%$ ƒ郛€ˆo€3ˆLŸ "Ë×ÙÛÞàâäçèêìíî íìêéçåãàÞÜÙ×S‰Z‚.mxtme[PD8+&%$#"‚ÔhˆG€M€ˆŠ  PÕØÚÜßáãåæèéêë êéèæåãáßÜÚØŒ‰¿‚Gˆ‹ˆƒ{rh\OB4'&%$#"!‚(€ˆ…!€`€ˆ«¢ ‡ÖØÛÝßáâäåç€è éèèçåäãáßÝÛÙ¹ ‰2‚a—››˜’‰sgYK=.&%$#"! ‚H€³€ˆmt€ˆß¢Í×ÙÛÝßàâãäååææååäãâàßÝÛÙ×,‰F‚Nž¦««¨ –‹~pbSD5'&%$#! ¿EŠ€ˆP…€ˆ?£ ÅÕ×ÙÛÜÞßàáâã âáàßÞÜÛÙ×Õ‰)4œª´º»·®¢•‡xiYJ:*&%$#"! 8 ˆ4€!ˆª£ ÃÓÕרÚÛÝÞßß‚à ßÞÝÜÚÙ×ÕÓˆF€ƒ¥³ÀÊÌŹ¬~n^N>.&%$#"! ¿E€ˆ…€(ˆ £ ÁÑÓÕÖØÙÚÛÜƒÝ ÜÛÚÙØÖÕÓш:F™©¹É€Î²¢’‚qaQA0'%$#"! Z퀈u.ˆ˜£¿ÏÑÒÔÕרÙÙÚÚÛÛÚÚÙÙØ×ÕÔÓÑψ }™ªºÊ€Îij£“ƒrbRB1'%$#"! (¼€ˆe)ˆ$£ ½ÍÏÐÒÓÔÕÖ×ר ××ÖÕÔÓÒÐÏ͇€+†–¦µÃÎÎÌ¿°¡‘qaQA1'%$#"! ž€ˆV"ˆ¤£ »ËÌÎÏÐÒÓÓÔƒÕ ÔÓÓÒÐÏÎÌˇ^GŸ¬¸Àþµ©›Œ}n^O?/&%$#"! –€ˆM‚„€ˆ?£ ºÈÊËÍÎÏÐÑÑƒÒ ÑÑÐÏÎÍËÊɇAUzˆ• ª°²°¨ž’…wiZJ;+&%$#"! ‘€ˆJ‚t€ˆÓ£¸ÆÈÉÊËÌÍ΀ÏÐЀÏÎÍÌËÊÉÈÆ‡3Wp}‰“› ¢ ›’ˆ|oaSE6'&%$#"!€€ˆG‚\€ˆœ£ µÄÅÇÈÉÊËËÌÌÍ ÌÌËËÊÉÈÇÅć)Ufq|…Œ’Œ…|qeYK>/'&%#"! €Š€ˆF‚DˆÀ¢ ³ÁÃÄÅÆÇÈÉÉƒÊ ÉÉÈÇÆÅÄÇ)KZenv|€‚€}woe[OB5(&%$#"! €–€ˆP‚'‡€ˆã¢ ±¿ÀÂÃÄÄÅÆÆƒÇ ÆÆÅÅÄÃÂÀ¿‡1=NW`gmpqpmhaYOD8,&&%$#"! €©€ˆZƒg€ˆ¡®½¾¿ÀÁÂÃÃ…ÄÃÃÂÁÀ¿¾½‡?/@IQX]`a`^YSKB8-'&%$#"! €ˆeƒ<ˆÑ¡ ¬º»¼½¾¿ÀÀÁÁ ÁÁÀÀ¿¾¾½»º‡W"3;BHMPQPNJD=5,'&%$##"! 耈zƒuˆ --ª¸¹º»¼¼½¾¾ƒ¿ ¾¾½½¼»º¹¸.-!…Z€',39=?A@>:5/'&&%$$#"! ‚wˆ*ƒ6‡€ˆ#Ÿ ¨µ¶·¸¹ºº»»ƒ¼€»º¹¸·¶µˆ%&')-/00.+''&&%$$#"! %§ˆD„`ˆ™ðž¦³´µ¶¶·¸¸…¹¸¸·¶¶µ´³ˆ3%&'‚& %%$##"! ‚L›€ˆe„$yˆ“I£°±²³´´µ¶··¶µ´´³²±°ˆÊ€#‡%$$#""! ‚ †(„+~‚ˆ › ®¯¯°±²²€³‚´ ³³²²±°°¯®€ ‰÷[éÖÇåK†$€#""! ƒ.‹ˆQ…3‚ˆ§˜šž«¬­®®¯€°ƒ±€°¯®®­¬«Kw£ÉÌËÊÈÇÅĬ…#€"!! ƒ逪€ˆ#…/zƒˆÁ˜œ¨©ª«¬¬­­…®­­¬°·ÀÉÒÔÔÓÒÑÏÎÍËÊÈÇÅÃd‚„"€! ƒ‚7YˆX†%q‚ˆŠŒ¯HL•𦧍¨©ªªƒ«°·¿ÇÏ×€ÚÙÙØ×ÖÕÔÓÑÐÎÍËÊÈÆ¼0‚ƒ!€ „‚‚ß•€ˆ†/† L‚ˆˆ‰‹’”µ0’—£¤¥¦¦§§®µ½ÆÏØÜÝ݃ÞÝÜÜÛÚØ×ÖÒ¶•xme—ÆÇ®%‚ …‚oƒ¯ˆt"‡1&gˆŠŒŽ“•—™œž¤ódÍ’1E„…ì$6É{!)1:B©´¹¿ÅËÑרÚÛÜÞßààá‚â ááàßÞÄšoF#ƒhÉÇšƒ†„Ê…¼ˆd‰00`‰‘”–˜šœŸ¡£¥¨ª¬®°³µ·¹»½ÀÂÄÆÈÊÌÎÐÒÔÖ×ÙÛÜÞßáâãäå倿åÚ°„Y/‰¼ÉÇ›„ „‹†ŒˆSŠ3"S{”–™› ¢¤¦¨«­¯±´¶¸º¼¿ÁÃÅÇÉËÎÐÒÔÖØÚÛÝßáâäåæçá ]; Œ8ÌËÈÆ2“&ˆ¤‡ˆUŒ*'Qvšž ¢¥§©«®°²´·¹»½ÀÂÄÆÈËÍÏÑÓÕ×ÙÛÝßáÛ½}\: ‘ 5·ÎÌÊÇÊé-3™Š‚ˆ_ Ž.IdxŒœ«®±³µ·º¼¾ÀÃÅÇÉÌÎÌij¤”ƒraO?–>žÔÒÐÍË€ª"‹'ŽÁ‚ˆn&”.9>BGKPMGA;5.(ž 6•Ø×ÕÓÑÎÔ½‚ž0…5ٿ؂ˆDÁ +ƒÜÝÛØÖÔÑÑf† ÿP8-%%.;UF•ƒˆh&¼ $e´ãâàÞÛÙ×ÕL#©?‡‚ˆ†`,· S—æéçåâàÞÛÙ3í¬À£ƒˆ‡[6²&a¤âðíëéçäâàÞ¸¯H…ˆzT,«#Pƒºóôòðîìêèæäß,J³²ºƒˆ‰‹Žz]@#£0U‹½ìóóòòñïîìêèæäª1·I·ˆ ‰‹’”•}eO<$— 5Uu–·Úêëíîî€ïîíìëêâšZ»I耈/ŠŒ‘“•˜šœŸ¡—}vmcYOE<=>??@ABCDO^m}ž®¿ÓÝßáãäæçèéê‚ëêàEÀ1ðO®ˆŒŽ’•—™›ž ¢¤§©«­¯²´¶¸º¼¿ÁÃÅÇÉËÍÏÑÓÕ×ÙÛÜÞàáãä忀çåͯž{Å7¿@Ì•‘”–˜šœŸ¡£¥¨ª¬®°²µ·¹»½¿ÁÃÅÇÉËÍÏÑÓÕÖØÚÛÝÞßàáÜŬ“~zzyyÈ*ÛBܰ˜› ¢¤¦¨ª­¯±³µ·¹»½¿ÁÃÅÇÉËÍÏÐÒÔÕ×ÔÆ·¨˜ˆ†yÍ¿ŽÝCÿâÏ¿²«­¯±³µ·¹»½¿ÁÃÄÀ»µ°ª¤œ’„ŒyׯÔKv€q•yãq•yãq•yãq•yãq•yãq•yãq•yãJ•Oÿÿÿÿÿÿÿÿÿÿÿÿ–t8mk@8ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ–æÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÆüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿBÎÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúGþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿç\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±Ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù ®ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±5þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü'Sÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ,åÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿo¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿âÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó!:Riqw}ƒŠ8Íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý3^ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™=g‘½ØðÿÿÿÿÿÿÿÿÿÿÿÿÿGYÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý I†´çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈT‘ÎýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô $üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÞ½}þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ@kÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ漑aG-äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú¿{Dÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýΊG ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø-ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝšVËÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿOwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï‘+Vÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢Äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿc©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú7þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô1þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²Íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü'AþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿIÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿoÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêTÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¿ ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï%ßÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú ¦ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý3>îÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸/þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€Wøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ1áÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌsýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞ|ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô šÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿfûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþAˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¡QÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ~ÿÿÿÿÿÿÿÿÿÿÿÿÿ‰äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøhÿÿÿÿÿÿÿÿÿÿÿÿÿ| ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿΤÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPEûÿÿÿÿÿÿÿÿÿÿÿþocÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢)ïÿÿÿÿÿÿÿÿÿÿÿÿw³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáâÿÿÿÿÿÿÿÿÿÿÿÿ¨êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú´ÿÿÿÿÿÿÿÿÿÿÿÿÑ 'üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ`mÿÿÿÿÿÿÿÿÿÿÿÿí"oÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ²-÷ÿÿÿÿÿÿÿÿÿÿÿüF¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿéÜÿÿÿÿÿÿÿÿÿÿÿÿvîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü'‚ÿÿÿÿÿÿÿÿÿÿÿÿ¹0ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿo$÷ÿÿÿÿÿÿÿÿÿÿÿø#{ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀµÿÿÿÿÿÿÿÿÿÿÿÿˆÈÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðOÿÿÿÿÿÿÿÿÿÿÿÿç òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý4×ÿÿÿÿÿÿÿÿÿÿÿÿb:þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ=WtsS:Uÿÿÿÿÿÿÿÿÿÿÿÿ̈ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌ PŸëÿÿÿÿÿÿÿÿã”EÐÿÿÿÿÿÿÿÿÿÿÿÿ>Ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿèlOÿÿÿÿÿÿÿÿÿÿÿÿÔõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþA`íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäS¾ÿÿÿÿÿÿÿÿÿÿÿÿyEþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ›!þÿÿÿÿÿÿÿÿÿÿÿþ”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ$Öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÿÿÿÿÿÿÿÿÂÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø(òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÛ ÞÿÿÿÿÿÿÿÿÿÿÿÿhøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿPÙÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁ8ÿÿÿÿÿÿÿÿÿÿÿÿùOÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ™|ÿÿÿÿÿÿÿÿÿÿÿÿÉ¡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáuÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿQÀÿÿÿÿÿÿÿÿÿÿÿÿšàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ ùÿÿÿÿÿÿÿÿÿÿÿÿlúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ_ŒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿhBÿÿÿÿÿÿÿÿÿÿÿÿÿQžÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêjÿÿÿÿÿÿÿÿÿÿÿÿÿB—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯dÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿA‘ÿÿÿÿÿÿÿÿÿÿÿÿÿ4—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·ÿÿÿÿÿÿÿÿÿÿÿÿÿ?—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ ÷ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáÙÿÿÿÿÿÿÿÿÿÿÿÿÿR—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯>ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåÿÿÿÿÿÿÿÿÿÿÿÿÿl—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯Yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÿÿÿÿÿÿÿÿÿÿÿÿÿ£—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯sÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôÿÿÿÿÿÿÿÿÿÿÿÿÿÜ—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯Œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿ1—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿäÿÿÿÿÿÿÿÿÿÿÿÿÿÿ——ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯wÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËÿÿÿÿÿÿÿÿÿÿÿÿÿÿó—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯]ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤—ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¯Cÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿCwÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿî\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿð+\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpqÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿMÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿê;\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿö ˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý‚\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿe‘ÃÕåõÔÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ5ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎ4\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè Ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý³A\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿidÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÎp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¸çÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑL\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØPÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöª’w]B)!rÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿî·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿß*!ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿyÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÀRþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ" ‚üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøx}ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¹$©þÿÿÿÿÿÿÿÿÿÿÿÿÿÿú•²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò#%yÉþÿÿÿÿÿÿÿÿü½nŸÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù<Igœ}cH‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü lþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±+1ÒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂ:‡üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ¼`2¹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿò‡$;¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿq;”ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpgÅþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp$n¯óÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp2s«Ùþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp+S| ¶ÊÝðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp$$##"rÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿp\ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿpyabause-0.9.13.1/src/qt/resources/icons/mouse.png000644 001750 001750 00000073054 12256006101 023606 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRù·vjó8sBIT|dˆ pHYsaa¨?§itEXtSoftwarewww.inkscape.org›î< IDATxœì½I¬$Išö™»Çþâí/÷ÌÊÊÊ®êꮩ®nr ™Á`4”8µpˆl ¥!©ƒ@è¤ÐE€¬ƒ ³ ÐÇ9Q”€ÄÄ‘ÔÃÞª»«»ºº³²–ÌÊååÛ_¬¾˜æÏÂÃvwˆ÷Ê?Àáæ¶¹¹¹}öýfnîN(¥¨põ@!Y/xÿý÷…ñ3þs‚V åÒ‚T÷îê€'öûï¿OàÃ?$ûûûÎÎÎ ‡Còo|ðóŸÿÐl6)t»] {{{ôOþäOXãHIEöˇŠäWŒàï¿ÿ>aÄ>;;#< ûûû^·ÛõF£Ç$ C²¾¾NAPèt:ÉÁÁív»É“'Oh£Ñ {{{‰ˆðÙ/*’_L NÞÿ}üùŸÿ¹÷æ›o’?þØïv»!Ä÷<Ï?;;ó}ß÷Çã±W«Õ¼0 =–~4Ñ ’ñxœDQÇã¸ÝnǃÁ ÞÞÞŽ÷÷÷“ Ù+¢_"T$¿ä „^½[­–Oñƒ øZE[q#Š"DQD“$¡”RÄqœPJ#¥tL) )¥Ñx<„žç !aÇQ†Ñééi¼³³?xð aD¯H~9,»ŠOðÁ`PkµZ5Ïóþ‘ïû˜$ Øp=IDQ„8ŽÇ1Â0DÇÇN绞猂 PJ‡qG£Ñˆ2ît:a·ÛÅ'Ÿ| B*5¿ðôQ*¬*xßÛÛóÖÖÖ¼(Š‚V«U«ÕjMd:qBÈÜæy!>€ÛI’Üð}/Š¢JéF’$kA´’$ixžWÛßßNNN¼ßÿýßÏÎÜWXaT$¿Øßß'ççç¤Ùlzkkk!¤EQ“Rê'I‚¬Øò÷}¾ï„[nPJ÷<ÏÛŠ¢h#I’5ÏóZõz½A©5›M¿ßïûûûûÞw¿û]2Í«"üŠ£2×/9>üðCòæ›o’ñxL½F£áSJÏóê| 5«A)MÍvÏóÀÌxÏó#6à™z…ÕBEò+‚^¯G†7<>¥4 ”zRr3‚óûéæBÖ I’À(I’¡çy(Šêa„¿Õjy'''0±PüR 2×¯ŽŽŽÈp8$Ðh4ˆïû^E¦«Ü©Ef;sRJ[š„¥´ ÇqàyžW«Õ¼8Ž DQT™è—•’_!„aH<Ï#Á”äÀ,Á™ùÎÜÓxÁTýƒé³uRê%IâQJY^„½ÂåA¥äWI’Ž˜fgÖ³àLvB(¥djê˜æUá’¢"ùWlÒ¹`ÄNÛ÷}p~Ñ/1*’W˜)©ÙË.VÉ+Sõ–2ÂWãñˉŠäWJRòãsÞ]™æW É+AEî+ˆŠä*\qT$¯À0§âüìz…Ë‹Šä²˜1݃`~½Tµâír¡"y†Š¸WÉ+0T$¿¢¨H^­‚ãI^þ ¡"ùÕ† Y• b*\^T$¯0Ï›m”R’$IÕ\RT$¯À "qEì+€ŠäWÆ$åÞR«Þ:»b¨H^AHê,Ù+³ýò¡"ùB†yH€tæÈÜqWŸg¾d¨Hþ…@Õ+â^QT$¿Ú˜!.{%/nÞ¼YH>ƒŠä_adÔ¼Rò+ŠŠäŒ¾»^áò¡"yÂ…/Ñ/7*’W`àÛBEê+„ŠäHŸ¡KÉ>«Žà’ "y… Wɯ6\_5­p…P‘ü+îÙyEò+ŠŠä*\qT$¯À@(¥$û7~{µfýr¢"ùÕ†+)+2_!T$¯À0ClÏóªE0WÉ+0H ]‘ýr£"y†ŠÈWɯ6*âV¨H^!EõÙ§+ŠŠäxðÍ„dã¸ê.*’W`‘·"õ%GEò Äo¡U³êWÉ+0Tmኢº±æàyóÍ¢ZÒzyQ‘ü ƒØ}žµZ sEQ‘¼BÕÏ ¯*’W`à› ^™ì—É+¸øx„‰…†aEöK„ŠäW¢1¹‚Ä¢R™í—É+T¸â¨H~á8v®ÚÂEuc¯ <ÏSþˆÜA•U…%¡"ù§ä.Ïɉê• —É+¤¨&Ù®&*’W`¨V¼]QT$¯`„Šè—É+0Ì‘Øó¼ô9y£ÑX|‰*‚jêôŠ!Žc’$ ±|9”ÒìB™ôg ü6ý2 ©V½]T$_1ؾ9¶¿¿OZ­‰ã˜0$IB¦* Ñ–ÅÔ©¶G)õøgíŒàI’$I¥”‘Ü@-û“ì¹ {ÜWAŒŠäK€†È6aäåË—ÞúúºG)õëõºßn·½8މïû„R*ŽIˆïMógªíð’$ñ˜‚'IâEQä%IâÇc#(åÜÂSJ/HÓAT@~T$Ï [³˜O*pÏ‘XÿèèÈÇA½^šÍ¦Oñ„Z³Ù$I’ »IHN<Ïó’$ñ’$ñ“$ñ)¥A’$AE!¤†a@)­ÅqLG£–#Á,ÙyP·2žiýVÉ ¡hlÂ;L²ä6Ü3qîܹ³Ñl67ƒ h¨hø¾ß$„4´!Í$IÖÎÎÎÚ„5ÏóÖìQJçÎHÍõ­8Ž¿N)=pJ9%„8®×ë'N}ß?'„œèEQ4ØÞÞŒÇãÓÁ`p°¿¿?˜–9KxÕ^æ'ë4xÌ„Mï0þW½ _ñëBBh­ª „ØÞÞnîîîîA°çyÞ!d²KÙ°7ÝïpûmäèUbXàý8¢”QJ)¥G„Ã$IŽ(¥‡I’8Œãøp<DQtøù矈1KîódŸ)²À-êT–ÃÄó+Òø+’CHj‰Unr÷îÝÍN§s³^¯ßN’¤ù³Ÿý쟳L¾ýíoÿ/„û”Ò]BÈZÁå/2»”Ü>Jé)€/~ô£ý€ä›ßüæAð0I’GQ=:<<üäÅ‹ƒL:‘òg-%‘uO<® !¾’溂ÔsÄå>|¸Ûn·ßò<ïë¾à!ä!ä&€›Z,CJé1€·Ø!€[îç%¤kzQ:“öìšÎ!d3I’_Ø×jµèyÞïû¾Z­†Û·oÓÛ·o?§”~J)}œ$Éã0 ÇãÇŽÏq¡þY+@¶Aà^ïU ýW†äb«HMÞ~ûíA¼çûþ[¾Ny‹ò&€]‹ómnnnÞbRº?Íö܅ÄqM'jã"8Ÿa4ý“º<ÏûNötnBnB~Ûó<A€V«… PJ_ñ@ÇÇãñ'ççç?þü óßðnÜW‚ôWšä bó›ÿÍo~ó7jµÚoB~‹òÛ^+âü­VëãããÄq‹>u,)·S˜mžE)9Ç…ççç_¸¶»»{ ÀºMÚé\Æ.€¿ÊÔ¿Ùlb}}Þ¼yó“$I>ˆ¢èÇãñøG¯^½úÉÑÑÑrg7•%æfmê2‘ýJ’œ#÷±þüçƒÁ`ŒÉ ˆô€B. ѯÉäö01¿wkµÚ¿íûþBþ:€6—¦´òAðÀ#ãñxúFãµÑhô²Ùl>0*T „4!ïyž÷^ÿa«ÕÂÖÖÖéÑÑÑôøñãïaBöpºÏ—èW‚äÁ½oûÛÿ!ä»ÊV-|ß¿`“Å# `ž\¶Ä.Óò0=ŸˆÀ&þ"²Çq|íÚµkìû~¬Î·¦Ö777ÿÇ7nü½çÏŸ?00žîÃéLÍùU'ú¥Nž!øMù/—Z QÏÆz½^xs½H%7c “¶!‹£xfæøÕ«Wˆ¢(-_%$Iòä“O>ùOOO_è`²è‡‘>7n_U¢_•÷ÉÉ{ï½÷G«Dpð}¿NÙž®ÿ0!£HÍe~²Î »•“óÈÂLü!F ”®ä#=Ï»óÚk¯ýwS‹l“gùkØÒâ/öäy¡T\j’O+•¼÷Þ{Åó¼ÿiÙåÉÂó¹}ûöàþÒ `Hp3oüc4r_ö1¹ŠìüxœÅÑ‘{ˆÞn·¶Z­›ƒÁ`€É~‘Lˆ‹õídZÞ¥÷L—Æ\g*þàÁƒ5J鱤2ÍH•yéû>Ø+¦qKǨ²ódýV}L®òã³$w­ßEƒâ]¿~ý·01Õ»¸0Ùùçæ©Ù¾ X™‚‚t»Ý¿OÙZø‰-Ô›¹Ù±ïûB%Å×ù‰ÂÊhø&ù›–9{Ç1Ø @6³îR¿ËÂÆÆÆ7ƒ ØÄ„àk˜LÀ±Çi5ppd™âRœ©8&3êÿÉΟ˭—›ª"ï¿ J. Sù±ãápB<Ï3šY_5¢û¾ß¸~ýú{˜'9›€›yn¾l\ ’OA¾ýíoÿ[ÄáJ¹N*hT¦¦$s‹Æå¦ªmâ¿ %·ñÏ3S½V«Áó&m–Ÿ|ãÇå_u’3Ÿ:ÿƒòWîmÂL ïfãr%×ù‰Â–©ä*?àb<.z)ÅÄm¢î¦ae}ccã&äÎ*ùÜ,{)`g×É;ï¼sƒò[…fZ Á³~&áÀ„äa¦ÏÊEåÓù©ümãd!›æórù‰!3ël<^æÌºI§(´Û훘¨w“Û²«ß¢I±·–}¥”| R«Õþ]X6“žÜ•পNÈÅ ;›]Ϧ7ñSù/BÉuþ"?JéÂgÖmïq‚ ¹¾¾~³Ïšì —¯ ÉɤÆÙö7tNá^Gç§r³vþZö\¢ÎÁ”h²8¦›I^:Q<öÏ33ãñl˜^å' +šÌ*LMv^ÍÉ—f²¯ ɧ ÷îÝ[#„üµÂ2”Üpå.‚༒g™dBËTrU‘¿è¸ßï˜ WÊ\éfVvÐívocBjFt©’“õ>+Gò­­­?À¤rògæpƒ] Î7j™Ûv†ÝDAeí¤(%WÅ1QrB.fÖ™ŠÛÔY¢ëâ”Aôf³¹]¯×»¸0ÕÙäÛÒfÙW‰äñ<ïo’YĶm`:÷ª¿W^¶’ç™Y·ñË{¯s‚lmmñ&;¿òm)ÏËW‚äS³…ܹs'ð7tN£½,Ì–àÀìúõ²¿Á.Šg«âºs™øQJÓ•nyÇã¶÷H·lt»Ý;03ÙR¨•z„¶¹¹ù»„í¼ù˜Þð<=¿Ì­Sræ—UrÑ9u~&a¦åaòוSqFr>ÌöóN¼;»Ï–QõØL·Ï‹v»}$çgس¯žBÊ”¶JŽÉ{µZ-÷¬z·‰cÎÞ¾bK<ËTrQ\×ËĽ^Ê&ÝlÕÛ$<²—¡øA4ÛíöÄ$_øV–®ää¢V !äK:‡u\u—ÅÓ©ùx™Z‹„Ä 4„‡1< ÁxrŒqš§ég™yæãQJÑëõ@A§Ó™ 3q˳y¹¨qQÊ-Âúúú­~¿ÿ³DË J~ýté$Ÿ‚¼ûî»ïx-W&‚–¡îºxYw­VÃh4R>+W'’=DdÖc ºˆh1é"F 3C=’Ùgý3n#ô5rŠ€œ"ÀÉäÇðé+¨W¶eùE0|¸ÊmòuU‡`ºç¯£h³}mm7ÙUÏËKÇÊÜ󼿞+ƒœæY^‚›*9{Nl¢äÖbcr c²‡1ÝCD¶ÅÝ~AÍ%Acr.þ5~ÂÊ¡—¨ÓçhàêôHÉ>†!<ÏC»Ýž¹6z³üLȬ Ë»wE»ÝÞÄм¹Î&ßx5/ýsÍ«@r‚ ÉÿÍÂ2,pEðÿÁó<ø¾?·Ò-Ï ) e‘´¨üÖ××o ƒ/!žaç'ßJ—¯„’߸qã·0ù”­{&%™ëE›íü_<_Õþ}$Þ†ýÅ*@i„d|Œxtˆx|„8<¥1:OSPš€&1<¿¯Ö…_ß„W[‡_[‡tâ>ñagä[8÷û‡ äÉÌ—`.‹™^D‡Ñét®áÂLç7žä€D–GX6És›ê6&ö² Ì.ˆñ’S'’Ó$BÔ‚°÷†gŸ î$?<ýiÚ¡±•n¬.\Õ[ä——ˆešë°±±qóåË—Ï1Or~†(q\¾t’·ÛíÝ¥.Ê[„Ù®:¿©Ù^¯×'_J‰Ž.2¤#ŒŠÞþ÷Ðõ¯€øµZ €¿Ö™!o£Ñ@«ÕB§ÓA³Ù„ïûÚÎLnÚˆG£z½úý>ƒ†ÃaúaÆ$9DÿÙ?Ãéÿqâ¡»÷Ö®ý&‚ч)É™’a¦g¯©ìqx^Ò¯­­]ùò%o® ¿àj±–Ir€ø¾Ÿ{<^†¹nBbWU¯Õj‡H†O0Þÿ3œ<û Ž~ ߣ¨ÕjhÕAµZ µZ ívÝnNgni¨èM¡ŠÏ7èF£F£íí‰iž$ z½Žqrr‚0 §„O0:þçûßG’$X[[Ãîînú¸2Ìô" Í×KQªÞjµv1ᛋfØK}^¾’“ÉfÍ!½ÔÏE…ót¦a¼»V«¡ßïãðð_€?G½^G§ÝHÕºÙlb}}é"Õu˜ÀVɳñù¸žç¡Ûíbmm ·nÝB¯×ÃÉÉÉ áÃ0Äh4§Ÿ~ŠÁ`€×_}æ“™uS¢‹ÊîªÔ&qmP¯××}ßoÆq¬ša/UÉIIzê“Nî¨÷æ›o¾¶¶¶öÈ!½’¨yU»hugî$IðôéS|úé§Ž¡ëõ:êõ:666°µµåLl[%WAÕ.Dal\~~Žããcœžž¦ãy¶òmgg÷ïßÇÚÚÚ\>"wv¯ +rofƒGýÙÉÉÉ/<ðåt{ àÀ€€@|e”So6›Ö³ê&ʬ [Á“$Á“'OðÅ_€RŠz½žN”5 ìîîbggÇè:t~2©ä¼êò~A`ccFØßßÇññ1jµÂ0Äéé)¾ÿýïckk ¯¿þ:Ö×ׯæEšç.ê. ³A§ÓÙ=99ùòöR_VYê˜Ü÷ýÍ)¡©MÒÛ[—GÖýüùs|üñÇð³ÎƳHÅ…ÜEšçº¼UJ®:®×ë¸}û6vwwqpp€ÃÃÔìgggøÞ÷¾‡àÎ;Öªmz.ãp“0S´Z­-ÌN¾Õ ža¿ZJ¾µµÕ$„|Ó6]ÑæºiC­VÃÚÚêõ:Úí6®_¿Žn·;·ÍFÉe~&a¦5`™jó~¢cæn4¸uëvwwñêÕ+"ŒÇc<~üÏŸ?ÇÛo¿v»]¨yΗuYæz½^_›N¾©fØÉ ÇÒÆäwïÞ}“ 5K PjQ<Âç5ßOOOñÑGa<£Õj¡Ñh Ùl¦cî쪳"Ìô2ÔÜDÅyÙ³aõz7oÞÄææ&ž={†óósA€Ñh„üà¸}û6îß¿Ïó 3Ï‹"v5'Ýnw÷øøøóÒ²_Š)\Í—Ar€xž÷W¬æ$µ*ÌU¹ÉÄÚ£GðüùóÉã°é"•µµ5ܸq#ýJ© ÁWQÉEÄæýM”<ënµZxýõ×qpp€W¯^¥ÓøòË/ñâÅ ¼ýöÛØÜÜ,|â-{]‹œ|;>>~„ Sýj*9¹h-ãñ<*n¢Â.`ùôû}|ðÁH’­V+Uðk×®akk˨LYY9m ï %ב]¦êYÿÝÝ]t»]<þgggð<£Ñ|ðnß¾‡æšô]›é8\•.{:´Z­m¨Ç好¬²L%·Z¯îJjUü¼ûýý}|ôÑG¨Õj©z¯¯¯ãúõëé?¸uå•ÕÕt×…™"ï̺(œ' óg~F÷îÝÃññ1^¾|‰Z­ß÷ñüùsœŸŸãwÞI—î.Ò\/RÍ3“oŒèÙ¯Ä\~%Ÿ‚ܺu« à-£È»šë²8¦ûGáË/¿D³ÙL·›7obccøÌ*·É±Îß2óœ3³c•›ßomm¡ÓéàÙ³gé 8ƒÁßÿþ÷ñÎ;ï ÓéBhþZò’Þ”ìµZ­A+Š"Þ\-o-œè‹þ$3€íííïØœÛÖt51×]•>Š"|ðÁxþü9::Ö××qÿþýÂNHñT1ÝTy‰üUǦnÞ¯^¯ãîÝ»ØÛÛÃÚÚZºúïÇ?þ1^¾|)LWĽ5M¯óS¡ÛíîaBꬹ>ó-vLô¥(yF“nE¨¸ÈϤQˆÐëõð³Ÿý ”Rt:´Z-t»]ܼyA8•W¥ÖËPòl^.3ëºYv:zž‡k×®¡ÑhàåË—©¢ÿò—¿ÄÉÉ >|h|-¶æº*½ÎO‡N§³sttÄ›ë¢1yáXÉÉä.“©Óx<^¤Š«ò×í÷÷÷ññǧo…µZ-lmmáÚµkRË£ä¦×«º&1³®#¾ŒôÙx¨×ëxöì™|øâåË—8??ÇoüÆoXÓeõ”wnJph6›˜—g£‘i¾…M¾-ã*€VÉ]L&sÍ$ÎÓ§OñÑG¡Ñh Ýn£ÝnãÆ¸~ýº”E<ÛyèüDa¶Påeâçê–Ý—V«…{÷îassNívƒÁ?øÁæþBSôý×ÅÓùeÑh4Ö1!3OtÑä[¡Š¾ð1ù[o½µCyÝ*‘¥É+‹' SÅyúô)?~œ’»ÛíâÎ;ØØØÐæ'+·Î-J+ —‘:KT›M•—Î/®s›Ö_¸uëvvvÒq:¥?üá…¿›Ò嫊# “ųíPëõúf ¾É·E’œ FÃxŒMjÓxDyˆâ|ñÅxüøqª ëëë¸sçNºôR•Þ¦¨Ôû2(¹IÙ]ïÛû¾k×®aoo/½ð£ý£ÑH›¨Ì.÷Jwm*BüV«µŽy¢—ú•˜…›ë„í"˜¼œ÷ÆBðÅ_à³Ï>KT·ÛÅ­[·f¾Ì⪮dש©(ÌvSåeS6S·Í½"„`{{×®]C»ÝN¿ûÁ`4YßcÙye~E½ÝngŸ—óo£e_V) !9™\=@|ß/tfÝ&I€ôFpö”™/­:6(Y<‘Ûä˜÷“Õª¼lÊ–ug¯™wÛÔ!!“çé»»»3ŠþãÿxòÝ<É5eóW…¹´-ÓzoµZlò-Kô¹å­$ïÍœbÑJîPά—QÙ67ù³Ï>ÃçŸ>Cð7n V3{—Æ´ášÀ”Ü¢2¸n²¼T~¢cÛkÖu¼Yloocgg'½O„TÑuù™ªyž@†é »±Š—2ù¶Ð1ù[o½uÀMmD‡ŠÌ£â<ÁŸz4ù·"#øöövúïlYZÂËÒ¨ÂtˆîZ–©ä:r³0S¢óç‘M¨™ZÙ°­­-ŒÇãt2îÉ“'ØÙÙIÿ°**ö\qc8Îl"B3±`þŒä¾ï#t»Ý{ÛÛÛãZ­¶vrrò£/¿üò_bVÑKQó…(ù‹/â/^ü%€ï÷~ïÙÌ'ß2ø¾ÏóP«ÕAºflÑâÕ«Wèv»hµZX[[Kÿ®É§±%¨,*Žà—QÉeÄ·QmQùEd·é(<ÏÃÎÎÎŒ¢ÿò—¿Ä{ï½'<_E 3›ŠÐÒÿÈAš‘;SÆ€o@E_b¢ä!.žŸ‚…(ùÇo'IÒ>88èû¾ÿZ’$i/Çþ*ÂnÀh4¥³¿%d2kÊ~ñ˶F£‘þà4Š"üú׿N¿‹Î>ݤJcæº×ù©Üº²¹ H%ÏCtÓ}¶ìª0ö[hÖÆÎÎÎðé§ŸâÞ½{èõzè÷û)¡ÇãñÌ}É*4û+N£Ñ˜ l›åÝ"ÔëõmÌšéó³ëlËeÆ—MrÝn÷Bþþææ&NOOÓ›ÁˆAz¢(šyÎÉoÌTâ+‘ÿ¿7#>û¹ ï~õ+ø¾?Cp¾'¶!µð"-ˆ­Ê·(²›„eáªä.DçÏaKhYZ]Øúú:F£QÚÆž={†ýýýôœ¼iÍ®³V«¥ó7¬Mñu%²I}0ø¾¿ ¤Ïʳë×g^T!$ß³òÒHN.Z pŸùó•ÃzIVÉÙ†Ïâ&I’? CDQ”îeäg¿ Ç8==1ÓÙxÌ–|&äͦQåíªêºü]àªä*·Ëx\V6]' Ë˜¬qgí†MÄ1ÓÚó<4›ÍtµSj™u|S] ƒïû[žçu’$‰411åù/¸öÏò…˜ë„{Š0©¿ˆüY20 €=®Ã0}Œ1 prr‚n·›šYëëëZ‚Ø(°Žø&ñ\U]ågÆ gÃl•Ü„èYB¸¨»ê|<êõ:Ö××gDÂ÷}¼ñÆh6›éOU -:¯iœŒ¿·¶¶v÷ôô4Âd\>ÂdÆýY¥°ÏA•n®ßºu«àö\@3•'›ôhµZsdùéOššZÌLgcx±DçÒ…ÙîMʱ5·Qqæ·Œñx¶Ì&VA6^§ÓIgÇ£(Âùù9ÆãñÌrf›ësq34›Í»§§§˜L¼0y´Ö€øK1Îj^ö²VÒívÎDGHûûû )Á»Ý.šÍ¦“ŠëÔZÇ•à2 Fu,+—nS¥ÑSä6½>ÓzÒ¥Õùñð<›››éüL³Ùįýë¹x²sËòuA£Ñ¸   ` @³ÿû̸Üõûì³”àÌLÏÆ7UqÕ9]ëÞUÕUǼŸŒÀ²²ÈÒ˜Ýöšl`’Þ¶ã‚ëëëiÛ ”âéÓ§Òt&å3uó¨×ë»:˜¼… ’ó¯Èg²/â•û.‰l+w?yò„ô&nll¤êTçQå[„*åQ¢UTr•{QõäÒQBҿвíÉ“'ˆãØøúLέC­VÛÀ„Ø¢‰·ÂÆä¥œ3-!ä5A¸Ömš&' C<þ<}”ƈn’Ÿiƒ1 ËÛpunÑ1ï'#°ª4ªsªÜ&×hZ?²ñ&Ròi€iÆî^¯‡£££”àív{îCŒ¶çÉúÙ6hÕ¹\ λee5Us]\[r›^Ÿ®L²t¶j®;ÇÚÚÚÌ*¶Çkó³"U<î;ìÙo°3s=·š/BÉž‘»˜BYÿO>ù$½aÍfkkkVfœ®á˜øåÝgýDnQLˆj¢ä²0Yç’u—Q²k6ñÓÝÿz½Žv»ï˜P¨Ú™ .í™û”jÕ[.5/UÉ···k„™ï¬çUpYœƒƒƒ™GfNGúb‹*Sõ0iŒy .+ŸNÍ‹Rrݹlë(±óÜ#YÞ l±Û>ýôSáZYÙtùËÒ3ßa—\$577K›eh4ÌБŸxË=ùVê˜Üó<á¤Pl¯øå—_ÎLžðÐrÊn»Wù©ˆ¿%—ߦóé‹Tn!é$\£Ñ¼xñ"Wþ&ñjµÚ&<äÕ\¤äÎD/œädr5dê|-¦JçïààQ͘é&Ì\ÉmJHYy•Kä^–’›”׆Ð6ùÉòÈs¿ÙÛgLÍu&»é¹Uñ|߯ù¾ß€ÞdwF©æºçyÒ™u ˜ {úôé̽FZ$¹uå·Q'Ó0ŠòÇE)¹¨LÙpÓŽÎ&,¯rëÂu%?ÓEæò6-ƒI46̔܉ìe?B›#¹keˆ*ôôô£Ñ(}¡Ÿýð./ò*»,LE,Y<™ŸÉq%79Ö•ÕåzMòÅSÏL,Dcó"H- ãHž%úJÉY¯³' ,¨|òäIJp6Ï£à:·iƒ3iøº½ÉyuDÌ–ÍEÉUÖÃ"®×ö¼yݾ溜¨F£NOOçÊ#+›I¹³¨Õj<ɳTYùÅ0»éa/kZaƒÁgggi¯Ûjµ„ßNÏãÖ•Áµ¡æ%¸ ¹]”\&:.ŠèÙóÙä-C÷žÍÙ'ŲÿSË#L¢xµZ­ùäá7bzeœ [[[5BȦ4’¢ÒL*ôÉ“'©Ižŕ«Ð†êa7ëg{_D¤5xѱ¬,:â‹ÂdJ.*SÞë…Éü\î‡)ا ØÓšÓÓÓ¹5í&å0íjµZz%wFiJ¾···ƒLá\{ÀleÇc¡V«¥f:ûVœ,έ:Ÿ*~Ñ*n¢î"÷"”Üäš]®K–—.žI\ÓøYw«ÕB­VKÕü‹/¾0>ŸmØ”äLÉ/¹N|ß¿¦äXIOž<™¹6Ïųy»v¦áEÂÆmJjÓð2‰.óÓÏ¥l²‰R›(¹Ê]ÔueýDñdá2˜Þs™»ÓéÌ|âûåË—Öy›„MIN0KôÕ\ñF&W@0Qò=Î_•Æ*ìèèR%ÏΨ«ò(Ë´3É[ÕØUå°QuvlC]Z[õ69‡,¾M½–q_³à­Åz½.}fž·}OÈ”ºN’í‚ @­VÃéé)’$1ÊSÆŽ§ bäJž’ÜE“œïq´J®ÍŒ«ƒƒƒ™GüɳqMó4W„™˜—ð:?æÛ(¹Ê:0-“M˜Ì¯ˆ:Á5?çyö÷÷­óTA± †Ü™«¥(ùíÛ·›„5vìÒ³ñ8;;C†éOßÙ„[7¶HÕ7m¸&j(ËG–¶L%7%µîzt”ȯhu–ÅS’™Ðð$WåizþZ­Æ?+—-ˆqRóRHÞétœgÖEqÙ„?.Ò嫪ì"ˆE(» aD×mªâ¢¾«’gÝ.å…ÉüŠ$µImÃXd³í'''Fç39!D¤à«¥ädRj2u®Ä7:f¯”ò³ê6=fYæ› \ÕQ·¹ËTr—r-ª>MÂdñta„´A€³³3£óëÎÃ0UsݘÜe=Bëù̆ããã™”7Õ]Pt‡°(e·!x6[%W¹MÊcz­E×cÞ0ðsCI’H?&¡‚¬Îƒ ¨cÖdÜúÊZñÖÑÆ‚Y'pvv–\öJiö¸ ¥W¡r›E§”"57-¿,­ ÑUeÕÅ3uëÊ_D˜Ib‚öì¸\—¯ S’g ¾šJîû~›¹óôœ¬—d×ÇMQ´šç9¿)QTù¨Èm£ä¢c×rˆ®¯Ì:Ëf ÏóæÆåE]—`òMJpbqÒ2à %·©ÃÃùžÓ4¯E¨yѦ§‹y¬Ê3’QŽU6Íó´&:A ßï+W¿Ù”É÷}6&ç‰N$›1–j®êÊfÎØ¦Ût —Á„Ïúå1Ûm•܆è¦å7u«°×ÅeÐØã\~–ݦóÈÂó<™’¯ž¹ Í¸˜3lRƒõ˜lkž›µH5·…©™l£œy•¼ˆ2¹`•T\”–o“µZM8.7Í‹‡ïûôÊm]±KQr“J>>>†ïûé&Õ˜lʸ>ë';§ ÁLT\–‡Íyu×T¤i^T®ƒ,-³.}ß—þ|Á¶3™šë"%w2Ó¢ä¶ „àôô45‰x%7IkslfsN“x6f»Î\Ö™êº2éLvsݕ캲¹ Š›ž“—ÇcÄqœ«3fH®Ú¬Q$ÉùB´ó˜DÀä)üx\•WÞÊ•¡¥·U&[e71ÕM”\g²Û*w‘–MEBU&Ü …+9¥4×ÇÎ+Ýû²Ì±ŸK˜©²›Tg’»œß%l™ãë¼eaÖ&Ÿç}^îMTÍÈd'†…-kíº3¢(‚çyé&ûƒä²Pfu Ó¡³î¬’ËLv[5/k,¾Èñµ ¹U&»e~ÙŸ®Þ˜$5öø ¸üÏ;‰e£¨&çÊ3&·9w^Kà²v"”Ò™a¦Œä&×Çq¡¶>PÉÏÎÎÎ\n–ª÷+º±Øßl IDAT6Ƽ¹H3ØVÅù}vų9GÞ²¯Z½—ýÃ$¿8ŽeÙ+¾p’ïïïÌ0Ö¤rm&ÞÊVˆURÓÜ´q›ší—Å„_v»`m– =]:•©¹Îä®à2f¸(€žm"V1N'\¡†f‹2Mó¬;«êº4.å¸J“w®eÏ3&/ˆ@qAð\•XÊ46¥ÔúWüìúUBžFï:ÁÆû™š¼ª!lÖ(ëYÕ¹>ÊåDÑ“M¦iM”W6ÎÃUãr›sÙ^‡eÖ廙RÊLu•Š[W@‘$ç gm®/ëóË‹lTE›æ&ñ]¯¯Èñê2ëtm*kº~€”{|&Rðå*9ÍÔ$¥ÔZɳ£2#5e±:.e›æ*å•Çe~6yÙ–Y‡eÞ“¼íCßu1¥4Á¬‚‹nMø…›ë²Jec;#yYHQ¦¹(j‚Mæ'J_D™LÂtqWùž‹Âx’Û¤×(9¸=Ë˨"JŸ]7½!«¸”µÌ±¥,ÌUItãj[%/Â’Z” ¿ìqzžÏdeê+;&ÏmªÅ“œîæ:›\\ÔM[VC3kc¦‹ÒɈ-»n›ü\:¦E™ðËP~¾Ž\‹{Fζ$sìDö²äÓiâ @.¢—iæ­Šinb¦gýTJn:/²¼&aº¸eÞK×¼³ ºÒó‹EVVÉÉPáL Mäû~.#z™=t‘$V5ò™…01VyâNJŸnT±´UEÜìä›koëO–&¯¹D´—åW†’ËÊœ§ü&õ’uëÂóÞ[—x„„aˆ0 Eêõṵ́ÒF˜¸%­ªÇgNÊWò/¹ã„Ng¦£(â3U0W¼ ~Ø‘“7›MDQ”*zv†½ˆ^Ù$MÑÊ-òÓíu~Ì-;¿’‹ò2-GÞëQÏ$¼Èû®ŠL^JaVfEF“n²°Á`p¹’ów"z™ ŸþùsJé‘qB®Z­V©ãò¢Í>W¥s%†ÈÍÛ*Ÿ(ì6²Un. ‹ÇÍ¬ËÆã¹'Ý€Å(y†¡ròMUQ¾ï£Ñh¤$ËË2Ídá:?YE¨×eQr‘ŸK'X¤²çi”Ò™ac†ØÜÜÌuîéÌz©*”Dr:¹¢t;88ø“²pÓ|L¥±Þs<+¿û¦ËKæ—G¹E~®{s/SÉmÊ›§Dç2õs¹Ÿ:ŒÇcŒÇãÔdßØØ°Ê+O2³Î=÷’V |%€äððp@)ýLhÒÓîîîΘIüê7[E×ÝåÖ5T‚Øœg”OýÙÔ‡M¾:?YÞ¦F)Á“$Áööv®seVºñ¦:SóÕ“gÀH>3ùf[á»»»3=i“Ý%<ëWDCvm¨:²óþ®Jnr®²îÚ±ÊüŠPy6\dcq›ï«‹Ê°¨™u`qJNƒÁÏ¥‘ *9´Ûí´¢‹0ÙmÃmÃD°mÐ"·ê8ZÉòQgÝ6ש+‡,¾)á‹ 7¥tæÙøx<ÆÞÞžs[c‡ç˜ŸY—Mºåº©¥‘œ^\!¼xñâ×Φa|<­›aww7“3ÓÝ ÆùÛ*‡I˜K\[§ä&JmGu¬r—UOº0WrËÀLõñx ß÷µ¯–š¸OOO¡ŸtË=³,FÉ€öz½0Žãï;e2­œíímPJÓ Fsq²nU~¼»H57ÝËÂdñTe4QòÎl²Ùõ\XÉS5öìÙܸÜźuëV:^bŠnš‡«r«ÂlÔGç'r›ó~®J.ËÏäX¤Ú¦uP”zÛœS> Ò‰ÝÍÍMÔëu§všÅÙÙ3Ó| `„ ’óÏȈ^:ÉéäJS’ƒQv\.I§t×ëu¬¯¯§=î`06P‘ÛįHbçip¢²Èˆ¦RISâËò“u¦î"ž—ð&e>6cCAWÕ'CÇQ¯×;ÁŬ:Oð1fW»1ÎäÆ"Íu ͸ܤ¢ø¸7oÞL•< C ‡Cãªó“…éÊX4ÑE*) Ïú™Ú$®ÎOÔ˜\Ÿ ³çÖÅw©k†$I0S3}mm-×Kywf<ÎÅù‰·™™ujsb m®S‰h\î¢hív­V+;F£\=zÖ¯(ò›Ý”8*b«ÊGÉmÊ•u›Ôƒ¨ º¸6qLý˜h°=¯â¢ô6ÊÞëõŽ1?á6Â…š>–¤ä¢q¹0AÞ¼y3“³ÍE¹ó4*U2Qo™[tl¢ä*˜*¹®,¦îEÕ«KH’ƒÁ ŒZ­6³N݆Ð"œœœð‹`d“n…½˜Â°’OMŽTɳãr]…©*w}}µZ-UsÞdwUnUXÞ½ÎOäVó~²Æ&"²i|‘¿êXå6éTËêHEåÊú±q8Sñ[·n]—,ÿLõz½SèU¼°WL©äÄãò4ТwäÃoܸQˆšËÂL{j]zÙùunQL”WEdÓø*ÿlÛ¦~LàJx‘_’$è÷û©ŠBÒoª›”M×v§ãqÑ*7~<>3éFm+D‚E›ë©šgÇå6ŠM·³³ƒ ÒÙÐ~¿ïÜ›»ßµÃpqËÎaªÌ&€*ÜV½eîEÖ³¨Y¿¬HܹsÇ(Ó¶z~~¾”ñ8°D%7—ÛýÞ½{i/<æ©Ùª¹,Ží^fKzѱŽðE(¹ì|¶d]³ ù]/ËSæ—Uñz½>³NÝôUÈ<_ØxX Éé¤&R%ŸŽËÿRO”Vë^__G·Û!9ûœIÏ/ò+joâ§s‹Žy?]c—m&iDþ¢c•Û´ŠÜËü²aý~f‰ôk¯½&Œ§r«ü¢( ÏÎÎØxœ>Ϋ8SòB ,^ÉNÍÇãñ?› °¨@‘ûÞ½{3ÓLÔ\f¢‹s$ºÎ-:V^Gb›t"ѱ©Û„র¹o¢°(ŠfÚËÆÆ:Žñ5™„Ÿžžîc~V'y)ÏÇ–a®3’'Ož<ùß̽+êZ¡F[[[i<ÓOD™ÞtiÁ-ÔC•¿-Ñó*¹JÅMÓ‰üUÇ*wQ®Ë=…õz=  „aˆ{÷î ãéòQ•éðððfßϼ´ñ8°<%§â³³³“(Šþ…4¢Cß½{7íƒÎÏϕôaÙ4@ŸÎmrœõ·QqÓ´6eR¹Eu ó+£þù0ÞêFØÛÛC½^—æ‘­U8C†£é÷ÜØx|”ÙD“n…}¡$§“ÚàÕ< š‰#JgîynÞ¼™*9ÿòŠ‹rèÂó]ç696%½jÓ¥µ)“©»‚ëÒËâPJqvv–¶$IpûöíÜäÎú3SŸQN·Îæ2͉e(9p1‹}þùçÿ€ÓlÓ ùݸq¾ïϨ¹èg 6 ÃE]Õ˜l‰iZvÒgýLÊìJpÓrËüTù ƒt> pëÖ-xžgU^]9€SWqFp¡¹NmožË4×ñh4ŒÇã?›‰àP¡Ù4wïÞMÕ|0 ×ëÍÄsQuSU‘…e•Då¥U)¹y”Üæœ2²«Üªz’…q(¥ˆã8‹F#A€k×®ÒîxŒÇã~f•¯â¼’—fªËSr€û:Æp8üSQ[àÝèv»©ÉΓdãËòÊ»×ùÙ¸E *óÓW]z?W·Ê¯ˆû-ïÙÙYJðápˆXß?]‡GGG¼©ÎT|€ ’gŸ‘Np` $§“áÕ<úõ¯ý”ÒýL<¡ÛÆïÁƒ3/œÍüöؤaH®Áh¯óÓ¹MÉn¢ÄºM—^ä¯:6u»\UºÁ`.| ¸víZúÈLTNS?Q¸ÀTgä`vÒ­4–§äŒàLÍGaþoˆ½(óó}÷ïßOÇ]l|.ËKvž¼qy?Y¸(ŽêXExqeÐ¥ù«ŽeÄWÕjŸ-‹KÜ(ŠR3}8"ܹsÇ(½ì<²z gƒÁ ‡ÙÅ/\(9?éVÚxX®¹p/ПýSUD—›››ØÞÞž1Ûƒ4/Û½Iª²ªâ¨Žy?%Ömºô"Õ±(̤>²aº8¦÷‡RŠÓÓÓÔLFxã7æ~\hJd§³êü:u¦àŒä¢E0¥`™$gæI þì³Ï~D)ý|&‚ÁÍ6ñcËy5ãØ¹á˜Ù´a‹Ü&Ç*«ˆ+ƒ.½Èß–ì*·M]ºìYçÎϦó_|±iOYˆü^`Ò¶ùÇfŒäÙç㌠¥`)$§“Z¡à¥³Ë\¹ø3{Q˜*¾çyxðàA: 8==•ªŒá5HË& 3!½J=L‰' ×mºô&e1¹U˜’ß|º0 q~~žÞûf³‰7nHÏ%».“øÐëõNÆãñó*ÎϪóãñÒLu`ùæ:#ºÔd7­dÝZ[[Ãîînz£ûý>úý¾6o“Ææ¢"·(¾ ÁxiuÐåaRYùu­jšÞŸ$Iprr’*y†xðàqþ¦åáqttô³ `²¦ú%/eå±4’s½¿0æã$I~!‰?³…éâß½{¾ï§D?;;Ãx<6n86Ê£óÓ¹EÇ2?Ò›lº·-| • ù´7c$OMögÏžýcL*.f/ÉS¹Ïbkk ÷îݳ"ºª<.n“cáUD5….?‘¿-ÙMÝ&u}rr’N´õû}BðöÛoϽx"KïÒ–²Øßß—©xòR†• 9‡“ýèèèY†ÿ\›¨€G)ŵk×ðÚk¯¥_”ÑÝD½³á"÷UVr[RÛ¨«ˆàßøÆ7Aî¶ KÃ0GGG¯0¯â=Ì*yi¿AÒaeHÎ]pj²¿zõ꘺³ñ…{IÞÆû½½=¼öÚk©éLj†¡ö¼:5ÊCnS%/e(yžzígîyž–àE´™,ž?þ9fgÔÁÉùGg 7Õ"ùÌlOÿõìÙ³G£ÑèFT¼í íwwwS¢3ÓýåË—JE×)È-;Î^ ÑDa®›*O“²©®ÍµÎø}‘7!³Œà£Ñ¨ÿêÕ«ç˜QÏ’œŸp+õãÿ{çòÛÈ–×ño½ìømçNÒtèî{o?n³`@£ $B Û™‹‘!X\!¤‘X2ÛaƒÐlØÀþ Ä.Û÷†îtç¦ãÄqlÇqÅÏr ûW>u|êe;‰ãÔW*ÕyTÛ‰?þþêœS§Ü4oNÐ{:årù¯!psû„)~±ÝÚ[]]µW•¡/S¹\Õ4-ª ö<8¹è=û}>¯´ô8õ¢+Š‚çÏŸCQ”‰þÏAön:999„·‹7q ÓXy©7ý‚^²,Ëv˜8Bö³³³÷kkkÿÇ¿+8’$Ù{ŸöǺíI+++$ °, ¦i², ¤R)áyla=[Ni¾ŽÍ³e^å|ݬäÖf× ~èMÓDµZE£Ñ°§#Çb1|òÉ'PųÝI~ôýŽmµZjµJ«¿Ðf¸>Üó.ná†]˜O'à˜ÓÐ9==ý˲:¾'Î(\£ýòò²cxM×uT*û¦–0_T¯c‚:¹èóÞ¤“}A?o¿[¯×ÃÙÙ™=LÖl6‹ÅlçŸÅÞOŒ‹³=ê,àncã7®¹ƒ|ø+G›‰!ä•JåC·Ûý'—s®u_(ðìÙ3†aƒ^­VQ©Tlwg“žv/wvókWT&4mYZ­J¥SOŒÈ…IDATt]‡®ëÈf³xñâÅXÄs]{^WWWõ‹‹‹ Æ{Óu8!ç;Üèû}£š;ÈQÈN׿íããã¿…à‰@ø_b7¹ý£Óé4^½zEQl7¹¼¼D¹\F¯×:v¸ù÷ :Þ øëtò ?4nÇy}&>ÍÿíèïËÞ¾³³ƒ§OŸ ÏåÛô³ûµS,ßÃéâMŒ×1êU¿•i¬¼ærÎÍí¸‹‹‹b³ÙtÓε1ÑÞ« UUñòåKär9ÛU.//Q*•<“,Jó¯Ä%Ù²yuò0ŸÓí±ßï£R© R©@×u4›Môz=|üñÇØÚÚšè7©kój4•F£qQzP¿À9…œÑ˜›¿}ûö'¦iE_G˜&*{üø1vwwí•_u]ÇÙÙ™ãžt·vDéI`gËoÚɃ¾Ï îͧ;NOOQ«Õl—e/_¾D6› õ¿šÅÿŸ•išæÑÑÑœË:‘‹7àãâÖ¬þA!5W½ë¬,˲¤ÁE—ÃÍ{½^½V«ýheeåG.çyö¶óõ~{·s777‘J¥°¿¿o/ñÜëõÐl6±²²‚¥¥%Ç9”&ïYç¿#nŸï:äåð~en@³yZ­^¯Û3Ûí6 …‚½>ú,Ûï|‘ÊåòQ»Ý¾‚sÈŒg!Ÿæò¡,8‡ÓzÚ‡‡‡ÿšÉdþ#‹}Û·轎Ë–e2|úé§øê«¯lÐ öl6‹\.ç¿åÛ 4_ÇæÝÊØr’ßç #¿‹°póy6MwþÑóê:ºÝ.>|h/<‰syÿA»Ýn«X,Áé⦳€ÏŰ+é_ÛWC'§M]YYyöðáÃ$)îrîDûI)‹8>>†¦iˆÇãˆÇãH&“( ö¢|“äýʯSAÝ€wr`,la¶ç666~q{{ûÇ4—s{QÙ¬ÜÛ+]«ÕðáÃû.*MÓì-N#“É ‘H_Côy‚Jt|Øÿ½ؽ^Ï^Úš:Ô Ã@·ÛE¿ßÇúú:¶··¡iZ(¨Ùôu„í"~U©TN0—Âô €Óv>,o`4tÖ`Î àÀÝ„pvÂ%1 Û?úè£?H§Ó¿çq¾p¶Î¯Ì+Mùz½Ž££#\]]!‹AUU¨ªjŸÉdJ¥°´´I’|¡¾i'§zºi‡Àîõz6ØôŒ¹ àP,NOO0œ ÓiȬ g˜ÎÞm6wa:éNBø†í…Ç?—Ëý¾Ož{¿²iÓ¢<_Öëõp~~ŽZ­†f³‰~¿o.IÒÔ’$Ù{6àì9,Ý1Ç.|aš&úý>,˲œÊ(½´´„L&ƒÕÕUär9Ç{÷ƒ›ÏßTØî÷=¯V«¥÷ïßïctã õ¦Ÿcäâ矺j߈2€wr@¶/aÐ —°üìÙ³?K$¿æqþXz–în?ØIÝnº®£Ñhàêê WWW0MÓ7»wk‡ÄÂMÎÎ:|"‘@2™D*•B6›E2™ô|°WÙ¬`Zï9°ºººªïïïÿeY4eU¦³×á:¸ÞtÌ©‹‹9 :¶çEÙxþüù_hšö‰Gcéë}’¼_90ÿòòr,¼¥-Ër„þò³iêÝO§Ó~ üÊg û¬ïv»í/¿üòsÃ0è>pšôRÁ¨³§á2 Óç²7׆p Û ôB:Þ{òäÉ_ɲ¼êÑÆXú¦Ãô °ûÕùoYÖX>Œ|:®&ÎÏ2l ¸išýýýýÏ›Íæ%ƯÃiLœç‡Ëè:|nÃtÒ¼¯ ã«áØóx%Œ–åièº~t||üç–eµ<ÚKuŽ0éIòna°W½×ñ~m‡}-QÝ$ùiÓa·,Ë:<<üªÙl60º?\Çä:P_ Ót—ÿ¸£[Y}5¬î<äŒ,0kµcôË|Y.—?/‹?´<–töûâL½×϶Ì+—Ò|(Ï–y•óuüg˜•Â:¹¨<(Ü^u3ü€œVw¡Î¶*°Óu¸Ûc‡ï” rFÔG ·1] ‹Å“$I[__ÿcÉeVœè„г唵Ã׉òl™W9_Çj‘œœÍ=žW±X|W*•>ÀÙ_Æé8{>—Ë9…ÕÂAθ90îè¶³ÿ ¬¯¯ÿ‘[èîçØ~@‡…û¾:9ŸŸ&-R±X|7œ®J~…ÑŒ¶*œó€;–V¾k€ vMÎÊe¢ -4‘Áð}uuõÛÛÛŸ)Š’ñhkâô$y·2¯òIcuÛNÎç§|ø0„ÿ«T*%ŒN3Úh£kpvG˜~r@ØãNS_ g2™÷ööþDUÕ Ÿö{¯´_~=ëAêg!¿ïH¸ù²ia÷{O†aô^ëº^ƒpºu”&¼ÐDZ‰}äðëIi¡!ÆzÜYGOa8ýÀr<ßyòäÉgñxü‰O{¡Ò~u~Çû;íq¬¦urQÝ4°Ožw:æÛ·o¿h·Û:ÆœÑF€“ƒóG°'¼Dϱ¤Ñ·uô€‚¢(ëOŸ>ýÃd2ùó>m†Jɇ) S? ]‡“ûå'\×õ‹ƒƒƒ×†a¬|ˆ^Ã(<§qpÞÁpà@8@§vÞÑiÂLÀòÞÞÞwóùüo¹õ¼Û¦ÃÔ‰òne^åÓK ó]¸‰v¿:^ÕjõôðððeY]8g²Q/:ßÑÆ•-àÀ=‚N×èÔG ç,///ÿÜÎÎÎ÷UUu½±eØ®o:H>LYºY+lûu§ñê÷û½£££·Õjõ £‡ЦªR:…‹ž_¶P€÷rÀ×Ñif\Ð š¦mîííýv:þŸv…éIòne^å“'Ò<÷°‹¤ëzíýû÷ûÝn—†¼èæ$7ÀÙqpÑd—…¸g® “£ÓZqYŒÂ÷Âæææ·677¿'Ër£]×üu=ÏNîV>i¨ÎË4Móäää]©T*b)]‹gÝ›†ÈØ[FpàB¸vÆÑ½è´–;uÈåä“ÉäÃGýÎÒÒÒcŸ¶…é ùIË'=N¤ëprQù4îÝjµô÷ïßï·Z­FîÍÎb#À rþ–Qvm6šè²€÷r` tÖÑÙžw ß öÂÖÖÖ7×ÖÖ¾£ªjn¼UGû´(ïVæUîW7k݆“ó2MÓ(•JNOO,Ë"÷¦ëoà´QxÎö ß À{ 9à :;i†®Ó3]«çdY^ÝÝÝýÕB¡ðmY–ݶ$ïV¤n’ãDºŽ±rQY˜Ðܲ,«Z­ž‹Å÷½^®¡ p ÏéF“:FÎMî-ê`³§ªÒk¸¾;®{ 9 ïy§ð\݆=‘H<ØÙÙùÍL&óÒç5BåýʃÖÏR“Œ•‹ÊÃÀ F£úáǃV«¥c´  ]apÚX÷¦ðœïA_xÀr[ÌÌ8v,»øD£ku‚= —Ïç?ÚÞÞþx<¾íóžy·2¯òiåu[cå¼Úí¶~|||P¯×kÁMk,à—Íd#¸Y÷îÀyýÝîæÝd“*‚œ‘t6|§ÞwºVgaÏÈ …§kkkßJ¥R%Òfáä7éâ¤Y\—û}ß®®®êårù¸Z­–1€›Öì#÷¦ðœœ6‚[äÞlx~o"ÈÇä¾³w²¥0îì™d2¹½µµõÍl6ûL’$×Ûygíä³?È÷b'çêÌz½^.•JÇWWW—ÁÍöœÓ³É(<¿äö,ÜmŒ–L¶%Œï`sS¹@輫Ç0ZVŠÆÕÓpº{@ZÓ´•|#ŸÏ¿R%ð»[Y˜úëЬ氓 ÃèU«Õ“R©t<ìPcáf{ÎY÷n0•Ѹ7ÁÍ»÷½¹þ)‚ÜC\øÎŽ©³®N×ëÆì”N˲œY^^~šÏç?N§Ó?¦GÞ¯<ì1A5k''™¦i6›Í‹Z­vv~~~6 sƒ›®½Ù6îp³îÍÂ}¯Âs^ä>òqu/ØY‡§Ð>%ËrzeeågóùüÓT*µ+˲pUšEqòay¿ÑhÔ...ʵZí¼ßïÓ½Ú"¸Ùqorp‚œÒ"禋Ƚ9EtruÑõ:…ñìu;»ÑAB–åÔòòòãB¡°—H$ÖUU>nù&ÆÇyM;^Þï÷ ]׫µZ­\¯×Ïûý>Û»Ýc6r`64ob3»Ñ£ŒèÇÀ îð¡"ÈCÈÅÕ½œžÝ'‡uKÃ-¾´´´œÍf¤Óéd2¹‹Å²Ã×áßǵ}F?¹}_ºÝn³Ùl^êº^×u½>|2IŸÙȵ ŒÜ—àÎî)twsnGÏùð}F_ì¡"È' žŸ¯a4ìÆŸtÙìtl @LÓ´d6›ÝJ§Óëñx<§iZJÓ´”,ËÂû›pr˲,Ã0: ÔF£nF£GUÑš,Ü|Xκ7΂΃í–G€»(‚|B¹„ð¼³³½ñ4©†6n6M°Ç¹séÇCÅb‰D"‘‹Åb™x<žŽÇã)MÓ’²,kŠ¢(’$©Š¢¨’$ וɲ,Ó²,s(£×ëµ»Ýn{¸ou:ÚÚ–e±CR,Ø5m<ÜlhÎΧicÁî1íGp‡Pù” ;ÊÓF®M{~®1›Ê´m?<‚Û$UU5EQ4UU5ŠLÓ4û™¦iöMÓ¤'‚ЗÝÌüžÛn64g!çæ»çXw÷Š Ÿ‘|`çÝ@å¡m1f¯1{NØÙm væý°ï‘:¶Ü4ýT8ïäl¾ °#¸'TùŒå»ÈÝY‡§°žŸÊâ\9ëên ³°³ï‘ÄÃÍBÎÎvxñîÍ»8õ ³y7¨ùŽ4ìî A~Mâ`§=ïî|HÏ:=ëø¢=–Ü3|‡tp`ÜÁY÷vsqvb‹ÛÆÇ^có`³Ï‹àžBä×,îF2QHÏoªËƃ- ÙiÏŽÜÏÉE ‹ çëø ·Pœý±‰àžRä7(wgw_ô#À—ù98›fßû%ð‚Üx`f¾W^u÷ŒA~ ¸;í)Í».¬èz[¶È½ùN7Öɱ£Š:àDyÑÆžëæÖØ×¨ò[–à¾s‘˳y¸^enm¹It}î¼[^tM-ž$¢/áµ*‚|Žä<›ϦÝê¼ÚœnÎæ½ gÓ^@µ}sŠ Ÿc¹¬.ã¬[½(ï%7èÙ´Äc_ªêÛSù“ײRC<‹ ín_ayô|)‚|äó0sE0ß EGŠ´àr}4o¤H‘Cä‘"-¸"È#EZpEGŠ´àŠ iÁA)Ò‚+‚„¸x‰;‚žw$­V«5557ÕjÚr1åR’%A‰J'ëÌU²J§R‹ öe›ÈnhMd}Q+É’B°à)búèç-š)|3q DvÍ]v\@2öÁÀu €÷fnHJK¶Ì}¬ÛÐ"ƒÏŽ,úm.ët¸eßw &:¯ŒeÑ¢ÖÜÖšöMm˜¬Ê #ITŽŒ5²dáŸ$‰zfödÇí9å¯?fmXÄŒBmI-*Õ’‰#…Cæœ1)e,ëéˆ~«õìiIßW; ‚Rã”Z!8阩©‰ÒÈYW€˜QÙðÖ¥}#•RrNå“Êm•3’5×½²aàÀ ˂ξŸx€PÙ3¶ä™gŒýòE‰û²Û†›Ä –\°êª_Fþ©Ý¶ìµ©Û†wí!è[dWdÙkSµmÃM;¡ÐxaxÃ.cxixÑ€píÞÃg ×ýÈþÜN°Ï^ÖW]IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/debug_cpu.png000644 001750 001750 00000002410 12256006060 026043 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;sBIT|dˆ¿IDAT8m”YlTUÇÿçÜ™;KË,¥-¥DH‘" ”–ˆlÆ„HÊ¢ÆÄãò bÀ¨‰¾’¨‰˜b"yФ¤ ˆ ÓF(JÛé6´t›ig:Îr—óùpït(ÓsΗsÏùÝÿýçûæ¯ïùÐ*q¾êü™ï¼úÆ» 7ÿ¹~ûÙ­Ï?­ëú}KívçH">QæVdÇGjçšN>ΰÌn|aÛÖL6w¸Üïy­®á™ UAß;µ+W™ŠO”¬¶=n—ëÒd"±*ðÇ#‘™sM§>  <ƒ=ÝúÜ–ººuk¯p.ùTUI˲\¢ä”¬U–횦åÈDB%g ÐuAÙl&ó×Õkuýýó*Þ¶}GUmíÒ¦êÅ‹D@¥æ+G46 »Ãn÷zÝ`#*œ“]ž²PÏ*>pôäû +=Ûä&Òa‚AD1 ‚ÈÇdĪ¢âí÷>HŽÇâû÷¼÷;òà«W~» Æ'tMÙ•Èpí ‚ñqszðà““Ç¾ú€žñõñ½;_|ù "a‚A*Dš}t¡cÇî—¾Ž¨¬¨Äú† =bAg©k#ã|C‘⮎öf÷‚‰Hü"̵h4†ÚåË1•Lâv[úúŒ¤ ¡Œ ç"ý½GRéÌ…"ðê5õ{¼îÒÏ{ÂàÜHJ°<Mèp:°Z­XßP©dÉdTU…ËåBKë hº†5uõßLÏd½ç~úáø°âì@oØ%„8Ì`¸F‹MÀïó!5“C4CYY,’„d2i*æèë9æ Tž)R,H4ú+¾%ˆÀÌÜ©ª†-›7!•Jaº7…ªªJ€1t‡Ã¦Ç†º.(¯|SÑiÀé9àÒÒ-Zfú2=EæõÍf³èêCQre÷;»À9‡Ýî@:žsÆ 4õ÷ÒÞ–"ʼnøä Çz˜•êë×Án³af& g‰‡Š¢`Qõ"´¶Þ*Üu!Uµ†l|r€Þ9`_ 8f!5Bd ¯¯ªª‚sÎ9ˆ^¯fv€1ø¼ž³:F‹ ÊÂf”­1<?l6†‡G idÙ†Dbj¶Üþ!0ôpØÎ-V[¸ªz±Ónçïk4ÛU»Ã=¸ÓÖ‡ÝÉbc¬Ð3òÌyÉ’'Ü,%EàÎ{wZ8ï$Âöüæd2 HN«˜mñP(\\]Kg²kü>o´¿?¢-{rYÕÍ­ÿnÜ´ù•Ë—.žhܵûÓãßÝwýêŸm”"+Ì¡o¹õßH_O*>Ö§§¾20Sé\I(Ô1¡ÂòGè^¨Ûé „L¨þÿ ݹIaʃ]IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/layer.png000644 001750 001750 00000001402 12256006060 025222 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;gAMA± üabKGDíXyøŒ pHYs  ’ù¥tIMEÐ } IˆIDATxÚ½“1ˆA†ÿÙäŠIPˆŽDÁX…Èb)ÄÂþº4iìDð íÄB®3Šhã‰'ˆÅÙÅ*•¶¦° w &9ØÝìîŒoß$7»äö8øx³»ÿyÌà¿ê›.*ŽÑî <„.ÆI肇_˜L]L&óêYea*npOÜ'ÑxŸÈj{Ga<¶1Ö†±úþu•{­/€˜ðtõ¯.{¦ÁÒ¦[O¦l:óD $ñòéE°~C•¢µ¯mÒõ{á³›z7OÐ:8pÃd\mÇÃlæ£×½¼ð›»5Âd#âYï‹Ä5@ݾ7♦,&eSÛöÀ ˆ·„$®ÃLX™µ÷FÑQLÉteŇe Þ¾Gãp]ßC«]‚jêš6õ¯˜”‰Æé´uhìûî,nÌé¤&èSý£$ãÉÔA:eA± É©Y2²}¡ ØTV»Ë©-h‰Ï;—1žò±â:a\“Xšý‘›tArÜ’¬}p2VlwõroÞs ‘ÔÝù(~J‰…ÇÁh4âu¹\æ*„ëQü$8œXƒxÁ#””’‡ªR©¨õõuÕl6ù[ü€iñ‡øJ ˆw±_êıTF™LƒÁý~¡oŽ·k®ëÂÀ’t*êõ:òù<ݺ´Z-„ªV«ˆ*77Ý3cˆÑ%îÊ„…¹haJNÞëõÐn·‘Ífùù(¹GbthLó…ïû¼õµµ5lll$¤=Þø± ¨%ãR©„ÍÍM $É9abNªV«¡Óé X,ò»Üó†‹8à|‚éñ˜°#ÇY’ž³Ḭ̈ÎhT–ȧ‰SDòÍ;þ üƒþöšâòKͶIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/cheats.png000644 001750 001750 00000002524 12256006060 025363 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;gAMA¯È7Šé pHYsHHFÉk>bKGDÿÿÿ ½§“¿IDATxÚµU[lUþæ²;»ÓíÒÝRÚ…6´´)PB•‹ˆBCÙ6[¬ÑÄhL|Pc"&>$¾˜øb Æê 4&†A¡‰—Ä„‹Hµ¡´-´ ÒÒvw»ÝËÜçÌxθ¨˜Æè_òåìü™ùþï|çÏÎu]< ˆ˜ŽãÀ4í]Çqáº÷‰â:_ à8ׇe]’¤… !“2™Beÿ@b¿ u–i¹¡,ºnz4 «Hï·WK¦óÜá›#--ËÞß·ïÍ‘?…-ËnÜ{]–ƒÏâ„ÂáàšH´L.‹„0~7…oÏ^ÇòeUx®c3ž÷žS¯é¹¾›Ð-²¾»ûkÞqÔ7x¡ë9}úd6™œYÛÐP³iýc+d¿‡AÝŒÞIâ§_o#“W!Ë„BA”–2Ê [÷žuâ`Q¬œÆ!5ö÷_.çóù‚8::*”””`ûö§¾”Ë)ÈA ”."œºð9äî9_÷1QJ5yP˜MkP|‚J$ *Û OÌ¡yy>¿ÎÊrËÆZœ‡rËÂ8Ú Û‚‹e™@qºyÌš§t$’œz—‡#°XÓh‰g€W-Tp 6Ç. Òø¹¹4r³³ÐUõßÿ6 ±´‘DblèúdÙÄ9ö¹{»ôH[‡öå4pÉ< šX´@B̹äì?5•LŒfuˤ֡Ñ{^áòò’™úzòêäät‹,ÏqÎ?†×ñvúƒÖUf¿T,âÑÕóÙBîÊ{tTñ‰~X mÙ²ijÞ„ÕE!v18|`o×ã¾îöfRRs<>ü†;7 ­xåÈWÇFC¡0¹`0è‰ügŽŒŒp{^ܹóÝֲﺻʆ_Þ>YSn=ÔÓ#þóÝÿ}ç% |Ös(ª*JÄ—kÝOïÚµÓa×ÑßñÐ.ÓßÃ(òÜ>hXtEXtSoftwareAdobe ImageReadyqÉe<IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/frame_skipping.png000644 001750 001750 00000002537 12256006060 027116 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l; pHYs  @¾áAtIMEÓ +–JˆÀbKGDÿÿÿ ½§“ìIDATxÚ}•]LÓgƽݲK/vµk’ŧ8Yq¢(‚ŒŠ‘YÑ®” A·àÜp~Edb¥nNN0¨Ø†o ¶–BZi)B…bÒ‚ 1K\|öž7v±¢»8iûžóþú<çœüÿkBBB±6%n³ú뽈ãÑæagë(÷ŽX'MxÒ“±HÞý™šü…I1›”Ý%bØ+%Ð]?€?~Œ;—ÿXn¹#§á\÷3ÐZœáŽ0åëൠzo¨Bòrø®Úß¾‚²@„†¢},{àËÍúC{>÷¥²8(¤÷íÛ¬§œµBÒ0^'[¶11ÆÒƒ¨ÿ%éeô–õJb®!û¤” ¤´æRâÔéô¨æ=;ÂÔ7óâá¨MÃbë7XhÉÄ|ÓQxbˆÕ*NÆa×ÖOk.dF—ëOi)FÑñD BÕkX?ÇéßH)A3öEä•ä }Žºtø»ÎcÙZ…•éA,ÏMÀ3iÃÂD/<¦r¸Zó`*“@þínß±”/òjò÷N–ŸÝóÏ•13ÕjEÀ`®dÛDª0üSé[ÔäÀo)ƒÛiógϰ²²¿ßÎÎN,,,`zz[œºë­;ŠÂ±¾äØM§2D‚aaÔFCÈkƒøˆEJáñ¯·9~{=žûçáóù`±X°´´Ä¹¹¹˜››ƒÉdÂÓ§OárŽÁ¡/ƒ¹B†Óé;¼Ä VÐV°žµ_KÁBïu¸]"•'Õ(“Éàv»¹b—Ë…'OžÀjéÇPc*.$ &rCÑ›ëö¾L$XœmÊÆÒŒ^¯f³™Ãçççé7¦¥¥ajj “““p:èééÁðð0ìýZüu[ ¶A‹Äz|{¤Z w×U,ÎÏggg133ÃJ¥R‡ÃáÀèè(FFF`{4cí9´ï±‚À´‹nÓ]ôrÛ'È6% ·?66†ÇÃn·Ãf³á‘ÅŒU1¯$¯›ï†«ÿ·§ÓéH¶ù™H$Boooȇ¨T*¡R©`P߀ªpßj°©ì&ŒUÌö4õ‘¦dûâÅ‹Ëå0°Z­´-|ýýý0õõA§T îç¤Õ`ci*FÚ¯bÒÉ{G°@¹¢’’ ¸ú®®.ôtëÑZ~Õù‰«ÀÑÙ»ž›«cdȃÁ@v©’••FCPòô1•TW__ŽÖ¨Š¥È=¼í9±‚ö8!:l@síÌí¥°[ PÇU ’mò9èõztwwCû M…(;›€ØÈ oî1Eä¹#;aø3f½lii!»\e[[}ç=Öjµ44öùíªrÔ\#G b¼ üÁΈõŠŠ {_hoƒ±½ÆÙæÀªª*RJö™Ú‡¬5mh©+EM‘W¿‹{±5%ÝŽï%ÛqòUd&oqSŽj¨–îÐ]b¼uxÌØvðýäã‰Wç,ÞˆÊQM žÝ Þ¿u‡LßZ=CIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/reset.png000644 001750 001750 00000002541 12256006060 025235 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l; pHYs íÀ,tIMEÓ 9'œJ¦bKGDùC»îIDATxÚ•kLSgÇ;Ñùa1û²ÄŸøaß4Y–C» Š.D]œ,+0 ´Á¸ ±3:3uHÕÎ+ 3œD'Ø0AœS, jU·Þ¤ÚÒíá¿ç=F–ºíŸüÓóÞ~ï{Þó>„DåÅx{’¬:Žc)6û³ÕütË‚J­Cf˜\Þ0ªh3Ꟶ†Í¹=‚{ôú¦¬ àýoÉþˆgJRÔ¤2p ü‚­øîª '~E 8 ¦ŠŒSuf¨¯ Fõ-ôÏ\µy1£¹à÷ðàvô8×)j²fÁe±`_¥…>RL-QîÆÖã^\h‰~çü”^}àÓàõŠ+9"–û,M“KüH9æƒRãŰͦÞá0¤ê0ÖžBù '¢Éᙄô„C?¡;n3 ¯Þ.b…å~¼jëKyl:Bí}7"t <7Qx‡BÀó‘qüC4@š:‚u%Àʽ>ŠåV}²¬b½ˆU)VP⋆ù G"ØHÎÕ„`4 ÷<¿cN¢©ýéÒÊC`kc¥âîÕ­9{—‹XécUJ\ØãÜ\:†LµŸ“‹«Üè Âä1#öÞAÛ$ü!^hk;[7W´6•”îWqIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/emu-compatibility.png000644 001750 001750 00000010171 12256006060 027546 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDR szzô tEXtX-Index0´Ãœb+IDATX  ßïÿÿÿÿÿÿÿþÿúûþÿÿÿÿýûýýÿÿÿþÿÿÿÿÿÿþÿÿýöüþãçöðñ÷üþ  üýôûüþ þýüÀÇâ·Áâèðþ  ÿ õöÖ üî%! ëîñ613 ÿýûþÿùü™ Æ¹¾Þ   òüÏÙù÷÷ óåнÀÛ ö 1,-þ’ŸÃ©²Ý ÿ þññöñïüþ 67.7K> ÿÿÿÿà忢¥Æ»¾ëþÿûöþ B3$#Ê×öÃÅïïðõùùâäï6 ýýúéíè øøüáäÜŒ•˜ÁÃý<üÿþúÿûúÿüüþüþÿõôüðñýöíêùñ/ :ÑÕÊâÝÝìçêøøöWUWëîïÜÞߺ¼¹ÊÉÛ 7üüøúú÷ÿòïéàïìñ +IîìÙÍÕÕ®ËÕðêí2+' ÎÕÔ²´´*(*@=AÿÿÿêêêÑ×Ó´¶²áßëUNg°±ëüÿþùóýùö .+77# ìîæÆÄÈËÌÖâääøúú$ ({„püûõú”‘Ýßá ýýýÕÕÖààâéç䟛œÚÕø¥¡“/257'"#öøîÌÊæÅËÝàðü  -÷ øýùúóÆÌÐÄͱ¬®=>=0/1ÿÿÿÿÿùùù‘‘‘??G ââòÜàäðòêÍÍÇÊÉÒßÛóÿþ )  ýþúøûúûöú áØÖÔÎ:8A…„„”†=?BòöðââëÕÔåååñûú % û÷ùúûúûþýÿüüúöçåÙðñØlikeblÿÿÿÿÿÿÿÿÿÿÿÿÿ¸¹³ÍÎÐ#ÓÎñ«©Ø"  ÿýÿðÿþþÿþþýþýúýüç64ooP,*1ÿÿÿÿÿÿÿÿýýùÿÿÞÚïge£êí ÿÿùýù þÿýÿþúñôÝUV.SQ-ûûò66>ÿªª±üô ÿåæï Þýú þÿûüÿüþýûýüòöøì?DvtUûúþþýÿÿÿáæáÉÉÎ úôöñþ" ÜÚïôëñýûýÿþøZ^9jlI ÿüÿÿúÿÿÿÿÿÿüúúûÞÞäøú÷  ÍÊáÁÂÛààíõöø*+BC,&&þþüÿÿÿÿÿÿýüüþüÿúÿÿÿÿÿÿÿóôòßßâøøúòòó àâßùúõûüù--+ýýÜÚÞòðö,+%ëêìØØØ&&&ÿýÿÿÏÑÔãåå  àè×âæß  ðïùÊÈÖÿþëìäøúòñóòÙÜÛûýýÍÏÏùûüòõøòôöíððÿýýêíæ íïÞ)) 01+ÿôõõåçç353ûþüãæåþÿÿÿ/-+åæèýþÿäãè "ààáðîî ôñüíìî ìïîÞßßààæüüýçæî#þÿÿÿæããÜÝÞô÷÷ âïèÙûöÜÞ'&#ÕÓÔéæè íðûXVQâäåÛÛÜüüûÿÿþÿúûýþýþùùúÿðñòóõö ùùùëêíÿÿûùüâââÿÿùüõ ÿÿÿòòò ÿÿÿÿÿÿÿÿÿëìîÒÓÕÿÿõõö ==;ýýÿרÙþæäã òòòããâ$!$+*'ÊËÏëíïæçåùùú120ÉÈÐ"!éëìââàßââüþþ/..ööø üüüÓÓÓçéêæåå#"!ÆÇÍ%%!ÒÒÓùùúýþþñðïùùüîïñ867ÒÓÖÖÕÙààâßàäHG@õö÷ïòð=9;òòôÚÛÝ÷öø$#!òôô êêéÓÿþÿþ ÚÚÚÿûòóò ÂÅÇ  ÿÿú ýüÿÙÜÛÚÞàôö÷æææñòòëëìûüùÙÜÙ ùûü ùùùÝÞß* þþû771ÛÛÜÐÐÑ#$#ýþÿðïïØØØýüýüþý+=<ÿËüûü  ÿÿÿ0.-ÃÄÅ¿¿Á%%$ææçWSQÈÊËëéé1.+ùø÷ÿÿÿÿýþþùùúþÿÿÿÿÿÿÿôõõÿþþÿÿÿÿÿÿÿÿþÿÿÿÿþÿèb\¾B«SúIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/settings.png000644 001750 001750 00000007270 12256006060 025757 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRójœ pHYs  šœ 9iCCPPhotoshop ICC profilexÚ–wTTׇϽwz¡Í0R†Þ»ÀÒ{“^Ea˜`(34±!¢EDš"HPÄ€ÑP$VD±T°$(1ET,oFÖ‹®¬¼÷òòûã¬oí³÷¹ûì½ÏZ’§/——KÊðƒ<œé‘Qtì€`€)LVFº_°{ÉËÍ…ž!r_ðzX¼pÓÐ3€NÿŸ¤Yé|蘛³9,ˆ8%K.¶ÏŠ˜—,f%f¾(Aˉ9a‘ >û,²£˜Ù©<¶ˆÅ9§³SÙbîñ¶L!GĈ¯ˆ 3¹œ,ß±FŠ0•+â7âØT3IlpX‰"61‰ä"âåàH _qÜW,àd Ä—rIKÏást–.ÝÔÚšA÷äd¥pÃ&+™ÉgÓ]ÒRÓ™¼ïüY2âÚÒEE¶4µ¶´4432ýªPÿuóoJÜÛEzø¹g­ÿ‹í¯üÒ`̉j³ó‹-® €Î-ÈÝûbÓ8€¤¨o׿ºMîòÐ]9ñLaŠ€.®+-%Mȧg¤3YºáŸ‡øþuAœxŸÃE„‰¦ŒËKµ›Çæ ¸i<:—÷ŸšøÃþ¤Å¹‰ÒøPcŒ€Ôu*@~í(  ÑûÅ]ÿ£o¾ø0 ~yá*“‹sÿï7ýgÁ¥â%ƒ›ð9Î%(„Îò3÷ÄÏ H*Ê@èC`¬€-pnÀøƒ VH©€²@Ø A1Ø ö€jPA3hÇA'8΃Kà¸nƒû`L€g`¼ a!2Dä!HÒ‡Ì d¹A¾P ÅB ByÐf¨*ƒª¡z¨ú: ‡®@ƒÐ]h š†~‡ÞÁL‚©°¬Ã Ø öCàUp¼Î… àp%Ü…;àóð5ø6< ?ƒç€¢Š" ÄñG¢x„¬GŠ ¤iEº‘>ä&2ŠÌ oQEG¢lQž¨P µµU‚ªFFu zQ7Qc¨YÔG4­ˆÖGÛ ½Ðètº]nB·£/¢o£'Я1 £±Âxb"1I˜µ˜Ì>Læf3Ž™Ãb±òX}¬ÖËÄ °…Ø*ìQìYìvûGÄ©àÌpî¸(—«ÀÁÁ á&q x)¼&Þïgãsð¥øF|7þ:~¿@&hì!„$Â&B%¡•p‘ð€ð’H$ª­‰D.q#±’xŒx™8F|K’!é‘\HÑ$!iééé.é%™LÖ";’£Èòr3ùùùEÂHÂK‚-±A¢F¢CbHâ¹$^RSÒIrµd®d…ä Éë’3Rx)-))¦Ôz©©“R#RsÒiSiéTéé#ÒW¤§d°2Z2n2l™™ƒ2dÆ)EâBaQ6S))T U›êEM¢S¿£Pgeed—ɆÉfËÖÈž–¥!4-š-…VJ;N¦½[¢´Äi gÉö%­K†–ÌË-•s”ãÈɵÉÝ–{'O—w“O–ß%ß)ÿP¥ §¨¥°_á¢ÂÌRêRÛ¥¬¥EK/½§+ê))®U<¨Ø¯8§¤¬ä¡”®T¥tAiF™¦ì¨œ¤\®|FyZ…¢b¯ÂU)W9«ò”.Kw¢§Ð+é½ôYUEUOU¡j½ê€ê‚š¶Z¨Z¾Z›ÚCu‚:C=^½\½G}VCEÃO#O£Eãž&^“¡™¨¹W³Os^K[+\k«V§Ö”¶œ¶—v®v‹ö²ŽƒÎ[º]†n²î>Ýz°ž…^¢^Þu}XßRŸ«¿OÐm`mÀ3h01$:f¶ŽÑŒ|ò:žkGï2î3þhba’bÒhrßTÆÔÛ4ß´Ûôw3=3–YÙ-s²¹»ùó.óËô—q–í_vÇ‚bág±Õ¢Ç⃥•%ß²ÕrÚJÃ*ÖªÖj„Ae0J—­ÑÖÎÖ¬OY¿µ±´Ø·ùÍÖÐ6ÙöˆíÔríåœåËÇíÔì˜võv£ötûXûö£ªL‡‡ÇŽêŽlÇ&ÇI']§$§£NÏMœùÎíÎó.6.ë\ι"®®E®n2n¡nÕnÜÕÜÜ[Üg=,<ÖzœóD{úxîòñRòby5{Íz[y¯óîõ!ùûTû<öÕóåûvûÁ~Þ~»ý¬Ð\Á[Ñéü½üwû? ÐXðc &0 °&ðIiP^P_0%8&øHðëçÒû¡:¡ÂО0É°è°æ°ùp×ð²ðÑãˆu×""¹‘]Qب°¨¦¨¹•n+÷¬œˆ¶ˆ.Œ^¥½*{Õ•Õ «SVŸŽ‘ŒaÆœˆEdžÇ‰}Ïôg60çâ¼âjãfY.¬½¬glGv9{šcÇ)ãLÆÛÅ—ÅO%Ø%ìN˜NtH¬Hœáºp«¹/’<“ê’æ“ý“%J OiKťƦžäÉð’y½iÊiÙiƒéúé…é£klÖìY3Ë÷á7e@«2ºTÑÏT¿PG¸E8–iŸY“ù&+,ëD¶t6/»?G/g{Îd®{î·kQkYk{òTó6å­sZW¿Z·¾gƒú†‚ =6ÞDØ”¼é§|“ü²üW›Ã7w(l,ßâ±¥¥P¢_8²ÕvkÝ6Ô6î¶íæÛ«¶,b]-6)®(~_Â*¹úé7•ß|Ú¿c Ô²tÿNÌNÞÎá]»—I—å–ïöÛÝQN//*µ'fÏ•Šeu{ {…{G+}+»ª4ªvV½¯N¬¾]ã\ÓV«X»½v~{ßÐ~Çý­uJuÅuïpÜ©÷¨ïhÐj¨8ˆ9˜yðIcXcß·Œo››šŠ›>â=t¸·Ùª¹ùˆâ‘Ò¸EØ2}4úèï\¿ëj5l­o£µDŽǞ~ûýðqŸã=''ZÐü¡¶Ò^ÔuätÌv&vŽvEv žô>ÙÓmÛÝþ£Ñ‡N©žª9-{ºô áLÁ™OgsÏÎK?7s>áüxOLÏý nõö\ô¹xù’û¥ }N}g/Û]>uÅæÊÉ«Œ«×,¯uô[ô·ÿdñSû€å@Çu«ë]7¬ot.<3ä0tþ¦ëÍK·¼n]»½âöàpèð‘è‘Ñ;ì;SwS—yoáþÆèE¥VøÉA¤ËZ9eBnolG¥W¯q•ë*W z¥(KSþâì¢6ËËýVûçÄØdæö'Áf޳ÍI'Œ õѸ³ëØË-êÈþ:ÃDˆ:Õ¨‰…Nï—[i¯ÿò“™Ñ»Ýâë³À7Ûv¼ïÃ8aCÎ…x¢½é£ã»èÞ&æBKD]Hg}f³†hem`lÎçëoǸyùjÊ_Î}fƒàbaê‹ ´‘.ƒê«ÞÓÞôñ›­t4„É–!§¡Êƒ°‚R>Ïà¯÷™Î2%h®uùôä‹<þJ“Eõ‰±]k¼0@Ç{W” ÎuÍoômW­5!ò>äõj5V Æ÷ù#9ÇÏ£iêÛÚ(!ò,ŠæÖ8#ãÙ†ÂãÙ¡ðëß[=¸ª±ˆ$Çëm¾EUGÃÌä¶ê>³dÑ‹ \½1Ï ‡‘*„·ìñhõ Z¸y·W Å3Àø*ØÚUQéÕÅ«I7„¯ª€Î8D0UM-ìO404Yf&/XQdJ\„™d»±uqOæç{6ÀÆt›X«%ÄÔ¦nk}8Pp|¯CÄ{–øjè6 ‰#<\q˜Éƒ• ƒ©]m®í^·› ¬§•«ÒÈ•7IƒÉ´bo¼Ô ˆ%½Râ·YÁß&Z*1Ö[‹ ËTœ§>¨p¤ kÎ (—óøf»M±ÆjS,Kº€ROƒÃPZË”Ä/¬þ¿Ý¼²"€^÷±Á°³òý²Ak(o*탬éhÄσ0l­’øÀðØØ'—Ñ‘å4à>Uj=DLá/û.ž~ósØÐØÚ J¹þØÜøIÕP¯”㮦'ûâÐQ¿ niÞɱב^,Rr¼õ#m¹LntXL.ÛL¬'/7y‰Ê–·ÆYÊD+w´4õñ(¼X$?}Ññiî?ž§¹¶’±‘غv*£‘¥xýùÁÁ$Að9œOm™nblÒúúliäú…hÈ4ÕèÂq<¾›ˆnäÉ•ËÞvâ±F—I]»ÎÒWfDëÓ@ò™ó¸¶óËwPª¯¾³£kçáýªö¹]„Ü­Þ2Ú'3þˆ©¡aI]Iš²> ç/ýí<¶¹hMý~ëTfì~o¬±Î«ßÓêz±¨Â¥l^æï<ð³Ssº”Éö‹µç6wúOS¤áŒkbLõמ¦ÑO,5°zQç…ÿsý sâ%ÅÁ }IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/sound.png000755 001750 001750 00000010322 12256006060 025242 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l; pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFíIDATxÚ„•]lS÷ÆÿsŽÏ9þv;Îq°óe')mB#(Œ¬-e#°iRw_Ô«i7Û¤I“v5MªD5 »Y§nê¤UhêÅØ4Jé$Æ´P `..Ð9_ÌIŸÿ.BµIë¶çúÕû>ïÇó¼BJÉÿÂ[oýjøV÷Ž{õ>ÿâøÏß@Ç‘€äÑÃôÇb“À+““{žyfëÀ½l¶øæ‰7¯ß¸þÞK/½|1Ÿÿ{EUTz£Q|~?.M㠢ʫøì¶m£BðJ$9L¦¬@0XN&“–®ë‘;vÌŒŒ O‰§d¾ êˇ¾¾Iýi” ¥ìªV«‡zzzŽ}óèÑm‘ÞH·td¯ïö©í£õFsûìì¥ëëë5M[õú|uÃ4QåËK)qçE—KûÉŽ©©íß¿¯[ÕTÊå2Ožlàt¼^/Ѿ(@ ´0?ÿŸÏ?âv{JŠ¢<Ð4í?g,Íåúži?ÝÒó±±±Aa¥@¹T¡V«!øý~¢Ñ(áH˜j¥B.wŸùùÅúÒòã7õ7”§¬µÍö%ºaü ¯¯ïõø–~LÓ$“É0wó&‹ ¬‹Ôë5¤”˜¦I(ÔÅÀÐ »÷ìajÇ.’É’ûîݻdz٬Q.—&„@H)9qòÓ–eM¹Ú­çξÏÇ}D±¸†ãHǦݶ‘Žƒ¢(H$Ò‘ô„Ã쟞æ‡#$ÜÎdì¹¹[‡Þ8þú_Ôk×ç|“Û·¿½u||àÝß½Ëß.þ•V»…ÏÀ¥ëtw÷0’!jYUEC×)•JÜ»s‡j¥ÂÔÎDÂ=JõIuìÕ×^û“¦ë®½‰ø–=¡P38Ã'W¯âóùp{¼›g7ñ$fY؇\.ÇÙ³æÓ{Y Ó¤Z©ðáùfæÈ ©d2í6Ì•p¤÷ù-ýý¢P(påòeüP†i004ÄÞ½Ï'ðúüx=,Ë⹉I"Ñ(†a uáq{ùàý³×ÖH $°ú¢“šeY‘@ Àí[·¨×ë„B! ÓD"1L“Å¥%:‡F³ÁêÊ*…Bè†Kw!¥¤X,’ÏçIïNcY}ÍçóÝå¢Õjáõzq{½˜¦‰ã8¬®¬ð¹iR«Õ0M“V«E±XdaažµÕUÁt$­V‹z½Ž¡D"½hªª– à âóûñx<˜n7ªª’ôˆ‡>£R©`š&ív›Ri¥ùyÇ!ê¢ÙlâH‰ßqƒèºŽJM+®—®jšÊððáÞŠP0L]׿ág9>ðEU°íõÚªª’Ç0tÔ é8ƒ’©B–––îj‹‹Kóóó¹T*•šœœ$›ýÓ41 ƒP(D$áñò2•J‰NÇÁíñ‰ô¢¨*µÚ¦hìN‡¯îßGrd˜û÷ïg¯ß¼ñ¡:wã“S§NU^xákGú¢QŠë%Úí¦i`¿Ÿ-ñ8ñ≢}}h.Žãà8HÁøø8Gfcš&ï¼óÛïÿæ×o_RJNž<©îÚµë÷étúèZ±È­Û …•M©»\¨ªŠÛ¶i6›Ô êõ:šª20`w:MWWˆÙÙÙ?ž>}úÛív»%2™ çÏŸvzzú—iÛîðøñcVVV©5› êt°m»c£»t<^ñþñDE®]»vãÂ… ßµ,ëãF£ÑW®\ù|Þ³¶¶v`llì‡étz¿×ëEJI£Ñ¤ÙlÒj·‘ŽƒæÒ0MÛ‚jµÊ¥K—®ær¹ÝÝÝçöíÛWªV«±¼¼üô%9J&“ ...î4 c&‘HL§R©T8ÖþÝgÚí6…B¡“ÍfåóùÙv»}&‹]Þºuëj(²…ˆr¹ü/sB,,,¸———c¥Ré+¶mOªª:æóù,]×}€h4µjµúÛ¶s.—k.܉ÅbóñxüI³Ùt¾ðãʆo‰…-üIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/mute.png000755 001750 001750 00000010425 12256006060 025070 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l; pHYs  šœ OiCCPPhotoshop ICC profilexÚSgTSé=÷ÞôBKˆ€”KoR RB‹€‘&*! Jˆ!¡ÙQÁEEÈ ˆŽŽ€ŒQ, Š Øä!¢Žƒ£ˆŠÊûá{£kÖ¼÷æÍþµ×>ç¬ó³ÏÀ –H3Q5€ ©BàƒÇÄÆáä.@ $p³d!sý#ø~<<+"À¾xÓ ÀM›À0‡ÿêB™\€„Àt‘8K€@zŽB¦@F€˜&S `ËcbãP-`'æÓ€ø™{[”! ‘ eˆDh;¬ÏVŠEX0fKÄ9Ø-0IWfH°·ÀÎ ² 0Qˆ…){`È##x„™FòW<ñ+®ç*x™²<¹$9E[-qWW.(ÎI+6aaš@.Ây™24àóÌ ‘àƒóýxήÎÎ6޶_-ê¿ÿ"bbãþåÏ«p@át~Ñþ,/³€;€mþ¢%îh^  u÷‹f²@µ éÚWópø~<ß5°j>{‘-¨]cöK'XtÀâ÷ò»oÁÔ(€hƒáÏwÿï?ýG %€fI’q^D$.Tʳ?ÇD *°AôÁ,ÀÁÜÁ ü`6„B$ÄÂBB d€r`)¬‚B(†Í°*`/Ô@4ÀQh†“p.ÂU¸=púažÁ(¼ AÈa!ÚˆbŠX#Ž™…ø!ÁH‹$ ɈQ"K‘5H1RŠT UHò=r9‡\Fº‘;È2‚ü†¼G1”²Q=Ô µC¹¨7„F¢ Ðdt1š ›Ðr´=Œ6¡çЫhÚ>CÇ0Àè3Äl0.ÆÃB±8, “c˱"¬ «Æ°V¬»‰õcϱwEÀ 6wB aAHXLXNØH¨ $4Ú 7 „QÂ'"“¨K´&ºùÄb21‡XH,#Ö/{ˆCÄ7$‰C2'¹I±¤TÒÒFÒnR#é,©›4H#“ÉÚdk²9”, +È…ääÃä3ää!ò[ b@q¤øSâ(RÊjJåå4åe˜2AU£šRݨ¡T5ZB­¡¶R¯Q‡¨4uš9̓IK¥­¢•Óhh÷i¯ètºÝ•N—ÐWÒËéGè—èôw †ƒÇˆg(›gw¯˜L¦Ó‹ÇT071ë˜ç™™oUX*¶*|‘Ê •J•&•*/T©ª¦ªÞª UóUËT©^S}®FU3Sã© Ô–«UªPëSSg©;¨‡ªg¨oT?¤~Yý‰YÃLÃOC¤Q ±_ã¼Æ c³x,!k «†u5Ä&±ÍÙ|v*»˜ý»‹=ª©¡9C3J3W³Ró”f?ã˜qøœtN ç(§—ó~ŠÞï)â)¦4L¹1e\kª–—–X«H«Q«Gë½6®í§¦½E»YûAÇJ'\'GgÎçSÙSݧ §M=:õ®.ªk¥¡»Dw¿n§î˜ž¾^€žLo§Þy½çú}/ýTýmú§õG X³ $Û Î<Å5qo</ÇÛñQC]Ã@C¥a•a—á„‘¹Ñ<£ÕFFŒiÆ\ã$ãmÆmÆ£&&!&KMêMîšRM¹¦)¦;L;LÇÍÌÍ¢ÍÖ™5›=1×2ç›ç›×›ß·`ZxZ,¶¨¶¸eI²äZ¦Yî¶¼n…Z9Y¥XUZ]³F­­%Ö»­»§§¹N“N«žÖgðñ¶É¶©·°åØÛ®¶m¶}agbg·Å®Ã“}º}ý= ‡Ù«Z~s´r:V:ޚΜî?}Åô–é/gXÏÏØ3ã¶Ë)ÄiS›ÓGgg¹sƒóˆ‹‰K‚Ë.—>.›ÆÝȽäJtõq]ázÒõ›³›Âí¨Û¯î6îiî‡ÜŸÌ4Ÿ)žY3sÐÃÈCàQåÑ? Ÿ•0k߬~OCOgµç#/c/‘W­×°·¥wª÷aï>ö>rŸã>ã<7Þ2ÞY_Ì7À·È·ËOÃož_…ßC#ÿdÿzÿѧ€%g‰A[ûøz|!¿Ž?:Ûeö²ÙíAŒ ¹AA‚­‚åÁ­!hÈì­!÷ç˜Î‘Îi…P~èÖÐaæa‹Ã~ '…‡…W†?ŽpˆXÑ1—5wÑÜCsßDúD–DÞ›g1O9¯-J5*>ª.j<Ú7º4º?Æ.fYÌÕXXIlK9.*®6nl¾ßüíó‡ââ ã{˜/È]py¡ÎÂô…§©.,:–@LˆN8”ðA*¨Œ%òw%Ž yÂÂg"/Ñ6шØC\*NòH*Mz’쑼5y$Å3¥,幄'©¼L LÝ›:žšv m2=:½1ƒ’‘qBª!M“¶gêgæfvˬe…²þÅn‹·/•Ék³¬Y- ¶B¦èTZ(×*²geWf¿Í‰Ê9–«ž+Íí̳ÊÛ7œïŸÿíÂá’¶¥†KW-X潬j9²‰Š®Û—Ø(Üxå‡oÊ¿™Ü”´©«Ä¹dÏfÒféæÞ-ž[–ª—æ—n ÙÚ´ ßV´íõöEÛ/—Í(Û»ƒ¶C¹£¿<¸¼e§ÉÎÍ;?T¤TôTúT6îÒݵa×ønÑî{¼ö4ìÕÛ[¼÷ý>ɾÛUUMÕfÕeûIû³÷?®‰ªéø–ûm]­NmqíÇÒý#¶×¹ÔÕÒ=TRÖ+ëGǾþïw- 6 UœÆâ#pDyäé÷ ß÷ :ÚvŒ{¬áÓvg/jBšòšF›Sšû[b[ºOÌ>ÑÖêÞzüGÛœ499â?rýéü§CÏdÏ&žþ¢þË®/~øÕë×Îјѡ—ò—“¿m|¥ýêÀë¯ÛÆÂƾÉx31^ôVûíÁwÜwï£ßOä| (ÿhù±õSЧû“““ÿ˜óüc3-ÛgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF0IDATxÚ„•klS‡Ç÷á{¯íkÇyøÛ!‰mBÈBÉk)¡ƒDåÕ½ª–j*Ò´‡4¤©[µN[7MšTU¢Z‡F7º‡Tulë4@£bUi mµ‘ZR-IH @!v$!ñuüˆ“Øw Ý—nû}:Žþç÷áèÁ4Mþ¯¾úǵÀn  ˜ŽïÝûëü„¿yQ(LÀäÖÍQåå À“ Ûjkׯ¹:<<ÿòÁƒ—>úø£ãÛ·ï<3>~;)‰¯ÝáÀ"ËÜÿÛÄ õõk'Ýnw{$õ;‹ŠŒH$âWÅÝÒÜüh8\Ý$Ü“ù,¤»ùDý^“i‚išÅ‹‹‹»JKK÷>þØcõn»Ä,˜ìMk³K¹Æ>x¿}aaá¦,Ësv]Ϫš†$ŠŸlš&…Ba›Å"?×ÜÔô³-[¾P"ɆaJ¥)ä Øív¼>/N§ÓÅöèº#lµÚ¢(ŽÊ²üið§•išÈËt]}!(·{}>nǘ™ÁH$Éd28¼^/[Û;hllâÆë»c±‰/NNMÿ<·”}I¼g-ßUTõYŸÏ·?  i—/_¦ïÒ%&âqæçÉf3˜¦‰¦i¸\Ŭ©ªäÁ¶6šš?O$’° V Ãø… H»v=‚$I>¯÷ÏÑhD2 Nv¾ÍÉÎN†¯^annŽT*I*•&“N“N¥˜áƵk ô÷“ΤY_WG¹ß°u~~á\ww×-I’½¡±ñOë×­«8zä(gÏô°¼²Œ®;±( %%¥„#a¼~?‚$! ª¢H$¸:8Èb2ISK î²Rq1µXóq__§¬(–Í¡`›ËUĉ7Opñüyt]Çj³²v`ÇŽ”ûý¬æ ]¹Â¹3gHõtág•¸ Ò}òŠ5<±ûq¢‘H«UÕ¶ÉenÏCÁ@@˜™™¡÷Ü9N'ºÃ( øüålÞü¡P‚ 0rmÔk‡x~Ó& /ý œNÌt.^à_=ݤ·n!TÂïó6È~¿ßít:èï'›Íâr¹P5 UÓ˜˜œ$ŸÏÅ(:~”=_ú +¦ÉS'¸;®;hŽÖòåí;øÝ¯Ÿþ!~¿Ï)ëºMP,–——±ÛíXív4M£P(07;˘¦166Fþ½Nžw•‘ÔTWÀ·OÃî)ð½v`{Š×ÉtÂ*ä‘›ùtuÂêꂸ7º)ÔÔλi ¯Voüs°;ÛÂ=Š;n™úP‰×f܋̹»ˆ•„’oÿîvÞÅGÁº;ó9E½O:3|IE@vüÂüïàÙä^ëƒCkózµŽ¢VbêÒ&P]m}:ž¬ãQÔ¾c÷ò!ÐÒ7ˆ‡W"`ÃN[ŸŒÐvãxPç/@ØhüŽ#8W@ßaŸš¨ÓIõWV:q›X‚^ZF~tèÇoÆ…à #æMgâU(6t½:ö(®‚–ÿ€~¹ u0Ÿ®•<Ù_g¾HÌ7vþü^Ð Cº@I‚Bœ£AJ©çóà<¸¬ Ìɘ(¯ìEëæ¹ÝzDb¨FÖÁ«ïi`g J*(Ÿ-ý8稣½DÔJ£‘æ¾–±eÿy6¶Hÿ´àö¾YE¡rIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/cd.png000644 001750 001750 00000002437 12256006060 024505 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRójœ gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe< PLTEþþþÿÿÿTVqTVqTVq`c‚df‡IKXUW\GIUz{l„•xl_56:c|uquvywxowyŽegs‰‰ƒŽ …yd•“l|xlc’«vy•qlv‘’ƒ‚†™›©˜š ˆu˜’y}w}~€‹„Ž‘“²‚…Ÿ†ˆ‰Œª ¡šnej„„“nmŸÄޝޱZ]u˜š²¢¤¶~y†{{•¨ªÍ­­³ro}“•µmo•˜¹œžÊž ¼_akl‰’•¼®°Ñor•ˆ‰£ˆŠ±¡£×¢¥Ð£¥Ç£¥Ó©«Ôª«Ð¬®Ó°²Ö²³ÃÅÆÖÆÇÚ^a`c„ad†be‡cf‡dg‰fiŒgjiljm‘lo“nq–ps™qtšsvtvštwžuxŸz}¦|§}€ª~«€„®‡Š¶ˆ‹¸‰Œ¹ŠºŒ½Žµ¿Ž‘À’Á’•Æ”˜Æ”˜É•™Ê™œÎšÎšÐœŸÓŸ¢×¡£Ô¢¥Û£¥Û£¦Ý¥©à¦ªá©«Ù©¬ã©¬äª­Ôª­æ«­Û¬¯é­°ê®±ì¯²î±´ñ³·ô´·Ô¶¹÷·¹Ô·ºø¸ºÔ¹ºÔ¹¼òº»Ôº½ý»¾é¾Âþ¿ÃþÀÄþÃÅãÃÆþÄÆáÅÇþÆÈÞÆÈþÇÈãÇÉþÈÊþÉÍþÊÍÞÊÎþËÍÞËÍéËÏþÎÐøÎÐþÏÑþÐÒéÐÒþÑÓþÓÕþÕ×òÖ×ôÖ×þ×Úò×ÚþÙÚáÙÛþÚÛæÚÜþÛÜ÷ÛÝþÜÝãÜÝñÜÞéÜÞþÝÞýÞßûÞßþßàþàáþáãþãäþãåøäæþåçþçç÷çèþèéþéêýéêþêëþëíþíîñíîòíîþððþññþóôþôô÷ôôøôõþõõúõ÷þùùýùùþúúþûûþüüþýýþþþþBn«PtRNS &')//>BNYj€…†‘šž¥§²³´¼½ÄÄÅÆÆËÌÑØßàãäìïïòòòóöö÷øøùùúúûýýýþþþþþþþþþþþþþþhj=’©IDATmÁ½nÓPà÷;ç³}\'NâÄIE â/l J€˜â ¸ff.10±õØ‘è‚:1° 1“ª)¡¨ %6iHâŸsíÎó.µVó‰(öç“Ó爈浗ÝÀæ&O‡o~j€$ /ßßÙò ÏQóÁ¥Ib@ ÞÚí‘%Ö,ù!õsÔï\)µ!@ŽÕ÷ñÙ6 7n¾–F hS˜Áàt©œ4ãð9—­ä2-Üi’WTðcÎQm-ÂY%Ç“ye<¦º£ltÄÕŠ±ZêUµ;È¡ÝP±àDÛZcÊ_‹½w½ínÔˆámp>`À$áçC÷ý‹µ$ಯ3]×Áað¬Ú©—%Œþ{Rj!ý´ñèîö½“ºç›}ŸåŽr½Šçù½h¤Z….ìêxxìª0Ž3ë5ü5•d“öª¯Âzµ6Š` ùîÁZ,Ž­V‌.4¤#øë·³=4}$¬±B:Ó7#@Â,F½8pˆ…`Ù—Wk­[OÛfv²éÎá0ˆp!¬Å›Y~HOf8GDøŸåf¶%’°ä»IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/load_state.png000644 001750 001750 00000001350 12256006060 026227 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<zIDATxÚÔKhAðÿîÖ =EzðyÅžšƒxÔÅ(Z‘ú`ƒ ‚ /*(¨½HˆX|ÌJ=Ø‹APDDÅB‘ˆâA,Ö‹„ØlÚýü23aœÆ¤X–]†ßþùvvADh˜Ã_|úDx'Ð^¤é¢QŽ~óá¸B¯ò±çu»xøøF*ô‹Ï)–‹ã;^‰¹Ã'&eS…"…Û+ ˆ(b|ªdP×Máz¼¨V¸‚ïŸSÍácÃÑ:|¶Â( :¸X¡>Ä$|?£š3¾ö¡h 晪+§ÙnFuîô~Eõˆ!¢<¯ËÈæD>VçÅÿa¢@ž#Ê"ó3f­î«Î9-q /› O ‹æðùyiªx‚ŽàØw¿·*1nš¼wH4y»Òª9%@Œ·ñk N¯mÅž¢1|&Lâtiž›“8U›ÓüÏ6þ8Y‡óX"Æ >– 6‰IÌór€ ñõþ÷6^Ødájæz+vg… _è,€ Ë»µæ¤f¾·hã/¶z,‘þˆº.‰úóâèÜáæŒCã;Gm|lwÀ÷usäá™jÁ2—œÀ¸ÇÍ5>ÃøÖç6þv ÀŒ^ß9ÿ77¾?ûOèÊBÆÉ4÷j8ÔnÙøÔÆ+ÓñfÛÍεeuc1¸l¾îãÍÓÙr³'ÀÁàÙ´ÃK¡‚ãˇ-¿5Šéh¢Ødhe€}oÎMåŠò£ˆÅCÈc*l6¹Û`טnNªùøÄÕ¹¢þ_±í¥i¾tQåVãrpõÐqˆŽã ¥l.ø+•2Âø˜*ƒJ'¿¢QkQa}ŽÛIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/quit.png000644 001750 001750 00000002216 12256006060 025074 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;sBIT|dˆtEXtSoftwarewww.inkscape.org›î< IDATxÚ¥UMOY=Uõºšþ gºíf„¨\8ˆbâÔˆÑÄ…‰+—.\‘YºÝ ‰ºpáÖ„ù*ÑÙ˜WŠ:êø‘‘¾Ä†nú‹†®šsÕ³ñ$·oõÇ;ïÜóî}mü4,ëçödr@Y–¯Àf¥RžX^Ù¨T®*!êï,‹X^[\~˜†Ó4u¶¼lnÏÞs¤®ÎÞyôªJó…æwïF¢»~Þ"M ê÷Ææ&LÉ^øŸóïÞ!òú5„S)Ó´Ei}G.ò+´µÁfS)„ ò_¾ ¿¸ˆÂ§Oˆ®®>â`k+ÊccNåBªw±‘ÍÂÙØ€@…BØqêâû÷c;¢É¤tvâïgÏ`‘($öÑ c}]o œJ<•(g2°ø…c×å˨óþñø1>¿yƒ UÆh‡jlD‚¶9‚]‡a­½swî ¥@a ù”ÉôóçHtuá» 4iý6<Œ†¹94E£[^sc“¶™ã㘤—¨§zëèQl<|›<”©ùL±A[1?@$‚äáÃŒ^¿ŽÖ‰ Ä+¸¹¾éï‡Õׇ7ßÛoß"s÷.ÇŽa6؆ÖÄØ‚eYH> Áï÷î!FEŠ-èRaˆ‡’Ë”é²U˜z‘#Y¦Mç*ÖåÙxrŸ›™ ¼oŸjß•7¶þÁñ³½gÙÙY|Ës¹)9kk’‡4<©^yªýйP“ZÞÝ¡z{!XæÚö¹7p\ãy,?ʱ]çÏcµ»»fGùÕ+䦧±25…â‹°<ÅvO‚!˜æÚ¤GìT‰á—ž>ÅÂè(?\»†"[H+fÛe/]ÂÌÅ‹èáajRö»=4Á8×|Ͼf…º"§Úâ ò<Íß¼‰üˆpt;oÜÀôƒðNP““ž ìÝ ƒã>~‚kúÖ-üê&Ó4çp4êö±Ã,£Z:êë¸rÍgÏâÿð'û}ñömt³ ›¢„xie¿òzP~kÁÒ-ÞqªÌ' ÊXs3YvŽÔ?ôtçû÷è ‡…°Žç±ª8N¹X*ÙñH¤ÚbµŠØË—°xp%K¬(Ïvªg¤8ÍJÁð‘š^þÌ;Z8ÕJ©4²É : ¡Ðÿ¶åÕMª¿áey¿Ì+`ji Â)V\ãŒÇÒ騸 «œ-—G„ó_DËð,’äTêIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/video.png000644 001750 001750 00000002140 12256006060 025214 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;'IDATxÚ•UMlE~»;^ÿ§vâD%± ’^«$¤Q¤^PUEò)7pˆT¤ Á¥ Bô‚8#.(R¹P*$ÔJ5¢!Km•¦X!IÜØ›†Ä?qb¯íÝå½]_ éifwÞûÞ÷¾y+Åã 'c±é¹@À3Æ9<ó°,«³6MÓØØØú~këÚ×0?¿q³T2Lëç¼Ë ñDâ°9ˆ†ŽÍ„Êüñ'›@Œ"Ìl$IOÍZÓtxøp.\˜‘w¯,û&€,Óæúú#8ûæ È ÀêRþ)D™ÿ;87 \NB:=““AŠ“™¢8Ìh¾X×ãSðþG“ðí7ƒ¼ù`â§¢6F¥Â)0Æœ@]7appB!V–ëZ}¥RFÄ‘4Èä¿%*›»ìµ`l«j ¸òùÈg’¨Û>½$ ²vÉ\OID"$Õ„F£ͦ†°ìH µý»°xí3,é8ZÁãðÊkï×ë·ƒ‹ùŸà÷ß~ô'M…!P vÀïï£NÉÑ_!):¥bÃì5¨Õ$@.PH.—Û+óRoŸW }5dy@3SĸK V1‹ÜîcölqrÛ÷@Q›ç½e¢ÙÌpÏ…‰½ô,¤"ÖÝRàÙUõ!°Ÿœ „ÉÚ“å“S‘”ªÅ=œ|™Ë%îAà 0šÚW#2mУ}$ÆDFHá· yiƒ¹É‘ôÀĺ]ª.zÉBÌÉÇN€£[ r=‡è&ö8{¨(—€¨Ñ[\×пN1øîc<ÐÓãÁDÜn6ÍCÌâ»té$:Ĩ ´ Õ\ ãôLŒÅ>1kûñÎ1Ó4zO”¹Î‰_ ¼ún(ÄüÔ¥Ú@úyé¦wÙÐBRÉŽK#áÚÊfws;;?ß‘úúF{Nœ8s¦·wø%BÀã¼ujbâ­7¦§Ÿ1Æ ïg2{f*µô \þõ¶ßßÿ'²T0Žolü’Êdî,‹‹O3›šz;tî܇óçÏ¿8«ªÐ‡ E‰Dàºkäó:ž °ÊúÚZý«W¼¼¸8{¿‰Ûd?ºÊããïÌ]¼øò\0(©¤i¡À¡Z%¶ÎÑ!æÅ¢ ûû:6,¢QÙ72Œ¹\g?½ukhµRÉ¥Erv¤§ò'OÖ÷ÖÖÆccªJåÒu÷ùdÒÍ‘“B¿<Ùn‘t¨îß/šVîªK ]¯[››×Sù|³šË=7X©°@£!«†aQp§Óá3šÍj<™Ü--,$—oÜøàr±˜¾K•CGÛîA7ÏžŽözû'‹¼€Á ÀœQo¶Z¹¿5mw{o/wïñ㥕jõ¯,)uç_÷Ó»þrâßIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/no_fullscreen.png000644 001750 001750 00000001403 12256006060 026745 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<•IDATxÚµ•=hAÇÿ;»Ù‹I¼|©1*b! AÐ&…‚ ±,¢ˆ h+v–Z(HJ# ŠMÄ "(v‚URÅ„„|Ç|Ýî|=gw†Û]½DòàÏ{sóæÇ{ïææ<"ÂVXpb`r°±Þï/ù R(H®DêíZlæ]žZ¯@óå§A=u¬1âˆb£È©T"£Äçc'ÍNF †Ô3°ü­àÏný7÷Ng14Yèà Ðô$¢;½éz½¯û/PBt¨ÉÂÎMNÃtÁU0ràðòhfzfëç»@s«›@mµ‰ÉÃeè¹5ÐÔøÍWPZ'9ÅQÔßÿÌþ͛ĥh3hñ&,qÈ#¹=ªVlÛ?³´` ‹º"Yu­¦€¼±žöÂ~¯¸áíjXšìTøÒðm±Æý9Š¿CkÄNÅÙ‚Â(./€f+€$4Œ®×:XÙÑ608 Ö>Ne·¢0Š—£ð:ÊðZK0æ’6®4;$P›ë>gÈ%ð}`íðv6-av‹¿2ÐÂk¯ÇökGì9°ó_ž{»àíéDøz lGc ( U{-%¬½‡·«l:mEÛãs *Xk÷0_1Õvÿý&Ýô‡üÖ>róµZK×K¿š‚vcöúHÃò´"!íãW¡c•Àtñ-p±ÛÓÕ¶Éù‰Kƒ J ι}—{î~§î6J((©¬2‹×Ör´ÒØÈF&*زÿ<†-²_çô¥êö·IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/button_ok.png000644 001750 001750 00000001374 12256006060 026122 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRójœ gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<kPLTEÿÿÿ[]dŠ‘—v…Œ’™Ÿªip€†Œ“™ ¥b[v|[{h[€‚‡‰”•š¡¬[‚¡\b•nœ\hn{ˆŽ•›¢¯[mnvw|‚ƒˆ‰‹š›¡©¸¹»ÀÁÇÈÍÑÖÚßãåìîö‰Ï¢×ö•æþœ ¢ ž Ä£©ªÓ(ø()Ç)+÷+.¶.6Õ6@Ë@PñP}Ú AtRNS 0000000000PP```ppŸŸŸŸŸŸŸŸŸŸŸ¯¿¿¿ÏÏÏßßïïïïïïïïïïïëG™õÊIDATÓc`Ä HÆøu $Ðù´Sì%ììÄyµ²}MÅ4ómEá‚<Y奯ì@–tA™D[=½¸"OŸ Äfõ,)´2¸T“s‹JrÔ õR3“¬„8•½‚Ó23U`æ)ø'$„ZºÄ'$D{ÈÁ­áp‹‹‹‹ŒÁn²Hn2 ƒ Ad§*úE€¹Š·¤\"€ÀۈͻfAa!®†ÌèA£ëè¬Ä„b2NNJX’ÉQCŒ‘…•&È_^&£IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/backup_ram.png000644 001750 001750 00000002440 12256006060 026215 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;sBIT|dˆtEXtTitleMade with SodipodiŸô/'tEXtAuthorUnknownŒ! òzTXtDescriptionxœKT(ÈL.)-J_~êô ,oIDATxœÍÐoluÇñÏýî®wý¿v-e+ÝÖ­( LnÑÈ$ø… £q•øÀ(ÿ>X|8ÿÌ‚ƒQN!²H6µ’Œ ÁÂþuŒáغ5nëÖ^{íÝïzç#ÐeˆB|àûù÷•o> ܲåù¯þ-|-ô>mOAbít]¼€«}XBËa5³(š¿Ë…)ÜÇL(ñØø¹htê™mÛ6NÞ &šF“ ¬óMa`l__’1hYñÞ' Äº!]‚ÄÞ‹“Ö»®,øPÕ²âî;›¹%,I¹1]×aMØÃò9xŽ FËA°X1òã7à90ñ¸ì-G¢´ÂW^^|d÷îÃõ³ë××M³,SçvÛ˜<Íc‘ á µ!ov»`)6ÁÄB¿†¢Šù0 Äå€f³~C©]Y»nnKë¡ã³6€£GNVV–9AÀèè†Æè t«VØÍ6‘€Èãè9²OÖ¿»……M$à/Ã:S‡‡'¿|ã­º×fLÙ¬Ú¦iy@i©Å^'ª¤>âéxˆ\DÜ ±3Ì…Ø»7¹ð;—)®DÙ~èfYšo×KUe+©!Báø¾ÀýøñóIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/pause.png000644 001750 001750 00000002205 12256006060 025225 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs ?@"ÈtIMEÓ %&KXÛ9IDATxÚ•ÍoEÆŸ™ÝY{m¯ãæ£Á&ŠPD6EÊ !!**RUÄ-¨8pàŠ”¿zˆÄ…—àTD9ʇH¥&UJÒ@;þØuìµ½;3ì8‰©í¦Bü¤G£{Ÿ{Þ÷Y‚G~orIH$¹b 'ÿËòË-œy„é<€K€œÁ8‹B!šuH~À:þµ´üüæòò2ï7î7œÈÐØ[±h$ñØpœŽ…1=\…É86K1lç Q²]WøîM&?yV~ùS°ß1î1ÈUñ…¹'M¼zÞÂì9‰³ (d rÕÇÖž‡ëÙ~È•ÑrÛ!Qúèyíú‰¹ÖsÒOcñø+¯Ïw^4ñTRCˆið8ÐòeGj“Ḏ٠†Ñ3 wórØiç‹r2»1w³Ù¬ì'Ï_Y‚a]¾ðB‚¼–a0m›4½µD0ò%¤”d°"Œü¾Ë‡-ÉÆ°v#“ÉxÚÑiWç‰Zzn*^H3P ´<ïs¶î7ÚJõ¦~z5˜7ÕZû´ Ob0FQkéØ)x©?·>ŠÛwõô•)/Å¢áxzÊvMBñþIœðíjåÔ5.$¦“·6CV©»˜÷R?ëFÍÅã4öáÔ(Nîù]ó[Û (r{ 8nUĨ@eÆ!ããÊ8 BLJ-·%Ú’©ÃÜvq³{Ó•(»Nmt ZšÑ6Ž€†¢æ¡R 0t‚‡á¸¢kþWÕ‚¡qœ@AàeKï<Ô`Ø·U)–I {ÎüÕÆM¢›× üÚÈ“æTW½¯Út¿f¢ÆÃ•q/%×ÄirìÌ "ý¦®À=».&91ñÐ5€8t›€äy*j{T¥” 4K‚iÆú/DGXç0‘VQèhÜIÈ?ïS}*¥xÓ¶/c$RÃÙc=Èd¢ÚV/ÉØÑwÕ³¢Q®…ùþí D°¸¸¨­j‹ŸÅ­Ø»³SgÒÉQ½Éž¨’¡ëÂT{ç6˨V¾Nã‹lkP¡ñDúí­¦‡§…ài$"1y$½{ ÿÎ <ÜÙqD±d¯ ñ« ò÷€j'„.¤Y±(ÇWlWŸqê~* Zº #U×}bp`{ÈmÙØ+8+#þÚÒ8ÍþŠ`YÇXz!)ejÍvµHÁöRÕºo„uŸDM:¥íæ‘’£X®á·íºøc§zX©8ߨ“›æS¯/èÿo šÃ›/ù4vQ€ÍPJ’–©™¨º¼!¸È«ÛW5M¿ûÀ~ Jÿ¤ß\­yžJ¨@Q½OKu”*~U§ª¤Ôí«½¹iW¿ÿ¥Ö$à€¦¨IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/log.png000644 001750 001750 00000001012 12256006060 024664 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs  šœtIMEÕŽ{)×IDATxÚÕ•»KAƿݻ3ñ|áã6jm'ØÚY‰½)B {+Á¿À&6‚ ¶ZÛha'Dˆø@E£¹Çºs AнÂá|;7¿ýn8æ„ÖI„DB!Æ×÷rÙ‰áœG ôß „P«Gà·~¯ëxMÒãÖXÈè•Ùó€v"2Ðê³BÊ“Ø?»ÇÆòÒŽ‹Íâè¦â׊§w)? šN_ØEC ||kNùA„.Wb~ºRÏoÒfƒs¯>…51²zð6™J¹na:D€Ù¤šÚ÷ HòÛH›:Ï#äVh“—¸~¨ÖèÃ.Íe°¶8±{TÂöaÄt{íï¶ìõÀL"%¾ …BGÐ|>f’Cª%Ñy0“œ3“¤ÐÖ3“ÔïŽÛÞX5z,¬;f¦t”‚í`¦”R[3“H&ðKî±²ßcfR_ÚqXì_Y†‘3é¼t{^Âìãë4tcL¢elâç1ú­J@Äsç²\¹øÿ¼O  Éò´6/IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/play.png000644 001750 001750 00000002352 12256006060 025060 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRójœ pHYs ?@"ÈtIMEÓ $*[õ¦S|PLTEeeeeeeeeeeee2hÌ2iÑ7e»eeeeeeeeeeeeOb…eee2hÌ2iÐ2gÉeee2iÑ2iÑ5fÀVby9e¶eee9e·2hÌ2iÎ2hËAc£2iÍ2iÑ8e¸2hÌ2hÍ7f¾3fÇFc˜9f¹8e¸Ic•Af©Fc™De£Bf©Cf§>c©2hÌ2iÑ2hË2iÒ2iÐ2hÊ2fÇ4gÄ2fÇ6fÀ3eÂ2iÒ2gÊ3hË2hÌ2iÐ4hÊ2hË4gÆ4gÇ2gÉ2fÈ2hË2hÊ2iÎ2jÐ2iÎ2hÌ4iÊ2gË4iÊ2kÕ2l×2jÓ4lÓArÍ3lÕ5lÒ=pÏ5kÑ=pÏ4kÓ7sâ>wà>wá?wÝFzÙFzÜN~ÖW„×H{Ú2oÞ2oà6yõ7zô8qÙ9zñ9~ü:ý;ý;€ÿCMOQST[`be’“˜™¡¡¢ª°¶¹½¿¿¿ÁÊÊÌÐÖÙÝÝÞååææèíîîîïððòòôýýýýýýýýþÐÀ•nbKGDˆH‡IDATÁÍkÓ`Àñï“üÒô=s¨e}‘A™R°àAÁ‹ zéÝ?ÎËŽú侃 Þ< „ ´&‚m§Ý²¦Ióö<~>Ô-×»4€áA·Y£H£í¢€Ãá =í;«ÕEþ`” À½É“ÙSϱè—/¯¾6íŸ%‡“ñ»a]}?«;;þ¤òP ŒNߤdzq¢±Œ÷Þ®Sa0~Ö×¹¡1õ_¡ íÙò6ç ñXæþ @÷žH£;©WÀÜnnÇÍNOZÍ^QFÌýÑ"D¹mq]{g9sÿ/¨š-ÜÔ\“mN˜°1()Ѝ0˜DÀGPv©e—®îÎ lg³Ï%âG HÎŒ‡bG’m÷vÇ‚pÞWz[%{aÑ9Ê_Üš…)‚p] Õ¯úÍ|«£0æsÅ™ÀæÊþñðH]6s½ þ¬3- ÿ]&ãÖkOa¢ó4ˆÖi‰ÕúîÎ ë.E'ë8+AtH¯m+Gû2ÓÀ,¯nÆ@bçIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/button_cancel.png000644 001750 001750 00000001220 12256006060 026724 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRójœ gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<üPLTEÿÿÿÄÊÌÏÑéDÄÊÒéCÄÅÆÆÈ ÊÒÕ#Ø)Û/à5ä<ÄÌÍÑèCÄÅÆÇ ÉÊÑÓ!Ö&Ú,Þ2â8æ?éé êê/ëGìLíZîCîKî_ïr-ð}5ñe%ñl*ò‡=óEõšMöPþþ þþ)þ5 þ@þKþVþ`%þi,þr4þ{<þ„Eþ‹Mþ“Vþ›_þ¡fþ¨pþ®xþ´€þº‰þÀ‘þÆšþË¢2­¶¶*tRNS 000000000000ÏÏÏÏÏïïïïïïïïïïïïïÔðÀùäIDATxÚmÐQkÂ0ð+‡‚°ÔM÷¶ïÿѵE­XÛ´·ûkb_v‡ü8ÈWà¿8øp·7¬cÒïM]%-}µMI†¤Ÿ#CK¨^úe•ÉÁmHîuŽYWËÎáV›îF’O­ÚænOV‚ݳŸ´Ää¨þ‚>«X9X_˜43ŽäÂôd:3øf¶eÞÌGòœ9í=ÑsKò’¸L{·,åWñ¬ )õ$[ñ:k‰G ćCìÆ¤8Ë¥C¯vH¦½¨Ãd2®L-Wê:½–ݼ#¥½T\SW ça€? ñÓá$ð%4IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/open_file.png000644 001750 001750 00000002121 12256006060 026045 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l;gAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe<ãIDATxÚµ•]ˆTeÆsæ¬ÎêÎîºæW¦;i›™a±¢ID]dATT’iYAAˆux¡uµQÑE.­Ù.Êš©Å´ÉŽëê:»3{fgæ|½_½Înk+ZDøÀÃóœ÷ÀÿûòNÂÃõÃuR‚ÿ¨—^ëÎhiÔMsV–ËbÉûï¬õþ÷Ä/¿Þý|KSªwëãmë·>ÖÖ¬´>ÀµdŒ¹¦ÿÒŽ7{VnßÝÓÛÙ5dªUcòyaFG•ù`Ö<ùì÷›¯z­Oüj6¯Ÿ‹Ö+RKøô›A/øjŬ]oÝ•¹©yçý÷µÒ”®CJƒª–©T’WÞêöJcÑ’OöÝëM¯ÛvÆìÙ¹¤ µŒ öäŽdÙÒ¾ˆ;VµXÄ1(5 ®íê »ß;ÑñíÉÄ œXßÏ„Ü+¯›Œá|Ï iòês+˜Q…qUimh[Ú̺U-›µ.|Ôy‚Ë`¥/ƒ½³ƒÇúÙÔž!³¸žÒhE²·ã"ƒ3ÉG÷°âÐJ´®­¹ŸflhŒ»V/øW¨WÕlÜ‘¥ýî¹yQC•Y6?S®ª…ÄÛûîÑ´˜I9 ç²}ÿIÖ¬ÖJ+”ÒhiÐÊ 'ú×?yxC+Óæ4s(G <=?÷–Ò}y kÊQ|~p˜Ì­sX¾ÐeÖ 3ðµ!ˆ/üÐà MÂSKçÕ¹ôœ‡&(äòGúº’!aÞ˜>x8Oöã6 yAÓlÝÃe±Î]€Ê¨í‘uŠžbd8Â/T¸p*w¾ê&H«e8¦Ë•’š¾ë Xw{#õ)¨›[GovŒ9‹Éžƒ¾ K†GGlZû^@àU-pðÉã*Ôi²ô»ÚuÙ²¶…ª†‹äF~;®Ð&Y;†8†0ÖDFÚNë#|3g‚ŠëdÜŒÑû­4cUøå´Ç#í d‹pÎ:ŠeM©baÂX¨… ðJ‚¡!åNÇIMgŠ’² EÇÄ=†ƒGÊܳ¼‰¡ 4¸° eSAã—R1Fº Z´¤Q„¤çÅÜÒœDGÚ‘ ð%Ã=;7 òôo8^Keèú±À¶‡æq[šqÍ! M‡(’ÖŠ8v¬]».âRÖÛç:ëŸ.Íþð¬ù’ 9U_ÒŸ+óàš©…ã€ë:$“ Û‰îLv›Øw“ý‡Shór ç«<ýh+W*™·Á µ$ŠAß £ˆÐv!4‘ôýîù“`œÇŠÜùL £/A€Zj”²)%Jjki»B …–z<µ¢êðÍ$àºýLÿEzù Ì$uIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/screenshot.png000644 001750 001750 00000002636 12256006060 026275 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l; pHYs  ÒÝ~ütIMEÓ JŒbKGDÿÿÿ ½§“+IDATµÁKL›ðÿ÷è‹¶_郖A¡PÆ«<2‚ÝÜftscΙŋY4ž<ÍÓâA¯f1ØÉdzp'ã„eF¢ÌÌm 6Æ …µú„–>èãë÷ã¸ÍD—øûÿA<ÞÁ 9ô6žòÅG &†ZšûŽª ØEâ_H$êè\îP‡FÓ{ÙlnýøâÅ6ö¸ñÈSo¶:s‚¢É“/Á.ÿ`c£Ž0Š™ÌÖJ¦HÒz„ °‰nwdÆhÄ׃ƒ¿KJ"ª1+?íNñä¥Ãþ«n/çÂ.Ïpá(ÇÔe±¼ðEY^!} @à@Jòðaç‡W®ô¡ººÜ$çý-4aï¾;’œºvýÞUA]4öèï·Ê;;›+ôzý;eeŽóe,(JiH’¢`/˜¡×—;ŽW]&ø ZHOÊS\+JöwNèÍ~ëãøÇÚk[[ÏÚÞÖhÊÂHŠ‚ ŒÀ€%4±8€4Ó!µ6? W@†_ÂgpÔš­;Ý÷šåÁÄø6vÑÁà‰ò|ž9k4Ö¾O’5 (³œ ©œ þ˜á @4 D‒:öÇPÄŒ`=Åw³§±Í•ÂY™¨¬¨°9¸°‹^[Ó|rð`ÏyI¢ÉlF _‰þY‚ Í‚€H2 (5 8BÝÇNnŸ{N ¨¨ƒôbÁ#S“¤¼ÀvÑ++©ßjkÓgu:†RàH.I-@âï$HP%BÐ¥}¸®C" í}÷¤¦¼çºÊÛišVñ<*¬wt˜«‹‹Ë¼'ä°C¥Ñ ²hb$43šµê$Ù0êý øjzCË„} ˆmÇc#ˆ'Öp´Í¦½}k:g)ŸÍªT;ÛNgÝšV*xÖ‚*­ õ éø,ˆì*âÉ Är3(ßHaÐ{?ÆÁÆ£°7*7Íc3;†Dd¯w9 “J;:2:z‡Â®±±˜¯·×ØPUemÌeu„œ6a3ÂÔÖîư’XÄþt 7æ§1¼v5|^ny Þ¸OP u¾&Ù1T”´'»ÊÂß^¦ñÑí|ÙÒqÊd‰ÒH&Ž{áy¸“^DÙ(¬ëØÎmc"X [ö4ŒÚ^¬z°I•…wcŸA…Qw–d áèéé6Sx,ˆ†ÛÛ´ú²2[g8ULý´B ¢ÃÖÃ,:±C¼ã[°›» S V@­-@Cƒ ÝmzÔÚ•˜Œ(+Eâ] …×Jl…²ŸRÈ:ufy=^µ7¡Ý\ •Ê ƒÆ«Q­–ÏÐà* ©Ô FQ0J"­æqk,ƒÃZ%—ô¹(ìáñf¶uŒÉQÛÙ=4‘ñ&€–€-‰ŒŽ„ & ÏK°ÄyÔˆöÉD8,¢Ô×"°zY"9>6¾Fc–…8>»ZSç9Yb¨m²èå(QNBVQ6‰€…!¡3‰—Ñ@|+ìߘõ//-»çFFZ\\œ§ñ„ïXò³µµÀã9‘ 5„åÈ‹ä¹}fØð? ñœþDƒL Ö±°IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/transfert.png000644 001750 001750 00000002151 12256006060 026120 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRójœ pHYs  šœgAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅF4PLTEÿÿÿTT¢unÀunÁ…—Ýqk¼yxÅTT¢unÀz‰Ë‰œälg¸TT¢WW¥][«c`°eb²y‡ÈŠåu‚Ã{‹Îkf·‹æ][ªtmÀTT¢WV¤ZY¨a_®ab«ab¬bc«cc«ff±hdµij·pj»_]¬ŸâTT¢hd´}ŒÏUU£xs¾›¬ä__¨{uÒ׆ƒÃ‡—ÚŒœÜ__¨hi®ig²jh´khµli¶mj·nj¸tt³tt´wt¾xw¸z{Á|z¼}Ã~~Ä{Æ€~Á€ǃ†Æ…ˆÅˆˆÂˆŠÊˆ‹Åˆ‘ш’ÖŠŠÎŒÅŽÎÅ””Ř•Ó˜ªæ™™Ñš–Ñœ Úž©âŸ£Ò¡žÖ£¤Ñ¦ªà¨¨ÕªªÑª®á®±ä¯±á³µá³¹é´´Ö¶·ç·¸â¹¿î¼¼â¼Âñ¿ÄîÁÆñÆÅäÆÊîÆÊñÊÎñÍÐîÍÖÿÎ×ÿÏÒðÑÙÿÑÚÿÒÚÿÓÚÿÔÔèÔÖðÔÛÿÔÜÿÕÝÿÖÝÿÖÞÿ×Þÿ×ßÿØßÿÙÜóÙàÿÚáÿÛáÿÜâÿÝãÿÞâûÞäÿßßîßäÿßåÿàæÿáæÿâãõâçÿâèÿãèÿäéÿåêÿæêÿæëÿççóçëÿèìÿééóéíÿêìúêîÿëêôëîÿëïÿìïÿíîúíðÿíñÿîñÿïðùïðûïòÿðóÿñóÿòóùòôÿóõÿôôùôöÿõ÷ÿöøÿ÷øÿøùÿøúÿùúÿúûÿûüÿüüÿýýÿþþÿÿÿÿ93ÝÄ4tRNSWWpppp‡œœœœœœœ¯¯ÀÀÎÎÛÛÛÛÛÛÛÛÛÛÛÛææïïïöûûþþþþþþ×V S_IDAT•c`d0bf`dd`62X$]TQ…ÅuÙÙ8õS[”Q„¹}ýÜ‚;zÛêd‘…4#¢ΘÐÞ\í%ƒ,¬¶fñÌÉ=-5%î¼HÂ:Ë×®˜;hJY¡“ \X,gÛ†• €¦4VeÚ¹ À„ woß4eRwK¶uÆG~¨°óÒ›Á¦äY¥-^³Èž"ìP:dÊ´|ˤ WnXfË VJoY 4¥Ø"±oÊÜë·¬¶á»D>kÒªµ•æñ-=“g/^³iû: °0ƒ\î‚~³˜êæÎ S¶í‚ 3(ƕ׷õMš²uL˜AÑ3¹¤¤¶hÊ’5Óƒxa Òîu@S&Îh2µÑf€ 3Hyô¶5´õ͈®˜§ÂŠfòžÝ×:cG\d¨:’0ƒ”ÿšõ»»l$xDC…¤w¦èq00p£3H™H1)-FFbJwÌËüSIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/cheat_search.png000644 001750 001750 00000002567 12256006060 026534 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l; pHYs  šœgAMAØëõª cHRMm˜sŽòqƒlƒ»ÔÈ1켞ˆÅ?íIDATxÚ¼•[hUÆ¿™3÷ÝÙûîìf7ݶ¡1MÒlm¥R¬¡ (¡o}PðEðEŸë›"QñÁÖAj‹(¥ÕbKÒ¤’Æ&&³›ÝMwvgosÙ9¾$ ö‚Ozà¼üáüΟïÎ÷1”Rüo‹a N'„ð ªê€,ËqE1822r"Nïƒ<©‹ÅöNLL|ÉdNÚ¶Ý`Y¶299ùv$µ,ë^.—{mddäMUUG øé8Në±æžš}Áó<:tñx¼¡ëz¨Óé`||Ü\^^/..‚‚;wî|¥ëúÙGÜ£UUóGŽ™œšš‚ëº$ ›››¡v»V«…ÍÍͰ¦i…Bày‚ <_©T®ë¶ŸÚq.—;pîܹï5M2 Åb¶m£Ùl¢Z­‚RŠt: I’ ’É$4Mƒ®ë·/\¸ðJµZ]}LcŽã„3gÎü$IÒèƒP,aZ­šÍ&–——õV«åH’p]NÍfµZ ±X,‰Dö-,,\|LŠd2y ÀÁB¡€F£Ñ?Øn·á8šÍæ-BH`mmmZ’$„B!X–×u¡(ÊÉh4z V«Ýý8NÏÖëu”J¥>̶mX–Û¶ašæ<˲½^²,C–eÔëuì\’J¥H"‘˜Þ³@‘†™.‹Ø³gfggÁó<666P.—az½Þu×u«Õj(•JÐuÑh‡F0D±X„¢(o0 Ã÷Á<Ï¿$Šâ3étªªBX–J)E¥ô>€.û¾_ èv»pÁ`ªªb``,ËNBÆú`ß÷jšÏóà8VWWa8ŽC*•ºàe&€¿†9Ïç× \.Ã4Mpß÷‘ÍfYJéT<88¨Çb1ˆ’„T&§Û…išp]¾ï/¸·3 –eoxžWéõz¨×ë`9™l„ãǽp8¼„ÈÌ©ÓKáÈ`¹ £´²Ó0Ìï†cÛ0ëõÇq¾í¿OBxß÷ߊ§´Ôð謇[¨l¬¡ Ú®<«ˆ‚¿²²ò—H&÷VªÕ)öêxU•©h¹]ü\(ÁÍÁŠ'ÆæJ©»ý£bÙ]y-L=ÌZ%À Îîp”RvG ϧl´Û!dz **»½9dŸÁÞ0a\§å$Ž]ÿn‹ÁŒ˜ÆäÌ~À±.]Y@D¥Vxð[:+·¾±ˆúú©i]€ã0¸üËu\5š”<$^¯»Î„bG¯èíÝë`Ær¸m¸øpn s¶BKý]ËêÌï€)¥– Šê]<¾´^F;± ‹Ÿ^ùŸÜ*¢a9_6ÍúçÌvbd2ÙÜ{’˜¥¾Ÿ"ç ¢p¯R,|ôpkë‹'X¶ÍåÞ Åg»¶½Ûó=ʰìFˬ])—Ïh=jôyY–(¥]Û¶ÿPû—K˲œ§”RÛ¶7löýø¿ Ó¿0O>»;ÛIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/actions/save_state.png000644 001750 001750 00000001432 12256006060 026247 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRÄ´l; pHYs  ÒÝ~ütIMEÓ7ÝS{"bKGDÿÿÿ ½§“§IDATxÚÕ”=kA†ßݽ«7‰æ«!……b!úD,Dñhm%¶é´Jeá?°ÖFQÐÆh—B¢– !J ÉÅ›½»w?ffwÆs&3â%›¤ÎÂáœYæ<çÝwg'î |ñþׇN´¤Tƒ¸"Í TB¡©y¡¦,¥²Y©šÂeŠß=ôv“·i2|ôiùY¼Ž3ôâå…ižµ0?‹'Ï_c–˜<;†§ï©îÝò*~¬®Ýóò3/Œ€©+*‰À=ÄÊ·uÌLŽÓÀÇ]IZ@SÖº¹Ãë°tûàÙ© \º0‡ós3Ç‚¥¬1 IœÄA°Tû o]¿‚ùsS˜>s$TkC–e$)û,hª$ÅÆÔØK ܽyjCôú×~í3hï^†Ý>вâu›bkù,ðõûM¯9íjmתiìÞªHò¼DŸàU%SÌV(”•;RÊfç?Á„²€,¢,*ö”‚ú¤¤ûš¦¬œZkŒb¥<Àª Œ†®Ð4ˆ(ºq„öÄa€8 ±[–<„Àú0Å5º§cܾqÕ(KI!X+æ!.+[ûxñê#z¥8Úc¬” fиm€’ëv+¸™­¨©asó¶·SnvŸ0ƒmööPÞ‡w»ˆJ\´ƒ¥SVSsLLtjæÏÝPQdP³×¡¡ûÆî4¤SLöµ+æê+$IáU9…möÙÿ„â8pVÈvÅ|.ùL­mƒÖ m< æ¬uíAnlW\V¾0hc•>bÚ†«ö/Ô×JJ Åëp^!O2j`+þÍ{+üËÉ~Ož¥EÝvŽû½dqë×ÎR%Ö~®Óãsͪÿ»ç²«·Ö{(JVŒEœØë/àÿE¯5ÚIEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/yabause.rc000755 001750 001750 00000000054 12256006101 023720 0ustar00guillaumeguillaume000000 000000 IDI_ICON1 ICON DISCARDABLE "../../logo.ico" yabause-0.9.13.1/src/qt/resources/icons/made.png000644 001750 001750 00000024520 12256006101 023356 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDRx(ºˆ©r pHYs  šœ MiCCPPhotoshop ICC profilexÚSwX“÷>ß÷eVBØð±—l"#¬ÈY¢’a„@Å…ˆ VœHUÄ‚Õ Hˆâ (¸gAŠˆZ‹U\8îܧµ}zïííû×û¼çœçüÎyÏ€&‘æ¢j9R…<:ØOHÄɽ€Hà æËÂgÅðyx~t°?ü¯opÕ.$ÇáÿƒºP&W ‘à"ç RÈ.TÈȰS³d ”ly|B"ª ìôI>Ø©“ÜØ¢©™(G$@»`UR,À ¬@".À®€Y¶2G€½vŽX@`€™B,Ì 8CÍ L 0Ò¿à©_p…¸HÀ˕͗KÒ3¸•Ðwòðàâ!âÂl±Ba)f ä"œ—›#HçLÎ ùÑÁþ8?çæäáæfçlïôÅ¢þkðo">!ñßþ¼ŒNÏïÚ_ååÖpǰu¿k©[ÚVhßù]3Û  Z Ðzù‹y8ü@ž¡PÈ< í%b¡½0ã‹>ÿ3áoà‹~öü@þÛzðqš@™­À£ƒýqanv®RŽçËB1n÷ç#þÇ…ýŽ)Ñâ4±\,ŠñX‰¸P"MÇy¹R‘D!É•âé2ñ–ý “w ¬†OÀN¶µËlÀ~î‹XÒv@~ó-Œ ‘g42y÷“¿ù@+Í—¤ã¼è\¨”LÆD *°A Á¬ÀœÁ¼ÀaD@ $À<Bä€ ¡–ATÀ:ص° šá´Á18 çà\ëp`žÂ¼† AÈa!:ˆbŽØ"ΙŽ"aH4’€¤ éˆQ"ÅÈr¤©Bj‘]H#ò-r9\@úÛÈ 2ŠüмG1”²QÔu@¹¨ŠÆ sÑt4]€–¢kÑ´=€¶¢§ÑKèut}ŠŽc€Ñ1fŒÙa\Œ‡E`‰X&ÇcåX5V5cX7vÀžaï$‹€ì^„Âl‚GXLXC¨%ì#´ºW ƒ„1Â'"“¨O´%zùÄxb:±XF¬&î!!ž%^'_“H$É’äN !%2I IkHÛH-¤S¤>ÒiœL&ëmÉÞä²€¬ —‘·O’ûÉÃä·:ňâL ¢$R¤”J5e?奟2B™ ªQÍ©žÔªˆ:ŸZIm vP/S‡©4uš%Í›Cˤ-£ÕКigi÷h/étº ݃E—ЗÒkèéçéƒôw † ƒÇHb(k{§·/™L¦Ó—™ÈT0×2™g˜˜oUX*ö*|‘Ê•:•V•~•çªTUsU?Õyª T«U«^V}¦FU³Pã© Ô«Õ©U»©6®ÎRwRPÏQ_£¾_ý‚úc ²†…F †H£Tc·Æ!Æ2eñXBÖrVë,k˜Mb[²ùìLvûv/{LSCsªf¬f‘fæqÍƱàð9ÙœJÎ!Î Î{--?-±Öj­f­~­7ÚzÚ¾ÚbírííëÚïup@,õ:m:÷u º6ºQº…ºÛuÏê>Ócëyé õÊõéÝÑGõmô£õêïÖïÑ7046l18cðÌcèk˜i¸Ñð„á¨Ëhº‘Äh£ÑI£'¸&î‡gã5x>f¬ob¬4ÞeÜkVyVõV׬IÖ\ë,ëmÖWlPW› ›:›Ë¶¨­›­Äv›mßâ)Ò)õSnÚ1ìüì ìšìí9öaö%ömöÏÌÖ;t;|rtuÌvlp¼ë¤á4éĩÃéWgg¡só5¦KË—v—Sm§Š§nŸzË•åîºÒµÓõ£›»›Ü­ÙmÔÝÌ=Å}«ûM.›É]Ã=ïAôð÷XâqÌã§›§Âóç/^v^Y^û½O³œ&žÖ0mÈÛÄ[à½Ë{`:>=eúÎé>Æ>ŸzŸ‡¾¦¾"ß=¾#~Ö~™~üžû;úËýø¿áyòñN`Áå½³k™¥5»/ >B Yr“oÀòùc3Üg,šÑÊZú0Ì&LÖކÏß~o¦ùLé̶ˆàGlˆ¸i™ù})*2ª.êQ´Stqt÷,Ö¬äYûg½Žñ©Œ¹;Ûj¶rvg¬jlRlc웸€¸ª¸x‡øEñ—t$ í‰äÄØÄ=‰ãsçlš3œäšT–tc®åÜ¢¹æéÎËžwç|þü/÷„óû%ÒŸ3gAMA±Ž|ûQ“ cHRMz%€ƒùÿ€éu0ê`:˜o’_ÅFmIDATxÚì{ytT׿÷^½Ú÷E%•J¥EBlƒÁ–ÁغÜäÄN qã9íž8±Óm÷Ì4>8žnû¤C&qÆKHp;q, 1NÛ#Ë,HBBK•¤’j{õj_ß2Ü¢T$vŸ33ÌÉ=uŽî»ïÞûÞûîw¿ûûýîõéÛ€?§ÿû‰þ3ÿoCþ¼]/-Õ2Öb>ɳ7ßRih™”8¯Ž–±’e2QŠIð)†IÈ&µš jK¬ˆu1É”vWëâUM[Ztb$‘ô&h1µÆŸŽ!’áóF^Yϱ Ì—Ñ®V)íVµÚiªáͲqxnÆ€A§PëÆ9ÏððY#ž(ðóÌ¥­ýª}°±ÆX,Q*Q 4ùr! -óG‰œäÙ[²»”ηlûÇ›Ÿ¶Ê+ g)èË5É?V3–Ä’é #¡h€Ñ y6žJɆ¡Ô¹´B«JrÙÄ¡·Ç®_Ø´ùáã§þyèZ:° ±Êá2Ô,´~ôÁÕéIŽô¹ ±j{ÇÃZÝÄ›ožIA¥¡‰|“¡%cV¼RÐ ”Ù÷`ånu8¹nVîMç ûÃÎM8xOüF“ºû°÷ úFr[×âÀS…nw½€£§cñx¬½ G¾g"’À¾ÇðÄOPSÇÀ†'P[ŽOaÛ?bßc¨­(4ìîÃÁc…òî>°ïžÈ@fVå„UúÂð‰Œ(ZÈr!¯ ‡”U¬v9Yñ2)qž:A½>˜L^=ê$<Ô‚³^¬¯Å¯®d~¿¦P±ÌV»¦¦ºÉaÕ!QqÂÜb*ª|éÂ[Ð5 I‰Ô :Kž€Ïàl] £@áûIÐÞV¸E€6é  ìÙ‰#Ï¢á¡Bóý‡áñ¡o´ÐÉ´5.ÛQ[H}#XÖP(|ö Ú—aç&ì?ŒîK³ ÏatV elÈéôTH™Ñ͵:¯„B͹¬øµŒÚ×/p—ßÑÊq_=2D -6¤òh¯Œ‚B†‘©ÐÕŸ…ÂkßýW­d-ªÿ¼e¶îMXÏ›\^(º}Ls×Ë"@&ÚÛÐÝnŽžFmö윅òñ`Û?ÂíƒÍf …"njË #·¬5…æÅtô4L:`º/¡»¯ô‘ïÏ©S_×,ÒâÐø¸_J³iZ¢t0&ÀQbG‡›=ÕÔj[¶Z­w2¥â{3Êý§ƒ#GFîªÆ–0¨P®è”%UˆçÀ¦áС½üåk]‡Îͳ‹D¾¥¥˜I‰äWÔ ªÆ /@‹Hímhk„Û7Kê­k ˆaøéÙƒØö"×7, j@Æ©om0éÐ}éóEyÿáÂÔ!I©`î¹§cû—Ú×/“(ÀÅ·@E¤9KÓÒ̲Õj«Qºeóì‹ù[áÐæ…Ó€J=ª¨2 Æˆ*êÍXh…Vp Pƒî¿¼yU,U‰"Äl,xózÀò¹<X|ðø,ÑŠéî6عi6Of@{¾¹© s dÔ×׳·Š=Ïcô-S÷¥ÙúE£Ûî\½ö¶uw.ZZîP3&z¾g¸¨Ñ Á§niHö…F¨Î vþ`0ÎåÊu0(̓gy¢›f5jM0*‘Ê¡ÎÅ°çØ›ý7 ñ¼ÌͶx œÑ龄HGOÃ〚rÔV ­GOcåîYzšù>¢ ìza¶Û=;qäû…L¦Š°!3†Ì•oòMŒ^½280Ö})45ß."æÝA Àj——RlëxîçÏ}º®^»ÿÁšo¼2b‘âÿ°cR¥ …Ÿ^WÈçT–$ˆd4D ×YDÒè™B<EA«ïw-»}Á|o6'+!€™ÌÌÙ‹¾|^¬­˜ƒBñ’4Ñh“n_!C P4%‰Rq#å»ÝÑuüô„{ºØ¼˜¡(J’$Òiž¦iQo~ncsmœ‡”³$ÑwÿÃÚÙÅpÊË)eFgí­CÑPj…¯w˕ᙱñø7¾VÙÜþ`ž1–ǦíÖ.šQP hZæb¿?Ý/QðÆËâZ˜zÿ™vƒšá^Ëð{Þ¾v…­o3ݬ!ó,<"€Æ„¹ ¶å­â…¾ Û'_LNËŠ—ä³]նɉP$1›`wkkìËWÖýö½sî €–V׺e ŸuŸ°ïŸN´¯Ùhm¢H?Ž:[Ëš†Þ®!·/b­4Ý~_Ûµ'ÇFÇêê|pÇ›¿|ÇíuÕ4ä²·Ï €ÒÐîø ªÄ¯$vô«£¹Ö¦w2:F ÎÅbyÁ —ÉÍÜ¿|áú›ÿ¹g$ñú]¬Fk£…N«„¢Ñuw#(JÌ¥$!GÑ E3åÙˆ$‰gÎçxÁª¡Þ{n«cù—¯¸:1 ³©òwKý§Îq uKM7h)ÊyN$¢a¥í¬.€s3¼ lݼãÀK}ý½¶¯’$ @­«¾÷ÃQ ·[&'BO>ý•Ó=ƒÞ‰°J%W©Õ5¶å+ë]ÕÖ?œPÊåŽJ³Z'÷z8ŠB.Ç¿ñî{ñlüæ7Éeù€'¾þÁƒgǓܿè6› mË›6n¾ã÷§Žg³Q¹\ŽO©ôrJIKY¤¼…ž8,¥3Qz“+ Äæ-7Ó^w6è+Ž XAç9qt2¯0”¡zCôÃç¸êʰdõ=z“íçÿv¶cÛý”ÊIÊFgL*йh ¥Uææ;¾Ä¦ÏŒEÿËÎu5­ëÞþñßLå[–-³-¯äí­?þ›;ÚÉŠM7ëu‰§§åÃ¥%é°!› ¨­ªÐÖ²²­ee_/€­›vÖ"£9ã~þÚÉ0[ðÍ2™|„K^¾ä¹k}ó~?0ï)ׇf®ÍØ]æ[¨Öt„Žø'}þ‰Âkp\Œãbׇݩd¥|I.[ð²"BÊEŠNi“QŽ4çÎeQ^²žêb¯\Žù eË‚ªr[™…Ò¸C¾@BÊ'F?üÑÏŸŸ¢è7^»ªºìïòÞ?ïÿ9•ôå#^>Íéõ†ÿõ›ÏžþŸï ¢IâsY^ BíºíW¯íÿM»y…eÇWR5}YÌ'cÉ%ûBáÙ¸u¼4Š4ïîÖÍ|¿Ù±;ã fa6Ѿfã‘'FÏ„{?}ü‘§”8qòóûö¾2z& ÆUïžëîé"DnkYYëª?zì“ÁLJŽ8`×w:º{ºö<ù<Áºà×´¬4Ì»¾ÓqôØ¡­›wììØ]úˆZWý¾½¯t÷t=ñÌ£¤ÃÞ9`Û®{»{º¼ÔÙÖ²ÀÑc‡L3é­›wDbÜÑc‡ä fËæG¼ÿ…ól:ËGÄ9@·.5ÜyÆæ¤CÁ0xj8 Â®kZ¬r­Yô½Î›Šœ 9 =“|éá¦%ÕÚ„8>Ùç?óæÌ™_ʨ|[d¥îŸZmi«R<ø¥–u¼Ä]lß»2¥ŽQ›DQàÍBÕ:FC~óÕ³u¥—¼ Ñ]œF;_-JVûšäéµue:½ŠRÜ!O gçþ‰ž¢8V j¡®Á%ËG*êl3úN\]¶ZâWÓ zá2ÍÞˆ§—½ú;¹xp&™?öµM”Eß/°+AQ4E¢Á‡†‚ A«7’L¦Tko˰c´˜¡@)c­]1Ÿ³7–ÁwH%樎Ës‚ 4£1îè±Cûö¾r÷š[7í8züPA£ fÂJîÉ1Rè™k_³ñ ”m-+‹ É%²Ò–>‚P¸ýŽwtsK©343vvÄ#DD -§(ã §¼\âB”Rˆ¸o8—2££Þ–]·(2q>ªËš[•Lƒ¡á.}ÝjIH ™éô¨,˜Ìƒ¢)*—M¦²"­ÀåîÃó­ýVÿr.“4T4 7—V蕵v±Ù½î,-z€i¿Å#Ñ<£Œ¨c ÈaàùkäS#1.ãº{º¾Ù±›èF4ÆQpO޵¯A­«ž@6»?v}·ãÀK{ž|žðHÿ®ïvD¢\qØ >dç«[7ïØóÄóDÇÌfCSK¥r‘¨gxDàR…H­yŽtÄYGœu˜­ë »N)3*eÆ8ëPVA`4üÙñÔïw"8õ|š;™™<—ç&rawÞ7\@ùÖê¹UúÌôÕOwèãÁ„J‰¿}câׇ?´sgݺ‡[6|Ã;ã÷—¶ýà̶'×$yv"’ž^¿z¡oâÄÕ ÏäTïõžsWFÇÝlØ;èùgÒA&Gö«BñDN ÈŠG>›¨‡{rŒØÅDæõ¾½¯´¯Ù¸oï+&ƒùG¯¿ð>zìÐÁÎWwvì&“€PugÇn"Jäq å&³¶»§Ë=9V몣óU®s4ÉO³ž@†ôVîPí¶9Œ¶ÙÉDZöLf«À±2 Å9VÖºÔÐàªïž;áŽåß^P®Lfy½¹¼éŽ/ ¹‚QÏ&?ýàЕñ¸Z‰‰ÿr&O¼ÌP‚Vż91:“öÆ¡PÉ.º'Ç8*ÉkefÈ`¥‰€Ä¦³Åø@“Ú S•W2@ƃ˜žÅ¢ GPøö#O ô5®z}ý½ÛvÝûÌ“Ï9p¢¯¿÷‰g=Øù*q ¿ÖϾøôÖÍ;ü°så½ ¤«Çÿú)b„x&Ǻ{ºFGÆ  yP­«žŒkÃâz«Qb£”gx„,€2³aÛ_n(Äà‹.ø>E´`®ú¥@(ÀnÓ+eÆP@Ã#À†%ƒ9ê*¯RUP—ŽOÎ4Å¥å°éqg“1wÃÒ €tN8}-éKãjRe¯ ¢¦p` ‹,ÂêJ|¥ —ý8Ei" E$%h)£L'F$6U^§*·Ûô¹ä°AnQ”På‚d{phr‚‹ß¹˜¢Ñ¬‰Þ˜¹Å ·ßÙøY÷õb ñ?t^“Чæ?Ž¢$I"äþ×_xöŧNÛ·vß `h„ûÍ©Oᬅ–ïØò›“¶¥Õ—óŒC†² Ze‰ekŽEíRª©"ôéÌÖèÈuc?·¨;×·X5Ñ(÷ÞYŒÂ[}Ñâ'*h$óH‰`Ìš»X¦·1ïŸí²YT`I5}Š]T†@·WS©w“JHJ’·©•õUUN3 …GG¨vFaÏ›¤:‡CÒ±Œ¨ U ˆJ­*ƒl]}ÍÚE¿}ïÜô$Wá47,17/nüìã!Ÿ— úcŽJ³«Ú¶x™ÃR¦SÐòñÑÀäDÈUmÛòåüöÂäD¨Ìi±U™Üç ½áJ—’,Œ°Uš­XäÓ±p€Z¯vU˜ûû'vvì&Và^{ÀòÛ`£Ô…¾ >"ZhùšÛZmNZ©M&ø¹V‡/È ‚𧬠Ôå p¬,êÀi*« ‘«Ëǧz>èŸà Ķ|©&–·^ÔÊ/¾?²Ö…œ2àãI|m ä2|ÌÇ‘¸Y@™ÕÚ´dÉÕ©áþ©™&Ô Ú*ñ®–BRj5TÛmúæZ›Âd$S/ ÿ1~)y£]=SélFä™d®ºÞ¶þž%Tj¹R)÷y9Ÿ—;}| @…Óüèc÷•Ù ži×ûW¦'¹R|?øí…-_^S+‚3ÁþOGCÓ‘|– Š0WZ–®_J,¢ 6gXg¶›ÖV»¼ñ ôO\êï}öŧv¾‰qö [ÈçdšÇƒ#n€Õâ\¶¬¡dÛ33+ß)LI³UPÊŒ´-–öKqÖ¡·Î(eÆ“o¡%Ü·ªÅUazùðä úö¯×_úõȆ²ì‡nEQ© ¯dðÐRh¼7›çCJi¡˜“åUeÊ{ê[|rñºãz¼ÕŽz3¦’ÚìXoi%æ=F^Éç§|ÊëΖº*¥§8r|~`|$“Ÿ\\°ÈAœé›ÆhÖlÞº¢óài™'2eNKÛÝ >úÕÙâÝb*¯¶üÇo¶Ë岃?;UTŠ¢V­¹QñC—Üáð|6TT”×,rœ¹Ð€ˆß'ÒQXy*›EòS—S´-@]NÙ›}J™€Q®zîÑûß9qþå·Nþäᄎ¢Áuú€‡ã„?M÷¬[ñÐÖõz£éË- ™„Ÿž“‡®eËÈÐÈÝåër¹^ÔrÞÆú£þ¸(A!ƒŠÁñ꾆¶R”‹;ß,aÅËçRæ\ÊœŠç7ƦÆ2y±}ÍÆÞGÙA‰8ÁžÐ„'`ôL˜”Š¿ö5£\jÆËI’D¼d’8›zÃìt”Ü-¶=ðRg­«Þ?þÝ¿]@Pf¥»%Ibþx4APÞóäó¤ÉÉwÎ׺ê}>ÿÀå1BçEÍT¹Í1o¥´•1YÉ8í´ÓN[“2ƒz&ý×Ã'¾¾‚i6‰±,%‰bÇCßXTcïÜ»ýãçŽþöÄ÷¾ûjºò­‰J“£ŠªYúÈ®ÿTUUUå¬ÒÒZJJœ»èϳHâ†SšB./8sñ£î±O¯pþÐ,IÄ®ðr|A Åô¤˜žŒg~ØÙÝÓµaû*â¿9«ÍN3€{¶¯Ú°}€ý¯¿°aûªîž.£Y“Íä‹öu©Çq#DW¸k2˜­MÔ†í«LFóáŸp}xæª/:¯ŸçCÓ1?òÔÎŽÝO<óè†í«"1Ž4‰Çe¸ÿž…øüô­„©,1NЙ0¼Q ²g6{J)3.X^¹çþ•ëœéçß<ÙP¦Ê ´Z¥ s‘×~y¸B‹ÿ~'þµóÝ‹><õÿµÌf­­­]¿~=I’r¾8' ¨ þB¥å&ññxíñDF#ár‡@}Uc•3l+³HT<ì&Áä¹DÆŽÜ Zí #ÑGÌçM{…ù#>ëö\‰h”ëëïµWïÞÔê ~ñýb ’àÑÑc‡¦ÇçËC«Ø˜Ÿ„±ˆÅ `ï‹OŸ|ç|ûšÝ=]›ª8M?ç£êF,Î:ì;ÅK• .ù¦û}ûõ‹ŠEùØ®¯oZL'clÏé½lêŸp[Âf7í¹ßXoF$–ÌårK–,1›Í©drxlB?#,Iâ^ _1`y" Ñ'Êt+äÄžÊÈÏ]ï¿Ð7143VŒA³b hMH­WÉH|Çd0÷~8JlÛO\£(êžûÎûgƒÞbÓñy ÿžD|HÂ_²<ÎßlJ&ÉH{nx›d¼—µ¬ÀPÒçrœ6„ bHÌ Qrô ïb€Q«6Òq™ï3­Bué“®Hˆu±Ï=¶õÇo|îé¯JŒÒbÒëN¼. ¼L&K§ÓCCCûüÓ™¾OÖ×A«„EAÄ)7&cˆ›äºE4>"²ÈZÕJ¥nýÕ–¹›Q‡’v.‹*’J? ÿw÷t=vhfŠ«hÏ7Qh ¥J^ô¤oÁ²ô-bE¸™êæ&r¹<ŸÏGbœÑh.mr©¿@s£ƒR<'cÌŸ:6Ly¹8ëÐ[£Y!:Ö;Âg- ,UzD•[õ·oKbI~*Ž-Ýxº÷Ê/_nY¸&á¶OÇäJ¥$IçÎ;wîÜk¯ÿL á£ßÎñÄ ×bVml9]u19 ,]U¡ðå„R™ŒÊ´FÁÂp+¶Ý¡ÒÐÙ¤v<Èf|’ª‚Ò˜ ¥ãÅ#Å7ï×”¦Ÿšˆdæ¦" 1¡Ùf Åî@nU£e4^¨FÄ%˜œ Í Q¥ÌØÔj+R˜¸¸¥ÝÎ *S¦@nâÊĬM6%‚±¤Àß8ML™¬”ÓN_ô΂Uº[C ×Ðr$„”°ª•DØ4¬q)ž ¨›á¶jc¯ΩjµÔ±‚«6|p4e%­Q f5È}"VUP´-–ã¡QËXm9oõ§þD^©Ã´RIsþ”Æ4åb²t6B±D6W8ãGª\n-¯åýnæú”Ÿ@<eþÐ PŠ‘õpN˜ôÏéÏÿ•õÿIúßfÙË*Á‰R4IEND®B`‚yabause-0.9.13.1/src/qt/resources/icons/yabause.png000644 001750 001750 00000004760 12256006101 024105 0ustar00guillaumeguillaume000000 000000 ‰PNG  IHDR }JbgAMA¯È7ŠétEXtSoftwareAdobe ImageReadyqÉe< ‚IDATxÚbüÿÿ?2 &!&Óåäqú@ ÛÝÔE@ªÖîùÿÿv_å€bq€‚ÿø'ˆ @Œèf Ø fÙÿòÊþ Ó÷°Ów Fàû^}ûù €z€üûO¿ý3O>|ë@ edVýÏÄbüj“0§ìRŸ‘Iæ?@ÁlÝäÜO/:„Ò @qF€bÚÁ 4òú¿¿ý€–ÿ©ïÞÿ‹e™§î6WžûO?g ° 0 Ò Å01€Âð: F êF&ñõÿÿ½dØæÂ ò'—÷>žO É¿¿Ï0ØV™ÿª:“þ³2Ë5¼±ÙYdÉ@ €à[ïÈíÀÈp™AM×ñ\QÝF;׺~ü~l² €@F¾ôØyƒó6³¬ã¬åWöÿûû—ašóú㱎ÿo¾ÿ¢ @ øáÙôæ{wŸým«ÝÁÄøßüëç O¿üÐ °/€Öœª1‡:|0ð#ÜwÞ{ €P¼ Š%¨iŒ01€+J8 yÿPÑô0Á¢î@ÁmJ¾gf5`fÓf`f¼ÅðýˉÀpsJùñv¯=_O ˆ¥1|ûúƒá××E !ò¬²«îÝ{@ðh†› ‡¬ (ª¬œÿ÷ ß‹6í±ù¿¹Ã\°T=+s,&mù Θ_+Zv1HH1pqs€m5©”áΕý‡&3\úûÿ¿53scÆñ‡”@ÕA© ¨™ Í¿ÿ'Ì96 «!ŽáÍýó®Ólyw[û1¼¹~‰ááÍk ‡Ÿ¿Ÿ @0Dõ,âç@ rÅ¥?ÿþücæ’dû÷ÍÈbÑïþy^}ói0¾ûñ{{ßÍ7^„’\‘Ã]¶IY³o20]åä±øô»=6µD09D±Ê/ \®€,J·X‘@W@,@âPò>0Ó!+>LD Àû-løÙ˜Îÿf€¨ùÿ˜ÿ±u‚ ËÂáÆÀÅç â;S/ž Äû¾®eæÔË11Ë€4ƒRéVB€‚‡3‹l?‹L¤l0Û†{7æ‚3 Ð5 g¿×6ªaøøá3Ó{AEÈðo Ø âo«3÷¢¿¿Ÿ0pr~ePRÑaàã—y%¤Ùŧ–ARJ˜áõó¥ å…ÀD÷d'óÌ n | òÊí«sÄ€I8)gH|¾ˆ˜<ƒŽ¾ÃÏï÷€øÍ‡®<¡zþ‚€%e §nƒJ+ Éÿ¶öË)¤Lbæ«|÷öCM¾ ÃBV›?~÷ýþ÷ÿŽ,/g7+ów€¹ ˆ{@K ¿ Ý?ÇðèÁ^>n0žÔàÌËÔ $*~Ä= ØLC˜7êÊÛOçzþ(Šœ€ø Zü‚ï¿ñ%0ÿÕó ­–ÌodååXX˜ÎÂÀŬ ’ Îq%0 9ÑÒοçOßüâ' Œ ¥Ü¬Lï\<Çpcï6†Ÿ?1|üù”F–_r¯â¹ ¢öóïÿ¼Ül¬ŠLÿÿdù6@üìÂëþÌüöëïwFFFó¦Ë//< )Rõ@Ì^îýúÇ0˜'ýü=‰Ff‚V‰ìû¿Öɨ—‚òˆýÝkÝbÀpaÅ–™gn„áÈ‹M@Qœˆb(kW€2Ü}ôò PØ$ aÏXvæ‡ðíÏA9ƒy Hþƒ±@ê>@Íú@èUçz À,2XØ‘ŒÿÃð÷ç6¦ÿøYÃÛrí J@ ¢¥ø÷ô›¿žn~ü{°ÙÃÀÎíÏðï"Aýù¹Ÿù߯-Ÿ~<ò…‰F¤@a`çòf`å``gce`cgeøøf+°Ž»’š,’@IT,½&ϿВg>»0ƒ¤\,ï_~ýüÍðãç7†o72üÿt<cØr;.P9JÎÿúþÖ4à.«(qìÕß“_ÿü—ã4c—vdàäbuì Oîocxúè(HÙ`:Bݤ D%t´ ¾ÿ Ư_\bxþh4È Ž~ Íýÿ¡Å!@!gDPÜ€Š= ÎâÕÀRÝd°€ƒ‰U&7s0<¾ŒáðÞ™0ƒA5-{[ç, Å?¾~ùÁpòðL†OÏ¡8j3Ô ôÇ @°¢”Iu¸ˆ­8<ë‘ZDûAlÿðvE`aÏÃà †¿0<z›AAYŸáÏï ß¿ý`xÿî=ÃÒ¹ Þ=e–Å+í9üþûO˜™?r²0½÷Úuó/Ô1*ÏA–ûq ³±ÐòÛhé”ØÎƒj_[ç¿Ð\¸#@¥ëŸ?Á–?¼aÑÌ `ëŒáC‘K–"÷ÿR^.C.66†ß¿2|ªyýý×K`Û¨B‘{ /ó_€b‚VéÉ@¬JDè–CK!P{QÈl8¼w ôž4Œt³iͰåLŒ çZ±Æj ³O³¶±6ôž¸”Á"§’AI]›AZ€—A‘SêóÏ¿ú˜ªƒ䀯Ðv0¨x÷‹®BèˆF}ÿÎEp-÷úÕ{†—/Þ‚éWN2øWvÆj  Ìÿþð½{xáRg Ãí³Þ<{Œ¦ß Iîÿø÷ä!€‚¥} {3Ëñ/ ^mR\ºò'ZtLU¿Y¥ëXYY^}|Ÿïó­Û’ç[ÿʲý²`ddðš¿õþ‡oâÿÿg ƒŸ'ÿ€ÞûtÀËo¿Þ}ûµS’—#¦äìÓ7„œ @¡á m]š yäˆÏP\Û€ZP€ PËlÖûŸ¿o>úøÝøÝ¯ßfÿþýg&Â+BlÊÎ=}S @8ë ƒ„€”:4[ªCK<˜…ß]¯—¿ÿ³Þù_äÙ©_ú,¯œ’{óáHí$`T!¶. ’+#`€¢ˆ‹×X©3šL¿~>eøýóˆïˆ­mŠ  X+ˆøö§'b@ xu `^öÑ«¾“ÚIEND®B`‚yabause-0.9.13.1/src/qt/resources/resources.qrc000644 001750 001750 00000003533 12256006101 023351 0ustar00guillaumeguillaume000000 000000 icons/actions/debug_cpu.png icons/actions/cheat_search.png icons/actions/about.png icons/actions/backup_ram.png icons/actions/button_cancel.png icons/actions/button_ok.png icons/actions/cd.png icons/actions/cheats.png icons/controller.png icons/actions/emu-compatibility.png icons/actions/fps.png icons/actions/frame_skipping.png icons/actions/fullscreen.png icons/actions/iso.png icons/actions/layer.png icons/actions/load_state.png icons/actions/log.png icons/mouse.png icons/actions/mute.png icons/actions/no_fullscreen.png icons/actions/open_file.png icons/actions/pause.png icons/actions/play.png icons/actions/quit.png icons/actions/reset.png icons/actions/save_state.png icons/actions/screenshot.png icons/actions/settings.png icons/actions/sound.png icons/actions/transfert.png icons/actions/video.png icons/3dcontroller.png icons/made.png icons/yabause.icns icons/yabause.png yabause-0.9.13.1/src/qt/VolatileSettings.h000644 001750 001750 00000000706 12256006055 022276 0ustar00guillaumeguillaume000000 000000 #ifndef VOLATILESETTINGS_H #define VOLATILESETTINGS_H #include #include #include #include class VolatileSettings : public QObject { Q_OBJECT protected: QHash mValues; public: void clear(); void setValue(const QString & key, const QVariant & value); void removeValue(const QString& key); QVariant value(const QString & key, const QVariant & defaultValue = QVariant()) const; }; #endif yabause-0.9.13.1/src/qt/YabauseSoftGL.cpp000644 001750 001750 00000003734 12256006055 022005 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "YabauseGL.h" #include "QtYabause.h" #include "../vidsoft.h" #include YabauseGL::YabauseGL( QWidget* p ) : QWidget( p ) { setFocusPolicy( Qt::StrongFocus ); if ( p ) { p->setFocusPolicy( Qt::StrongFocus ); setFocusProxy( p ); } } void YabauseGL::showEvent( QShowEvent* e ) { } void YabauseGL::resizeGL( int w, int h ) { updateView( QSize( w, h ) ); } void YabauseGL::updateView( const QSize& s ) { } void YabauseGL::swapBuffers() { this->update(this->rect()); } QImage YabauseGL::grabFrameBuffer(bool withAlpha) { return QImage(); } void YabauseGL::paintEvent( QPaintEvent * event ) { int buf_width, buf_height; if (dispbuffer == NULL) return; VIDCore->GetGlSize( &buf_width, &buf_height ); #ifdef USE_RGB_555 QImage image = QImage((uchar *) dispbuffer, buf_width, buf_height, QImage::Format_RGB555); #elif USE_RGB_565 QImage image = QImage((uchar *) dispbuffer, buf_width, buf_height, QImage::Format_RGB16); #else QImage image = QImage((uchar *) dispbuffer, buf_width, buf_height, QImage::Format_RGB32); #endif image = image.rgbSwapped(); QPainter p(this); p.drawImage(this->rect(), image); } void YabauseGL::makeCurrent() { } yabause-0.9.13.1/src/qt/Settings.h000755 001750 001750 00000003060 12256006055 020575 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SETTINGS_H #define SETTINGS_H #include #ifndef PROGRAM_NAME #define PROGRAM_NAME PACKAGE #endif #ifndef PROGRAM_VERSION #define PROGRAM_VERSION VERSION #endif class QMainWindow; QString getDataDirPath(); class Settings : public QSettings { Q_OBJECT public: Settings( QObject* = 0 ); ~Settings(); static void setIniInformations( const QString& = PROGRAM_NAME, const QString& = PROGRAM_VERSION ); static QString programName(); static QString programVersion(); virtual void restoreState( QMainWindow* ); virtual void saveState( QMainWindow* ); virtual void setDefaultSettings(); protected: static QString mProgramName; static QString mProgramVersion; }; #endif // PSETTINGS_H yabause-0.9.13.1/src/qt/VolatileSettings.cpp000644 001750 001750 00000001035 12256006054 022624 0ustar00guillaumeguillaume000000 000000 #include "VolatileSettings.h" #include "Settings.h" #include "QtYabause.h" void VolatileSettings::clear() { mValues.clear(); } void VolatileSettings::setValue(const QString & key, const QVariant & value) { mValues[key] = value; } void VolatileSettings::removeValue(const QString & key) { mValues.remove(key); } QVariant VolatileSettings::value(const QString & key, const QVariant & defaultValue) const { if (mValues.contains(key)) return mValues[key]; Settings * s = QtYabause::settings(); return s->value(key, defaultValue); } yabause-0.9.13.1/src/qt/YabauseThread.cpp000755 001750 001750 00000024373 12256006055 022063 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006, 2013 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "YabauseThread.h" #include "Settings.h" #include "VolatileSettings.h" #include "ui/UIPortManager.h" #include "ui/UIYabause.h" #include "../peripheral.h" #include #include #include YabauseThread::YabauseThread( QObject* o ) : QObject( o ) { mPause = true; mTimerId = -1; mInit = -1; } YabauseThread::~YabauseThread() { deInitEmulation(); } yabauseinit_struct* YabauseThread::yabauseConf() { return &mYabauseConf; } void YabauseThread::initEmulation() { reloadSettings(); mInit = YabauseInit( &mYabauseConf ); SetOSDToggle(showFPS); } void YabauseThread::deInitEmulation() { YabauseDeInit(); mInit = -1; } bool YabauseThread::pauseEmulation( bool pause, bool reset ) { if ( mPause == pause && !reset ) { return true; } if ( mInit == 0 && reset ) { deInitEmulation(); } if ( mInit < 0 ) { initEmulation(); } if ( mInit < 0 ) { emit error( QtYabause::translate( "Can't initialize Yabause" ), false ); return false; } mPause = pause; if ( mPause ) { ScspMuteAudio(SCSP_MUTE_SYSTEM); killTimer( mTimerId ); mTimerId = -1; } else { ScspUnMuteAudio(SCSP_MUTE_SYSTEM); mTimerId = startTimer( 0 ); } VolatileSettings * vs = QtYabause::volatileSettings(); if (vs->value("autostart").toBool()) { if (vs->value("autostart/binary").toBool()) { MappedMemoryLoadExec( vs->value("autostart/binary/filename").toString().toLocal8Bit().constData(), vs->value("autostart/binary/address").toUInt()); } else if (vs->value("autostart/load").toBool()) { YabLoadStateSlot( QtYabause::volatileSettings()->value( "General/SaveStates", getDataDirPath() ).toString().toLatin1().constData(), vs->value("autostart/load/slot").toInt() ); } vs->setValue("autostart", false); } emit this->pause( mPause ); return true; } bool YabauseThread::resetEmulation() { if ( mInit < 0 ) { return false; } YabauseReset(); emit reset(); return true; } void YabauseThread::reloadControllers() { PerPortReset(); QtYabause::clearPadsBits(); Settings* settings = QtYabause::settings(); for ( uint port = 1; port < 3; port++ ) { settings->beginGroup( QString( "Input/Port/%1/Id" ).arg( port ) ); QStringList ids = settings->childGroups(); settings->endGroup(); ids.sort(); foreach ( const QString& id, ids ) { uint type = settings->value( QString( UIPortManager::mSettingsType ).arg( port ).arg( id ) ).toUInt(); switch ( type ) { case PERPAD: { PerPad_struct* padbits = PerPadAdd( port == 1 ? &PORTDATA1 : &PORTDATA2 ); settings->beginGroup( QString( "Input/Port/%1/Id/%2/Controller/%3/Key" ).arg( port ).arg( id ).arg( type ) ); QStringList padKeys = settings->childKeys(); settings->endGroup(); padKeys.sort(); foreach ( const QString& padKey, padKeys ) { const QString key = settings->value( QString( UIPortManager::mSettingsKey ).arg( port ).arg( id ).arg( type ).arg( padKey ) ).toString(); PerSetKey( key.toUInt(), padKey.toUInt(), padbits ); } break; } case PERWHEEL: QtYabause::mainWindow()->appendLog( "Wheel controller type is not yet supported" ); break; case PER3DPAD: { PerAnalog_struct* analogbits = Per3DPadAdd( port == 1 ? &PORTDATA1 : &PORTDATA2 ); settings->beginGroup( QString( "Input/Port/%1/Id/%2/Controller/%3/Key" ).arg( port ).arg( id ).arg( type ) ); QStringList analogKeys = settings->childKeys(); settings->endGroup(); analogKeys.sort(); foreach ( const QString& analogKey, analogKeys ) { const QString key = settings->value( QString( UIPortManager::mSettingsKey ).arg( port ).arg( id ).arg( type ).arg( analogKey ) ).toString(); PerSetKey( key.toUInt(), analogKey.toUInt(), analogbits ); } break; } case PERGUN: QtYabause::mainWindow()->appendLog( "Gun controller type is not yet supported" ); break; case PERKEYBOARD: QtYabause::mainWindow()->appendLog( "Keyboard controller type is not yet supported" ); break; case PERMOUSE: { PerMouse_struct* mousebits = PerMouseAdd( port == 1 ? &PORTDATA1 : &PORTDATA2 ); settings->beginGroup( QString( "Input/Port/%1/Id/%2/Controller/%3/Key" ).arg( port ).arg( id ).arg( type ) ); QStringList mouseKeys = settings->childKeys(); settings->endGroup(); mouseKeys.sort(); foreach ( const QString& mouseKey, mouseKeys ) { const QString key = settings->value( QString( UIPortManager::mSettingsKey ).arg( port ).arg( id ).arg( type ).arg( mouseKey ) ).toString(); PerSetKey( key.toUInt(), mouseKey.toUInt(), mousebits ); } break; } case 0: // Unconnected break; default: QtYabause::mainWindow()->appendLog( "Invalid controller type" ); break; } } } } static struct tm *localtime_qt(const QDateTime &input, struct tm *result) { QDate date(input.date()); result->tm_year = date.year() - 1900; result->tm_mon = date.month(); result->tm_mday = date.day(); result->tm_wday = date.dayOfWeek(); result->tm_yday = date.dayOfYear(); QTime time(input.time()); result->tm_sec = time.second(); result->tm_min = time.minute(); result->tm_hour = time.hour(); return result; } void YabauseThread::reloadClock() { QString tmp; Settings* s = QtYabause::settings(); if (mYabauseConf.basetime == 0) tmp = ""; else tmp = QDateTime::fromTime_t(mYabauseConf.basetime).toString(); // Clock sync mYabauseConf.clocksync = (int)s->value( "General/ClockSync", mYabauseConf.clocksync ).toBool(); tmp = s->value( "General/FixedBaseTime", tmp ).toString(); if (!tmp.isEmpty() && mYabauseConf.clocksync) { QDateTime date = QDateTime::fromString(tmp, Qt::ISODate); mYabauseConf.basetime = (long)date.toTime_t(); } else { mYabauseConf.basetime = 0; } } void YabauseThread::reloadSettings() { //QMutexLocker l( &mMutex ); // get settings pointer VolatileSettings* vs = QtYabause::volatileSettings(); // reset yabause conf resetYabauseConf(); // read & apply settings mYabauseConf.m68kcoretype = vs->value( "Advanced/M68KCore", mYabauseConf.m68kcoretype ).toInt(); mYabauseConf.percoretype = vs->value( "Input/PerCore", mYabauseConf.percoretype ).toInt(); mYabauseConf.sh2coretype = vs->value( "Advanced/SH2Interpreter", mYabauseConf.sh2coretype ).toInt(); mYabauseConf.vidcoretype = vs->value( "Video/VideoCore", mYabauseConf.vidcoretype ).toInt(); mYabauseConf.osdcoretype = vs->value( "Video/OSDCore", mYabauseConf.osdcoretype ).toInt(); mYabauseConf.sndcoretype = vs->value( "Sound/SoundCore", mYabauseConf.sndcoretype ).toInt(); mYabauseConf.cdcoretype = vs->value( "General/CdRom", mYabauseConf.cdcoretype ).toInt(); mYabauseConf.carttype = vs->value( "Cartridge/Type", mYabauseConf.carttype ).toInt(); const QString r = vs->value( "Advanced/Region", mYabauseConf.regionid ).toString(); if ( r.isEmpty() || r == "Auto" ) mYabauseConf.regionid = 0; else { switch ( r[0].toLatin1() ) { case 'J': mYabauseConf.regionid = 1; break; case 'T': mYabauseConf.regionid = 2; break; case 'U': mYabauseConf.regionid = 4; break; case 'B': mYabauseConf.regionid = 5; break; case 'K': mYabauseConf.regionid = 6; break; case 'A': mYabauseConf.regionid = 0xA; break; case 'E': mYabauseConf.regionid = 0xC; break; case 'L': mYabauseConf.regionid = 0xD; break; } } mYabauseConf.biospath = strdup( vs->value( "General/Bios", mYabauseConf.biospath ).toString().toLatin1().constData() ); mYabauseConf.cdpath = strdup( vs->value( "General/CdRomISO", mYabauseConf.cdpath ).toString().toLatin1().constData() ); showFPS = vs->value( "General/ShowFPS", false ).toBool(); mYabauseConf.buppath = strdup( vs->value( "Memory/Path", mYabauseConf.buppath ).toString().toLatin1().constData() ); mYabauseConf.mpegpath = strdup( vs->value( "MpegROM/Path", mYabauseConf.mpegpath ).toString().toLatin1().constData() ); mYabauseConf.cartpath = strdup( vs->value( "Cartridge/Path", mYabauseConf.cartpath ).toString().toLatin1().constData() ); mYabauseConf.videoformattype = vs->value( "Video/VideoFormat", mYabauseConf.videoformattype ).toInt(); emit requestSize( QSize( vs->value( "Video/WinWidth", 0 ).toInt(), vs->value( "Video/WinHeight", 0 ).toInt() ) ); emit requestFullscreen( vs->value( "Video/Fullscreen", false ).toBool() ); emit requestVolumeChange( vs->value( "Sound/Volume", 100 ).toInt() ); reloadClock(); reloadControllers(); } bool YabauseThread::emulationRunning() { //QMutexLocker l( &mMutex ); return mTimerId != -1 && !mPause; } bool YabauseThread::emulationPaused() { //QMutexLocker l( &mMutex ); return mPause; } void YabauseThread::resetYabauseConf() { // free structure memset( &mYabauseConf, 0, sizeof( yabauseinit_struct ) ); // fill default structure mYabauseConf.m68kcoretype = M68KCORE_C68K; mYabauseConf.percoretype = QtYabause::defaultPERCore().id; mYabauseConf.sh2coretype = SH2CORE_DEFAULT; mYabauseConf.vidcoretype = QtYabause::defaultVIDCore().id; mYabauseConf.sndcoretype = QtYabause::defaultSNDCore().id; mYabauseConf.cdcoretype = QtYabause::defaultCDCore().id; mYabauseConf.carttype = CART_NONE; mYabauseConf.regionid = 0; mYabauseConf.biospath = 0; mYabauseConf.cdpath = 0; mYabauseConf.buppath = 0; mYabauseConf.mpegpath = 0; mYabauseConf.cartpath = 0; mYabauseConf.videoformattype = VIDEOFORMATTYPE_NTSC; } void YabauseThread::timerEvent( QTimerEvent* ) { //mPause = true; //mRunning = true; //while ( mRunning ) { if ( !mPause ) PERCore->HandleEvents(); //else //msleep( 25 ); //sleep( 0 ); } } yabause-0.9.13.1/src/qt/QtYabause.cpp000644 001750 001750 00000025570 12256006054 021234 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006, 2013 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "QtYabause.h" #include "ui/UIYabause.h" #include "Settings.h" #include "VolatileSettings.h" #include #include #include #include #include #include // cores #ifdef Q_OS_WIN extern CDInterface SPTICD; #endif extern "C" { M68K_struct * M68KCoreList[] = { &M68KDummy, #ifdef HAVE_C68K &M68KC68K, #endif NULL }; SH2Interface_struct *SH2CoreList[] = { &SH2Interpreter, &SH2DebugInterpreter, #ifdef SH2_DYNAREC &SH2Dynarec, #endif NULL }; PerInterface_struct *PERCoreList[] = { &PERDummy, &PERQT, #ifdef HAVE_LIBSDL &PERSDLJoy, #endif #ifdef __APPLE__ &PERMacJoy, #endif #ifdef HAVE_DIRECTINPUT &PERDIRECTX, #endif NULL }; CDInterface *CDCoreList[] = { &DummyCD, &ISOCD, #ifndef UNKNOWN_ARCH &ArchCD, #endif NULL }; SoundInterface_struct *SNDCoreList[] = { &SNDDummy, #ifdef HAVE_LIBSDL &SNDSDL, #endif #ifdef HAVE_LIBAL &SNDAL, #endif #ifdef HAVE_DIRECTSOUND &SNDDIRECTX, #endif #ifdef ARCH_IS_MACOSX &SNDMac, #endif NULL }; VideoInterface_struct *VIDCoreList[] = { &VIDDummy, #ifdef HAVE_LIBGL &VIDOGL, #endif &VIDSoft, NULL }; #ifdef YAB_PORT_OSD OSD_struct *OSDCoreList[] = { &OSDDummy, #ifdef HAVE_LIBGLUT &OSDGlut, #endif &OSDSoft, NULL }; #endif } // main window QPointer mUIYabause = 0; // settings object QPointer mSettings = 0; QPointer mVolatileSettings = 0; // ports padbits QMap mPort1PadsBits; QMap mPort2PadsBits; QMap mPort1MouseBits; QMap mPort2MouseBits; QMap mPort1AnalogBits; QMap mPort2AnalogBits; extern "C" { void YuiErrorMsg(const char *string) { QtYabause::mainWindow()->appendLog( string ); } void YuiSwapBuffers() { QtYabause::mainWindow()->swapBuffers(); } #if defined(HAVE_DIRECTINPUT) || defined(HAVE_DIRECTSOUND) HWND DXGetWindow() { return (HWND)mUIYabause->winId(); } #endif } UIYabause* QtYabause::mainWindow( bool create ) { if ( !mUIYabause && create ) mUIYabause = new UIYabause; return mUIYabause; } Settings* QtYabause::settings( bool create ) { if ( !mSettings && create ) mSettings = new Settings(); return mSettings; } VolatileSettings* QtYabause::volatileSettings( bool create ) { if ( !mVolatileSettings && create ) mVolatileSettings = new VolatileSettings(); return mVolatileSettings; } QList QtYabause::getTranslationList() { QList translations; #ifdef HAVE_LIBMINI18N QDir transDir=QDir(YTSDIR, "*.yts"); foreach(QString file, transDir.entryList()) { if (!file.endsWith(".yts")) continue; translation_struct trans; trans.file = QString(YTSDIR) + QString("/") + file; // Let's get it down just to language code QStringList string = file.remove(".yts").split("_"); if (file.startsWith("yabause_")) string.removeFirst(); QString localeStr = string.join("_"); // Find the locale QLocale locale = QLocale(localeStr); // Now we should be good for the language name trans.name = locale.nativeLanguageName(); translations.append(trans); } #endif return translations; } int QtYabause::setTranslationFile() { #ifdef HAVE_LIBMINI18N const QString s = settings()->value( "General/Translation" ).toString(); if ( ! s.isEmpty() ) { if (s == "#") return 0; // magic value for "no translation" (aka english) const char* filePath = qstrdup( s.toLocal8Bit().constData() ); if ( mini18n_set_locale( filePath ) == 0 ) { QtYabause::retranslateApplication(); if ( logTranslation() != 0 ) qWarning( "Can't log translation !" ); return 0; } } if ( mini18n_set_domain( YTSDIR ) == 0 ) { QtYabause::retranslateApplication(); if ( logTranslation() != 0 ) qWarning( "Can't log translation !" ); return 0; } #endif return -1; } int QtYabause::logTranslation() { #ifdef HAVE_LIBMINI18N if (settings()->value( "General/LogUntranslated", false ).toBool()) { const QString s = settings()->value( "General/Translation" ).toString().replace( ".yts", "_log.yts" ); if ( s.isEmpty() ) return 0; const char* filePath = qstrdup( s.toLocal8Bit().constData() ); return mini18n_set_log( filePath ); } #endif return 0; } void QtYabause::closeTranslation() { #ifdef HAVE_LIBMINI18N mini18n_close(); #endif } QString QtYabause::translate( const QString& string ) { #ifdef HAVE_LIBMINI18N return QString::fromUtf8( _( string.toUtf8().constData() ) ); #else return string; #endif } void QtYabause::retranslateWidgetOnly( QWidget* widget ) { #ifdef HAVE_LIBMINI18N if ( !widget ) return; widget->setAccessibleDescription( translate( widget->accessibleDescription() ) ); widget->setAccessibleName( translate( widget->accessibleName() ) ); widget->setStatusTip( translate( widget->statusTip() ) ); widget->setStyleSheet( translate( widget->styleSheet() ) ); widget->setToolTip( translate( widget->toolTip() ) ); widget->setWhatsThis( translate( widget->whatsThis() ) ); widget->setWindowIconText( translate( widget->windowIconText() ) ); widget->setWindowTitle( translate( widget->windowTitle() ) ); #endif } void QtYabause::retranslateWidget( QWidget* widget ) { #ifdef HAVE_LIBMINI18N if ( !widget ) return; // translate all widget based members retranslateWidgetOnly( widget ); // get class name const QString className = widget->metaObject()->className(); if ( className == "QWidget" ) return; else if ( className == "QLabel" ) { QLabel* l = qobject_cast( widget ); l->setText( translate( l->text() ) ); } else if ( className == "QAbstractButton" || widget->inherits( "QAbstractButton" ) ) { QAbstractButton* ab = qobject_cast( widget ); ab->setText( translate( ab->text() ) ); } else if ( className == "QGroupBox" ) { QGroupBox* gb = qobject_cast( widget ); gb->setTitle( translate( gb->title() ) ); } else if ( className == "QMenu" || className == "QMenuBar" ) { QList menus; if ( className == "QMenuBar" ) menus = qobject_cast( widget )->findChildren(); else menus << qobject_cast( widget ); foreach ( QMenu* m, menus ) { m->setTitle( translate( m->title() ) ); // retranslate menu actions foreach ( QAction* a, m->actions() ) { a->setIconText( translate( a->iconText() ) ); a->setStatusTip( translate( a->statusTip() ) ); a->setText( translate( a->text() ) ); a->setToolTip( translate( a->toolTip() ) ); a->setWhatsThis( translate( a->whatsThis() ) ); } } } else if ( className == "QTreeWidget" ) { QTreeWidget* tw = qobject_cast( widget ); QTreeWidgetItem* twi = tw->headerItem(); for ( int i = 0; i < twi->columnCount(); i++ ) { twi->setStatusTip( i, translate( twi->statusTip( i ) ) ); twi->setText( i, translate( twi->text( i ) ) ); twi->setToolTip( i, translate( twi->toolTip( i ) ) ); twi->setWhatsThis( i, translate( twi->whatsThis( i ) ) ); } } else if ( className == "QTabWidget" ) { QTabWidget* tw = qobject_cast( widget ); for ( int i = 0; i < tw->count(); i++ ) tw->setTabText( i, translate( tw->tabText( i ) ) ); } // translate children foreach ( QWidget* w, widget->findChildren() ) retranslateWidget( w ); #endif } void QtYabause::retranslateApplication() { #ifdef HAVE_LIBMINI18N foreach ( QWidget* widget, QApplication::allWidgets() ) retranslateWidget( widget ); #endif } const char* QtYabause::getCurrentCdSerial() { return cdip ? cdip->itemnum : 0; } M68K_struct* QtYabause::getM68KCore( int id ) { for ( int i = 0; M68KCoreList[i] != NULL; i++ ) if ( M68KCoreList[i]->id == id ) return M68KCoreList[i]; return 0; } SH2Interface_struct* QtYabause::getSH2Core( int id ) { for ( int i = 0; SH2CoreList[i] != NULL; i++ ) if ( SH2CoreList[i]->id == id ) return SH2CoreList[i]; return 0; } PerInterface_struct* QtYabause::getPERCore( int id ) { for ( int i = 0; PERCoreList[i] != NULL; i++ ) if ( PERCoreList[i]->id == id ) return PERCoreList[i]; return 0; } CDInterface* QtYabause::getCDCore( int id ) { for ( int i = 0; CDCoreList[i] != NULL; i++ ) if ( CDCoreList[i]->id == id ) return CDCoreList[i]; return 0; } SoundInterface_struct* QtYabause::getSNDCore( int id ) { for ( int i = 0; SNDCoreList[i] != NULL; i++ ) if ( SNDCoreList[i]->id == id ) return SNDCoreList[i]; return 0; } VideoInterface_struct* QtYabause::getVDICore( int id ) { for ( int i = 0; VIDCoreList[i] != NULL; i++ ) if ( VIDCoreList[i]->id == id ) return VIDCoreList[i]; return 0; } CDInterface QtYabause::defaultCDCore() { #ifndef UNKNOWN_ARCH return ArchCD; #else return DummyCD; #endif } SoundInterface_struct QtYabause::defaultSNDCore() { #ifdef HAVE_LIBSDL return SNDSDL; #else return SNDDummy; #endif } VideoInterface_struct QtYabause::defaultVIDCore() { return VIDSoft; } OSD_struct QtYabause::defaultOSDCore() { return OSDSoft; } PerInterface_struct QtYabause::defaultPERCore() { #ifdef HAVE_LIBSDL return PERSDLJoy; #elif defined (HAVE_DIRECTINPUT) return PERDIRECTX; #else return PERQT; #endif } SH2Interface_struct QtYabause::defaultSH2Core() { return SH2Interpreter; } QMap* QtYabause::portPadsBits( uint portNumber ) { switch ( portNumber ) { case 1: return &mPort1PadsBits; break; case 2: return &mPort2PadsBits; break; default: return 0; break; } } void QtYabause::clearPadsBits() { mPort1PadsBits.clear(); mPort2PadsBits.clear(); } QMap* QtYabause::portAnalogBits( uint portNumber ) { switch ( portNumber ) { case 1: return &mPort1AnalogBits; break; case 2: return &mPort2AnalogBits; break; default: return 0; break; } } void QtYabause::clear3DAnalogBits() { mPort1AnalogBits.clear(); mPort2AnalogBits.clear(); } QMap* QtYabause::portMouseBits( uint portNumber ) { switch ( portNumber ) { case 1: return &mPort1MouseBits; break; case 2: return &mPort2MouseBits; break; default: return 0; break; } } void QtYabause::clearMouseBits() { mPort1MouseBits.clear(); mPort2MouseBits.clear(); } yabause-0.9.13.1/src/qt/yabause.desktop.in000644 001750 001750 00000000237 12256006060 022251 0ustar00guillaumeguillaume000000 000000 [Desktop Entry] Type=Application Name=Yabause (Qt port) Comment=Sega Saturn emulator TryExec=yabause Exec=@YAB_PORT_NAME@ Icon=yabause Categories=KDE;Qt;Game; yabause-0.9.13.1/src/qt/CMakeLists.txt000644 001750 001750 00000017416 12256006105 021367 0ustar00guillaumeguillaume000000 000000 project( yabause-qt ) yab_port_start() option(YAB_USE_QT5 "Use Qt 5 if available." off) if(YAB_USE_QT5) find_package(Qt5Widgets) add_definitions(-DUSE_QT5=1) if( YAB_WANT_OPENGL ) find_package(Qt5OpenGL) endif() include_directories(${Qt5Widgets_INCLUDE_DIRS}) add_definitions(${Qt5Widgets_DEFINITIONS}) if (WIN32) find_package(Qt5Core) endif() else(YAB_USE_QT5) find_package(Qt4) if (NOT QT4_FOUND) message(STATUS "NO QT4_FOUND") return() endif (NOT QT4_FOUND) set( QT_USE_QTCORE TRUE ) set( QT_USE_QTGUI TRUE ) if( YAB_WANT_OPENGL ) set( QT_USE_QTOPENGL TRUE ) endif() if (WIN32) set( QT_USE_QTMAIN TRUE ) endif() # dunno what it does exactly ... but seem required include( ${QT_USE_FILE} ) endif(YAB_USE_QT5) # qt resources file set( yabause_qt_RESOURCES resources/resources.qrc ) if ( WIN32 ) # Windows port libraries set( yabause_qt_windows_LIBRARIES vfw32 ) # resources set ( yabause_qt_windows_RESOURCES resources/icons/yabause.rc ) endif ( WIN32 ) if(APPLE) find_library(AUDIO_LIBRARY AudioUnit) set(yabause_qt_macosx_LIBRARIES ${AUDIO_LIBRARY}) endif() # qt forms set( yabause_qt_FORMS ui/UIYabause.ui ui/UISettings.ui ui/UIAbout.ui ui/UICheats.ui ui/UICheatAR.ui ui/UICheatRaw.ui ui/UICheatSearch.ui ui/UIWaitInput.ui ui/UIBackupRam.ui ui/UIPortManager.ui ui/UIPadSetting.ui ui/UI3DControlPadSetting.ui ui/UIMouseSetting.ui ui/UIDebugCPU.ui ui/UIDebugSCSP.ui ui/UIDebugVDP1.ui ui/UIDebugVDP2.ui ui/UIDebugVDP2Viewer.ui ui/UIHexInput.ui ui/UIMemoryTransfer.ui ui/UIMemoryEditor.ui ui/UIMemorySearch.ui ) # pure C headers set( yabause_qt_HEADERS QtYabause.h CommonDialogs.h PerQt.h Arguments.h ) # C headers needing moc set( yabause_qt_MOC_HEADERS ui/UIYabause.h ui/UISettings.h ui/UIAbout.h ui/UICheats.h ui/UICheatAR.h ui/UICheatRaw.h ui/UICheatSearch.h ui/UIWaitInput.h ui/UIBackupRam.h ui/UIPortManager.h ui/UIControllerSetting.h ui/UIPadSetting.h ui/UI3DControlPadSetting.h ui/UIMouseSetting.h ui/UIShortcutManager.h ui/UIDebugCPU.h ui/UIDebugM68K.h ui/UIDebugSCSP.h ui/UIDebugSCUDSP.h ui/UIDebugSH2.h ui/UIDebugVDP1.h ui/UIDebugVDP2.h ui/UIDebugVDP2Viewer.h ui/UIDisasm.h ui/UIHexInput.h ui/UIMemoryTransfer.h ui/UIHexEditor.h ui/UIMemoryEditor.h ui/UIMemorySearch.h YabauseGL.h VolatileSettings.h Settings.h YabauseThread.h ) # C sources set( yabause_qt_SOURCES main.cpp QtYabause.cpp CommonDialogs.cpp PerQt.c ui/UIYabause.cpp ui/UISettings.cpp ui/UIAbout.cpp ui/UICheats.cpp ui/UICheatAR.cpp ui/UICheatRaw.cpp ui/UICheatSearch.cpp ui/UIWaitInput.cpp ui/UIBackupRam.cpp ui/UIPortManager.cpp ui/UIControllerSetting.cpp ui/UIPadSetting.cpp ui/UI3DControlPadSetting.cpp ui/UIMouseSetting.cpp ui/UIShortcutManager.cpp ui/UIDebugCPU.cpp ui/UIDebugM68K.cpp ui/UIDebugSCSP.cpp ui/UIDebugSCUDSP.cpp ui/UIDebugSH2.cpp ui/UIDebugVDP1.cpp ui/UIDebugVDP2.cpp ui/UIDebugVDP2Viewer.cpp ui/UIDisasm.cpp ui/UIHexInput.cpp ui/UIMemoryTransfer.cpp ui/UIHexEditor.cpp ui/UIMemoryEditor.cpp ui/UIMemorySearch.cpp Settings.cpp VolatileSettings.cpp YabauseThread.cpp Arguments.cpp ) if( OPENGL_FOUND ) set( yabause_qt_SOURCES ${yabause_qt_SOURCES} YabauseGL.cpp ) else() set( yabause_qt_SOURCES ${yabause_qt_SOURCES} YabauseSoftGL.cpp ) endif() if(YAB_USE_QT5) QT5_ADD_RESOURCES( yabause_qt_RCC_RESOURCES ${yabause_qt_RESOURCES} ) QT5_WRAP_UI( yabause_qt_UI_FORMS ${yabause_qt_FORMS} ) QT5_WRAP_CPP( yabause_qt_MOC_SOURCES ${yabause_qt_MOC_HEADERS} ) else(YAB_USE_QT5) QT4_ADD_RESOURCES( yabause_qt_RCC_RESOURCES ${yabause_qt_RESOURCES} ) QT4_WRAP_UI( yabause_qt_UI_FORMS ${yabause_qt_FORMS} ) QT4_WRAP_CPP( yabause_qt_MOC_SOURCES ${yabause_qt_MOC_HEADERS} ) endif(YAB_USE_QT5) set( yabause_qt_SOURCES ${yabause_qt_SOURCES} resources/icons/yabause.icns ) set_source_files_properties( resources/icons/yabause.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources ) add_executable( yabause-qt WIN32 ${yabause_qt_windows_SOURCES} ${yabause_qt_windows_RESOURCES} ${yabause_qt_RCC_RESOURCES} ${yabause_qt_HEADERS} ${yabause_qt_MOC_HEADERS} ${yabause_qt_SOURCES} ${yabause_qt_UI_FORMS} ${yabause_qt_MOC_SOURCES} ) if (YAB_USE_QT5) include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/ui ${Qt5Widgets_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS} ) else(YAB_USE_QT5) include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/ui ${QT_INCLUDE_DIR} ${QT_QTCORE_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR} ${QT_QTOPENGL_INCLUDE_DIR} ) endif(YAB_USE_QT5) # APPLE // not necessary mac os x, but i don't care ;) if (APPLE) set_target_properties( yabause-qt PROPERTIES MACOSX_BUNDLE true ) set_target_properties( yabause-qt PROPERTIES MACOSX_BUNDLE_ICON_FILE yabause.icns ) set_target_properties( yabause-qt PROPERTIES MACOSX_BUNDLE_LONG_VERSION_STRING "${YAB_VERSION}" ) set_target_properties( yabause-qt PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME Yabause ) set_target_properties( yabause-qt PROPERTIES MACOSX_BUNDLE_SHORT_VERSION_STRING "${YAB_VERSION}" ) set_target_properties( yabause-qt PROPERTIES MACOSX_BUNDLE_COPYRIGHT "(c) Yabause Team" ) endif (APPLE) target_link_libraries( yabause-qt yabause ) target_link_libraries( yabause-qt ${YABAUSE_LIBRARIES} ) target_link_libraries( yabause-qt ${PORT_LIBRARIES} ) target_link_libraries( yabause-qt ${yabause_qt_windows_LIBRARIES} ) target_link_libraries( yabause-qt ${yabause_qt_macosx_LIBRARIES} ) if (YAB_USE_QT5) target_link_libraries( yabause-qt ${Qt5Widgets_LIBRARIES} ${Qt5OpenGL_LIBRARIES} ${Qt5Core_QTMAIN_LIBRARIES} ${Qt5Network_LIBRARIES} ) else(YAB_USE_QT5) target_link_libraries( yabause-qt ${QT_LIBRARIES} ) endif(YAB_USE_QT5) yab_port_success(yabause-qt) configure_file(yabause.desktop.in ${YAB_PORT_NAME}.desktop) if (WIN32) install(TARGETS yabause-qt DESTINATION ".") if (GLUT_FOUND) install(FILES ${GLUT_INCLUDE_DIR}/../freeglut.dll DESTINATION ".") endif () if (SDL_FOUND) get_filename_component(SDL_BINARY_DIR ${SDL_LIBRARY} PATH) install(DIRECTORY ${SDL_BINARY_DIR}/ DESTINATION "." FILES_MATCHING PATTERN "SDL*.dll") endif () if(YAB_USE_QT5) install(FILES ${QT_BINARY_DIR}/Qt5Core.dll DESTINATION ".") install(FILES ${QT_BINARY_DIR}/Qt5Gui.dll DESTINATION ".") install(FILES ${QT_BINARY_DIR}/Qt5OpenGL.dll DESTINATION ".") else() install(FILES ${QT_BINARY_DIR}/QtCore4.dll DESTINATION ".") install(FILES ${QT_BINARY_DIR}/QtGui4.dll DESTINATION ".") install(FILES ${QT_BINARY_DIR}/QtOpenGL4.dll DESTINATION ".") endif() install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../AUTHORS DESTINATION "." RENAME AUTHORS.txt) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../ChangeLog DESTINATION "." RENAME ChangeLog.txt) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../COPYING DESTINATION "." RENAME COPYING.txt) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../README DESTINATION "." RENAME README.txt) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../../README.WIN DESTINATION "." RENAME README.WIN.txt) if (MINGW) get_filename_component( Mingw_Path ${CMAKE_CXX_COMPILER} PATH ) if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "x86_64" OR "${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64") install(DIRECTORY ${Mingw_Path}/ DESTINATION "." FILES_MATCHING PATTERN "libgcc_s_*.dll" PATTERN "libstdc++-*.dll") else() install(DIRECTORY ${Mingw_Path}/ DESTINATION "." FILES_MATCHING PATTERN "libgcc_s_*.dll" PATTERN "mingwm10.dll") endif() endif () else () install(TARGETS yabause-qt DESTINATION "bin") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${YAB_PORT_NAME}.desktop DESTINATION "share/applications") install(FILES "doc/yabause.1" DESTINATION "${YAB_MAN_DIR}/man1" RENAME "${YAB_PORT_NAME}.1") install(FILES "resources/icons/yabause.png" DESTINATION "share/pixmaps") endif () yabause-0.9.13.1/src/qt/mkspecs/win32-x11-g++/qplatformdefs.h000644 001750 001750 00000010772 12256006055 025241 0ustar00guillaumeguillaume000000 000000 /**************************************************************************** ** ** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved. ** ** This file is part of the qmake spec of the Qt Toolkit. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://trolltech.com/products/qt/licenses/licensing/opensource/ ** ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://trolltech.com/products/qt/licenses/licensing/licensingoverview ** or contact the sales department at sales@trolltech.com. ** ** In addition, as a special exception, Trolltech gives you certain ** additional rights. These rights are described in the Trolltech GPL ** Exception version 1.0, which can be found at ** http://www.trolltech.com/products/qt/gplexception/ and in the file ** GPL_EXCEPTION.txt in this package. ** ** In addition, as a special exception, Trolltech, as the sole copyright ** holder for Qt Designer, grants users of the Qt/Eclipse Integration ** plug-in the right for the Qt/Eclipse Integration to link to ** functionality provided by Qt Designer and its related libraries. ** ** Trolltech reserves all rights not expressly granted herein. ** ** Trolltech ASA (c) 2007 ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #ifndef QPLATFORMDEFS_H #define QPLATFORMDEFS_H #ifdef UNICODE #ifndef _UNICODE #define _UNICODE #endif #endif // Get Qt defines/settings #include "qglobal.h" #include #include #include #include #include #include #include #include #include #include #if !defined(_WIN32_WINNT) || (_WIN32_WINNT-0 < 0x0500) typedef enum { NameUnknown = 0, NameFullyQualifiedDN = 1, NameSamCompatible = 2, NameDisplay = 3, NameUniqueId = 6, NameCanonical = 7, NameUserPrincipal = 8, NameCanonicalEx = 9, NameServicePrincipal = 10, NameDnsDomain = 12 } EXTENDED_NAME_FORMAT, *PEXTENDED_NAME_FORMAT; #endif #define Q_FS_FAT #ifdef QT_LARGEFILE_SUPPORT #define QT_STATBUF struct _stati64 // non-ANSI defs #define QT_STATBUF4TSTAT struct _stati64 // non-ANSI defs #define QT_STAT ::_stati64 #define QT_FSTAT ::_fstati64 #else #define QT_STATBUF struct _stat // non-ANSI defs #define QT_STATBUF4TSTAT struct _stat // non-ANSI defs #define QT_STAT ::_stat #define QT_FSTAT ::_fstat #endif #define QT_STAT_REG _S_IFREG #define QT_STAT_DIR _S_IFDIR #define QT_STAT_MASK _S_IFMT #if defined(_S_IFLNK) # define QT_STAT_LNK _S_IFLNK #endif #define QT_FILENO _fileno #define QT_OPEN ::_open #define QT_CLOSE ::_close #ifdef QT_LARGEFILE_SUPPORT #define QT_LSEEK ::_lseeki64 #ifndef UNICODE #define QT_TSTAT ::_stati64 #else #define QT_TSTAT ::_wstati64 #endif #else #define QT_LSEEK ::_lseek #ifndef UNICODE #define QT_TSTAT ::_stat #else #define QT_TSTAT ::_wstat #endif #endif #define QT_READ ::_read #define QT_WRITE ::_write #define QT_ACCESS ::_access #define QT_GETCWD ::_getcwd #define QT_CHDIR ::_chdir #define QT_MKDIR ::_mkdir #define QT_RMDIR ::_rmdir #define QT_OPEN_LARGEFILE 0 #define QT_OPEN_RDONLY _O_RDONLY #define QT_OPEN_WRONLY _O_WRONLY #define QT_OPEN_RDWR _O_RDWR #define QT_OPEN_CREAT _O_CREAT #define QT_OPEN_TRUNC _O_TRUNC #define QT_OPEN_APPEND _O_APPEND #if defined(O_TEXT) # define QT_OPEN_TEXT _O_TEXT # define QT_OPEN_BINARY _O_BINARY #endif #define QT_FOPEN ::fopen #ifdef QT_LARGEFILE_SUPPORT #define QT_FSEEK ::fseeko64 #define QT_FTELL ::ftello64 #else #define QT_FSEEK ::fseek #define QT_FTELL ::ftell #endif #define QT_FGETPOS ::fgetpos #define QT_FSETPOS ::fsetpos #define QT_FPOS_T fpos_t #ifdef QT_LARGEFILE_SUPPORT #define QT_OFF_T off64_t #else #define QT_OFF_T long #endif #define QT_SIGNAL_ARGS int #define QT_VSNPRINTF ::_vsnprintf #define QT_SNPRINTF ::_snprintf # define F_OK 0 # define X_OK 1 # define W_OK 2 # define R_OK 4 #endif // QPLATFORMDEFS_H yabause-0.9.13.1/src/qt/mkspecs/win32-x11-g++/qmake.conf000644 001750 001750 00000006457 12256006055 024173 0ustar00guillaumeguillaume000000 000000 # # qmake configuration for win32-x11-g++ # # Written for MinGW # QT_WINE_PATH=$(HOME)/.wine/drive_c/Development/Qt QT_WIN32_VERSION=$(QT_WIN32_VERSION) QT_WIN32_PATH=$${QT_WINE_PATH}/$${QT_WIN32_VERSION} #QT_WIN32_PATH=$(QT_WIN32_PATH) MAKEFILE_GENERATOR = MINGW TEMPLATE = app CONFIG += qt warn_on release link_prl copy_dir_files debug_and_release debug_and_release_target precompile_header QT += core gui DEFINES += UNICODE QT_LARGEFILE_SUPPORT QMAKE_COMPILER_DEFINES += __GNUC__ WIN32 QMAKE_EXT_OBJ = .o QMAKE_EXT_RES = _res.o QMAKE_CC = i586-mingw32msvc-gcc QMAKE_LEX = flex QMAKE_LEXFLAGS = QMAKE_YACC = byacc QMAKE_YACCFLAGS = -d QMAKE_CFLAGS = QMAKE_CFLAGS_DEPS = -M QMAKE_CFLAGS_WARN_ON = -Wall QMAKE_CFLAGS_WARN_OFF = -w QMAKE_CFLAGS_RELEASE = -O2 QMAKE_CFLAGS_DEBUG = -g QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses QMAKE_CXX = i586-mingw32msvc-g++ QMAKE_CXXFLAGS = $$QMAKE_CFLAGS QMAKE_CXXFLAGS_DEPS = $$QMAKE_CFLAGS_DEPS QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD QMAKE_CXXFLAGS_RTTI_ON = -frtti QMAKE_CXXFLAGS_RTTI_OFF = -fno-rtti QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions -mthreads QMAKE_CXXFLAGS_EXCEPTIONS_OFF = -fno-exceptions QMAKE_INCDIR = /usr/i586-mingw32msvc/include QMAKE_INCDIR_QT = $${QT_WIN32_PATH}/include QMAKE_LIBDIR_QT = $${QT_WIN32_PATH}/lib QMAKE_RUN_CC = $(CC) -c $(CFLAGS) $(INCPATH) -o $obj $src QMAKE_RUN_CC_IMP = $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $< QMAKE_RUN_CXX = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $obj $src QMAKE_RUN_CXX_IMP = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< QMAKE_LINK = i586-mingw32msvc-g++ QMAKE_LFLAGS = -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -mwindows QMAKE_LFLAGS_EXCEPTIONS_ON = -mthreads -Wl QMAKE_LFLAGS_EXCEPTIONS_OFF = QMAKE_LFLAGS_RELEASE = -Wl,-s QMAKE_LFLAGS_DEBUG = QMAKE_LFLAGS_CONSOLE = -Wl,-subsystem,console QMAKE_LFLAGS_WINDOWS = -Wl,-subsystem,windows QMAKE_LFLAGS_DLL = -shared QMAKE_LINK_OBJECT_MAX = 10 QMAKE_LINK_OBJECT_SCRIPT= object_script QMAKE_LIBS = QMAKE_LIBS_CORE = -lkernel32 -luser32 -lshell32 -luuid -lole32 -ladvapi32 -lws2_32 QMAKE_LIBS_GUI = -lgdi32 -lcomdlg32 -loleaut32 -limm32 -lwinmm -lwinspool -lws2_32 -lole32 -luuid -luser32 -ladvapi32 QMAKE_LIBS_NETWORK = -lws2_32 QMAKE_LIBS_OPENGL = -lopengl32 -lglu32 -lgdi32 -luser32 QMAKE_LIBS_COMPAT = -ladvapi32 -lshell32 -lcomdlg32 -luser32 -lgdi32 -lws2_32 QMAKE_LIBS_QT_ENTRY = -lmingw32 -lqtmain MINGW_IN_SHELL = 1 QMAKE_DIR_SEP = / QMAKE_COPY = cp QMAKE_COPY_DIR = cp -r QMAKE_MOVE = mv QMAKE_DEL_FILE = rm -f QMAKE_MKDIR = mkdir -p QMAKE_DEL_DIR = rm -rf QMAKE_CHK_DIR_EXISTS = test -d QMAKE_MOC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}moc-qt4 QMAKE_UIC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}uic-qt4 QMAKE_IDC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}idc-qt4 QMAKE_RCC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}rcc QMAKE_IDL = midl QMAKE_LIB = i586-mingw32-ar -ru QMAKE_RC = i586-mingw32msvc-windres QMAKE_ZIP = zip -r -9 QMAKE_STRIP = i586-mingw32msvc-strip QMAKE_STRIPFLAGS_LIB += --strip-unneeded load(qt_config) yabause-0.9.13.1/src/qt/mkspecs/win32-osx-g++/qplatformdefs.h000644 001750 001750 00000010772 12256006055 025441 0ustar00guillaumeguillaume000000 000000 /**************************************************************************** ** ** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved. ** ** This file is part of the qmake spec of the Qt Toolkit. ** ** This file may be used under the terms of the GNU General Public ** License version 2.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of ** this file. Please review the following information to ensure GNU ** General Public Licensing requirements will be met: ** http://trolltech.com/products/qt/licenses/licensing/opensource/ ** ** If you are unsure which license is appropriate for your use, please ** review the following information: ** http://trolltech.com/products/qt/licenses/licensing/licensingoverview ** or contact the sales department at sales@trolltech.com. ** ** In addition, as a special exception, Trolltech gives you certain ** additional rights. These rights are described in the Trolltech GPL ** Exception version 1.0, which can be found at ** http://www.trolltech.com/products/qt/gplexception/ and in the file ** GPL_EXCEPTION.txt in this package. ** ** In addition, as a special exception, Trolltech, as the sole copyright ** holder for Qt Designer, grants users of the Qt/Eclipse Integration ** plug-in the right for the Qt/Eclipse Integration to link to ** functionality provided by Qt Designer and its related libraries. ** ** Trolltech reserves all rights not expressly granted herein. ** ** Trolltech ASA (c) 2007 ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ****************************************************************************/ #ifndef QPLATFORMDEFS_H #define QPLATFORMDEFS_H #ifdef UNICODE #ifndef _UNICODE #define _UNICODE #endif #endif // Get Qt defines/settings #include "qglobal.h" #include #include #include #include #include #include #include #include #include #include #if !defined(_WIN32_WINNT) || (_WIN32_WINNT-0 < 0x0500) typedef enum { NameUnknown = 0, NameFullyQualifiedDN = 1, NameSamCompatible = 2, NameDisplay = 3, NameUniqueId = 6, NameCanonical = 7, NameUserPrincipal = 8, NameCanonicalEx = 9, NameServicePrincipal = 10, NameDnsDomain = 12 } EXTENDED_NAME_FORMAT, *PEXTENDED_NAME_FORMAT; #endif #define Q_FS_FAT #ifdef QT_LARGEFILE_SUPPORT #define QT_STATBUF struct _stati64 // non-ANSI defs #define QT_STATBUF4TSTAT struct _stati64 // non-ANSI defs #define QT_STAT ::_stati64 #define QT_FSTAT ::_fstati64 #else #define QT_STATBUF struct _stat // non-ANSI defs #define QT_STATBUF4TSTAT struct _stat // non-ANSI defs #define QT_STAT ::_stat #define QT_FSTAT ::_fstat #endif #define QT_STAT_REG _S_IFREG #define QT_STAT_DIR _S_IFDIR #define QT_STAT_MASK _S_IFMT #if defined(_S_IFLNK) # define QT_STAT_LNK _S_IFLNK #endif #define QT_FILENO _fileno #define QT_OPEN ::_open #define QT_CLOSE ::_close #ifdef QT_LARGEFILE_SUPPORT #define QT_LSEEK ::_lseeki64 #ifndef UNICODE #define QT_TSTAT ::_stati64 #else #define QT_TSTAT ::_wstati64 #endif #else #define QT_LSEEK ::_lseek #ifndef UNICODE #define QT_TSTAT ::_stat #else #define QT_TSTAT ::_wstat #endif #endif #define QT_READ ::_read #define QT_WRITE ::_write #define QT_ACCESS ::_access #define QT_GETCWD ::_getcwd #define QT_CHDIR ::_chdir #define QT_MKDIR ::_mkdir #define QT_RMDIR ::_rmdir #define QT_OPEN_LARGEFILE 0 #define QT_OPEN_RDONLY _O_RDONLY #define QT_OPEN_WRONLY _O_WRONLY #define QT_OPEN_RDWR _O_RDWR #define QT_OPEN_CREAT _O_CREAT #define QT_OPEN_TRUNC _O_TRUNC #define QT_OPEN_APPEND _O_APPEND #if defined(O_TEXT) # define QT_OPEN_TEXT _O_TEXT # define QT_OPEN_BINARY _O_BINARY #endif #define QT_FOPEN ::fopen #ifdef QT_LARGEFILE_SUPPORT #define QT_FSEEK ::fseeko64 #define QT_FTELL ::ftello64 #else #define QT_FSEEK ::fseek #define QT_FTELL ::ftell #endif #define QT_FGETPOS ::fgetpos #define QT_FSETPOS ::fsetpos #define QT_FPOS_T fpos_t #ifdef QT_LARGEFILE_SUPPORT #define QT_OFF_T off64_t #else #define QT_OFF_T long #endif #define QT_SIGNAL_ARGS int #define QT_VSNPRINTF ::_vsnprintf #define QT_SNPRINTF ::_snprintf # define F_OK 0 # define X_OK 1 # define W_OK 2 # define R_OK 4 #endif // QPLATFORMDEFS_H yabause-0.9.13.1/src/qt/mkspecs/win32-osx-g++/qmake.conf000644 001750 001750 00000007360 12256006055 024365 0ustar00guillaumeguillaume000000 000000 # # qmake configuration for win32-g++ # # Written for MinGW # CROSS_MINGW_PATH = /usr/local/i386-mingw32-3.4.5/ CROSS_MINGW_BIN_PREFIX = $${CROSS_MINGW_PATH}bin/i386-mingw32- CROSS_WIN32_QT_PATH = $(CROSS_WIN32_QT_PATH)/4.4.0/ CROSS_WIN32_INCLUDE = $${CROSS_MINGW_PATH}i386-mingw32/include MAKEFILE_GENERATOR = MINGW TEMPLATE = app CONFIG += qt warn_on release link_prl copy_dir_files debug_and_release debug_and_release_target precompile_header QT += core gui DEFINES += UNICODE QT_LARGEFILE_SUPPORT QMAKE_COMPILER_DEFINES += __GNUC__ WIN32 QMAKE_EXT_OBJ = .o QMAKE_EXT_RES = _res.o ##cross-compil adaptation QMAKE_CC = $${CROSS_MINGW_BIN_PREFIX}gcc QMAKE_LEX = flex QMAKE_LEXFLAGS = QMAKE_YACC = byacc QMAKE_YACCFLAGS = -d QMAKE_CFLAGS = QMAKE_CFLAGS_DEPS = -M QMAKE_CFLAGS_WARN_ON = -Wall QMAKE_CFLAGS_WARN_OFF = -w QMAKE_CFLAGS_RELEASE = -O2 QMAKE_CFLAGS_DEBUG = -g QMAKE_CFLAGS_YACC = -Wno-unused -Wno-parentheses ##cross-compil adaptation QMAKE_CXX = $${CROSS_MINGW_BIN_PREFIX}g++ QMAKE_CXXFLAGS = $$QMAKE_CFLAGS QMAKE_CXXFLAGS_DEPS = $$QMAKE_CFLAGS_DEPS QMAKE_CXXFLAGS_WARN_ON = $$QMAKE_CFLAGS_WARN_ON QMAKE_CXXFLAGS_WARN_OFF = $$QMAKE_CFLAGS_WARN_OFF QMAKE_CXXFLAGS_RELEASE = $$QMAKE_CFLAGS_RELEASE QMAKE_CXXFLAGS_DEBUG = $$QMAKE_CFLAGS_DEBUG QMAKE_CXXFLAGS_YACC = $$QMAKE_CFLAGS_YACC QMAKE_CXXFLAGS_THREAD = $$QMAKE_CFLAGS_THREAD QMAKE_CXXFLAGS_RTTI_ON = -frtti QMAKE_CXXFLAGS_RTTI_OFF = -fno-rtti QMAKE_CXXFLAGS_EXCEPTIONS_ON = -fexceptions -mthreads QMAKE_CXXFLAGS_EXCEPTIONS_OFF = -fno-exceptions ##cross-compil adaptation QMAKE_INCDIR = $${CROSS_WIN32_INCLUDE} ##cross-compil adaptation QMAKE_INCDIR_QT = $${CROSS_WIN32_QT_PATH}include ##cross-compil adaptation QMAKE_LIBDIR_QT = $${CROSS_WIN32_QT_PATH}lib QMAKE_RUN_CC = $(CC) -c $(CFLAGS) $(INCPATH) -o $obj $src QMAKE_RUN_CC_IMP = $(CC) -c $(CFLAGS) $(INCPATH) -o $@ $< QMAKE_RUN_CXX = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $obj $src QMAKE_RUN_CXX_IMP = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $< ##cross-compil adaptation QMAKE_LINK = $${CROSS_MINGW_BIN_PREFIX}g++ ##cross-compil adaptation QMAKE_LFLAGS = -mthreads -Wl,-enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -mwindows QMAKE_LFLAGS_EXCEPTIONS_ON = -mthreads -Wl QMAKE_LFLAGS_EXCEPTIONS_OFF = QMAKE_LFLAGS_RELEASE = -Wl,-s QMAKE_LFLAGS_DEBUG = QMAKE_LFLAGS_CONSOLE = -Wl,-subsystem,console QMAKE_LFLAGS_WINDOWS = -Wl,-subsystem,windows QMAKE_LFLAGS_DLL = -shared QMAKE_LINK_OBJECT_MAX = 10 QMAKE_LINK_OBJECT_SCRIPT= object_script QMAKE_LIBS = QMAKE_LIBS_CORE = -lkernel32 -luser32 -lshell32 -luuid -lole32 -ladvapi32 -lws2_32 QMAKE_LIBS_GUI = -lgdi32 -lcomdlg32 -loleaut32 -limm32 -lwinmm -lwinspool -lws2_32 -lole32 -luuid -luser32 -ladvapi32 QMAKE_LIBS_NETWORK = -lws2_32 QMAKE_LIBS_OPENGL = -lopengl32 -lglu32 -lgdi32 -luser32 QMAKE_LIBS_COMPAT = -ladvapi32 -lshell32 -lcomdlg32 -luser32 -lgdi32 -lws2_32 QMAKE_LIBS_QT_ENTRY = -lmingw32 -lqtmain ##cross-compil adaptation QMAKE_DIR_SEP = / QMAKE_COPY = cp QMAKE_COPY_DIR = cp -r QMAKE_MOVE = mv QMAKE_DEL_FILE = rm -f QMAKE_MKDIR = mkdir -p QMAKE_DEL_DIR = rm -rf ##cross-compil adaptation QMAKE_MOC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}moc QMAKE_UIC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}uic QMAKE_IDC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}idc QMAKE_RCC = $$[QT_INSTALL_BINS]$${DIR_SEPARATOR}rcc QMAKE_IDL = midl QMAKE_LIB = $${CROSS_MINGW_BIN_PREFIX}ar -ru QMAKE_RC = $${CROSS_MINGW_BIN_PREFIX}windres QMAKE_ZIP = zip -r -9 ##cross-compil adaptation QMAKE_STRIP = $${CROSS_MINGW_BIN_PREFIX}strip QMAKE_STRIPFLAGS_LIB += --strip-unneeded load(qt_config) yabause-0.9.13.1/src/qt/CommonDialogs.cpp000755 001750 001750 00000004063 12256006105 022063 0ustar00guillaumeguillaume000000 000000 /* Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "CommonDialogs.h" #include "QtYabause.h" bool CommonDialogs::question( const QString& m, const QString& c ) { return QMessageBox::question( QApplication::activeWindow(), c, m, QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) == QMessageBox::Yes; } void CommonDialogs::warning( const QString& m, const QString& c ) { QMessageBox::warning( QApplication::activeWindow(), c, m ); } void CommonDialogs::information( const QString& m, const QString& c ) { QMessageBox::information( QApplication::activeWindow(), QtYabause::translate( c ), m ); } QString CommonDialogs::getItem( const QStringList i, const QString& l, const QString& c ) { bool b; const QString s = QInputDialog::getItem( QApplication::activeWindow(), c, l, i, 0, false, &b ); return b ? s : QString(); } QString CommonDialogs::getSaveFileName( const QString& d, const QString& c, const QString& f ) { return QFileDialog::getSaveFileName( QApplication::activeWindow(), c, d, f ); } QString CommonDialogs::getOpenFileName( const QString& d, const QString& c, const QString& f ) { return QFileDialog::getOpenFileName( QApplication::activeWindow(), c, d, f ); } QString CommonDialogs::getExistingDirectory( const QString& d, const QString& c, QFileDialog::Options o ) { return QFileDialog::getExistingDirectory( QApplication::activeWindow(), c, d, o ); } yabause-0.9.13.1/src/qt/translations/yabause_fr.ts000644 001750 001750 00000122506 12256006054 024040 0ustar00guillaumeguillaume000000 000000 CommonDialogs Question... Warning... Information... Get Item... Get Save File Name... Get Open File Name... Get Existing Directory... UIAbout About... Yabause Qt Gui<br />Based on Yabause %1<br /><a href="http://yabause.org">http://yabause.org</a><br />The Yabause Team<br /><a href="mailto:pasnox@gmail.com">Filipe AZEVEDO</a> http://www.monkeystudio.org <a href="http://www.monkeystudio.org"><img src=":/icons/icons/made.png"></a> Ok UIBackupRam Backup Ram Manager Device List Save List Delete Format Save Information File Name : Comment : Language : Data Size : Block Size : %1/%2 blocks free Japanese English French German Spanish Italian Unknow (%1) Are you sure you want to delete '%1' ? Are you sure you want to format '%1' ? UICheatAR Add Action Replay Code Action Replay Code : Description : <font color="red"><b>WARNING:</b> Master Codes are <b>NOT</b> supported.</font> UICheatRaw Add Raw Memory Code Cheat Type Enable Byte Write Word Write Long Write Address : Value : Description : UICheats Cheats Code Description Status &Delete &Clear Add Codes... &Action Replay &Raw Memory Address Cheats File &Save To File &Load From File Enabled Disabled Unable to add code Unable to change description Invalid Address Invalid Value Unable to remove code Choose a cheat file to save to Yabause Cheat Files (*.yct);;All Files (*) Unable to open file for loading Choose a cheat file to open Unable to open file for saving UISettings Settings General Bios ... Cd-Rom Save States Video Video Core Resolution Width Fullscreen Height Video Format Sound Sound Core Cart/Memory Cartridge Memory Mpeg ROM Input Up - A Down B Left C Right X L Left trigger Y R Right trigger Z Start Advanced Region SH2 Interpreter CD Rom Drive... Choose a bios file The dummies cores don't need configuration. Choose a cdrom drive Choose a cdrom mount point Choose a folder to store save states Choose a cartridge file Choose a memory file Choose a mpeg rom UIWaitInput Waiting Input... UIYabause Yabause Qt GUI Volume Mute/UnMute Sound &File Save State Load State &View &Layer &Debug &Help Tools Emulation toolBar &Quit Ctrl+Q Start Stop &Settings... Ctrl+S R&un Pause &Pause &Reset Ctrl+R Transfer Ctrl+N Screenshot Ctrl+Print Frame Skip/Limiter ScrollLock FPS Vdp1 &Fullscreen Alt+Return Log MSH2 SSH2 Vdp2 M68K SCU-DSP SCSP Memory Dump &About... NBG0 NBG1 NBG2 NBG3 RBG0 1 Ctrl+1 2 Ctrl+2 3 Ctrl+3 4 Ctrl+4 5 Ctrl+5 6 Ctrl+6 7 Ctrl+7 8 Ctrl+8 9 Ctrl+9 Alt+1 Alt+2 Alt+3 Alt+4 Alt+5 Alt+6 Alt+7 Alt+8 Alt+9 To File... Ctrl+T From File... Ctrl+F Cheats List... Ctrl+L Backup Manager... Ctrl+B Open ISO... Ctrl+I Open CD Rom... Ctrl+C Emu-Compatibility http://www.emu-compatibility.com/yabause/index.php?lang=uk Sound... Video Driver 0 Ctrl+0 Alt+0 Yabause is not initialized, can't open ISO. Select your iso/cue/bin file Yabause Save State (*.iso *.cue *.bin) Yabause is not initialized, can't open CD Rom. Select a cdrom volume Couldn't save state file Couldn't load state file Choose a file to save your state Yabause Save State (*.yss ) Select a file to load your state %1 Images (*.%2) Choose a location for your screenshot An error occur while writing the screenshot. Yabause is not initialized, can't manage backup ram. yabause-0.9.13.1/src/qt/translations/yabause_pt.ts000644 001750 001750 00000124621 12256006054 024054 0ustar00guillaumeguillaume000000 000000 CommonDialogs Question... Questão... Warning... Aviso... Information... Informação... Get Item... Obter Item... Get Save File Name... Obter o Nome do Arquivo ao Salvar... Get Open File Name... Obter o Nome do Arquivo Aberto... Get Existing Directory... Obter o Diretório Existente... UIAbout About... Sobre... Yabause Qt Gui<br />Based on Yabause %1<br /><a href="http://yabause.org">http://yabause.org</a><br />The Yabause Team<br /><a href="mailto:pasnox@gmail.com">Filipe AZEVEDO</a> Gui Qt do Yabause<br />Baseado no Yabause %1<br /><a href="http://yabause.org">http://yabause.org</a><br />O Time Yabause<br /><a href="mailto:felipefpl@ig.com.br">Felipe</a> http://www.monkeystudio.org http://www.monkeystudio.org <a href="http://www.monkeystudio.org"><img src=":/icons/icons/made.png"></a> <a href="http://www.monkeystudio.org"><img src=":/icons/icons/made.png"></a> Ok Ok UIBackupRam %1/%2 blocks free %1/%2 blocos livres Japanese Japonês English Inglês French Francês German Alemão Spanish Espanhol Italian Italiano Unknow (%1) Desconhecido (%1) Are you sure you want to delete '%1' ? Você tem certeza que você quer apagar '%1'? Are you sure you want to format '%1' ? Você tem certeza que você quer formatar '%1'? Backup Ram Manager Gerenciador de Backup Ram Device List Lista de Dispositivos Save List Lista de Saves Delete Apagar Format Formatar Save Information Salvar as Informações File Name : Nome do Arquivo : Comment : Comentário : Language : Idioma : Data Size : Tamanho dos Dados : Block Size : Tamanho dos Blocos : UICheatAR Add Action Replay Code Adicionar o Código do Action Replay Action Replay Code : Código do Action Replay : Description : Descrição : <font color="red"><b>WARNING:</b> Master Codes are <b>NOT</b> supported.</font> <font color="red"><b>WARNING:</b> Os Códigos Mestres <b>NÃO</b> são suportados.</font> UICheatRaw Add Raw Memory Code Adicionar Código de Memória Raw Cheat Type Tipo de Trapaça Enable Ativar Byte Write Escrita do Byte Word Write Escrita da Palavra Long Write Escrita Longa Address : Endereço : Value : Valor : Description : Descrição : UICheats Enabled Ativado Disabled Desativado Unable to add code Incapaz de adicionar código Unable to change description Incapaz de mudar a descrição Invalid Address Endereço Inválido Invalid Value Valor Inválido Unable to remove code Incapaz de remover o código Choose a cheat file to save to Escolher um arquivo de trapaça para salvar Yabause Cheat Files (*.yct);;All Files (*) Arquivos de Trapaça do Yabause (*.yct);;Todos os Arquivos (*) Unable to open file for loading Incapaz de abrir o arquivo para carregar Choose a cheat file to open Escolha um arquivo de trapaça para abrí-lo Unable to open file for saving Incapaz de abrir o arquivo para salvá-lo Cheats Trapaças Code Código Description Descrição Status Status &Delete &Apagar &Clear &Limpar Add Codes... Adicionar Códigos... &Action Replay &Action Replay &Raw Memory Address &Endereço da Memória Raw Cheats File Arquivo de Trapaças &Save To File &Salvar como Arquivo &Load From File &Carregar do Arquivo UISettings CD Rom Drive... Drive de CD-Rom... Choose a bios file Escolha um arquivo bios The dummies cores don't need configuration. Os dummies cores não precisam de configuração. Choose a cdrom drive Escolha um drive de cd-rom Choose a cdrom mount point Escolha um ponto de montagem do cd-rom Choose a folder to store save states Escolha uma pasta para armazenar os save states Choose a cartridge file Escolha um arquivo de cartucho Choose a memory file Escolha um arquivo de memória Choose a mpeg rom Escolha uma rom mpeg Settings Configurações General Geral Bios Bios ... ... Cd-Rom Cd-Rom Save States Save States Video Vídeo Video Core Video Core Resolution Resolução Width Largura Fullscreen Tela Cheia Height Altura Video Format Formato do Vídeo Sound Som Sound Core Sound Core Cart/Memory Cart/Memória Cartridge Cartucho Memory Memória Mpeg ROM ROM Mpeg Input Entrada de Dados Up Pra cima - - A A Down Pra Baixo B B Left Esquerda C C Right Direita X X L L Left trigger Gatilho esquerdo Y Y R R Right trigger Gatilho direito Z Z Start Iniciar Advanced Avançado Region Região SH2 Interpreter SH2 Interpreter UIWaitInput Waiting Input... Esperando a Entrada de Dados... UIYabause Yabause is not initialized, can't open ISO. O Yabause não foi iniciado, não pôde abrir a ISO. Select your iso/cue/bin file Selecione seu arquivo iso/cue/bin Yabause Save State (*.iso *.cue *.bin) Save State do Yabause (*.iso*.cue*.bin) Yabause is not initialized, can't open CD Rom. O Yabause não foi iniciado, não pôde abrir o CD-Rom. Select a cdrom volume Selecione um volume de cd-rom Couldn't save state file Não pôde salvar o state em um arquivo Couldn't load state file Não pôde carregar o arquivo state Choose a file to save your state Escolha um arquivo para salvar seu state Yabause Save State (*.yss ) Save State do Yabause (*.yss) Select a file to load your state Selecione um arquivo para carregar seu state %1 Images (*.%2) Imagens %1 (*.%2) Choose a location for your screenshot Escolha um local para o seu screenshot An error occur while writing the screenshot. Um erro ocorreu enquanto escrevendo seu screenshot. Yabause is not initialized, can't manage backup ram. O Yabuase não foi iniciado, não pôde gerenciar a backup ram. Yabause Qt GUI GUI Qt do Yabause Volume Volume Mute/UnMute Sound Som Mudo/Não Mudo &File &Arquivo Save State Save State Load State Load State &View &Visualizar &Layer &Camada &Debug &Debug &Help &Ajuda Tools Ferramentas Emulation Emulação toolBar Barra de ferramentas &Quit &Sair Ctrl+Q Ctrl+Q Start Iniciar Stop Parar &Settings... &Configurações... Ctrl+S Ctrl+S R&un E&xecutar Pause Pausar &Pause &Pausar &Reset &Resetar Ctrl+R Ctrl+R Transfer Transferência Ctrl+N Ctrl+N Screenshot Screenshot Ctrl+Print Ctrl+Imprimir Frame Skip/Limiter Frame Skip/Limitador ScrollLock ScrollLock FPS FPS Vdp1 Vdp1 &Fullscreen &Tela Cheia Alt+Return Alt+Return Log Log MSH2 MSH2 SSH2 SSH2 Vdp2 Vdp2 M68K M68K SCU-DSP SCU-DSP SCSP SCSP Memory Dump Dump de Memória &About... &Sobre... NBG0 NBG0 NBG1 NBG1 NBG2 NBG2 NBG3 NBG3 RBG0 RBG0 1 1 Ctrl+1 Ctrl+1 2 2 Ctrl+2 Ctrl+2 3 3 Ctrl+3 Ctrl+3 4 4 Ctrl+4 Ctrl+4 5 5 Ctrl+5 Ctrl+5 6 6 Ctrl+6 Ctrl+6 7 7 Ctrl+7 Ctrl+7 8 8 Ctrl+8 Ctrl+8 9 9 Ctrl+9 Ctrl+9 Alt+1 Alt+1 Alt+2 Alt+2 Alt+3 Alt+3 Alt+4 Alt+4 Alt+5 Alt+5 Alt+6 Alt+6 Alt+7 Alt+7 Alt+8 Alt+8 Alt+9 Alt+9 To File... Para o Arquivo... Ctrl+T Ctrl+T From File... Do Arquivo... Ctrl+F Ctrl+F Cheats List... Lista de Trapaças... Ctrl+L Ctrl+L Backup Manager... Gerenciador de Backup... Ctrl+B Ctrl+B Open ISO... Abrir ISO... Ctrl+I Ctrl+I Open CD Rom... Abrir CD-Rom... Ctrl+C Ctrl+C Emu-Compatibility Compatibilidade do Emu http://www.emu-compatibility.com/yabause/index.php?lang=uk http://www.emu-compatibility.com/yabause/index.php?lang=uk Sound... Som... Video Driver Driver de Vídeo 0 0 Ctrl+0 Ctrl+0 Alt+0 Alt+0 yabause-0.9.13.1/src/qt/CommonDialogs.h000755 001750 001750 00000004030 12256006054 021525 0ustar00guillaumeguillaume000000 000000 /* Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef COMMONDIALOGS_H #define COMMONDIALOGS_H #include #include #include #include namespace CommonDialogs { bool question( const QString& message, const QString& caption = QT_TRANSLATE_NOOP( "CommonDialogs", "Question..." ) ); void warning( const QString& message, const QString& caption = QT_TRANSLATE_NOOP( "CommonDialogs", "Warning..." ) ); void information( const QString& message, const QString& caption = "Information..." ); QString getItem( const QStringList items, const QString& label, const QString& caption = QT_TRANSLATE_NOOP( "CommonDialogs", "Get Item..." ) ); QString getSaveFileName( const QString& directory = QString(), const QString& caption = QT_TRANSLATE_NOOP( "CommonDialogs", "Get Save File Name..." ), const QString& filter = QString() ); QString getOpenFileName( const QString& directory = QString(), const QString& caption = QT_TRANSLATE_NOOP( "CommonDialogs", "Get Open File Name..." ), const QString& filter = QString() ); QString getExistingDirectory( const QString& directory = QString(), const QString& caption = QT_TRANSLATE_NOOP( "CommonDialogs", "Get Existing Directory..." ), QFileDialog::Options options = QFileDialog::ShowDirsOnly ); }; #endif // COMMONDIALOGS_H yabause-0.9.13.1/src/qt/YabauseGL.cpp000644 001750 001750 00000002752 12256006055 021150 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "YabauseGL.h" #include "QtYabause.h" YabauseGL::YabauseGL( QWidget* p ) : QGLWidget( p ) { setFocusPolicy( Qt::StrongFocus ); if ( p ) { p->setFocusPolicy( Qt::StrongFocus ); setFocusProxy( p ); } } void YabauseGL::showEvent( QShowEvent* e ) { // hack for clearing the the gl context QGLWidget::showEvent( e ); QSize s = size(); resize( 0, 0 ); resize( s ); } void YabauseGL::resizeGL( int w, int h ) { updateView( QSize( w, h ) ); } void YabauseGL::updateView( const QSize& s ) { const QSize size = s.isValid() ? s : this->size(); glViewport( 0, 0, size.width(), size.height() ); if ( VIDCore ) VIDCore->Resize( size.width(), size.height(), 0 ); } yabause-0.9.13.1/src/qt/PerQt.c000644 001750 001750 00000002741 12256006105 020021 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "PerQt.h" int PERQTInit(void); void PERQTDeInit(void); int PERQTHandleEvents(void); u32 PERQTScan(u32 flags); void PERQTFlush(void); void PERQTKeyName(u32 key, char *name, int size); PerInterface_struct PERQT = { PERCORE_QT, "Qt Keyboard Input Interface", PERQTInit, PERQTDeInit, PERQTHandleEvents, PERQTScan, 0, PERQTFlush, PERQTKeyName }; int PERQTInit(void) { return 0; } void PERQTDeInit(void) {} int PERQTHandleEvents(void) { if ( YabauseExec() != 0 ) return -1; return 0; } u32 PERQTScan(u32 flags) { return 0; } void PERQTFlush(void) {} void PERQTKeyName(u32 key, char *name, int size) { snprintf(name, size, "%x", (unsigned int)key); } yabause-0.9.13.1/src/qt/YabauseThread.h000755 001750 001750 00000003532 12256006060 021516 0ustar00guillaumeguillaume000000 000000 /* Copyright 2005 Guillaume Duhamel Copyright 2005-2006, 2013 Theo Berkau Copyright 2008 Filipe Azevedo This file is part of Yabause. Yabause is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Yabause 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Yabause; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef YABAUSETHREAD_H #define YABAUSETHREAD_H #include #include #include #include "QtYabause.h" class YabauseThread : public QObject { Q_OBJECT public: YabauseThread( QObject* owner = 0 ); virtual ~YabauseThread(); yabauseinit_struct* yabauseConf(); bool emulationRunning(); bool emulationPaused(); inline int init() const { return mInit; } protected: yabauseinit_struct mYabauseConf; bool showFPS; QMutex mMutex; bool mPause; int mTimerId; int mInit; void initEmulation(); void deInitEmulation(); void resetYabauseConf(); void timerEvent( QTimerEvent* ); public slots: bool pauseEmulation( bool pause, bool reset ); bool resetEmulation(); void reloadControllers(); void reloadClock(); void reloadSettings(); signals: void requestSize( const QSize& size ); void requestFullscreen( bool fullscreen ); void requestVolumeChange( int volume ); void error( const QString& error, bool internal = true ); void pause( bool paused ); void reset(); }; #endif // YABAUSETHREAD_H yabause-0.9.13.1/src/qt/Arguments.cpp000644 001750 001750 00000013436 12256006055 021302 0ustar00guillaumeguillaume000000 000000 #include "Arguments.h" #include "VolatileSettings.h" #include "QtYabause.h" #include #include #include #include #include #include #include #include #include #include namespace Arguments { void autoframeskip(const QString& param); void autoload(const QString& param); void autostart(const QString& param); void binary(const QString& param); void bios(const QString& param); void cdrom(const QString& param); void fullscreen(const QString& param); void help(const QString& param); void iso(const QString& param); void nobios(const QString& param); void nosound(const QString& param); void version(const QString& param); struct Option { const char * shortname; const char * longname; const char * parameter; const char * description; unsigned short priority; void (*callback)(const QString& param); }; static Option LAST_OPTION = { NULL, NULL, NULL, NULL, 0 }; static Option availableOptions[] = { { NULL, "--autoframeskip=", "0|1", "Enable or disable auto frame skipping / limiting.", 2, autoframeskip }, { NULL, "--autoload=", "", "Automatically start emulation and load a save state.",1, autoload }, { "-a", "--autostart", NULL, "Automatically start emulation.", 1, autostart }, { NULL, "--binary=", "[:ADDRESS]", "Use a binary file.", 1, binary }, { "-b", "--bios=", "", "Choose a bios file.", 3, bios }, { "-c", "--cdrom=", "", "Choose the cdrom device.", 4, cdrom }, { "-f", "--fullscreen", NULL, "Start the emulator in fullscreen.", 5, fullscreen }, { "-h", "--help", NULL, "Show this help and exit.", 0, help }, { "-i", "--iso=", "", "Choose a dump file.", 4, iso }, { "-nb", "--no-bios", NULL, "Use the emulated bios", 3, nobios }, { "-ns", "--no-sound", NULL, "Turns sound off.", 6, nosound }, { "-v", "--version", NULL, "Show version and exit.", 0, version }, LAST_OPTION }; void parse() { QVector