diff --git a/3rdparty/miniz/.clang-format b/3rdparty/miniz/.clang-format new file mode 100644 index 0000000..5bdf624 --- /dev/null +++ b/3rdparty/miniz/.clang-format @@ -0,0 +1,46 @@ +# +# http://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +AccessModifierOffset: -4 +ConstructorInitializerIndentWidth: 4 +AlignEscapedNewlinesLeft: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakTemplateDeclarations: false +AlwaysBreakBeforeMultilineStrings: false +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BinPackParameters: true +ColumnLimit: 0 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +DerivePointerBinding: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +ObjCSpaceBeforeProtocolList: true +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 60 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerBindsToType: false +SpacesBeforeTrailingComments: 1 +Cpp11BracedListStyle: false +Standard: Cpp03 +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +BreakBeforeBraces: Allman +IndentFunctionDeclarationAfterType: false +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpaceAfterControlStatementKeyword: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 4 diff --git a/3rdparty/miniz/.gitignore b/3rdparty/miniz/.gitignore new file mode 100644 index 0000000..81631c6 --- /dev/null +++ b/3rdparty/miniz/.gitignore @@ -0,0 +1,2 @@ +/bin +/build diff --git a/3rdparty/miniz/.travis.yml b/3rdparty/miniz/.travis.yml new file mode 100644 index 0000000..7814bfb --- /dev/null +++ b/3rdparty/miniz/.travis.yml @@ -0,0 +1,2 @@ +language: c +script: bash amalgamate.sh \ No newline at end of file diff --git a/3rdparty/miniz/CMakeLists.txt b/3rdparty/miniz/CMakeLists.txt new file mode 100644 index 0000000..d9eb94a --- /dev/null +++ b/3rdparty/miniz/CMakeLists.txt @@ -0,0 +1,52 @@ +PROJECT(miniz_actual C) +cmake_minimum_required(VERSION 2.8.12) +if(CMAKE_BUILD_TYPE STREQUAL "") + # CMake defaults to leaving CMAKE_BUILD_TYPE empty. This screws up + # differentiation between debug and release builds. + set(CMAKE_BUILD_TYPE "Release" CACHE STRING + "Choose the type of build, options are: None (CMAKE_CXX_FLAGS or \ +CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif () + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) + +set(miniz_SOURCE miniz.c miniz_zip.c miniz_tinfl.c miniz_tdef.c) + +add_library(miniz_actual ${miniz_SOURCE}) +target_include_directories(miniz_actual PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") + +set(EXAMPLE1_SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/examples/example1.c") +set(EXAMPLE2_SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/examples/example2.c") +set(EXAMPLE3_SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/examples/example3.c") +set(EXAMPLE4_SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/examples/example4.c") +set(EXAMPLE5_SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/examples/example5.c") +set(EXAMPLE6_SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/examples/example6.c") +set(MINIZ_TESTER_SRC_LIST + "${CMAKE_CURRENT_SOURCE_DIR}/tests/miniz_tester.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/tests/timer.cpp") + +add_executable(example1 ${EXAMPLE1_SRC_LIST}) +target_link_libraries(example1 miniz_actual) +add_executable(example2 ${EXAMPLE2_SRC_LIST}) +target_link_libraries(example2 miniz_actual) +add_executable(example3 ${EXAMPLE3_SRC_LIST}) +target_link_libraries(example3 miniz_actual) +add_executable(example4 ${EXAMPLE4_SRC_LIST}) +target_link_libraries(example4 miniz_actual) +add_executable(example5 ${EXAMPLE5_SRC_LIST}) +target_link_libraries(example5 miniz_actual) +add_executable(example6 ${EXAMPLE6_SRC_LIST}) +target_link_libraries(example6 miniz_actual) +if(${UNIX}) + target_link_libraries(example6 m) +endif() + +# add_executable(miniz_tester ${MINIZ_TESTER_SRC_LIST}) +# target_link_libraries(miniz_tester miniz) + +install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + ) +file(GLOB INSTALL_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) +install(FILES ${INSTALL_HEADERS} DESTINATION include/${PROJECT_NAME}) \ No newline at end of file diff --git a/3rdparty/miniz/ChangeLog.md b/3rdparty/miniz/ChangeLog.md new file mode 100644 index 0000000..c821783 --- /dev/null +++ b/3rdparty/miniz/ChangeLog.md @@ -0,0 +1,176 @@ +## Changelog + +### 2.1.0 + + - More instances of memcpy instead of cast and use memcpy per default + - Remove inline for c90 support + - New function to read files via callback functions when adding them + - Fix out of bounds read while reading Zip64 extended information + - guard memcpy when n == 0 because buffer may be NULL + - Implement inflateReset() function + - Move comp/decomp alloc/free prototypes under guarding #ifndef MZ_NO_MALLOC + - Fix large file support under Windows + - Don't warn if _LARGEFILE64_SOURCE is not defined to 1 + - Fixes for MSVC warnings + - Remove check that path of file added to archive contains ':' or '\' + - Add !defined check on MINIZ_USE_ALIGNED_LOADS_AND_STORES + +### 2.0.8 + + - Remove unimplemented functions (mz_zip_locate_file and mz_zip_locate_file_v2) + - Add license, changelog, readme and example files to release zip + - Fix heap overflow to user buffer in tinfl_status tinfl_decompress + - Fix corrupt archive if uncompressed file smaller than 4 byte and the file is added by mz_zip_writer_add_mem* + +### 2.0.7 + + - Removed need in C++ compiler in cmake build + - Fixed a lot of uninitialized value errors found with Valgrind by memsetting m_dict to 0 in tdefl_init + - Fix resource leak in mz_zip_reader_init_file_v2 + - Fix assert with mz_zip_writer_add_mem* w/MZ_DEFAULT_COMPRESSION + - cmake build: install library and headers + - Remove _LARGEFILE64_SOURCE requirement from apple defines for large files + +### 2.0.6 + + - Improve MZ_ZIP_FLAG_WRITE_ZIP64 documentation + - Remove check for cur_archive_file_ofs > UINT_MAX because cur_archive_file_ofs is not used after this point + - Add cmake debug configuration + - Fix PNG height when creating png files + - Add "iterative" file extraction method based on mz_zip_reader_extract_to_callback. + - Option to use memcpy for unaligned data access + - Define processor/arch macros as zero if not set to one + +### 2.0.4/2.0.5 + + - Fix compilation with the various omission compile definitions + +### 2.0.3 + +- Fix GCC/clang compile warnings +- Added callback for periodic flushes (for ZIP file streaming) +- Use UTF-8 for file names in ZIP files per default + +### 2.0.2 + +- Fix source backwards compatibility with 1.x +- Fix a ZIP bit not being set correctly + +### 2.0.1 + +- Added some tests +- Added CI +- Make source code ANSI C compatible + +### 2.0.0 beta + +- Matthew Sitton merged miniz 1.x to Rich Geldreich's vogl ZIP64 changes. Miniz is now licensed as MIT since the vogl code base is MIT licensed +- Miniz is now split into several files +- Miniz does now not seek backwards when creating ZIP files. That is the ZIP files can be streamed +- Miniz automatically switches to the ZIP64 format when the created ZIP files goes over ZIP file limits +- Similar to [SQLite](https://www.sqlite.org/amalgamation.html) the Miniz source code is amalgamated into one miniz.c/miniz.h pair in a build step (amalgamate.sh). Please use miniz.c/miniz.h in your projects +- Miniz 2 is only source back-compatible with miniz 1.x. It breaks binary compatibility because structures changed + +### v1.16 BETA Oct 19, 2013 + +Still testing, this release is downloadable from [here](http://www.tenacioussoftware.com/miniz_v116_beta_r1.7z). Two key inflator-only robustness and streaming related changes. Also merged in tdefl_compressor_alloc(), tdefl_compressor_free() helpers to make script bindings easier for rustyzip. I would greatly appreciate any help with testing or any feedback. + +The inflator in raw (non-zlib) mode is now usable on gzip or similar streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). This version should never read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. + +The inflator now has a new failure status TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. This is scary behavior if the caller didn't know when to stop accepting output (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum). v1.16 will instead return TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. + +- The inflator coroutine func. is subtle and complex so I'm being cautious about this release. I would greatly appreciate any help with testing or any feedback. + I feel good about these changes, and they've been through several hours of automated testing, but they will probably not fix anything for the majority of prev. users so I'm + going to mark this release as beta for a few weeks and continue testing it at work/home on various things. +- The inflator in raw (non-zlib) mode is now usable on gzip or similiar data streams that have a bunch of bytes following the raw deflate data (problem discovered by rustyzip author williamw520). + This version should *never* read beyond the last byte of the raw deflate data independent of how many bytes you pass into the input buffer. This issue was caused by the various Huffman bitbuffer lookahead optimizations, and + would not be an issue if the caller knew and enforced the precise size of the raw compressed data *or* if the compressed data was in zlib format (i.e. always followed by the byte aligned zlib adler32). + So in other words, you can now call the inflator on deflate streams that are followed by arbitrary amounts of data and it's guaranteed that decompression will stop exactly on the last byte. +- The inflator now has a new failure status: TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS (-4). Previously, if the inflator was starved of bytes and could not make progress (because the input buffer was empty and the + caller did not set the TINFL_FLAG_HAS_MORE_INPUT flag - say on truncated or corrupted compressed data stream) it would append all 0's to the input and try to soldier on. + This is scary, because in the worst case, I believe it was possible for the prev. inflator to start outputting large amounts of literal data. If the caller didn't know when to stop accepting output + (because it didn't know how much uncompressed data was expected, or didn't enforce a sane maximum) it could continue forever. v1.16 cannot fall into this failure mode, instead it'll return + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS immediately if it needs 1 or more bytes to make progress, the input buf is empty, and the caller has indicated that no more input is available. This is a "soft" + failure, so you can call the inflator again with more input and it will try to continue, or you can give up and fail. This could be very useful in network streaming scenarios. +- Added documentation to all the tinfl return status codes, fixed miniz_tester so it accepts double minus params for Linux, tweaked example1.c, added a simple "follower bytes" test to miniz_tester.cpp. +### v1.15 r4 STABLE - Oct 13, 2013 + +Merged over a few very minor bug fixes that I fixed in the zip64 branch. This is downloadable from [here](http://code.google.com/p/miniz/downloads/list) and also in SVN head (as of 10/19/13). + + +### v1.15 - Oct. 13, 2013 + +Interim bugfix release while I work on the next major release with zip64 and streaming compression/decompression support. Fixed the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com), which could cause the locate files func to not find files when this flag was specified. Also fixed a bug in mz_zip_reader_extract_to_mem_no_alloc() with user provided read buffers (thanks kymoon). I also merged lots of compiler fixes from various github repo branches and Google Code issue reports. I finally added cmake support (only tested under for Linux so far), compiled and tested with clang v3.3 and gcc 4.6 (under Linux), added defl_write_image_to_png_file_in_memory_ex() (supports Y flipping for OpenGL use, real-time compression), added a new PNG example (example6.c - Mandelbrot), and I added 64-bit file I/O support (stat64(), etc.) for glibc. + +- Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug + would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() + (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). +- Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size +- Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. + Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). +- Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes +- mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed +- Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. +- Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti +- Merged MZ_FORCEINLINE fix from hdeanclark +- Fix include before config #ifdef, thanks emil.brink +- Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can + set it to 1 for real-time compression). +- Merged in some compiler fixes from paulharris's github repro. +- Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. +- Added example6.c, which dumps an image of the mandelbrot set to a PNG file. +- Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. +- In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled +- In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch + +### v1.14 - May 20, 2012 + +(SVN Only) Minor tweaks to get miniz.c compiling with the Tiny C Compiler, added #ifndef MINIZ_NO_TIME guards around utime.h includes. Adding mz_free() function, so the caller can free heap blocks returned by miniz using whatever heap functions it has been configured to use, MSVC specific fixes to use "safe" variants of several functions (localtime_s, fopen_s, freopen_s). + +MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). + +Compiler specific fixes, some from fermtect. I upgraded to TDM GCC 4.6.1 and now static __forceinline is giving it fits, so I'm changing all usage of __forceinline to MZ_FORCEINLINE and forcing gcc to use __attribute__((__always_inline__)) (and MSVC to use __forceinline). Also various fixes from fermtect for MinGW32: added #include , 64-bit ftell/fseek fixes. + +### v1.13 - May 19, 2012 + +From jason@cornsyrup.org and kelwert@mtu.edu - Most importantly, fixed mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bits. Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. Other stuff: + +Eliminated a bunch of warnings when compiling with GCC 32-bit/64. Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). + +Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) + +Fix ftell() usage in a few of the examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). Fix fail logic handling in mz_zip_add_mem_to_archive_file_in_place() so it always calls mz_zip_writer_finalize_archive() and mz_zip_writer_end(), even if the file add fails. + +- From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. +- Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. +- Eliminated a bunch of warnings when compiling with GCC 32-bit/64. +- Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly +"Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). +- Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. +- Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. +- Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. +- Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) +- Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). + +### v1.12 - 4/12/12 + +More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. +level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. + +### v1.11 - 5/28/11 + +Added statement from unlicense.org + +### v1.10 - 5/27/11 + +- Substantial compressor optimizations: +- Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). +- Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. +- Refactored the compression code for better readability and maintainability. +- Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large drop in throughput on some files). + +### v1.09 - 5/15/11 + +Initial stable release. + + diff --git a/3rdparty/miniz/LICENSE b/3rdparty/miniz/LICENSE new file mode 100644 index 0000000..b6ff45a --- /dev/null +++ b/3rdparty/miniz/LICENSE @@ -0,0 +1,22 @@ +Copyright 2013-2014 RAD Game Tools and Valve Software +Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + +All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/3rdparty/miniz/amalgamate.sh b/3rdparty/miniz/amalgamate.sh new file mode 100755 index 0000000..eea63d4 --- /dev/null +++ b/3rdparty/miniz/amalgamate.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +set -e + +mkdir -p amalgamation +OUTPUT_PREFIX=amalgamation/miniz + +cat miniz.h > $OUTPUT_PREFIX.h +cat miniz.c > $OUTPUT_PREFIX.c +cat miniz_common.h >> $OUTPUT_PREFIX.h +cat miniz_tdef.c >> $OUTPUT_PREFIX.c +cat miniz_tdef.h >> $OUTPUT_PREFIX.h +cat miniz_tinfl.c >> $OUTPUT_PREFIX.c +cat miniz_tinfl.h >> $OUTPUT_PREFIX.h +cat miniz_zip.c >> $OUTPUT_PREFIX.c +cat miniz_zip.h >> $OUTPUT_PREFIX.h + + +sed -i '0,/#include "miniz.h"/{s/#include "miniz.h"/#include "miniz.h"/}' $OUTPUT_PREFIX.c +for i in miniz miniz_common miniz_tdef miniz_tinfl miniz_zip +do + sed -i "s/#include \"$i.h\"//g" $OUTPUT_PREFIX.h + sed -i "s/#include \"$i.h\"//g" $OUTPUT_PREFIX.c +done + +echo "int main() { return 0; }" > main.c +echo "Test compile with GCC..." +gcc -pedantic -Wall main.c $OUTPUT_PREFIX.c -o test.out +echo "Test compile with GCC ANSI..." +gcc -ansi -pedantic -Wall main.c $OUTPUT_PREFIX.c -o test.out +if command -v clang +then + echo "Test compile with clang..." + clang -Wall -Wpedantic -fsanitize=unsigned-integer-overflow main.c $OUTPUT_PREFIX.c -o test.out +fi +for def in MINIZ_NO_STDIO MINIZ_NO_TIME MINIZ_NO_ARCHIVE_APIS MINIZ_NO_ARCHIVE_WRITING_APIS MINIZ_NO_ZLIB_APIS MINIZ_NO_ZLIB_COMPATIBLE_NAMES MINIZ_NO_MALLOC +do + echo "Test compile with GCC and define $def..." + gcc -ansi -pedantic -Wall main.c $OUTPUT_PREFIX.c -o test.out -D${def} +done +rm test.out +rm main.c + +cp ChangeLog.md amalgamation/ +cp LICENSE amalgamation/ +cp readme.md amalgamation/ +mkdir -p amalgamation/examples +cp examples/* amalgamation/examples/ + +cd amalgamation +! test -e miniz.zip || rm miniz.zip +cat << EOF | zip -@ miniz +miniz.c +miniz.h +ChangeLog.md +LICENSE +readme.md +examples/example1.c +examples/example2.c +examples/example3.c +examples/example4.c +examples/example5.c +examples/example6.c +EOF +cd .. + +echo "Amalgamation created." + + diff --git a/3rdparty/miniz/examples/example1.c b/3rdparty/miniz/examples/example1.c new file mode 100644 index 0000000..d6e33fa --- /dev/null +++ b/3rdparty/miniz/examples/example1.c @@ -0,0 +1,105 @@ +// example1.c - Demonstrates miniz.c's compress() and uncompress() functions (same as zlib's). +// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +#include +#include "miniz.h" +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +// The string to compress. +static const char *s_pStr = "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson." \ + "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."; + +int main(int argc, char *argv[]) +{ + uint step = 0; + int cmp_status; + uLong src_len = (uLong)strlen(s_pStr); + uLong cmp_len = compressBound(src_len); + uLong uncomp_len = src_len; + uint8 *pCmp, *pUncomp; + uint total_succeeded = 0; + (void)argc, (void)argv; + + printf("miniz.c version: %s\n", MZ_VERSION); + + do + { + // Allocate buffers to hold compressed and uncompressed data. + pCmp = (mz_uint8 *)malloc((size_t)cmp_len); + pUncomp = (mz_uint8 *)malloc((size_t)src_len); + if ((!pCmp) || (!pUncomp)) + { + printf("Out of memory!\n"); + return EXIT_FAILURE; + } + + // Compress the string. + cmp_status = compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len); + if (cmp_status != Z_OK) + { + printf("compress() failed!\n"); + free(pCmp); + free(pUncomp); + return EXIT_FAILURE; + } + + printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, (mz_uint32)cmp_len); + + if (step) + { + // Purposely corrupt the compressed data if fuzzy testing (this is a very crude fuzzy test). + uint n = 1 + (rand() % 3); + while (n--) + { + uint i = rand() % cmp_len; + pCmp[i] ^= (rand() & 0xFF); + } + } + + // Decompress. + cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len); + total_succeeded += (cmp_status == Z_OK); + + if (step) + { + printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, total_succeeded); + } + else + { + if (cmp_status != Z_OK) + { + printf("uncompress failed!\n"); + free(pCmp); + free(pUncomp); + return EXIT_FAILURE; + } + + printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, (mz_uint32)uncomp_len); + + // Ensure uncompress() returned the expected data. + if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t)src_len))) + { + printf("Decompression failed!\n"); + free(pCmp); + free(pUncomp); + return EXIT_FAILURE; + } + } + + free(pCmp); + free(pUncomp); + + step++; + + // Keep on fuzzy testing if there's a non-empty command line. + } while (argc >= 2); + + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/3rdparty/miniz/examples/example2.c b/3rdparty/miniz/examples/example2.c new file mode 100644 index 0000000..c3a84ba --- /dev/null +++ b/3rdparty/miniz/examples/example2.c @@ -0,0 +1,164 @@ +// example2.c - Simple demonstration of miniz.c's ZIP archive API's. +// Note this test deletes the test archive file "__mz_example2_test__.zip" in the current directory, then creates a new one with test data. +// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. + +#if defined(__GNUC__) + // Ensure we get the 64-bit variants of the CRT's file I/O calls + #ifndef _FILE_OFFSET_BITS + #define _FILE_OFFSET_BITS 64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE 1 + #endif +#endif + +#include +#include "miniz_zip.h" + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +// The string to compress. +static const char *s_pTest_str = + "MISSION CONTROL I wouldn't worry too much about the computer. First of all, there is still a chance that he is right, despite your tests, and" \ + "if it should happen again, we suggest eliminating this possibility by allowing the unit to remain in place and seeing whether or not it" \ + "actually fails. If the computer should turn out to be wrong, the situation is still not alarming. The type of obsessional error he may be" \ + "guilty of is not unknown among the latest generation of HAL 9000 computers. It has almost always revolved around a single detail, such as" \ + "the one you have described, and it has never interfered with the integrity or reliability of the computer's performance in other areas." \ + "No one is certain of the cause of this kind of malfunctioning. It may be over-programming, but it could also be any number of reasons. In any" \ + "event, it is somewhat analogous to human neurotic behavior. Does this answer your query? Zero-five-three-Zero, MC, transmission concluded."; + +static const char *s_pComment = "This is a comment"; + +int main(int argc, char *argv[]) +{ + int i, sort_iter; + mz_bool status; + size_t uncomp_size; + mz_zip_archive zip_archive; + void *p; + const int N = 50; + char data[2048]; + char archive_filename[64]; + static const char *s_Test_archive_filename = "__mz_example2_test__.zip"; + + assert((strlen(s_pTest_str) + 64) < sizeof(data)); + + printf("miniz.c version: %s\n", MZ_VERSION); + + (void)argc, (void)argv; + + // Delete the test archive, so it doesn't keep growing as we run this test + remove(s_Test_archive_filename); + + // Append a bunch of text files to the test archive + for (i = (N - 1); i >= 0; --i) + { + sprintf(archive_filename, "%u.txt", i); + sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i); + + // Add a new file to the archive. Note this is an IN-PLACE operation, so if it fails your archive is probably hosed (its central directory may not be complete) but it should be recoverable using zip -F or -FF. So use caution with this guy. + // A more robust way to add a file to an archive would be to read it into memory, perform the operation, then write a new archive out to a temp file and then delete/rename the files. + // Or, write a new archive to disk to a temp file, then delete/rename the files. For this test this API is fine. + status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, archive_filename, data, strlen(data) + 1, s_pComment, (uint16)strlen(s_pComment), MZ_BEST_COMPRESSION); + if (!status) + { + printf("mz_zip_add_mem_to_archive_file_in_place failed!\n"); + return EXIT_FAILURE; + } + } + + // Add a directory entry for testing + status = mz_zip_add_mem_to_archive_file_in_place(s_Test_archive_filename, "directory/", NULL, 0, "no comment", (uint16)strlen("no comment"), MZ_BEST_COMPRESSION); + if (!status) + { + printf("mz_zip_add_mem_to_archive_file_in_place failed!\n"); + return EXIT_FAILURE; + } + + // Now try to open the archive. + memset(&zip_archive, 0, sizeof(zip_archive)); + + status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, 0); + if (!status) + { + printf("mz_zip_reader_init_file() failed!\n"); + return EXIT_FAILURE; + } + + // Get and print information about each file in the archive. + for (i = 0; i < (int)mz_zip_reader_get_num_files(&zip_archive); i++) + { + mz_zip_archive_file_stat file_stat; + if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) + { + printf("mz_zip_reader_file_stat() failed!\n"); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + + printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u, Is Dir: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size, mz_zip_reader_is_file_a_directory(&zip_archive, i)); + + if (!strcmp(file_stat.m_filename, "directory/")) + { + if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) + { + printf("mz_zip_reader_is_file_a_directory() didn't return the expected results!\n"); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + } + } + + // Close the archive, freeing any resources it was using + mz_zip_reader_end(&zip_archive); + + // Now verify the compressed data + for (sort_iter = 0; sort_iter < 2; sort_iter++) + { + memset(&zip_archive, 0, sizeof(zip_archive)); + status = mz_zip_reader_init_file(&zip_archive, s_Test_archive_filename, sort_iter ? MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY : 0); + if (!status) + { + printf("mz_zip_reader_init_file() failed!\n"); + return EXIT_FAILURE; + } + + for (i = 0; i < N; i++) + { + sprintf(archive_filename, "%u.txt", i); + sprintf(data, "%u %s %u", (N - 1) - i, s_pTest_str, i); + + // Try to extract all the files to the heap. + p = mz_zip_reader_extract_file_to_heap(&zip_archive, archive_filename, &uncomp_size, 0); + if (!p) + { + printf("mz_zip_reader_extract_file_to_heap() failed!\n"); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + + // Make sure the extraction really succeeded. + if ((uncomp_size != (strlen(data) + 1)) || (memcmp(p, data, strlen(data)))) + { + printf("mz_zip_reader_extract_file_to_heap() failed to extract the proper data\n"); + mz_free(p); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + + printf("Successfully extracted file \"%s\", size %u\n", archive_filename, (uint)uncomp_size); + printf("File data: \"%s\"\n", (const char *)p); + + // We're done. + mz_free(p); + } + + // Close the archive, freeing any resources it was using + mz_zip_reader_end(&zip_archive); + } + + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/3rdparty/miniz/examples/example3.c b/3rdparty/miniz/examples/example3.c new file mode 100644 index 0000000..a97ba84 --- /dev/null +++ b/3rdparty/miniz/examples/example3.c @@ -0,0 +1,269 @@ +// example3.c - Demonstrates how to use miniz.c's deflate() and inflate() functions for simple file compression. +// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. +#include +#include +#include "miniz.h" + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +#define BUF_SIZE (1024 * 1024) +static uint8 s_inbuf[BUF_SIZE]; +static uint8 s_outbuf[BUF_SIZE]; + +int main(int argc, char *argv[]) +{ + const char *pMode; + FILE *pInfile, *pOutfile; + uint infile_size; + int level = Z_BEST_COMPRESSION; + z_stream stream; + int p = 1; + const char *pSrc_filename; + const char *pDst_filename; + long file_loc; + + printf("miniz.c version: %s\n", MZ_VERSION); + + if (argc < 4) + { + printf("Usage: example3 [options] [mode:c or d] infile outfile\n"); + printf("\nModes:\n"); + printf("c - Compresses file infile to a zlib stream in file outfile\n"); + printf("d - Decompress zlib stream in file infile to file outfile\n"); + printf("\nOptions:\n"); + printf("-l[0-10] - Compression level, higher values are slower.\n"); + return EXIT_FAILURE; + } + + while ((p < argc) && (argv[p][0] == '-')) + { + switch (argv[p][1]) + { + case 'l': + { + level = atoi(&argv[1][2]); + if ((level < 0) || (level > 10)) + { + printf("Invalid level!\n"); + return EXIT_FAILURE; + } + break; + } + default: + { + printf("Invalid option: %s\n", argv[p]); + return EXIT_FAILURE; + } + } + p++; + } + + if ((argc - p) < 3) + { + printf("Must specify mode, input filename, and output filename after options!\n"); + return EXIT_FAILURE; + } + else if ((argc - p) > 3) + { + printf("Too many filenames!\n"); + return EXIT_FAILURE; + } + + pMode = argv[p++]; + if (!strchr("cCdD", pMode[0])) + { + printf("Invalid mode!\n"); + return EXIT_FAILURE; + } + + pSrc_filename = argv[p++]; + pDst_filename = argv[p++]; + + printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); + + // Open input file. + pInfile = fopen(pSrc_filename, "rb"); + if (!pInfile) + { + printf("Failed opening input file!\n"); + return EXIT_FAILURE; + } + + // Determine input file's size. + fseek(pInfile, 0, SEEK_END); + file_loc = ftell(pInfile); + fseek(pInfile, 0, SEEK_SET); + + if ((file_loc < 0) || (file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + + // Open output file. + pOutfile = fopen(pDst_filename, "wb"); + if (!pOutfile) + { + printf("Failed opening output file!\n"); + return EXIT_FAILURE; + } + + printf("Input file size: %u\n", infile_size); + + // Init the z_stream + memset(&stream, 0, sizeof(stream)); + stream.next_in = s_inbuf; + stream.avail_in = 0; + stream.next_out = s_outbuf; + stream.avail_out = BUF_SIZE; + + if ((pMode[0] == 'c') || (pMode[0] == 'C')) + { + // Compression. + uint infile_remaining = infile_size; + + if (deflateInit(&stream, level) != Z_OK) + { + printf("deflateInit() failed!\n"); + return EXIT_FAILURE; + } + + for ( ; ; ) + { + int status; + if (!stream.avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint n = my_min(BUF_SIZE, infile_remaining); + + if (fread(s_inbuf, 1, n, pInfile) != n) + { + printf("Failed reading from input file!\n"); + return EXIT_FAILURE; + } + + stream.next_in = s_inbuf; + stream.avail_in = n; + + infile_remaining -= n; + //printf("Input bytes remaining: %u\n", infile_remaining); + } + + status = deflate(&stream, infile_remaining ? Z_NO_FLUSH : Z_FINISH); + + if ((status == Z_STREAM_END) || (!stream.avail_out)) + { + // Output buffer is full, or compression is done, so write buffer to output file. + uint n = BUF_SIZE - stream.avail_out; + if (fwrite(s_outbuf, 1, n, pOutfile) != n) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + stream.next_out = s_outbuf; + stream.avail_out = BUF_SIZE; + } + + if (status == Z_STREAM_END) + break; + else if (status != Z_OK) + { + printf("deflate() failed with status %i!\n", status); + return EXIT_FAILURE; + } + } + + if (deflateEnd(&stream) != Z_OK) + { + printf("deflateEnd() failed!\n"); + return EXIT_FAILURE; + } + } + else if ((pMode[0] == 'd') || (pMode[0] == 'D')) + { + // Decompression. + uint infile_remaining = infile_size; + + if (inflateInit(&stream)) + { + printf("inflateInit() failed!\n"); + return EXIT_FAILURE; + } + + for ( ; ; ) + { + int status; + if (!stream.avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint n = my_min(BUF_SIZE, infile_remaining); + + if (fread(s_inbuf, 1, n, pInfile) != n) + { + printf("Failed reading from input file!\n"); + return EXIT_FAILURE; + } + + stream.next_in = s_inbuf; + stream.avail_in = n; + + infile_remaining -= n; + } + + status = inflate(&stream, Z_SYNC_FLUSH); + + if ((status == Z_STREAM_END) || (!stream.avail_out)) + { + // Output buffer is full, or decompression is done, so write buffer to output file. + uint n = BUF_SIZE - stream.avail_out; + if (fwrite(s_outbuf, 1, n, pOutfile) != n) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + stream.next_out = s_outbuf; + stream.avail_out = BUF_SIZE; + } + + if (status == Z_STREAM_END) + break; + else if (status != Z_OK) + { + printf("inflate() failed with status %i!\n", status); + return EXIT_FAILURE; + } + } + + if (inflateEnd(&stream) != Z_OK) + { + printf("inflateEnd() failed!\n"); + return EXIT_FAILURE; + } + } + else + { + printf("Invalid mode!\n"); + return EXIT_FAILURE; + } + + fclose(pInfile); + if (EOF == fclose(pOutfile)) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + + printf("Total input bytes: %u\n", (mz_uint32)stream.total_in); + printf("Total output bytes: %u\n", (mz_uint32)stream.total_out); + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/3rdparty/miniz/examples/example4.c b/3rdparty/miniz/examples/example4.c new file mode 100644 index 0000000..eb591d4 --- /dev/null +++ b/3rdparty/miniz/examples/example4.c @@ -0,0 +1,102 @@ +// example4.c - Uses tinfl.c to decompress a zlib stream in memory to an output file +// Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +#include "miniz_tinfl.h" +#include +#include + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +static int tinfl_put_buf_func(const void* pBuf, int len, void *pUser) +{ + return len == (int)fwrite(pBuf, 1, len, (FILE*)pUser); +} + +int main(int argc, char *argv[]) +{ + int status; + FILE *pInfile, *pOutfile; + uint infile_size, outfile_size; + size_t in_buf_size; + uint8 *pCmp_data; + long file_loc; + + if (argc != 3) + { + printf("Usage: example4 infile outfile\n"); + printf("Decompresses zlib stream in file infile to file outfile.\n"); + printf("Input file must be able to fit entirely in memory.\n"); + printf("example3 can be used to create compressed zlib streams.\n"); + return EXIT_FAILURE; + } + + // Open input file. + pInfile = fopen(argv[1], "rb"); + if (!pInfile) + { + printf("Failed opening input file!\n"); + return EXIT_FAILURE; + } + + // Determine input file's size. + fseek(pInfile, 0, SEEK_END); + file_loc = ftell(pInfile); + fseek(pInfile, 0, SEEK_SET); + + if ((file_loc < 0) || (file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + + pCmp_data = (uint8 *)malloc(infile_size); + if (!pCmp_data) + { + printf("Out of memory!\n"); + return EXIT_FAILURE; + } + if (fread(pCmp_data, 1, infile_size, pInfile) != infile_size) + { + printf("Failed reading input file!\n"); + return EXIT_FAILURE; + } + + // Open output file. + pOutfile = fopen(argv[2], "wb"); + if (!pOutfile) + { + printf("Failed opening output file!\n"); + return EXIT_FAILURE; + } + + printf("Input file size: %u\n", infile_size); + + in_buf_size = infile_size; + status = tinfl_decompress_mem_to_callback(pCmp_data, &in_buf_size, tinfl_put_buf_func, pOutfile, TINFL_FLAG_PARSE_ZLIB_HEADER); + if (!status) + { + printf("tinfl_decompress_mem_to_callback() failed with status %i!\n", status); + return EXIT_FAILURE; + } + + outfile_size = ftell(pOutfile); + + fclose(pInfile); + if (EOF == fclose(pOutfile)) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + + printf("Total input bytes: %u\n", (uint)in_buf_size); + printf("Total output bytes: %u\n", outfile_size); + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/3rdparty/miniz/examples/example5.c b/3rdparty/miniz/examples/example5.c new file mode 100644 index 0000000..de23a53 --- /dev/null +++ b/3rdparty/miniz/examples/example5.c @@ -0,0 +1,328 @@ +// example5.c - Demonstrates how to use miniz.c's low-level tdefl_compress() and tinfl_inflate() API's for simple file to file compression/decompression. +// The low-level API's are the fastest, make no use of dynamic memory allocation, and are the most flexible functions exposed by miniz.c. +// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +// For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. + +// Purposely disable a whole bunch of stuff this low-level example doesn't use. +#define MINIZ_NO_STDIO +#define MINIZ_NO_ARCHIVE_APIS +#define MINIZ_NO_TIME +#define MINIZ_NO_ZLIB_APIS +#define MINIZ_NO_MALLOC +#include "miniz.h" + +// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). +#include +#include + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +// IN_BUF_SIZE is the size of the file read buffer. +// IN_BUF_SIZE must be >= 1 +#define IN_BUF_SIZE (1024*4) +static uint8 s_inbuf[IN_BUF_SIZE]; + +// COMP_OUT_BUF_SIZE is the size of the output buffer used during compression. +// COMP_OUT_BUF_SIZE must be >= 1 and <= OUT_BUF_SIZE +//#define COMP_OUT_BUF_SIZE (1024*512) +#define COMP_OUT_BUF_SIZE OUT_BUF_SIZE + +// OUT_BUF_SIZE is the size of the output buffer used during decompression. +// OUT_BUF_SIZE must be a power of 2 >= TINFL_LZ_DICT_SIZE (because the low-level decompressor not only writes, but reads from the output buffer as it decompresses) +#define OUT_BUF_SIZE (TINFL_LZ_DICT_SIZE) +//#define OUT_BUF_SIZE (1024*512) +static uint8 s_outbuf[OUT_BUF_SIZE]; + +// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). +// This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. +tdefl_compressor g_deflator; + +int main(int argc, char *argv[]) +{ + const char *pMode; + FILE *pInfile, *pOutfile; + uint infile_size; + int level = 9; + int p = 1; + const char *pSrc_filename; + const char *pDst_filename; + const void *next_in = s_inbuf; + size_t avail_in = 0; + void *next_out = s_outbuf; + size_t avail_out = OUT_BUF_SIZE; + size_t total_in = 0, total_out = 0; + long file_loc; + + assert(COMP_OUT_BUF_SIZE <= OUT_BUF_SIZE); + + printf("miniz.c example5 (demonstrates tinfl/tdefl)\n"); + + if (argc < 4) + { + printf("File to file compression/decompression using the low-level tinfl/tdefl API's.\n"); + printf("Usage: example5 [options] [mode:c or d] infile outfile\n"); + printf("\nModes:\n"); + printf("c - Compresses file infile to a zlib stream in file outfile\n"); + printf("d - Decompress zlib stream in file infile to file outfile\n"); + printf("\nOptions:\n"); + printf("-l[0-10] - Compression level, higher values are slower, 0 is none.\n"); + return EXIT_FAILURE; + } + + while ((p < argc) && (argv[p][0] == '-')) + { + switch (argv[p][1]) + { + case 'l': + { + level = atoi(&argv[1][2]); + if ((level < 0) || (level > 10)) + { + printf("Invalid level!\n"); + return EXIT_FAILURE; + } + break; + } + default: + { + printf("Invalid option: %s\n", argv[p]); + return EXIT_FAILURE; + } + } + p++; + } + + if ((argc - p) < 3) + { + printf("Must specify mode, input filename, and output filename after options!\n"); + return EXIT_FAILURE; + } + else if ((argc - p) > 3) + { + printf("Too many filenames!\n"); + return EXIT_FAILURE; + } + + pMode = argv[p++]; + if (!strchr("cCdD", pMode[0])) + { + printf("Invalid mode!\n"); + return EXIT_FAILURE; + } + + pSrc_filename = argv[p++]; + pDst_filename = argv[p++]; + + printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); + + // Open input file. + pInfile = fopen(pSrc_filename, "rb"); + if (!pInfile) + { + printf("Failed opening input file!\n"); + return EXIT_FAILURE; + } + + // Determine input file's size. + fseek(pInfile, 0, SEEK_END); + file_loc = ftell(pInfile); + fseek(pInfile, 0, SEEK_SET); + + if ((file_loc < 0) || (file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + + // Open output file. + pOutfile = fopen(pDst_filename, "wb"); + if (!pOutfile) + { + printf("Failed opening output file!\n"); + return EXIT_FAILURE; + } + + printf("Input file size: %u\n", infile_size); + + if ((pMode[0] == 'c') || (pMode[0] == 'C')) + { + // The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing. + static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + + tdefl_status status; + uint infile_remaining = infile_size; + + // create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). + mz_uint comp_flags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[MZ_MIN(10, level)] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + + // Initialize the low-level compressor. + status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); + if (status != TDEFL_STATUS_OKAY) + { + printf("tdefl_init() failed!\n"); + return EXIT_FAILURE; + } + + avail_out = COMP_OUT_BUF_SIZE; + + // Compression. + for ( ; ; ) + { + size_t in_bytes, out_bytes; + + if (!avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint n = my_min(IN_BUF_SIZE, infile_remaining); + + if (fread(s_inbuf, 1, n, pInfile) != n) + { + printf("Failed reading from input file!\n"); + return EXIT_FAILURE; + } + + next_in = s_inbuf; + avail_in = n; + + infile_remaining -= n; + //printf("Input bytes remaining: %u\n", infile_remaining); + } + + in_bytes = avail_in; + out_bytes = avail_out; + // Compress as much of the input as possible (or all of it) to the output buffer. + status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, infile_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + + next_in = (const char *)next_in + in_bytes; + avail_in -= in_bytes; + total_in += in_bytes; + + next_out = (char *)next_out + out_bytes; + avail_out -= out_bytes; + total_out += out_bytes; + + if ((status != TDEFL_STATUS_OKAY) || (!avail_out)) + { + // Output buffer is full, or compression is done or failed, so write buffer to output file. + uint n = COMP_OUT_BUF_SIZE - (uint)avail_out; + if (fwrite(s_outbuf, 1, n, pOutfile) != n) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + next_out = s_outbuf; + avail_out = COMP_OUT_BUF_SIZE; + } + + if (status == TDEFL_STATUS_DONE) + { + // Compression completed successfully. + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + // Compression somehow failed. + printf("tdefl_compress() failed with status %i!\n", status); + return EXIT_FAILURE; + } + } + } + else if ((pMode[0] == 'd') || (pMode[0] == 'D')) + { + // Decompression. + uint infile_remaining = infile_size; + + tinfl_decompressor inflator; + tinfl_init(&inflator); + + for ( ; ; ) + { + size_t in_bytes, out_bytes; + tinfl_status status; + if (!avail_in) + { + // Input buffer is empty, so read more bytes from input file. + uint n = my_min(IN_BUF_SIZE, infile_remaining); + + if (fread(s_inbuf, 1, n, pInfile) != n) + { + printf("Failed reading from input file!\n"); + return EXIT_FAILURE; + } + + next_in = s_inbuf; + avail_in = n; + + infile_remaining -= n; + } + + in_bytes = avail_in; + out_bytes = avail_out; + status = tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, s_outbuf, (mz_uint8 *)next_out, &out_bytes, (infile_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0) | TINFL_FLAG_PARSE_ZLIB_HEADER); + + avail_in -= in_bytes; + next_in = (const mz_uint8 *)next_in + in_bytes; + total_in += in_bytes; + + avail_out -= out_bytes; + next_out = (mz_uint8 *)next_out + out_bytes; + total_out += out_bytes; + + if ((status <= TINFL_STATUS_DONE) || (!avail_out)) + { + // Output buffer is full, or decompression is done, so write buffer to output file. + uint n = OUT_BUF_SIZE - (uint)avail_out; + if (fwrite(s_outbuf, 1, n, pOutfile) != n) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + next_out = s_outbuf; + avail_out = OUT_BUF_SIZE; + } + + // If status is <= TINFL_STATUS_DONE then either decompression is done or something went wrong. + if (status <= TINFL_STATUS_DONE) + { + if (status == TINFL_STATUS_DONE) + { + // Decompression completed successfully. + break; + } + else + { + // Decompression failed. + printf("tinfl_decompress() failed with status %i!\n", status); + return EXIT_FAILURE; + } + } + } + } + else + { + printf("Invalid mode!\n"); + return EXIT_FAILURE; + } + + fclose(pInfile); + if (EOF == fclose(pOutfile)) + { + printf("Failed writing to output file!\n"); + return EXIT_FAILURE; + } + + printf("Total input bytes: %u\n", (mz_uint32)total_in); + printf("Total output bytes: %u\n", (mz_uint32)total_out); + printf("Success.\n"); + return EXIT_SUCCESS; +} diff --git a/3rdparty/miniz/examples/example6.c b/3rdparty/miniz/examples/example6.c new file mode 100644 index 0000000..abbb64f --- /dev/null +++ b/3rdparty/miniz/examples/example6.c @@ -0,0 +1,162 @@ +// example6.c - Demonstrates how to miniz's PNG writer func +// Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. +// Mandlebrot set code from http://rosettacode.org/wiki/Mandelbrot_set#C +// Must link this example against libm on Linux. + +// Purposely disable a whole bunch of stuff this low-level example doesn't use. +#define MINIZ_NO_STDIO +#define MINIZ_NO_TIME +#define MINIZ_NO_ZLIB_APIS +#include "miniz.h" + +// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). +#include +#include +#include + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +typedef struct +{ + uint8 r, g, b; +} rgb_t; + +static void hsv_to_rgb(int hue, int min, int max, rgb_t *p) +{ + const int invert = 0; + const int saturation = 1; + const int color_rotate = 0; + + if (min == max) max = min + 1; + if (invert) hue = max - (hue - min); + if (!saturation) { + p->r = p->g = p->b = 255 * (max - hue) / (max - min); + return; + } + double h = fmod(color_rotate + 1e-4 + 4.0 * (hue - min) / (max - min), 6); + double c = 255.0f * saturation; + double X = c * (1 - fabs(fmod(h, 2) - 1)); + + p->r = p->g = p->b = 0; + + switch((int)h) { + case 0: p->r = c; p->g = X; return; + case 1: p->r = X; p->g = c; return; + case 2: p->g = c; p->b = X; return; + case 3: p->g = X; p->b = c; return; + case 4: p->r = X; p->b = c; return; + default:p->r = c; p->b = X; + } +} + +int main(int argc, char *argv[]) +{ + (void)argc, (void)argv; + + // Image resolution + const int iXmax = 4096; + const int iYmax = 4096; + + // Output filename + static const char *pFilename = "mandelbrot.png"; + + int iX, iY; + const double CxMin = -2.5; + const double CxMax = 1.5; + const double CyMin = -2.0; + const double CyMax = 2.0; + + double PixelWidth = (CxMax - CxMin) / iXmax; + double PixelHeight = (CyMax - CyMin) / iYmax; + + // Z=Zx+Zy*i ; Z0 = 0 + double Zx, Zy; + double Zx2, Zy2; // Zx2=Zx*Zx; Zy2=Zy*Zy + + int Iteration; + const int IterationMax = 200; + + // bail-out value , radius of circle + const double EscapeRadius = 2; + double ER2=EscapeRadius * EscapeRadius; + + uint8 *pImage = (uint8 *)malloc(iXmax * 3 * iYmax); + + // world ( double) coordinate = parameter plane + double Cx,Cy; + + int MinIter = 9999, MaxIter = 0; + + for(iY = 0; iY < iYmax; iY++) + { + Cy = CyMin + iY * PixelHeight; + if (fabs(Cy) < PixelHeight/2) + Cy = 0.0; // Main antenna + + for(iX = 0; iX < iXmax; iX++) + { + uint8 *color = pImage + (iX * 3) + (iY * iXmax * 3); + + Cx = CxMin + iX * PixelWidth; + + // initial value of orbit = critical point Z= 0 + Zx = 0.0; + Zy = 0.0; + Zx2 = Zx * Zx; + Zy2 = Zy * Zy; + + for (Iteration=0;Iteration> 8; + color[2] = 0; + + if (Iteration < MinIter) + MinIter = Iteration; + if (Iteration > MaxIter) + MaxIter = Iteration; + } + } + + for(iY = 0; iY < iYmax; iY++) + { + for(iX = 0; iX < iXmax; iX++) + { + uint8 *color = (uint8 *)(pImage + (iX * 3) + (iY * iXmax * 3)); + + uint Iterations = color[0] | (color[1] << 8U); + + hsv_to_rgb(Iterations, MinIter, MaxIter, (rgb_t *)color); + } + } + + // Now write the PNG image. + { + size_t png_data_size = 0; + void *pPNG_data = tdefl_write_image_to_png_file_in_memory_ex(pImage, iXmax, iYmax, 3, &png_data_size, 6, MZ_FALSE); + if (!pPNG_data) + fprintf(stderr, "tdefl_write_image_to_png_file_in_memory_ex() failed!\n"); + else + { + FILE *pFile = fopen(pFilename, "wb"); + fwrite(pPNG_data, 1, png_data_size, pFile); + fclose(pFile); + printf("Wrote %s\n", pFilename); + } + + // mz_free() is by default just an alias to free() internally, but if you've overridden miniz's allocation funcs you'll probably need to call mz_free(). + mz_free(pPNG_data); + } + + free(pImage); + + return EXIT_SUCCESS; +} diff --git a/3rdparty/miniz/miniz.c b/3rdparty/miniz/miniz.c new file mode 100644 index 0000000..d3e7665 --- /dev/null +++ b/3rdparty/miniz/miniz.c @@ -0,0 +1,628 @@ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include "miniz.h" + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) +{ + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; + } + + while (buf_len) + { + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; + } + + return ~crc32; +} +#endif + +void mz_free(void *p) +{ + MZ_FREE(p); +} + +void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +{ + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +void miniz_def_free_func(void *opaque, void *address) +{ + (void)opaque, (void)address; + MZ_FREE(address); +} +void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +{ + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) +{ + return MZ_VERSION; +} + +#ifndef MINIZ_NO_ZLIB_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) +{ + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) + { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; + } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflateReset(mz_streamp pStream) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + + pDecomp = (inflate_state *)pStream->state; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + /* pDecomp->m_window_bits = window_bits */; + + return MZ_OK; +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif /*MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ diff --git a/3rdparty/miniz/miniz.h b/3rdparty/miniz/miniz.h new file mode 100644 index 0000000..90e4860 --- /dev/null +++ b/3rdparty/miniz/miniz.h @@ -0,0 +1,477 @@ +/* miniz.c 2.1.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich , last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once + +#include "miniz_common.h" +#include "miniz_tdef.h" +#include "miniz_tinfl.h" + +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ + +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ + +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ + +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ + +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ + +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif + +#include + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#else +#define MINIZ_HAS_64BIT_REGISTERS 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API Definitions. */ + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; + +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +/* Compression strategies. */ +enum +{ + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* Method */ +#define MZ_DEFLATED 8 + +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); + +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +#define MZ_VERSION "10.1.0" +#define MZ_VERNUM 0xA100 +#define MZ_VER_MAJOR 10 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 + +#ifndef MINIZ_NO_ZLIB_APIS + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +const char *mz_version(void); + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +mz_ulong mz_compressBound(mz_ulong source_len); + +/* Initializes a decompressor. */ +int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ + +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/miniz/miniz_common.h b/3rdparty/miniz/miniz_common.h new file mode 100644 index 0000000..a10dae8 --- /dev/null +++ b/3rdparty/miniz/miniz_common.h @@ -0,0 +1,91 @@ +#pragma once +#include +#include +#include +#include + +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ + +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + int m_dummy; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t +#else +#define MZ_TIME_T time_t +#endif + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern void miniz_def_free_func(void *opaque, void *address); +extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/miniz/miniz_tdef.c b/3rdparty/miniz/miniz_tdef.c new file mode 100644 index 0000000..0419347 --- /dev/null +++ b/3rdparty/miniz/miniz_tdef.c @@ -0,0 +1,1581 @@ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include "miniz_tdef.h" +#include "miniz.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Compression (independent from all decompression API's) */ + +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; + +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 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, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 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, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) + { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) + { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) + { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) + { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } + else + { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) +{ + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) + { + MZ_ASSERT(TDEFL_LZ_DICT_SIZE_BITS >= 8); + int hdr0 = 0x08 | ((TDEFL_LZ_DICT_SIZE_BITS - 8) << 4); + TDEFL_PUT_BITS(hdr0, 8); + TDEFL_PUT_BITS(31 - (hdr0 << 8)%31 , 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); + + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) + { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) + { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) + { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } + else + { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); + break; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) +{ + mz_uint32 ret; + memcpy(&ret, p, sizeof(mz_uint32)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) +#endif +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) + { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); +#else + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; +#endif + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) + { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) +{ + return d->m_adler32; +} + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc() +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/miniz/miniz_tdef.h b/3rdparty/miniz/miniz_tdef.h new file mode 100644 index 0000000..22c1355 --- /dev/null +++ b/3rdparty/miniz/miniz_tdef.h @@ -0,0 +1,191 @@ +#pragma once +#include "miniz_common.h" + +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Compression API Definitions */ + +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 + +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE_BITS = 12, //15, + TDEFL_LZ_DICT_SIZE = (1u << TDEFL_LZ_DICT_SIZE_BITS), + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; + +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc(void); +void tdefl_compressor_free(tdefl_compressor *pComp); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/miniz/miniz_tinfl.c b/3rdparty/miniz/miniz_tinfl.c new file mode 100644 index 0000000..4832642 --- /dev/null +++ b/3rdparty/miniz/miniz_tinfl.c @@ -0,0 +1,746 @@ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + +#include "miniz_tinfl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do \ + { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +#ifdef USE_MU +#include "pico.h" +#define TINFL_CALL __not_in_flash("tinfl_decompress") +#else +#define TINFL_CALL +#endif +tinfl_status TINFL_CALL tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) + { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); +#else + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; +#endif + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + while(counter>2) + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + counter -= 3; + } + if (counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) + { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + + TINFL_CR_FINISH + +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) + { + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) +{ + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) + { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +#ifndef MINIZ_NO_MALLOC +tinfl_decompressor *tinfl_decompressor_alloc() +{ + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} + +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} +#endif + +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/miniz/miniz_tinfl.h b/3rdparty/miniz/miniz_tinfl.h new file mode 100644 index 0000000..c905b18 --- /dev/null +++ b/3rdparty/miniz/miniz_tinfl.h @@ -0,0 +1,146 @@ +#pragma once +#include "miniz_common.h" +/* ------------------- Low-level Decompression API Definitions */ + +#ifdef __cplusplus +extern "C" { +#endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tinfl_decompressor *tinfl_decompressor_alloc(void); +void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +//#define TINFL_LZ_DICT_SIZE 32768 +#define TINFL_LZ_DICT_SIZE 4096 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, +// TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_BITS = 4, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#else +#define TINFL_USE_64BIT_BITBUF 0 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} +#endif diff --git a/3rdparty/miniz/miniz_zip.c b/3rdparty/miniz/miniz_zip.c new file mode 100644 index 0000000..955ee20 --- /dev/null +++ b/3rdparty/miniz/miniz_zip.c @@ -0,0 +1,4710 @@ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ +#include "miniz_zip.h" + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- .ZIP archive reading */ + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include + +#if defined(_MSC_VER) || defined(__MINGW64__) +static FILE *mz_fopen(const char *pFilename, const char *pMode) +{ + FILE *pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; +} +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) +{ + FILE *pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; +} +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat64 +#define MZ_FILE_STAT _stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__GNUC__) && defined(_LARGEFILE64_SOURCE) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__APPLE__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum +{ + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct +{ + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag +{ + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) || defined(NDEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) +{ + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] +#else +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] +#endif + +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) + { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) +{ + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) +{ + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) +{ + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + if (n > 0) + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) +{ + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) +{ +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) + { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) +{ + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) +{ + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; + + if (size <= 1U) + return; + + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) + { + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; + } + + end = size - 1; + while (end > 0) + { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) + { + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) + { + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) + { + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data; + void* buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) + { + buf = MZ_MALLOC(ext_data_size); + if(buf==NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8*)buf; + } + else + { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) + { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_OBJ(*pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; + } + } + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) +{ + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} + +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) +{ + mz_uint64 file_size; + MZ_FILE *pFile; + + if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } + + file_size = MZ_FTELL64(pFile); + } + + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) +{ + mz_uint64 cur_file_ofs; + + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) +{ + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} + +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; + + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; + } + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; + } + + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) + { + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + } + } + + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) +{ + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const uint32_t size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + uint32_t file_index = pIndices[(uint32_t)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) +{ + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} + +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; + + if (pIndex) + *pIndex = 0; + + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + { + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } + + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } +#endif + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) + { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) + { + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return NULL; + } + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; + mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + } + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + } +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do + { + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) + { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + else + { + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) + { + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) + { + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; + } + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; + } + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); +#endif + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); + } + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; +} + +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } +#endif + } + + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); + + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); +#endif + + return status; +} + +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} + +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; + + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; + + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } + + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; + + file_crc32 = MZ_READ_LE32(pSrc); + + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } + + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else + { + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + + mz_zip_array_clear(pZip, &file_data_array); + + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) + { + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } + } + + return MZ_TRUE; + +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} + +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + uint32_t i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) + { + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else + { + if (pZip->m_total_files >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) + { + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + } + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; + } + + if (pErr) + *pErr = actual_err; + + return success; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +/* ------------------- .ZIP archive writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); +} + +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) +{ + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) +{ + return mz_zip_writer_init_v2(pZip, existing_size, 0); +} + +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) + { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) +{ + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} + +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) +{ + MZ_FILE *pFile; + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_OBJ(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) +{ + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; + + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_zip_internal_state *pState; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) +{ + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} + +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} + +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) +{ + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; + + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); +} + +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) +{ + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + if (!pZip->m_pState->m_zip64) + { + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; + + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + + MZ_CLEAR_OBJ(local_dir_header); + + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + if (uncomp_size) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 gen_flags = MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = size_to_add, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + mz_uint64 file_ofs = 0; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } + + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } + +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } +#endif + + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; + + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + if (uncomp_size && level) + { + method = MZ_DEFLATED; + } + + MZ_CLEAR_OBJ(local_dir_header); + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (uncomp_size) + { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (uncomp_remaining) + { + mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((read_callback(callback_opaque, file_ofs, pRead_buf, n) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + if (read_callback(callback_opaque, file_ofs, pRead_buf, in_buf_size)!= in_buf_size) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + file_ofs += in_buf_size; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? flush : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO + +static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); + + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + + return MZ_FREAD(pBuf, 1, n, pSrc_file); +} + +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, size_to_add, pFile_time, pComment, comment_size, level_and_flags, + user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); +} + +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); + + MZ_FCLOSE(pSrc_file); + + return status; +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) +{ + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) + { + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if ((pExt) && (ext_len)) + { + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + } + + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; + + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) + { + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); + } + + if (!pState->m_zip64) + { + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + while (src_archive_bytes_remaining) + { + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; + } + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) + { + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ + + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0)); + const mz_uint32 src_crc32 = pSrc_descriptor[0]; + const mz_uint64 src_comp_size = pSrc_descriptor[1]; + const mz_uint64 src_uncomp_size = pSrc_descriptor[2]; + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) + { + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; + } + + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + } + + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + { + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + } + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) +{ + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += central_dir_size; + } + + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; + + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + /* Write zip64 end of central directory locator */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; + } + + /* Write end of central directory record */ + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) +{ + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + *ppBuf = NULL; + *pSize = 0; + + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) +{ + return mz_zip_writer_end_internal(pZip, MZ_TRUE); +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) +{ + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); +} + +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) +{ + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; + } + else + { + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } + } + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if (!mz_zip_writer_end_internal(&zip_archive, status)) + { + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; + } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; +} + +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; +} + +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) +{ + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} + +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) + { + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write calledback failed"; + default: + break; + } + + return "unknown error"; +} + +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; + + return pZip->m_pState->m_zip64; +} + +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; +} + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_total_files : 0; +} + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) +{ + if (!pZip) + return 0; + return pZip->m_archive_size; +} + +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; +} + +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; +} + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) + { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ diff --git a/3rdparty/miniz/miniz_zip.h b/3rdparty/miniz/miniz_zip.h new file mode 100644 index 0000000..9314361 --- /dev/null +++ b/3rdparty/miniz/miniz_zip.h @@ -0,0 +1,436 @@ + +#pragma once +#include "miniz.h" + +/* ------------------- ZIP archive reading/writing */ + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; + +typedef struct +{ + /* Central directory file index. */ + mz_uint32 m_file_index; + + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; + + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + +#ifndef MINIZ_NO_TIME + MZ_TIME_T m_time; +#endif + + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; + + /* File's compressed size. */ + mz_uint64 m_comp_size; + + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; + + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; + + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; + + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; + + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; + +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct +{ + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct +{ + mz_zip_archive *pZip; + mz_uint flags; + + int status; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32; +#endif + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +} mz_zip_reader_extract_iter_state; + +/* -------- ZIP reading */ + +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is < 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); +#endif + +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +/* -------- ZIP reading or writing */ + +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +void mz_zip_zero_struct(mz_zip_archive *pZip); + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +int mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); +#endif + +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + uint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + uint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); +#endif + +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); +#endif + +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); + +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 size_to_add, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); +#endif + +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a poiner to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus +} +#endif + +#endif /* MINIZ_NO_ARCHIVE_APIS */ diff --git a/3rdparty/miniz/readme.md b/3rdparty/miniz/readme.md new file mode 100644 index 0000000..74b7eb3 --- /dev/null +++ b/3rdparty/miniz/readme.md @@ -0,0 +1,37 @@ +## Miniz + +Miniz is a lossless, high performance data compression library in a single source file that implements the zlib (RFC 1950) and Deflate (RFC 1951) compressed data format specification standards. It supports the most commonly used functions exported by the zlib library, but is a completely independent implementation so zlib's licensing requirements do not apply. Miniz also contains simple to use functions for writing .PNG format image files and reading/writing/appending .ZIP format archives. Miniz's compression speed has been tuned to be comparable to zlib's, and it also has a specialized real-time compressor function designed to compare well against fastlz/minilzo. + +## Usage + +Please use the files from the [releases page](https://github.com/richgel999/miniz/releases) in your projects. Do not use the git checkout directly! The different source and header files are [amalgamated](https://www.sqlite.org/amalgamation.html) into one `miniz.c`/`miniz.h` pair in a build step (`amalgamate.sh`). Include `miniz.c` and `miniz.h` in your project to use Miniz. + +## Features + +* MIT licensed +* A portable, single source and header file library written in plain C. Tested with GCC, clang and Visual Studio. +* Easily tuned and trimmed down by defines +* A drop-in replacement for zlib's most used API's (tested in several open source projects that use zlib, such as libpng and libzip). +* Fills a single threaded performance vs. compression ratio gap between several popular real-time compressors and zlib. For example, at level 1, miniz.c compresses around 5-9% better than minilzo, but is approx. 35% slower. At levels 2-9, miniz.c is designed to compare favorably against zlib's ratio and speed. See the miniz performance comparison page for example timings. +* Not a block based compressor: miniz.c fully supports stream based processing using a coroutine-style implementation. The zlib-style API functions can be called a single byte at a time if that's all you've got. +* Easy to use. The low-level compressor (tdefl) and decompressor (tinfl) have simple state structs which can be saved/restored as needed with simple memcpy's. The low-level codec API's don't use the heap in any way. +* Entire inflater (including optional zlib header parsing and Adler-32 checking) is implemented in a single function as a coroutine, which is separately available in a small (~550 line) source file: miniz_tinfl.c +* A fairly complete (but totally optional) set of .ZIP archive manipulation and extraction API's. The archive functionality is intended to solve common problems encountered in embedded, mobile, or game development situations. (The archive API's are purposely just powerful enough to write an entire archiver given a bit of additional higher-level logic.) + +## Known Problems + +* No support for encrypted archives. Not sure how useful this stuff is in practice. +* Minimal documentation. The assumption is that the user is already familiar with the basic zlib API. I need to write an API wiki - for now I've tried to place key comments before each enum/API, and I've included 6 examples that demonstrate how to use the module's major features. + +## Special Thanks + +Thanks to Alex Evans for the PNG writer function. Also, thanks to Paul Holden and Thorsten Scheuermann for feedback and testing, Matt Pritchard for all his encouragement, and Sean Barrett's various public domain libraries for inspiration (and encouraging me to write miniz.c in C, which was much more enjoyable and less painful than I thought it would be considering I've been programming in C++ for so long). + +Thanks to Bruce Dawson for reporting a problem with the level_and_flags archive API parameter (which is fixed in v1.12) and general feedback, and Janez Zemva for indirectly encouraging me into writing more examples. + +## Patents + +I was recently asked if miniz avoids patent issues. miniz purposely uses the same core algorithms as the ones used by zlib. The compressor uses vanilla hash chaining as described [here](http://www.gzip.org/zlib/rfc-deflate.html#algorithm). Also see the [gzip FAQ](http://www.gzip.org/#faq11). In my opinion, if miniz falls prey to a patent attack then zlib/gzip are likely to be at serious risk too. + + +[![Build Status](https://travis-ci.org/uroni/miniz.svg?branch=master)](https://travis-ci.org/uroni/miniz) \ No newline at end of file diff --git a/3rdparty/miniz/test.sh b/3rdparty/miniz/test.sh new file mode 100644 index 0000000..25d651a --- /dev/null +++ b/3rdparty/miniz/test.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -e + +. amalgamate.sh + +g++ tests/miniz_tester.cpp tests/timer.cpp amalgamation/miniz.c -o miniz_tester -I. -ggdb -O2 + +for i in 1 2 3 4 5 6 +do + gcc examples/example$i.c amalgamation/miniz.c -o example$i -lm -I. -ggdb +done + +mkdir -p test_scratch +if ! test -e "test_scratch/linux-4.8.11" +then + cd test_scratch + wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.8.11.tar.xz -O linux-4.8.11.tar.xz + tar xf linux-4.8.11.tar.xz + cd .. +fi + +cd test_scratch +../miniz_tester -v a linux-4.8.11 +../miniz_tester -v -r a linux-4.8.11 +../miniz_tester -v -b -r a linux-4.8.11 +../miniz_tester -v -a a linux-4.8.11 + +mkdir -p large_file +truncate -s 5G large_file/lf +../miniz_tester -v -a a large_file diff --git a/3rdparty/miniz/tests/miniz_tester.cpp b/3rdparty/miniz/tests/miniz_tester.cpp new file mode 100644 index 0000000..fb9cbd1 --- /dev/null +++ b/3rdparty/miniz/tests/miniz_tester.cpp @@ -0,0 +1,1881 @@ +// miniz_tester.cpp +// Note: This module is not intended to make a good example, or be used for anything other than testing. +// It's something quick I put together last year to help regression test miniz/tinfl under Linux/Win32/Mac. It's derived from LZHAM's test module. +#ifdef _MSC_VER +#pragma warning (disable:4127) // warning C4127: conditional expression is constant +#endif + +#if defined(__GNUC__) + // Ensure we get the 64-bit variants of the CRT's file I/O calls + #ifndef _FILE_OFFSET_BITS + #define _FILE_OFFSET_BITS 64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE 1 + #endif +#endif + +#include "miniz.h" +#include "miniz_zip.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "timer.h" + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define TDEFL_PRINT_OUTPUT_PROGRESS + +#if defined(WIN32) + #define WIN32_LEAN_AND_MEAN + #include + #define FILE_STAT_STRUCT _stat + #define FILE_STAT _stat +#else + #include + #define Sleep(ms) usleep(ms*1000) + #define _aligned_malloc(size, alignment) memalign(alignment, size) + #define _aligned_free free + #define fopen fopen64 + #define _fseeki64 fseeko64 + #define _ftelli64 ftello64 + #define _stricmp strcasecmp + #define FILE_STAT_STRUCT stat64 + #define FILE_STAT stat64 +#endif + +#ifdef WIN32 +#define QUAD_INT_FMT "%I64u" +#else +#define QUAD_INT_FMT "%llu" +#endif + +#ifdef _DEBUG +const bool g_is_debug = true; +#else +const bool g_is_debug = false; +#endif + +typedef unsigned char uint8; +typedef unsigned int uint; +typedef unsigned int uint32; +typedef unsigned long long uint64; +typedef long long int64; + +#define TDEFLTEST_COMP_INPUT_BUFFER_SIZE 1024*1024*2 +#define TDEFLTEST_COMP_OUTPUT_BUFFER_SIZE 1024*1024*2 +#define TDEFLTEST_DECOMP_INPUT_BUFFER_SIZE 1024*1024*2 + +static float s_max_small_comp_ratio, s_max_large_comp_ratio; + +struct comp_options +{ + comp_options() : + m_level(7), + m_unbuffered_decompression(false), + m_verify_compressed_data(false), + m_randomize_params(false), + m_randomize_buffer_sizes(false), + m_z_strat(Z_DEFAULT_STRATEGY), + m_random_z_flushing(false), + m_write_zlib_header(true), + m_archive_test(false), + m_write_archives(false) + { + } + + void print() + { + printf("Level: %u\n", m_level); + printf("Write zlib header: %u\n", (uint)m_write_zlib_header); + printf("Unbuffered decompression: %u\n", (uint)m_unbuffered_decompression); + printf("Verify compressed data: %u\n", (uint)m_verify_compressed_data); + printf("Randomize parameters: %u\n", m_randomize_params); + printf("Randomize buffer sizes: %u\n", m_randomize_buffer_sizes); + printf("Deflate strategy: %u\n", m_z_strat); + printf("Random Z stream flushing: %u\n", m_random_z_flushing); + printf("Archive test: %u\n", m_archive_test); + printf("Write archives: %u\n", m_write_archives); + } + + uint m_level; + bool m_unbuffered_decompression; + bool m_verify_compressed_data; + bool m_randomize_params; + bool m_randomize_buffer_sizes; + uint m_z_strat; + bool m_random_z_flushing; + bool m_write_zlib_header; + bool m_archive_test; + bool m_write_archives; +}; + +#define RND_SHR3(x) (x ^= (x << 17), x ^= (x >> 13), x ^= (x << 5)) + +#if 0 +static void random_fill(uint8 *pDst, size_t len, uint32 x) +{ + x ^= (x << 16); + if (!x) x++; + + while (len) + { + RND_SHR3(x); uint64 l0 = x & 0xFFF; + RND_SHR3(x); uint64 l1 = x & 0xFFF; + RND_SHR3(x); uint64 l2 = x & 0xFFF; + RND_SHR3(x); uint c = x; + + uint l = (uint)(((l0*l1*l2)/(16769025ULL) * 32) / 4095); + l = (uint)my_max(1,my_min(l, len)); + len -= l; + + while (l--) + { + *pDst++ = (uint8)c; + } + + if (((int)x < 0) && len) + { + *pDst++ = 0; + len--; + } + } +} +#endif + +static void print_usage() +{ + printf("Usage: [options] [mode] inpath/infile [outfile]\n"); + printf("\n"); + printf("Modes:\n"); + printf("c - Compress \"infile\" to \"outfile\"\n"); + printf("d - Decompress \"infile\" to \"outfile\"\n"); + printf("a - Recursively compress all files under \"inpath\"\n"); + printf("r - Archive decompression test\n"); + printf("\n"); + printf("Options:\n"); + printf("-m[0-10] - Compression level: 0=fastest (Huffman only), 9=best (10=uber)\n"); + printf("-u - Use unbuffered decompression on files that can fit into memory.\n"); + printf(" Unbuffered decompression is faster, but may have more I/O overhead.\n"); + printf("-v - Immediately decompress compressed file after compression for verification.\n"); + printf("-z - Do not write zlib header\n"); + printf("-r - Randomize parameters during recursive testing\n"); + printf("-b - Randomize input/output buffer sizes\n"); + printf("-h - Use random z_flushing\n"); + printf("-x# - Set rand() seed to value\n"); + printf("-t# - Set z_strategy to value [0-4]\n"); + printf("-a - Create single-file archives instead of files during testing\n"); + printf("-w - Test archive cloning\n"); +} + +static void print_error(const char *pMsg, ...) +{ + char buf[1024]; + + va_list args; + va_start(args, pMsg); + vsnprintf(buf, sizeof(buf), pMsg, args); + va_end(args); + + buf[sizeof(buf) - 1] = '\0'; + + fprintf(stderr, "Error: %s", buf); +} + +static FILE* open_file_with_retries(const char *pFilename, const char* pMode) +{ + const uint cNumRetries = 8; + for (uint i = 0; i < cNumRetries; i++) + { + FILE* pFile = fopen(pFilename, pMode); + if (pFile) + return pFile; + Sleep(250); + } + return NULL; +} + +static bool ensure_file_exists_and_is_readable(const char *pFilename) +{ + FILE *p = fopen(pFilename, "rb"); + if (!p) + return false; + + _fseeki64(p, 0, SEEK_END); + uint64 src_file_size = _ftelli64(p); + _fseeki64(p, 0, SEEK_SET); + + if (src_file_size) + { + char buf[1]; + if (fread(buf, 1, 1, p) != 1) + { + fclose(p); + return false; + } + } + fclose(p); + return true; +} + +static bool ensure_file_is_writable(const char *pFilename) +{ + const int cNumRetries = 8; + for (int i = 0; i < cNumRetries; i++) + { + FILE *pFile = fopen(pFilename, "wb"); + if (pFile) + { + fclose(pFile); + return true; + } + Sleep(250); + } + return false; +} + +static int simple_test1(const comp_options &options) +{ + (void)options; + + size_t cmp_len = 0; + + const char *p = "This is a test.This is a test.This is a test.1234567This is a test.This is a test.123456"; + size_t uncomp_len = strlen(p); + + void *pComp_data = tdefl_compress_mem_to_heap(p, uncomp_len, &cmp_len, TDEFL_WRITE_ZLIB_HEADER); + if (!pComp_data) + { + free(pComp_data); + print_error("Compression test failed!\n"); + return EXIT_FAILURE; + } + + printf("Uncompressed size: %u\nCompressed size: %u\n", (uint)uncomp_len, (uint)cmp_len); + + size_t decomp_len = 0; + void *pDecomp_data = tinfl_decompress_mem_to_heap(pComp_data, cmp_len, &decomp_len, TINFL_FLAG_PARSE_ZLIB_HEADER); + + if ((!pDecomp_data) || (decomp_len != uncomp_len) || (memcmp(pDecomp_data, p, uncomp_len))) + { + free(pComp_data); + free(pDecomp_data); + print_error("Compression test failed!\n"); + return EXIT_FAILURE; + } + + printf("Low-level API compression test succeeded.\n"); + + free(pComp_data); + free(pDecomp_data); + + return EXIT_SUCCESS; +} + +static int simple_test2(const comp_options &options) +{ + (void)options; + + uint8 cmp_buf[1024], decomp_buf[1024]; + uLong cmp_len = sizeof(cmp_buf); + + const char *p = "This is a test.This is a test.This is a test.1234567This is a test.This is a test.123456"; + uLong uncomp_len = (uLong)strlen(p); + + int status = compress(cmp_buf, &cmp_len, (const uint8*)p, uncomp_len); + if (status != Z_OK) + { + print_error("Compression test failed!\n"); + return EXIT_FAILURE; + } + + printf("Uncompressed size: %u\nCompressed size: %u\n", (uint)uncomp_len, (uint)cmp_len); + + if (cmp_len > compressBound(uncomp_len)) + { + print_error("compressBound() returned bogus result\n"); + return EXIT_FAILURE; + } + + uLong decomp_len = sizeof(decomp_buf); + status = uncompress(decomp_buf, &decomp_len, cmp_buf, cmp_len);; + + if ((status != Z_OK) || (decomp_len != uncomp_len) || (memcmp(decomp_buf, p, uncomp_len))) + { + print_error("Compression test failed!\n"); + return EXIT_FAILURE; + } + + printf("zlib API compression test succeeded.\n"); + + return EXIT_SUCCESS; +} + +static bool compress_file_zlib(const char* pSrc_filename, const char *pDst_filename, const comp_options &options) +{ + printf("Testing: Streaming zlib compression\n"); + + FILE *pInFile = fopen(pSrc_filename, "rb"); + if (!pInFile) + { + print_error("Unable to read file: %s\n", pSrc_filename); + return false; + } + + FILE *pOutFile = fopen(pDst_filename, "wb"); + if (!pOutFile) + { + print_error("Unable to create file: %s\n", pDst_filename); + return false; + } + + _fseeki64(pInFile, 0, SEEK_END); + uint64 src_file_size = _ftelli64(pInFile); + _fseeki64(pInFile, 0, SEEK_SET); + + fputc('D', pOutFile); fputc('E', pOutFile); fputc('F', pOutFile); fputc('0', pOutFile); + fputc(options.m_write_zlib_header, pOutFile); + + for (uint i = 0; i < 8; i++) + fputc(static_cast((src_file_size >> (i * 8)) & 0xFF), pOutFile); + + uint cInBufSize = TDEFLTEST_COMP_INPUT_BUFFER_SIZE; + uint cOutBufSize = TDEFLTEST_COMP_OUTPUT_BUFFER_SIZE; + if (options.m_randomize_buffer_sizes) + { + cInBufSize = 1 + (rand() % 4096); + cOutBufSize = 1 + (rand() % 4096); + } + printf("Input buffer size: %u, Output buffer size: %u\n", cInBufSize, cOutBufSize); + + uint8 *in_file_buf = static_cast(_aligned_malloc(cInBufSize, 16)); + uint8 *out_file_buf = static_cast(_aligned_malloc(cOutBufSize, 16)); + if ((!in_file_buf) || (!out_file_buf)) + { + print_error("Out of memory!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + uint64 src_bytes_left = src_file_size; + + uint in_file_buf_size = 0; + uint in_file_buf_ofs = 0; + + uint64 total_output_bytes = 0; + + timer_ticks start_time = timer::get_ticks(); + + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); + + timer_ticks init_start_time = timer::get_ticks(); + int status = deflateInit2(&zstream, options.m_level, Z_DEFLATED, options.m_write_zlib_header ? Z_DEFAULT_WINDOW_BITS : -Z_DEFAULT_WINDOW_BITS, 9, options.m_z_strat); + timer_ticks total_init_time = timer::get_ticks() - init_start_time; + + if (status != Z_OK) + { + print_error("Failed initializing compressor!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + printf("deflateInit2() took %3.3fms\n", timer::ticks_to_secs(total_init_time)*1000.0f); + + uint32 x = my_max(1, (uint32)(src_file_size ^ (src_file_size >> 32))); + + for ( ; ; ) + { + if (src_file_size) + { + double total_elapsed_time = timer::ticks_to_secs(timer::get_ticks() - start_time); + double total_bytes_processed = static_cast(src_file_size - src_bytes_left); + double comp_rate = (total_elapsed_time > 0.0f) ? total_bytes_processed / total_elapsed_time : 0.0f; + +#ifdef TDEFL_PRINT_OUTPUT_PROGRESS + for (int i = 0; i < 15; i++) + printf("\b\b\b\b"); + printf("Progress: %3.1f%%, Bytes Remaining: %3.1fMB, %3.3fMB/sec", (1.0f - (static_cast(src_bytes_left) / src_file_size)) * 100.0f, src_bytes_left / 1048576.0f, comp_rate / (1024.0f * 1024.0f)); + printf(" \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); +#endif + } + + if (in_file_buf_ofs == in_file_buf_size) + { + in_file_buf_size = static_cast(my_min(cInBufSize, src_bytes_left)); + + if (fread(in_file_buf, 1, in_file_buf_size, pInFile) != in_file_buf_size) + { + printf("\n"); + print_error("Failure reading from source file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + deflateEnd(&zstream); + return false; + } + + src_bytes_left -= in_file_buf_size; + + in_file_buf_ofs = 0; + } + + zstream.next_in = &in_file_buf[in_file_buf_ofs]; + zstream.avail_in = in_file_buf_size - in_file_buf_ofs; + zstream.next_out = out_file_buf; + zstream.avail_out = cOutBufSize; + + int flush = !src_bytes_left ? Z_FINISH : Z_NO_FLUSH; + if ((flush == Z_NO_FLUSH) && (options.m_random_z_flushing)) + { + RND_SHR3(x); + if ((x & 15) == 0) + { + RND_SHR3(x); + flush = (x & 31) ? Z_SYNC_FLUSH : Z_FULL_FLUSH; + } + } + status = deflate(&zstream, flush); + + uint num_in_bytes = (in_file_buf_size - in_file_buf_ofs) - zstream.avail_in; + uint num_out_bytes = cOutBufSize - zstream.avail_out; + if (num_in_bytes) + { + in_file_buf_ofs += (uint)num_in_bytes; + assert(in_file_buf_ofs <= in_file_buf_size); + } + + if (num_out_bytes) + { + if (fwrite(out_file_buf, 1, static_cast(num_out_bytes), pOutFile) != num_out_bytes) + { + printf("\n"); + print_error("Failure writing to destination file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + deflateEnd(&zstream); + return false; + } + + total_output_bytes += num_out_bytes; + } + + if (status != Z_OK) + break; + } + +#ifdef TDEFL_PRINT_OUTPUT_PROGRESS + for (int i = 0; i < 15; i++) + { + printf("\b\b\b\b \b\b\b\b"); + } +#endif + + src_bytes_left += (in_file_buf_size - in_file_buf_ofs); + + uint32 adler32 = zstream.adler; + deflateEnd(&zstream); + + timer_ticks end_time = timer::get_ticks(); + double total_time = timer::ticks_to_secs(my_max(1, end_time - start_time)); + + uint64 cmp_file_size = _ftelli64(pOutFile); + + _aligned_free(in_file_buf); + in_file_buf = NULL; + _aligned_free(out_file_buf); + out_file_buf = NULL; + + fclose(pInFile); + pInFile = NULL; + fclose(pOutFile); + pOutFile = NULL; + + if (status != Z_STREAM_END) + { + print_error("Compression failed with status %i\n", status); + return false; + } + + if (src_bytes_left) + { + print_error("Compressor failed to consume entire input file!\n"); + return false; + } + + printf("Success\n"); + printf("Input file size: " QUAD_INT_FMT ", Compressed file size: " QUAD_INT_FMT ", Ratio: %3.2f%%\n", src_file_size, cmp_file_size, src_file_size ? ((1.0f - (static_cast(cmp_file_size) / src_file_size)) * 100.0f) : 0.0f); + printf("Compression time: %3.6f\nConsumption rate: %9.1f bytes/sec, Emission rate: %9.1f bytes/sec\n", total_time, src_file_size / total_time, cmp_file_size / total_time); + printf("Input file adler32: 0x%08X\n", adler32); + if (src_file_size) + { + if (src_file_size >= 256) + s_max_large_comp_ratio = my_max(s_max_large_comp_ratio, cmp_file_size / (float)src_file_size); + else + s_max_small_comp_ratio = my_max(s_max_small_comp_ratio, cmp_file_size / (float)src_file_size); + } + //printf("Max small comp ratio: %f, Max large comp ratio: %f\n", s_max_small_comp_ratio, s_max_large_comp_ratio); + + return true; +} + +static bool decompress_file_zlib(const char* pSrc_filename, const char *pDst_filename, comp_options options) +{ + FILE *pInFile = fopen(pSrc_filename, "rb"); + if (!pInFile) + { + print_error("Unable to read file: %s\n", pSrc_filename); + return false; + } + + _fseeki64(pInFile, 0, SEEK_END); + uint64 src_file_size = _ftelli64(pInFile); + _fseeki64(pInFile, 0, SEEK_SET); + if (src_file_size < (5+9)) + { + print_error("Compressed file is too small!\n"); + fclose(pInFile); + return false; + } + + int h0 = fgetc(pInFile); + int h1 = fgetc(pInFile); + int h2 = fgetc(pInFile); + int h3 = fgetc(pInFile); + int zlib_header = fgetc(pInFile); + if ((h0 != 'D') | (h1 != 'E') || (h2 != 'F') || (h3 != '0')) + { + print_error("Unrecognized/invalid header in file: %s\n", pSrc_filename); + fclose(pInFile); + return false; + } + + FILE *pOutFile = fopen(pDst_filename, "wb"); + if (!pOutFile) + { + print_error("Unable to create file: %s\n", pDst_filename); + fclose(pInFile); + return false; + } + + uint64 orig_file_size = 0; + for (uint i = 0; i < 8; i++) + orig_file_size |= (static_cast(fgetc(pInFile)) << (i * 8)); + + int total_header_bytes = ftell(pInFile); + + // Avoid running out of memory on large files when using unbuffered decompression. + if ((options.m_unbuffered_decompression) && (orig_file_size > 768*1024*1024)) + { + printf("Output file is too large for unbuffered decompression - switching to streaming decompression.\n"); + options.m_unbuffered_decompression = false; + } + + if (options.m_unbuffered_decompression) + printf("Testing: Unbuffered decompression\n"); + else + printf("Testing: Streaming decompression\n"); + + uint cInBufSize = options.m_unbuffered_decompression ? static_cast(src_file_size) : TDEFLTEST_DECOMP_INPUT_BUFFER_SIZE; + uint out_buf_size = options.m_unbuffered_decompression ? static_cast(orig_file_size) : TINFL_LZ_DICT_SIZE; + + if ((options.m_randomize_buffer_sizes) && (!options.m_unbuffered_decompression)) + { + cInBufSize = 1 + (rand() % 4096); + } + + printf("Input buffer size: %u, Output buffer size: %u\n", cInBufSize, out_buf_size); + + uint8 *in_file_buf = static_cast(_aligned_malloc(cInBufSize, 16)); + uint8 *out_file_buf = static_cast(_aligned_malloc(out_buf_size, 16)); + if ((!in_file_buf) || (!out_file_buf)) + { + print_error("Failed allocating output buffer!\n"); + _aligned_free(in_file_buf); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + uint64 src_bytes_left = src_file_size - total_header_bytes; + uint64 dst_bytes_left = orig_file_size; + + uint in_file_buf_size = 0; + uint in_file_buf_ofs = 0; + uint out_file_buf_ofs = 0; + + timer_ticks start_time = timer::get_ticks(); + double decomp_only_time = 0; + + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); + + timer_ticks init_start_time = timer::get_ticks(); + int status = zlib_header ? inflateInit(&zstream) : inflateInit2(&zstream, -Z_DEFAULT_WINDOW_BITS); + timer_ticks total_init_time = timer::get_ticks() - init_start_time; + if (status != Z_OK) + { + print_error("Failed initializing decompressor!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + printf("inflateInit() took %3.3fms\n", timer::ticks_to_secs(total_init_time)*1000.0f); + + for ( ; ; ) + { + if (in_file_buf_ofs == in_file_buf_size) + { + in_file_buf_size = static_cast(my_min(cInBufSize, src_bytes_left)); + + if (fread(in_file_buf, 1, in_file_buf_size, pInFile) != in_file_buf_size) + { + print_error("Failure reading from source file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + deflateEnd(&zstream); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + src_bytes_left -= in_file_buf_size; + + in_file_buf_ofs = 0; + } + + uint num_in_bytes = (in_file_buf_size - in_file_buf_ofs); + uint num_out_bytes = (out_buf_size - out_file_buf_ofs); + zstream.next_in = in_file_buf + in_file_buf_ofs; + zstream.avail_in = num_in_bytes; + zstream.next_out = out_file_buf + out_file_buf_ofs; + zstream.avail_out = num_out_bytes; + + { + timer decomp_only_timer; + decomp_only_timer.start(); + status = inflate(&zstream, options.m_unbuffered_decompression ? Z_FINISH : Z_SYNC_FLUSH); + decomp_only_time += decomp_only_timer.get_elapsed_secs(); + } + num_in_bytes -= zstream.avail_in; + num_out_bytes -= zstream.avail_out; + + if (num_in_bytes) + { + in_file_buf_ofs += (uint)num_in_bytes; + assert(in_file_buf_ofs <= in_file_buf_size); + } + + out_file_buf_ofs += (uint)num_out_bytes; + + if ((out_file_buf_ofs == out_buf_size) || (status == Z_STREAM_END)) + { + if (fwrite(out_file_buf, 1, static_cast(out_file_buf_ofs), pOutFile) != out_file_buf_ofs) + { + print_error("Failure writing to destination file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + inflateEnd(&zstream); + fclose(pInFile); + fclose(pOutFile); + return false; + } + out_file_buf_ofs = 0; + } + + if (num_out_bytes > dst_bytes_left) + { + print_error("Decompressor wrote too many bytes to destination file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + inflateEnd(&zstream); + fclose(pInFile); + fclose(pOutFile); + return false; + } + dst_bytes_left -= num_out_bytes; + + if (status != Z_OK) + break; + } + _aligned_free(in_file_buf); + in_file_buf = NULL; + + _aligned_free(out_file_buf); + out_file_buf = NULL; + + src_bytes_left += (in_file_buf_size - in_file_buf_ofs); + + uint32 adler32 = zstream.adler; + inflateEnd(&zstream); + + timer_ticks end_time = timer::get_ticks(); + double total_time = timer::ticks_to_secs(my_max(1, end_time - start_time)); + + fclose(pInFile); + pInFile = NULL; + + fclose(pOutFile); + pOutFile = NULL; + + if (status != Z_STREAM_END) + { + print_error("Decompression FAILED with status %i\n", status); + return false; + } + + if ((src_file_size < UINT_MAX) && (orig_file_size < UINT_MAX)) + { + if ((((size_t)zstream.total_in + total_header_bytes) != src_file_size) || (zstream.total_out != orig_file_size)) + { + print_error("Decompression FAILED to consume all input or write all expected output!\n"); + return false; + } + } + + if (dst_bytes_left) + { + print_error("Decompressor FAILED to output the entire output file!\n"); + return false; + } + + if (src_bytes_left) + { + print_error("Decompressor FAILED to read " QUAD_INT_FMT " bytes from input buffer\n", src_bytes_left); + } + + printf("Success\n"); + printf("Source file size: " QUAD_INT_FMT ", Decompressed file size: " QUAD_INT_FMT "\n", src_file_size, orig_file_size); + if (zlib_header) printf("Decompressed adler32: 0x%08X\n", adler32); + printf("Overall decompression time (decompression init+I/O+decompression): %3.6f\n Consumption rate: %9.1f bytes/sec, Decompression rate: %9.1f bytes/sec\n", total_time, src_file_size / total_time, orig_file_size / total_time); + printf("Decompression only time (not counting decompression init or I/O): %3.6f\n Consumption rate: %9.1f bytes/sec, Decompression rate: %9.1f bytes/sec\n", decomp_only_time, src_file_size / decomp_only_time, orig_file_size / decomp_only_time); + + return true; +} + +static bool compare_files(const char *pFilename1, const char* pFilename2) +{ + FILE* pFile1 = open_file_with_retries(pFilename1, "rb"); + if (!pFile1) + { + print_error("Failed opening file: %s\n", pFilename1); + return false; + } + + FILE* pFile2 = open_file_with_retries(pFilename2, "rb"); + if (!pFile2) + { + print_error("Failed opening file: %s\n", pFilename2); + fclose(pFile1); + return false; + } + + _fseeki64(pFile1, 0, SEEK_END); + int64 fileSize1 = _ftelli64(pFile1); + _fseeki64(pFile1, 0, SEEK_SET); + + _fseeki64(pFile2, 0, SEEK_END); + int64 fileSize2 = _ftelli64(pFile2); + _fseeki64(pFile2, 0, SEEK_SET); + + if (fileSize1 != fileSize2) + { + print_error("Files to compare are not the same size: %I64i vs. %I64i.\n", fileSize1, fileSize2); + fclose(pFile1); + fclose(pFile2); + return false; + } + + const uint cBufSize = 1024 * 1024; + std::vector buf1(cBufSize); + std::vector buf2(cBufSize); + + int64 bytes_remaining = fileSize1; + while (bytes_remaining) + { + const uint bytes_to_read = static_cast(my_min(cBufSize, bytes_remaining)); + + if (fread(&buf1.front(), bytes_to_read, 1, pFile1) != 1) + { + print_error("Failed reading from file: %s\n", pFilename1); + fclose(pFile1); + fclose(pFile2); + return false; + } + + if (fread(&buf2.front(), bytes_to_read, 1, pFile2) != 1) + { + print_error("Failed reading from file: %s\n", pFilename2); + fclose(pFile1); + fclose(pFile2); + return false; + } + + if (memcmp(&buf1.front(), &buf2.front(), bytes_to_read) != 0) + { + print_error("File data comparison failed!\n"); + fclose(pFile1); + fclose(pFile2); + return false; + } + + bytes_remaining -= bytes_to_read; + } + + fclose(pFile1); + fclose(pFile2); + return true; +} + +static bool zip_create(const char *pZip_filename, const char *pSrc_filename) +{ + mz_zip_archive zip; + memset(&zip, 0, sizeof(zip)); + if ((rand() % 100) >= 10) + zip.m_file_offset_alignment = 1 << (rand() & 15); + if (!mz_zip_writer_init_file(&zip, pZip_filename, 65537)) + { + print_error("Failed creating zip archive \"%s\" (1)!\n", pZip_filename); + return false; + } + + mz_bool success = MZ_TRUE; + + const char *pStr = "This is a test!This is a test!This is a test!\n"; + size_t comp_size; + void *pComp_data = tdefl_compress_mem_to_heap(pStr, strlen(pStr), &comp_size, 256); + success &= mz_zip_writer_add_mem_ex(&zip, "precomp.txt", pComp_data, comp_size, "Comment", (uint16)strlen("Comment"), MZ_ZIP_FLAG_COMPRESSED_DATA, strlen(pStr), mz_crc32(MZ_CRC32_INIT, (const uint8 *)pStr, strlen(pStr))); + + success &= mz_zip_writer_add_mem(&zip, "cool/", NULL, 0, 0); + + success &= mz_zip_writer_add_mem(&zip, "1.txt", pStr, strlen(pStr), 9); + int n = rand() & 4095; + for (int i = 0; i < n; i++) + { + char name[256], buf[256], comment[256]; + sprintf(name, "t%u.txt", i); + sprintf(buf, "%u\n", i*5377); + sprintf(comment, "comment: %u\n", i); + success &= mz_zip_writer_add_mem_ex(&zip, name, buf, strlen(buf), comment, (uint16)strlen(comment), i % 10, 0, 0); + } + + const char *pTestComment = "test comment"; + success &= mz_zip_writer_add_file(&zip, "test.bin", pSrc_filename, pTestComment, (uint16)strlen(pTestComment), 9); + + if (ensure_file_exists_and_is_readable("changelog.txt")) + success &= mz_zip_writer_add_file(&zip, "changelog.txt", "changelog.txt", "This is a comment", (uint16)strlen("This is a comment"), 9); + + if (!success) + { + mz_zip_writer_end(&zip); + remove(pZip_filename); + print_error("Failed creating zip archive \"%s\" (2)!\n", pZip_filename); + return false; + } + + if (!mz_zip_writer_finalize_archive(&zip)) + { + mz_zip_writer_end(&zip); + remove(pZip_filename); + print_error("Failed creating zip archive \"%s\" (3)!\n", pZip_filename); + return false; + } + + mz_zip_writer_end(&zip); + + struct FILE_STAT_STRUCT stat_buf; + FILE_STAT(pZip_filename, &stat_buf); + uint64 actual_file_size = stat_buf.st_size; + if (zip.m_archive_size != actual_file_size) + { + print_error("Archive's actual size and zip archive object's size differ for file \"%s\"!\n", pZip_filename); + return false; + } + + printf("Created zip file \"%s\", file size: " QUAD_INT_FMT "\n", pZip_filename, zip.m_archive_size); + return true; +} + +static size_t zip_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)pOpaque, (void)ofs, (void)pBuf, (void)n; + return n; +} + +static bool zip_extract(const char *pZip_filename, const char *pDst_filename) +{ + mz_zip_archive zip; + memset(&zip, 0, sizeof(zip)); + if (!mz_zip_reader_init_file(&zip, pZip_filename, 0)) + { + print_error("Failed opening zip archive \"%s\"!\n", pZip_filename); + return false; + } + + int file_index = mz_zip_reader_locate_file(&zip, "test.bin", "test Comment", 0); + int alt_file_index = mz_zip_reader_locate_file(&zip, "test.bin", "test Comment e", 0); + if ((file_index < 0) || (alt_file_index >= 0)) + { + print_error("Archive \"%s\" is missing test.bin file!\n", pZip_filename); + mz_zip_reader_end(&zip); + return false; + } + + alt_file_index = mz_zip_reader_locate_file(&zip, "test.bin", NULL, 0); + if (alt_file_index != file_index) + { + print_error("mz_zip_reader_locate_file() failed!\n", pZip_filename); + mz_zip_reader_end(&zip); + return false; + } + + if (!mz_zip_reader_extract_to_file(&zip, file_index, pDst_filename, 0)) + { + print_error("Failed extracting test.bin from archive \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + mz_zip_reader_end(&zip); + return false; + } + + for (uint i = 0; i < mz_zip_reader_get_num_files(&zip); i++) + { + mz_zip_archive_file_stat stat; + if (!mz_zip_reader_file_stat(&zip, i, &stat)) + { + print_error("Failed testing archive -1 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + mz_zip_reader_end(&zip); + return false; + } + //printf("\"%s\" %I64u %I64u\n", stat.m_filename, stat.m_comp_size, stat.m_uncomp_size); + size_t size = 0; + + mz_bool status = mz_zip_reader_extract_to_callback(&zip, i, zip_write_callback, NULL, 0); + if (!status) + { + print_error("Failed testing archive -2 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + mz_zip_reader_end(&zip); + return false; + } + + if (stat.m_uncomp_size<100*1024*1024) + { + void *p = mz_zip_reader_extract_to_heap(&zip, i, &size, 0); + if (!p) + { + print_error("Failed testing archive -3 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + mz_zip_reader_end(&zip); + return false; + } + free(p); + } + + if (stat.m_uncomp_size<100*1024*1024) + { + /* Use iterative reader to read onto heap in one big chunk */ + mz_zip_reader_extract_iter_state *pIter = mz_zip_reader_extract_iter_new(&zip, i, 0); + void *p = malloc(stat.m_uncomp_size); + if ((!pIter) && (0 != stat.m_uncomp_size)) + { + print_error("Failed testing archive -4 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + free(p); + mz_zip_reader_end(&zip); + return false; + } + if (pIter) + { + if (stat.m_uncomp_size != mz_zip_reader_extract_iter_read(pIter, p, stat.m_uncomp_size) ) + { + print_error("Failed testing archive -5 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + free(p); + mz_zip_reader_extract_iter_free(pIter); + mz_zip_reader_end(&zip); + return false; + } + if (MZ_TRUE != mz_zip_reader_extract_iter_free(pIter)) + { + print_error("Failed testing archive -6 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + free(p); + mz_zip_reader_end(&zip); + return false; + } + } + free(p); + } + + if (stat.m_uncomp_size<100*1024) + { + /* Use iterative reader to read file one byte at a time */ + mz_zip_reader_extract_iter_state *pIter = mz_zip_reader_extract_iter_new(&zip, i, 0); + uint8_t byBuffer; + if ((!pIter) && (0 != stat.m_uncomp_size)) + { + print_error("Failed testing archive -7 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + mz_zip_reader_end(&zip); + return false; + } + if (pIter) + { + for ( uint64_t uiIndex = 0; uiIndex < stat.m_uncomp_size; uiIndex++ ) + { + if (sizeof(byBuffer) != mz_zip_reader_extract_iter_read(pIter, &byBuffer, sizeof(byBuffer))) + { + print_error("Failed testing archive -8 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + mz_zip_reader_extract_iter_free(pIter); + mz_zip_reader_end(&zip); + return false; + } + } + if (MZ_TRUE != mz_zip_reader_extract_iter_free(pIter)) + { + print_error("Failed testing archive -9 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip))); + mz_zip_reader_end(&zip); + return false; + } + } + } + } + printf("Verified %u files\n", mz_zip_reader_get_num_files(&zip)); + + mz_zip_reader_end(&zip); + + printf("Extracted file \"%s\"\n", pDst_filename); + return true; +} + +typedef std::vector< std::string > string_array; + +#if defined(WIN32) +static bool find_files(std::string pathname, const std::string &filename, string_array &files, bool recursive, int depth = 0) +{ + if (!pathname.empty()) + { + char c = pathname[pathname.size() - 1]; + if ((c != ':') && (c != '\\') && (c != '/')) + pathname += "\\"; + } + + WIN32_FIND_DATAA find_data; + + HANDLE findHandle = FindFirstFileA((pathname + filename).c_str(), &find_data); + if (findHandle == INVALID_HANDLE_VALUE) + { + HRESULT hres = GetLastError(); + if ((!depth) && (hres != NO_ERROR) && (hres != ERROR_FILE_NOT_FOUND)) + return false; + } + else + { + do + { + const bool is_directory = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + const bool is_system = (find_data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0; + const bool is_hidden = false;//(find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0; + + std::string filename(find_data.cFileName); + + if ((!is_directory) && (!is_system) && (!is_hidden)) + files.push_back(pathname + filename); + + } while (FindNextFileA(findHandle, &find_data)); + + FindClose(findHandle); + } + + if (recursive) + { + string_array paths; + + HANDLE findHandle = FindFirstFileA((pathname + "*").c_str(), &find_data); + if (findHandle != INVALID_HANDLE_VALUE) + { + do + { + const bool is_directory = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + const bool is_system = (find_data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0; + const bool is_hidden = false;//(find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0; + + std::string filename(find_data.cFileName); + + if ((is_directory) && (!is_hidden) && (!is_system)) + paths.push_back(filename); + + } while (FindNextFileA(findHandle, &find_data)); + + FindClose(findHandle); + + for (uint i = 0; i < paths.size(); i++) + { + const std::string &path = paths[i]; + if (path[0] == '.') + continue; + + if (!find_files(pathname + path, filename, files, true, depth + 1)) + return false; + } + } + } + + return true; +} +#else +#include +#include +static bool find_files(std::string pathname, const std::string &pattern, string_array &files, bool recursive, int depth = 0) +{ + if (!pathname.empty()) + { + char c = pathname[pathname.size() - 1]; + if ((c != ':') && (c != '\\') && (c != '/')) + pathname += "/"; + } + + DIR *dp = opendir(pathname.c_str()); + + if (!dp) + return depth ? true : false; + + string_array paths; + + for ( ; ; ) + { + struct dirent *ep = readdir(dp); + if (!ep) + break; + + const bool is_directory = (ep->d_type & DT_DIR) != 0; + const bool is_file = (ep->d_type & DT_REG) != 0; + + if (ep->d_name[0] == '.') + continue; + + std::string filename(ep->d_name); + + if (is_directory) + { + if (recursive) + paths.push_back(filename); + } + else if (is_file) + { + if (0 == fnmatch(pattern.c_str(), filename.c_str(), 0)) + files.push_back(pathname + filename); + } + } + + closedir(dp); + dp = NULL; + + if (recursive) + { + for (uint i = 0; i < paths.size(); i++) + { + const std::string &path = paths[i]; + if (!find_files(pathname + path, pattern, files, true, depth + 1)) + return false; + } + } + + return true; +} +#endif + +static bool test_recursive(const char *pPath, comp_options options) +{ + string_array files; + if (!find_files(pPath, "*", files, true)) + { + print_error("Failed finding files under path \"%s\"!\n", pPath); + return false; + } + + uint total_files_compressed = 0; + uint64 total_source_size = 0; + uint64 total_comp_size = 0; + +#ifdef WIN32 + MEMORYSTATUS initial_mem_status; + GlobalMemoryStatus(&initial_mem_status); +#endif + + timer_ticks start_tick_count = timer::get_ticks(); + + const int first_file_index = 0; + + uint unique_id = static_cast(timer::get_init_ticks()); + char cmp_file[256], decomp_file[256]; + + sprintf(cmp_file, "__comp_temp_%u__.tmp", unique_id); + sprintf(decomp_file, "__decomp_temp_%u__.tmp", unique_id); + + for (uint file_index = first_file_index; file_index < files.size(); file_index++) + { + const std::string &src_file = files[file_index]; + + printf("***** [%u of %u] Compressing file \"%s\" to \"%s\"\n", 1 + file_index, (uint)files.size(), src_file.c_str(), cmp_file); + + if ((strstr(src_file.c_str(), "__comp_temp") != NULL) || (strstr(src_file.c_str(), "__decomp_temp") != NULL)) + { + printf("Skipping temporary file \"%s\"\n", src_file.c_str()); + continue; + } + + FILE *pFile = fopen(src_file.c_str(), "rb"); + if (!pFile) + { + printf("Skipping unreadable file \"%s\"\n", src_file.c_str()); + continue; + } + _fseeki64(pFile, 0, SEEK_END); + int64 src_file_size = _ftelli64(pFile); + + if (src_file_size) + { + _fseeki64(pFile, 0, SEEK_SET); + if (fgetc(pFile) == EOF) + { + printf("Skipping unreadable file \"%s\"\n", src_file.c_str()); + fclose(pFile); + continue; + } + } + + fclose(pFile); + + if (!ensure_file_is_writable(cmp_file)) + { + print_error("Unable to create file \"%s\"!\n", cmp_file); + return false; + } + + comp_options file_options(options); + if (options.m_randomize_params) + { + file_options.m_level = rand() % 11; + file_options.m_unbuffered_decompression = (rand() & 1) != 0; + file_options.m_z_strat = rand() % (Z_FIXED + 1); + file_options.m_write_zlib_header = (rand() & 1) != 0; + file_options.m_random_z_flushing = (rand() & 1) != 0; + file_options.print(); + } + + bool status; + if (file_options.m_archive_test) + { + printf("Creating test archive with file \"%s\", size " QUAD_INT_FMT "\n", src_file.c_str(), src_file_size); + status = zip_create(cmp_file, src_file.c_str()); + } + else + status = compress_file_zlib(src_file.c_str(), cmp_file, file_options); + if (!status) + { + print_error("Failed compressing file \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file); + return false; + } + + if (file_options.m_verify_compressed_data) + { + printf("Decompressing file \"%s\" to \"%s\"\n", cmp_file, decomp_file); + + if (!ensure_file_is_writable(decomp_file)) + { + print_error("Unable to create file \"%s\"!\n", decomp_file); + return false; + } + + if (file_options.m_archive_test) + status = zip_extract(cmp_file, decomp_file); + else + status = decompress_file_zlib(cmp_file, decomp_file, file_options); + + if (!status) + { + print_error("Failed decompressing file \"%s\" to \"%s\"\n", src_file.c_str(), decomp_file); + return false; + } + + printf("Comparing file \"%s\" to \"%s\"\n", decomp_file, src_file.c_str()); + + if (!compare_files(decomp_file, src_file.c_str())) + { + print_error("Failed comparing decompressed file data while compressing \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file); + return false; + } + else + { + printf("Decompressed file compared OK to original file.\n"); + } + } + + int64 cmp_file_size = 0; + pFile = fopen(cmp_file, "rb"); + if (pFile) + { + _fseeki64(pFile, 0, SEEK_END); + cmp_file_size = _ftelli64(pFile); + fclose(pFile); + } + + total_files_compressed++; + total_source_size += src_file_size; + total_comp_size += cmp_file_size; + +#ifdef WIN32 + MEMORYSTATUS mem_status; + GlobalMemoryStatus(&mem_status); + + const int64 bytes_allocated = initial_mem_status.dwAvailVirtual- mem_status.dwAvailVirtual; + + printf("Memory allocated relative to first file: %I64i\n", bytes_allocated); +#endif + + printf("\n"); + } + + timer_ticks end_tick_count = timer::get_ticks(); + + double total_elapsed_time = timer::ticks_to_secs(end_tick_count - start_tick_count); + + printf("Test successful: %f secs\n", total_elapsed_time); + printf("Total files processed: %u\n", total_files_compressed); + printf("Total source size: " QUAD_INT_FMT "\n", total_source_size); + printf("Total compressed size: " QUAD_INT_FMT "\n", total_comp_size); + printf("Max small comp ratio: %f, Max large comp ratio: %f\n", s_max_small_comp_ratio, s_max_large_comp_ratio); + + remove(cmp_file); + remove(decomp_file); + + return true; +} + +static size_t dummy_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) +{ + (void)ofs; (void)pBuf; + uint32 *pCRC = (uint32*)pOpaque; + *pCRC = mz_crc32(*pCRC, (const uint8*)pBuf, n); + return n; +} + +static bool test_archives(const char *pPath, comp_options options) +{ + (void)options; + + string_array files; + if (!find_files(pPath, "*.zip", files, true)) + { + print_error("Failed finding files under path \"%s\"!\n", pPath); + return false; + } + + uint total_archives = 0; + uint64 total_bytes_processed = 0; + uint64 total_files_processed = 0; + uint total_errors = 0; + +#ifdef WIN32 + MEMORYSTATUS initial_mem_status; + GlobalMemoryStatus(&initial_mem_status); +#endif + + const int first_file_index = 0; + uint unique_id = static_cast(timer::get_init_ticks()); + char cmp_file[256], decomp_file[256]; + + sprintf(decomp_file, "__decomp_temp_%u__.tmp", unique_id); + + string_array failed_archives; + + for (uint file_index = first_file_index; file_index < files.size(); file_index++) + { + const std::string &src_file = files[file_index]; + + printf("***** [%u of %u] Testing archive file \"%s\"\n", 1 + file_index, (uint)files.size(), src_file.c_str()); + + if ((strstr(src_file.c_str(), "__comp_temp") != NULL) || (strstr(src_file.c_str(), "__decomp_temp") != NULL)) + { + printf("Skipping temporary file \"%s\"\n", src_file.c_str()); + continue; + } + + FILE *pFile = fopen(src_file.c_str(), "rb"); + if (!pFile) + { + printf("Skipping unreadable file \"%s\"\n", src_file.c_str()); + continue; + } + _fseeki64(pFile, 0, SEEK_END); + int64 src_file_size = _ftelli64(pFile); + fclose(pFile); + + (void)src_file_size; + + sprintf(cmp_file, "__comp_temp_%u__.zip", file_index); + + mz_zip_archive src_archive; + memset(&src_archive, 0, sizeof(src_archive)); + + if (!mz_zip_reader_init_file(&src_archive, src_file.c_str(), 0)) + { + failed_archives.push_back(src_file); + + print_error("Failed opening archive \"%s\"!\n", src_file.c_str()); + total_errors++; + continue; + } + + mz_zip_archive dst_archive; + memset(&dst_archive, 0, sizeof(dst_archive)); + if (options.m_write_archives) + { + if (!ensure_file_is_writable(cmp_file)) + { + print_error("Unable to create file \"%s\"!\n", cmp_file); + return false; + } + + if (!mz_zip_writer_init_file(&dst_archive, cmp_file, 0)) + { + print_error("Failed creating archive \"%s\"!\n", cmp_file); + total_errors++; + continue; + } + } + + int i; + //for (i = 0; i < mz_zip_reader_get_num_files(&src_archive); i++) + for (i = mz_zip_reader_get_num_files(&src_archive) - 1; i >= 0; --i) + { + if (mz_zip_reader_is_file_encrypted(&src_archive, i)) + continue; + + mz_zip_archive_file_stat file_stat; + bool status = mz_zip_reader_file_stat(&src_archive, i, &file_stat) != 0; + + int locate_file_index = mz_zip_reader_locate_file(&src_archive, file_stat.m_filename, NULL, 0); + if (locate_file_index != i) + { + mz_zip_archive_file_stat locate_file_stat; + mz_zip_reader_file_stat(&src_archive, locate_file_index, &locate_file_stat); + if (_stricmp(locate_file_stat.m_filename, file_stat.m_filename) != 0) + { + print_error("mz_zip_reader_locate_file() failed!\n"); + return false; + } + else + { + printf("Warning: Duplicate filenames in archive!\n"); + } + } + + if ((file_stat.m_method) && (file_stat.m_method != MZ_DEFLATED)) + continue; + + if (status) + { + char name[260]; + mz_zip_reader_get_filename(&src_archive, i, name, sizeof(name)); + + size_t extracted_size = 0; + void *p = mz_zip_reader_extract_file_to_heap(&src_archive, name, &extracted_size, 0); + if (!p) + status = false; + + uint32 extracted_crc32 = MZ_CRC32_INIT; + if (!mz_zip_reader_extract_file_to_callback(&src_archive, name, dummy_zip_file_write_callback, &extracted_crc32, 0)) + status = false; + + if (mz_crc32(MZ_CRC32_INIT, (const uint8*)p, extracted_size) != extracted_crc32) + status = false; + + mz_zip_reader_extract_iter_state *pIter = mz_zip_reader_extract_file_iter_new(&src_archive, name, 0); + void *q = malloc(extracted_size); + mz_zip_reader_extract_iter_read(pIter, q, extracted_size); + mz_zip_reader_extract_iter_free(pIter); + + if (mz_crc32(MZ_CRC32_INIT, (const uint8*)q, extracted_size) != extracted_crc32) + status = false; + + free(q); + free(p); + + if (options.m_write_archives) + { + if ((status) && (!mz_zip_writer_add_from_zip_reader(&dst_archive, &src_archive, i))) + { + print_error("Failed adding new file to archive \"%s\"!\n", cmp_file); + status = false; + } + } + + total_bytes_processed += file_stat.m_uncomp_size; + total_files_processed++; + } + + if (!status) + break; + } + + mz_zip_reader_end(&src_archive); + + //if (i < mz_zip_reader_get_num_files(&src_archive)) + if (i >= 0) + { + failed_archives.push_back(src_file); + + print_error("Failed processing archive \"%s\"!\n", src_file.c_str()); + total_errors++; + } + + if (options.m_write_archives) + { + if (!mz_zip_writer_finalize_archive(&dst_archive) || !mz_zip_writer_end(&dst_archive)) + { + failed_archives.push_back(src_file); + + print_error("Failed finalizing archive \"%s\"!\n", cmp_file); + total_errors++; + } + } + + total_archives++; + +#ifdef WIN32 + MEMORYSTATUS mem_status; + GlobalMemoryStatus(&mem_status); + + const int64 bytes_allocated = initial_mem_status.dwAvailVirtual- mem_status.dwAvailVirtual; + + printf("Memory allocated relative to first file: %I64i\n", bytes_allocated); +#endif + + printf("\n"); + } + + printf("Total archives processed: %u\n", total_archives); + printf("Total errors: %u\n", total_errors); + printf("Total bytes processed: " QUAD_INT_FMT "\n", total_bytes_processed); + printf("Total archive files processed: " QUAD_INT_FMT "\n", total_files_processed); + + printf("List of failed archives:\n"); + for (uint i = 0; i < failed_archives.size(); ++i) + printf("%s\n", failed_archives[i].c_str()); + + remove(cmp_file); + remove(decomp_file); + + return true; +} + +int main_internal(string_array cmd_line) +{ + comp_options options; + + if (!cmd_line.size()) + { + print_usage(); + if (simple_test1(options) || simple_test2(options)) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } + + enum op_mode_t + { + OP_MODE_INVALID = -1, + OP_MODE_COMPRESS = 0, + OP_MODE_DECOMPRESS = 1, + OP_MODE_ALL = 2, + OP_MODE_ARCHIVES = 3 + }; + + op_mode_t op_mode = OP_MODE_INVALID; + + for (int i = 0; i < (int)cmd_line.size(); i++) + { + const std::string &str = cmd_line[i]; + if (str[0] == '-') + { + if (str.size() < 2) + { + print_error("Invalid option: %s\n", str.c_str()); + return EXIT_FAILURE; + } + switch (tolower(str[1])) + { + case 'u': + { + options.m_unbuffered_decompression = true; + break; + } + case 'm': + { + int comp_level = atoi(str.c_str() + 2); + if ((comp_level < 0) || (comp_level > (int)10)) + { + print_error("Invalid compression level: %s\n", str.c_str()); + return EXIT_FAILURE; + } + + options.m_level = comp_level; + break; + } + case 'v': + { + options.m_verify_compressed_data = true; + break; + } + case 'r': + { + options.m_randomize_params = true; + break; + } + case 'b': + { + options.m_randomize_buffer_sizes = true; + break; + } + case 'h': + { + options.m_random_z_flushing = true; + break; + } + case 'x': + { + int seed = atoi(str.c_str() + 2); + srand(seed); + printf("Using random seed: %i\n", seed); + break; + } + case 't': + { + options.m_z_strat = my_min(Z_FIXED, my_max(0, atoi(str.c_str() + 2))); + break; + } + case 'z': + { + options.m_write_zlib_header = false; + break; + } + case 'a': + { + options.m_archive_test = true; + break; + } + case 'w': + { + options.m_write_archives = true; + break; + } + default: + { + print_error("Invalid option: %s\n", str.c_str()); + return EXIT_FAILURE; + } + } + + cmd_line.erase(cmd_line.begin() + i); + i--; + + continue; + } + + if (str.size() != 1) + { + print_error("Invalid mode: %s\n", str.c_str()); + return EXIT_FAILURE; + } + switch (tolower(str[0])) + { + case 'c': + { + op_mode = OP_MODE_COMPRESS; + break; + } + case 'd': + { + op_mode = OP_MODE_DECOMPRESS; + break; + } + case 'a': + { + op_mode = OP_MODE_ALL; + break; + } + case 'r': + { + op_mode = OP_MODE_ARCHIVES; + break; + } + default: + { + print_error("Invalid mode: %s\n", str.c_str()); + return EXIT_FAILURE; + } + } + cmd_line.erase(cmd_line.begin() + i); + break; + } + + if (op_mode == OP_MODE_INVALID) + { + print_error("No mode specified!\n"); + print_usage(); + return EXIT_FAILURE; + } + + printf("Using options:\n"); + options.print(); + printf("\n"); + + int exit_status = EXIT_FAILURE; + + switch (op_mode) + { + case OP_MODE_COMPRESS: + { + if (cmd_line.size() < 2) + { + print_error("Must specify input and output filenames!\n"); + return EXIT_FAILURE; + } + else if (cmd_line.size() > 2) + { + print_error("Too many filenames!\n"); + return EXIT_FAILURE; + } + + const std::string &src_file = cmd_line[0]; + const std::string &cmp_file = cmd_line[1]; + + bool comp_result = compress_file_zlib(src_file.c_str(), cmp_file.c_str(), options); + if (comp_result) + exit_status = EXIT_SUCCESS; + + if ((comp_result) && (options.m_verify_compressed_data)) + { + char decomp_file[256]; + + sprintf(decomp_file, "__decomp_temp_%u__.tmp", (uint)timer::get_ms()); + + if (!decompress_file_zlib(cmp_file.c_str(), decomp_file, options)) + { + print_error("Failed decompressing file \"%s\" to \"%s\"\n", cmp_file.c_str(), decomp_file); + return EXIT_FAILURE; + } + + printf("Comparing file \"%s\" to \"%s\"\n", decomp_file, src_file.c_str()); + + if (!compare_files(decomp_file, src_file.c_str())) + { + print_error("Failed comparing decompressed file data while compressing \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file.c_str()); + return EXIT_FAILURE; + } + else + { + printf("Decompressed file compared OK to original file.\n"); + } + + remove(decomp_file); + } + + break; + } + case OP_MODE_DECOMPRESS: + { + if (cmd_line.size() < 2) + { + print_error("Must specify input and output filenames!\n"); + return EXIT_FAILURE; + } + else if (cmd_line.size() > 2) + { + print_error("Too many filenames!\n"); + return EXIT_FAILURE; + } + if (decompress_file_zlib(cmd_line[0].c_str(), cmd_line[1].c_str(), options)) + exit_status = EXIT_SUCCESS; + break; + } + case OP_MODE_ALL: + { + if (!cmd_line.size()) + { + print_error("No directory specified!\n"); + return EXIT_FAILURE; + } + else if (cmd_line.size() != 1) + { + print_error("Too many filenames!\n"); + return EXIT_FAILURE; + } + if (test_recursive(cmd_line[0].c_str(), options)) + exit_status = EXIT_SUCCESS; + break; + } + case OP_MODE_ARCHIVES: + { + if (!cmd_line.size()) + { + print_error("No directory specified!\n"); + return EXIT_FAILURE; + } + else if (cmd_line.size() != 1) + { + print_error("Too many filenames!\n"); + return EXIT_FAILURE; + } + if (test_archives(cmd_line[0].c_str(), options)) + exit_status = EXIT_SUCCESS; + break; + } + default: + { + print_error("No mode specified!\n"); + print_usage(); + return EXIT_FAILURE; + } + } + + return exit_status; +} + +int main(int argc, char *argv[]) +{ +#if defined(_WIN64) || defined(__LP64__) || defined(_LP64) + printf("miniz.c x64 Command Line Test App - Compiled %s %s\n", __DATE__, __TIME__); +#else + printf("miniz.c x86 Command Line Test App - Compiled %s %s\n", __DATE__, __TIME__); +#endif + timer::get_ticks(); + + string_array cmd_line; + for (int i = 1; i < argc; i++) + cmd_line.push_back(std::string(argv[i])); + + int exit_status = main_internal(cmd_line); + + return exit_status; +} diff --git a/3rdparty/miniz/tests/timer.cpp b/3rdparty/miniz/tests/timer.cpp new file mode 100644 index 0000000..bb35b6c --- /dev/null +++ b/3rdparty/miniz/tests/timer.cpp @@ -0,0 +1,152 @@ +// File: timer.cpp - Simple high-precision timer class. Supports Win32, X360, and POSIX/Linux +#include +#include +#include +#include + +#include "timer.h" + +#if defined(WIN32) +#include +#elif defined(_XBOX) +#include +#endif + +unsigned long long timer::g_init_ticks; +unsigned long long timer::g_freq; +double timer::g_inv_freq; + +#if defined(WIN32) || defined(_XBOX) +inline void query_counter(timer_ticks *pTicks) +{ + QueryPerformanceCounter(reinterpret_cast(pTicks)); +} +inline void query_counter_frequency(timer_ticks *pTicks) +{ + QueryPerformanceFrequency(reinterpret_cast(pTicks)); +} +#elif defined(__GNUC__) +#include +inline void query_counter(timer_ticks *pTicks) +{ + struct timeval cur_time; + gettimeofday(&cur_time, NULL); + *pTicks = static_cast(cur_time.tv_sec)*1000000ULL + static_cast(cur_time.tv_usec); +} +inline void query_counter_frequency(timer_ticks *pTicks) +{ + *pTicks = 1000000; +} +#endif + +timer::timer() : + m_start_time(0), + m_stop_time(0), + m_started(false), + m_stopped(false) +{ + if (!g_inv_freq) + init(); +} + +timer::timer(timer_ticks start_ticks) +{ + if (!g_inv_freq) + init(); + + m_start_time = start_ticks; + + m_started = true; + m_stopped = false; +} + +void timer::start(timer_ticks start_ticks) +{ + m_start_time = start_ticks; + + m_started = true; + m_stopped = false; +} + +void timer::start() +{ + query_counter(&m_start_time); + + m_started = true; + m_stopped = false; +} + +void timer::stop() +{ + assert(m_started); + + query_counter(&m_stop_time); + + m_stopped = true; +} + +double timer::get_elapsed_secs() const +{ + assert(m_started); + if (!m_started) + return 0; + + timer_ticks stop_time = m_stop_time; + if (!m_stopped) + query_counter(&stop_time); + + timer_ticks delta = stop_time - m_start_time; + return delta * g_inv_freq; +} + +timer_ticks timer::get_elapsed_us() const +{ + assert(m_started); + if (!m_started) + return 0; + + timer_ticks stop_time = m_stop_time; + if (!m_stopped) + query_counter(&stop_time); + + timer_ticks delta = stop_time - m_start_time; + return (delta * 1000000ULL + (g_freq >> 1U)) / g_freq; +} + +void timer::init() +{ + if (!g_inv_freq) + { + query_counter_frequency(&g_freq); + g_inv_freq = 1.0f / g_freq; + + query_counter(&g_init_ticks); + } +} + +timer_ticks timer::get_init_ticks() +{ + if (!g_inv_freq) + init(); + + return g_init_ticks; +} + +timer_ticks timer::get_ticks() +{ + if (!g_inv_freq) + init(); + + timer_ticks ticks; + query_counter(&ticks); + return ticks - g_init_ticks; +} + +double timer::ticks_to_secs(timer_ticks ticks) +{ + if (!g_inv_freq) + init(); + + return ticks * g_inv_freq; +} + diff --git a/3rdparty/miniz/tests/timer.h b/3rdparty/miniz/tests/timer.h new file mode 100644 index 0000000..df6e01f --- /dev/null +++ b/3rdparty/miniz/tests/timer.h @@ -0,0 +1,40 @@ +// File: timer.h +#pragma once + +typedef unsigned long long timer_ticks; + +class timer +{ +public: + timer(); + timer(timer_ticks start_ticks); + + void start(); + void start(timer_ticks start_ticks); + + void stop(); + + double get_elapsed_secs() const; + inline double get_elapsed_ms() const { return get_elapsed_secs() * 1000.0f; } + timer_ticks get_elapsed_us() const; + + static void init(); + static inline timer_ticks get_ticks_per_sec() { return g_freq; } + static timer_ticks get_init_ticks(); + static timer_ticks get_ticks(); + static double ticks_to_secs(timer_ticks ticks); + static inline double ticks_to_ms(timer_ticks ticks) { return ticks_to_secs(ticks) * 1000.0f; } + static inline double get_secs() { return ticks_to_secs(get_ticks()); } + static inline double get_ms() { return ticks_to_ms(get_ticks()); } + +private: + static timer_ticks g_init_ticks; + static timer_ticks g_freq; + static double g_inv_freq; + + timer_ticks m_start_time; + timer_ticks m_stop_time; + + bool m_started : 1; + bool m_stopped : 1; +}; diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d2b939e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.12) + +# Pull in PICO SDK (must be before project) +include(pico_sdk_import.cmake) + +# We also need PICO EXTRAS +include(pico_extras_import.cmake) + +project(khan C CXX) +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +# Add pico targets to the build +pico_sdk_init() + +add_subdirectory(stream) +add_subdirectory(khan) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ae5234 --- /dev/null +++ b/README.md @@ -0,0 +1,161 @@ +# Overview + +This README is specific to the "pico" fork of unrealspeccyp. See [here](README_USP) for the original unrealspeccyp readme. + +## Features + +* Spectrum 48K and 128K emulator based on [unrealspeccyp](https://bitbucket.org/djdron/unrealspeccyp/wiki/Home). +* VGA output. +* Beeper output pin for 48K spectrum (i.e. just wiggling the pin like the real Spectrum!). +* AY sound output for 128K spectrum. +* .TAP, .SNA, .Z80 support (embedded in binary). +* This port was development during pre-silicon RP2040 development. Therefore, it had to run at 48Mhz, and indeed. + both 48K and 128K emulators still run by default at a 48Mhz system clock. +* Read/Write on RP2040 GPIO pins - because why not? (see bitbang app). + +## Some caveats + +* The sole reason for this project's existence was as a system test/bit of fun during early development of the RP2040, it is + not intended to be the best, full-featured emulator. I have open sourced it as people keep asking! +* 'Khan' is the codename I used to keep what I was doing secret :-) +* No doubt these terse instructions will prove frustrating for some; feel free to submit PRs! +* You cannot currently use beeper/tape noise and AY sound at the same time (as they use different mechanisms) - + actually I think there is support for using one PWM channel for each, but IDK if it still works. +* I did some recent cleanup in preparation for open-source: + * Untangled the horrible mess of symlinks that had this building within another makefile project prior to the Pico + SDK. + * Added USB keyboard support (this seems fine - though I had to hunt for more RAM, so it is possible this may + break something - you can run with or without). + * Hopefully got a license on everything important. + * Added some I2S support - this is only for AY sound as the beeper just wiggles a pin. Sadly my I2S decoder + doesn't like 22050Hz, so I had to up it to 24000Hz. I don't necessarily recommend using this if you can use PWM + (the default). + +# Running + +## Pin Outs + +If you want to know about pin usage and features, the best thing to do is run: + +e.g. +```bash +$ picotool info -a khan128_usb.elf +File khan128_usb.elf: + +Program Information + name: khan128_usb + features: SDL event forwarder support + USB keyboard support + UART stdin / stdout + binary start: 0x10000000 + binary end: 0x1003b2ec + +Fixed Pin Information + 0-4: Red 0-5 + 6-10: Green 0-5 + 11-15: Blue 0-5 + 16: HSync + 17: VSync + 20: UART1 TX + 21: UART1 RX + 28: AY sound (PWM) + +Build Information + sdk version: 1.5.1-develop + pico_board: vgaboard + boot2_name: boot2_w25q080 + build date: May 29 2023 + build attributes: Release +``` + +These video/audio pins match for example the Pimoroni Pico VGA Demo Base which itself is based on the suggested +Raspberry Pi Documentation [here](https://datasheets.raspberrypi.com/rp2040/hardware-design-with-rp2040.pdf) +and the design files zipped [here](https://datasheets.raspberrypi.com/rp2040/VGA-KiCAD.zip). + +## Keyboard input + +* **USB Keyboard** - Depending on the version you have, you can use a real USB keyboard (if supported by TinyUSB on + RP2040). I know +RaspberryPi keyboards work, and my old Dell keyboard. If in doubt, the older and crappier the better! USB keyboard + support is enabled for binaries with names ending in `_usb`. + +* **SDL Event Forwader** - This is a simple app that tunnels input from a window on the host machine over UART to + the RP2040: https://github.com/kilograham/sdl_event_forwarder. Note however this sometimes seems to get keys stuck + on the spectrum. This is enabled by default in all builds. + +* **Raw UART** - Not very useful, but you can enable this, and allow typing a few keys on the spectrum (you need to + find this in the `CMakeLists.txt` to enable it). + +### Keys + +These are the same as regular unrealspeccyp. Good luck typing! +* Shift keys on your keyboard are "Caps Shift". +* Alt or Ctrl keys on your keyboard are "Symbol Shift". + +# Building + +You should use the latest `develop` branch of +[pico-sdk](https://github.com/raspberrypi/pico-sdk/tree/develop) and +[pico-extras](https://github.com/raspberrypi/pico-extras/tree/develop). + +## Building for RP2040 + +This should work on Linux and Mac (and possibly Windows; I haven't tried). + +To build, from the project root directory: +``` +mkdir build +cd build +cmake -DPICO_SDK_PATH=path/to/pico-sdk -DPICO_BOARD=vgaboard .. +make -j4 +``` + +## Building for RP2040 + +This should work on Linux and Mac (and possibly Windows; I haven't tried). + +To build: +``` +mkdir build +cd build +cmake -DPICO_SDK_PATH=path/to/pico-sdk -DPICO_BOARD=vgaboard .. +make -j4 +``` + +You need to specify `PICO_EXTRAS_PATH` as well if `pico-extras` isn't in a sibling directory of `pico-sdk`. + +Note the output binaries are in `build/khan`. + +## Building for PICO_PLATFORM=host + +You can run this on your Linux/macOS host if you have `libsdl2-dev` etc. installed using the `host` platform +mode of the SDK. + +You need [pico_host_sdl](https://github.com/raspberrypi/pico-host-sdl) to replace the video/audio support with native. + +To build: +``` +mkdir native_build +cd native_build +cmake -DPICO_SDK_PATH=path/to/pico-sdk -DPICO_PLATFORM=host -DPICO_SDK_PRE_LIST_DIRS=/path/to/pico_host_sdl .. +make -j4 +``` + +Note the output binaries are in `native_build/khan`. There is no different between `_usb` variants and non `_usb` +variants. + +# Implementation Notes + +Not much here atm, sorry: + +* We have A really quite small/fast Z80 emulator - this might be useful + * The generation is a bit batshit; I figured, at the time, that the simplest thing to do would be to use some C++ + hackery, to execute the code for each instruction, using crafted C++ classes, such that assignments etc. are + overloaded to print out ARM assembly. (shiver) + * If I recalled, the smallest version of the code is 2K, the slightly bigger one is 3K. +* The beeper/tape implementation just wiggles a pin, which it tries to do at exactly the right time by passing run + lengths to a PIO state machine. Note that to support volume, a short PWM pulse is repeated for each length of the run. +* The small AY emulator is also quite handy, though again is a bit crazy, because I thought it would be important to + do some oversampling, and frankly, I'm no longer sure that is necessary. Also, I wonder if it is missing some + feature or other (I don't recall), as the `down` demo in `kahn_turbo` sounds different on native. +* I didn't make a proper port in the platform/ directory, sorry. This is probably possible, but wasn't my focus. diff --git a/README b/README_USP similarity index 97% rename from README rename to README_USP index 3390135..da2eca1 100755 --- a/README +++ b/README_USP @@ -1,10 +1,10 @@ -Portable ZX-Spectrum emulator supports Z80 128K (Pentagon) AY/YM, Beeper, Beta Disk, Tape, Kempston Joystick/Mouse, Snapshots, Replays. - -Supported formats: sna, z80, szx, rzx, tap, tzx, csw, trd, scl, fdi, td0, udi, zip. - -Created to be ported to many platforms such as Windows/Linux/Mac/Symbian/WinMobile/Dingoo A320/Android/PSP/Raspberry Pi, ... - -Authors : SMT, Dexus, Alone Coder, deathsoft. -Portable version : djdron, scor. - -Homepage: https://bitbucket.org/djdron/unrealspeccyp +Portable ZX-Spectrum emulator supports Z80 128K (Pentagon) AY/YM, Beeper, Beta Disk, Tape, Kempston Joystick/Mouse, Snapshots, Replays. + +Supported formats: sna, z80, szx, rzx, tap, tzx, csw, trd, scl, fdi, td0, udi, zip. + +Created to be ported to many platforms such as Windows/Linux/Mac/Symbian/WinMobile/Dingoo A320/Android/PSP/Raspberry Pi, ... + +Authors : SMT, Dexus, Alone Coder, deathsoft. +Portable version : djdron, scor. + +Homepage: https://bitbucket.org/djdron/unrealspeccyp diff --git a/devices/device.cpp b/devices/device.cpp index 0bf3331..05e2c79 100644 --- a/devices/device.cpp +++ b/devices/device.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -18,16 +19,22 @@ along with this program. If not, see . #include "../std.h" #include "device.h" - +#ifdef USE_KHAN_GPIO +// hack +#include "khan_lib.h" +#endif //============================================================================= // eDevices::eDevices //----------------------------------------------------------------------------- eDevices::eDevices() { memset(items, 0, sizeof(items)); +#ifndef USE_HACKED_DEVICE_ABSTRACTION memset(items_io_read, 0, sizeof(items_io_read)); memset(items_io_write, 0, sizeof(items_io_write)); +#endif } +#ifndef NO_USE_DESTRUCTORS //============================================================================= // eDevices::~eDevices //----------------------------------------------------------------------------- @@ -38,11 +45,13 @@ eDevices::~eDevices() SAFE_DELETE(items[i]); } } +#endif //============================================================================= // eDevices::Init //----------------------------------------------------------------------------- void eDevices::Init() { +#ifndef USE_HACKED_DEVICE_ABSTRACTION int io_read_count = 0; for(; items_io_read[io_read_count]; ++io_read_count); int io_write_count = 0; @@ -89,6 +98,7 @@ void eDevices::Init() } *dl = NULL; } +#endif } //============================================================================= // eDevices::Reset @@ -138,6 +148,7 @@ void eDevices::_Add(eDeviceId id, eDevice* d) assert(d && !items[id]); d->Init(); items[id] = d; +#ifndef USE_HACKED_DEVICE_ABSTRACTION if(d->IoNeed()&eDevice::ION_READ) { eDevice** dl = items_io_read; @@ -152,4 +163,144 @@ void eDevices::_Add(eDeviceId id, eDevice* d) ++dl; *dl = d; } +#endif +} + +#ifdef USE_HACKED_DEVICE_ABSTRACTION +#include "../platform/platform.h" +#include "../speccy.h" +#ifndef NO_USE_TAPE +#include "input/tape.h" +#endif +#ifndef NO_USE_BEEPER +#include "sound/beeper.h" +#endif +#ifndef NO_USE_AY +#include "sound/ay.h" +#endif +#ifndef NO_USE_KEMPSTON +#include "input/kempston_joy.h" +#endif +#include "input/keyboard.h" +#include "ula.h" +#include "memory.h" + +static struct { + eUla *ula; + eKeyboard *keyboard; +#ifndef NO_USE_TAPE + eTape *tape; +#endif +#ifndef NO_USE_BEEPER + eBeeper *beeper; +#endif +#ifndef NO_USE_128K + eRom *rom; + eRam *ram; +#endif +#ifndef NO_USE_AY + eAY *ay; +#endif +#ifndef NO_USE_KEMPSTON + eKempstonJoy *kempston_joy; +#endif + bool cached; +} _devices; + +static void cache_devices() { + if (!_devices.cached) { + _devices.ula = xPlatform::Handler()->Speccy()->Devices().Get(); + _devices.keyboard = xPlatform::Handler()->Speccy()->Devices().Get(); +#ifndef NO_USE_TAPE + _devices.tape = xPlatform::Handler()->Speccy()->Devices().Get(); +#endif +#ifndef NO_USE_BEEPER + _devices.beeper = xPlatform::Handler()->Speccy()->Devices().Get(); +#endif +#ifndef NO_USE_AY + _devices.ay = xPlatform::Handler()->Speccy()->Devices().Get(); +#endif +#ifndef NO_USE_KEMPSTON + _devices.kempston_joy = xPlatform::Handler()->Speccy()->Devices().Get(); +#endif +#ifndef NO_USE_128K + _devices.rom = xPlatform::Handler()->Speccy()->Devices().Get(); + _devices.ram = xPlatform::Handler()->Speccy()->Devices().Get(); +#endif + _devices.cached = true; + } +} +extern "C" byte static_device_io_read(int port, int tact) { + byte v = 0xff; + cache_devices(); + if (port == 0xff) { + _devices.ula->IoRead(port, &v, tact); + } else if (!(port&1)) { + _devices.keyboard->IoRead(port, &v, tact); + // force press of enter +// if (port == 49150) { +// v = 190; +// } +#ifndef NO_USE_TAPE + _devices.tape->IoRead(port, &v, tact); +#endif +#ifdef USE_KHAN_GPIO + } else if (0b11101011 == (port & 0xff)) { + v = khan_gpio_read(port >> 8); +#endif + } +#ifndef NO_USE_AY + if ((port & 0xC0FF) == 0xC0FD) + { + v = _devices.ay->Read(); + } +#endif +#ifndef NO_USE_KEMPSTON + if (!(port&0x20)) { + // A13,A15 not used in decoding + uint p = port | 0xfa00; + if (!(p == 0xfadf || p == 0xfbdf || p == 0xffdf)) { + _devices.kempston_joy->IoRead(port, &v, tact); + } + } +#endif +#if !defined(NO_USE_FDD) || !defined(NO_USE_REPLAY) +#error todo +#endif + return v; +} + + +// order of args important for assembler +extern "C" void static_device_io_write(byte v, int port, int tact) { + cache_devices(); + if (!(port&1)) { + _devices.ula->IoWrite(port, v, tact); + } +#ifdef USE_KHAN_GPIO + if (0b11101011 == (port & 0xff)) { + khan_gpio_write(port >> 8, v); + } +#endif +#ifndef NO_USE_BEEPER + _devices.beeper->IoWrite(port, v, tact); +#endif +#if !defined(NO_USE_128K) + if(!(port & 2)) { + if (!(port & 0x8000)) // zx128 port + { + _devices.rom->IoWrite(port, v, tact); + _devices.ram->IoWrite(port, v, tact); + _devices.ula->SwitchScreen(!(v & 0x08), tact); + } +#ifndef NO_USE_AY + if ((port & 0xC0FF) == 0xC0FD) { + _devices.ay->Select(v); + } else if ((port & 0xC000) == 0x8000) { + _devices.ay->Write(tact, v); + } +#endif + } +#endif } +#endif diff --git a/devices/device.h b/devices/device.h index 7ef34fb..62878d0 100644 --- a/devices/device.h +++ b/devices/device.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -28,23 +29,55 @@ class eDevice { public: eDevice() {} +#ifndef NO_USE_DESTRUCTORS virtual ~eDevice() {} +#endif virtual void Init() {} virtual void Reset() {} virtual void FrameStart(dword tacts) {} virtual void FrameUpdate() {} virtual void FrameEnd(dword tacts) {} +#ifndef USE_HACKED_DEVICE_ABSTRACTION enum eIoNeed { ION_READ = 0x01, ION_WRITE = 0x02 }; virtual bool IoRead(word port) const { return false; } virtual bool IoWrite(word port) const { return false; } + virtual dword IoNeed() const { return 0; } +#endif virtual void IoRead(word port, byte* v, int tact) {} virtual void IoWrite(word port, byte v, int tact) {} - virtual dword IoNeed() const { return 0; } }; -enum eDeviceId { D_ROM, D_RAM, D_ULA, D_KEYBOARD, D_KEMPSTON_JOY, D_KEMPSTON_MOUSE, D_BEEPER, D_AY, D_WD1793, D_TAPE, D_COUNT }; +enum eDeviceId { + D_ROM, + D_RAM, + D_ULA, + D_KEYBOARD, +#ifndef NO_USE_KEMPSTON + D_KEMPSTON_JOY, +#ifndef USE_MU + D_KEMPSTON_MOUSE, +#endif +#endif +#ifndef NO_USE_BEEPER + D_BEEPER, +#endif +#ifndef NO_USE_AY + D_AY, +#endif +#ifndef NO_USE_FDD + D_WD1793, +#endif +#ifndef NO_USE_TAPE + D_TAPE, +#endif + D_COUNT +}; +#ifdef USE_HACKED_DEVICE_ABSTRACTION +extern "C" byte static_device_io_read(int port, int tact); +extern "C" void static_device_io_write(byte v, int port, int tact); +#endif //***************************************************************************** // eDevices //----------------------------------------------------------------------------- @@ -52,7 +85,10 @@ class eDevices { public: eDevices(); + +#ifndef NO_USE_DESTRUCTORS ~eDevices(); +#endif void Init(); void Reset(); @@ -62,17 +98,25 @@ class eDevices byte IoRead(word port, int tact) { +#ifndef USE_HACKED_DEVICE_ABSTRACTION byte v = 0xff; eDevice** dl = io_read_cache[io_read_map[port]]; while(*dl) (*dl++)->IoRead(port, &v, tact); return v; +#else + return static_device_io_read(port, tact); +#endif } void IoWrite(word port, byte v, int tact) { +#ifndef USE_HACKED_DEVICE_ABSTRACTION eDevice** dl = io_write_cache[io_write_map[port]]; while(*dl) (*dl++)->IoWrite(port, v, tact); +#else + static_device_io_write(v, port, tact); +#endif } void FrameStart(dword tacts); @@ -83,12 +127,14 @@ class eDevices void _Add(eDeviceId id, eDevice* d); eDevice* _Get(eDeviceId id) const { return items[id]; } eDevice* items[D_COUNT]; +#ifndef USE_HACKED_DEVICE_ABSTRACTION eDevice* items_io_read[D_COUNT + 1]; eDevice* items_io_write[D_COUNT + 1]; byte io_read_map[0x10000]; byte io_write_map[0x10000]; eDevice* io_read_cache[0x100][9]; eDevice* io_write_cache[0x100][9]; +#endif }; #endif//__DEVICE_H__ diff --git a/devices/input/kempston_joy.cpp b/devices/input/kempston_joy.cpp index c7898bd..6a26a8a 100644 --- a/devices/input/kempston_joy.cpp +++ b/devices/input/kempston_joy.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -16,17 +17,19 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#ifndef NO_USE_KEMPSTON #include "../../std.h" #include "kempston_joy.h" void eKempstonJoy::Init() { Reset(); } void eKempstonJoy::Reset() { state = 0; } -//============================================================================= -// eKempstonJoy::IoRead -//----------------------------------------------------------------------------- -bool eKempstonJoy::IoRead(word port) const -{ +#ifndef USE_MU +//============================================================================= +// eKempstonJoy::IoRead +//----------------------------------------------------------------------------- +bool eKempstonJoy::IoRead(word port) const +{ if(port&0x20) return false; // skip kempston mouse ports @@ -34,10 +37,11 @@ bool eKempstonJoy::IoRead(word port) const if((port == 0xfadf || port == 0xfbdf || port == 0xffdf)) return false; return true; -} -//============================================================================= -// eKempstonJoy::IoRead -//----------------------------------------------------------------------------- +} +#endif +//============================================================================= +// eKempstonJoy::IoRead +//----------------------------------------------------------------------------- void eKempstonJoy::IoRead(word port, byte* v, int tact) { *v = state; @@ -48,25 +52,26 @@ struct eButton char key; byte bit; }; +#ifndef USE_MU enum { BUTTONS_COUNT = 5 }; static const eButton buttons[BUTTONS_COUNT] = { - {'r', 0x01 }, - {'l', 0x02 }, - {'d', 0x04 }, - {'u', 0x08 }, - {'f', 0x10 }, + {'r', KEMPSTON_R }, + {'l', KEMPSTON_L }, + {'d', KEMPSTON_D }, + {'u', KEMPSTON_U }, + {'f', KEMPSTON_F }, }; -//============================================================================= -// eKempstonJoy::OnKey -//----------------------------------------------------------------------------- +//============================================================================= +// eKempstonJoy::OnKey +//----------------------------------------------------------------------------- void eKempstonJoy::OnKey(char _key, bool _down) { KeyState(_key, _down); } -//============================================================================= -// eKempstonJoy::KeyState -//----------------------------------------------------------------------------- +//============================================================================= +// eKempstonJoy::KeyState +//----------------------------------------------------------------------------- void eKempstonJoy::KeyState(char _key, bool _down) { for(int i = 0; i < BUTTONS_COUNT; ++i) @@ -82,3 +87,5 @@ void eKempstonJoy::KeyState(char _key, bool _down) } } } +#endif +#endif \ No newline at end of file diff --git a/devices/input/kempston_joy.h b/devices/input/kempston_joy.h index 8d516fc..c8eebcf 100644 --- a/devices/input/kempston_joy.h +++ b/devices/input/kempston_joy.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -23,19 +24,38 @@ along with this program. If not, see . #pragma once +#define KEMPSTON_R 0x01 +#define KEMPSTON_L 0x02 +#define KEMPSTON_D 0x04 +#define KEMPSTON_U 0x08 +#define KEMPSTON_F 0x10 + class eKempstonJoy : public eDevice { public: virtual void Init(); virtual void Reset(); - virtual bool IoRead(word port) const; +#ifndef USE_MU + virtual bool IoRead(word port) const; +#endif virtual void IoRead(word port, byte* v, int tact); - void OnKey(char key, bool down); +#ifndef USE_MU + void OnKey(char key, bool down); +#else + inline void setState(byte _state) { + state = _state; + } +#endif static eDeviceId Id() { return D_KEMPSTON_JOY; } + +#ifndef USE_MU virtual dword IoNeed() const { return ION_READ; } +#endif protected: +#ifndef USE_MU void KeyState(char key, bool down); +#endif byte state; }; diff --git a/devices/input/keyboard.cpp b/devices/input/keyboard.cpp index 8bafe94..aafd988 100644 --- a/devices/input/keyboard.cpp +++ b/devices/input/keyboard.cpp @@ -1,22 +1,23 @@ -/* -Portable ZX-Spectrum emulator. -Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor - -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 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - -#include "../../std.h" +/* +Portable ZX-Spectrum emulator. +Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson + +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 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "../../std.h" #include "keyboard.h" //============================================================================= @@ -33,13 +34,15 @@ void eKeyboard::Reset() { memset(kbd, 0xff, sizeof(kbd)); } -//============================================================================= -// eKeyboard::IoRead -//----------------------------------------------------------------------------- -bool eKeyboard::IoRead(word port) const -{ - return !(port&1); -} +#ifndef USE_HACKED_DEVICE_ABSTRACTION +//============================================================================= +// eKeyboard::IoRead +//----------------------------------------------------------------------------- +bool eKeyboard::IoRead(word port) const +{ + return !(port&1); +} +#endif //============================================================================= // eKeyboard::IoRead //----------------------------------------------------------------------------- @@ -53,11 +56,11 @@ void eKeyboard::IoRead(word port, byte* v, int tact) byte eKeyboard::Read(byte scan) const { byte res = 0xbf; - for(int i = 0; i < 8; i++) - { - if(!(scan & (1<. #include "../../z80/z80.h" #include "../memory.h" #include "tape.h" +#include +#include "../../options_common.h" + +#ifndef NO_USE_TAPE //============================================================================= // eTape::Init @@ -28,6 +33,7 @@ along with this program. If not, see . void eTape::Init() { eInherited::Init(); +#ifdef USE_LEGACY_TAPE_COMPARISON max_pulses = 0; tape_err = 0; @@ -38,6 +44,10 @@ void eTape::Init() tape_infosize = 0; appendable = 0; + +#endif + tape_instance = NULL; + pulse_iterator = NULL; } //============================================================================= // eTape::Reset @@ -66,15 +76,22 @@ void eTape::Stop() //----------------------------------------------------------------------------- bool eTape::Started() const { - return tape.play_pointer != NULL; +#ifdef USE_LEGACY_TAPE_COMPARISON + assert((tape.play_pointer != NULL) == tape.playing); +#endif + return tape.playing; } //============================================================================= // eTape::Inserted //----------------------------------------------------------------------------- bool eTape::Inserted() const { - return tape_image != NULL; +#ifdef USE_LEGACY_TAPE_COMPARISON + assert((tape_image != NULL) == (tape_instance != NULL)); +#endif + return tape_instance != NULL; } +#ifndef USE_HACKED_DEVICE_ABSTRACTION //============================================================================= // eTape::IoRead //----------------------------------------------------------------------------- @@ -82,6 +99,7 @@ bool eTape::IoRead(word port) const { return !(port&1); } +#endif //============================================================================= // eTape::IoRead //----------------------------------------------------------------------------- @@ -89,6 +107,7 @@ void eTape::IoRead(word port, byte* v, int tact) { *v |= TapeBit(tact) & 0x40; } +#ifdef USE_LEGACY_TAPE_COMPARISON //============================================================================= // eTape::FindPulse //----------------------------------------------------------------------------- @@ -119,6 +138,7 @@ void eTape::FindTapeIndex() if(tape.play_pointer >= tape_image + tapeinfo[i].pos) tape.index = i; } +#ifndef USE_MU_SIMPLIFICATIONS //============================================================================= // eTape::FindTapeSizes //----------------------------------------------------------------------------- @@ -134,41 +154,63 @@ void eTape::FindTapeSizes() tapeinfo[i].t_size = sz; } } +#endif +#endif //============================================================================= // eTape::StopTape //----------------------------------------------------------------------------- void eTape::StopTape() { +#ifdef USE_LEGACY_TAPE_COMPARISON FindTapeIndex(); if(tape.play_pointer >= tape.end_of_tape) tape.index = 0; tape.play_pointer = 0; - tape.edge_change = 0x7FFFFFFFFFFFFFFFLL; - tape.tape_bit = -1; +#endif +// tape.edge_change = 0x7FFFFFFFFFFFFFFFLL; + tape.edge_change = std::numeric_limits::max(); + tape.playing = false; + tape.tape_bit = 0xff; +#ifndef NO_USE_FAST_TAPE speccy->CPU()->HandlerStep(NULL); +#endif } //============================================================================= // eTape::ResetTape //----------------------------------------------------------------------------- void eTape::ResetTape() { + pulse_iterator = tape_instance ? tape_instance->reset() : NULL; +#ifdef USE_LEGACY_TAPE_COMPARISON tape.index = 0; tape.play_pointer = 0; +#endif + tape.tape_bit = 0xff; tape.edge_change = 0x7FFFFFFFFFFFFFFFLL; - tape.tape_bit = -1; +#ifndef NO_USE_FAST_TAPE speccy->CPU()->HandlerStep(NULL); +#endif + if (tape_instance) { + pulse_iterator = tape_instance->reset(); + } else { + pulse_iterator = NULL; + } + tape.playing = false; } //============================================================================= // eTape::StartTape //----------------------------------------------------------------------------- void eTape::StartTape() { - if(!tape_image) + if(!Inserted()) return; +#ifdef USE_LEGACY_TAPE_COMPARISON tape.play_pointer = tape_image + tapeinfo[tape.index].pos; tape.end_of_tape = tape_image + tape_imagesize; +#endif tape.edge_change = speccy->T(); - tape.tape_bit = -1; + tape.tape_bit = 0xff; + tape.playing = true; // speccy->CPU()->FastEmul(FastTapeEmul); } //============================================================================= @@ -176,23 +218,29 @@ void eTape::StartTape() //----------------------------------------------------------------------------- void eTape::CloseTape() { +#ifdef USE_LEGACY_TAPE_COMPARISON if(tape_image) { - free(tape_image); + mu_free(tape_image); tape_image = 0; } if(tapeinfo) { - free(tapeinfo); + mu_free(tapeinfo); tapeinfo = 0; } tape.play_pointer = 0; // stop tape tape.index = 0; // rewind tape tape_err = max_pulses = tape_imagesize = tape_infosize = 0; +#endif tape.edge_change = 0x7FFFFFFFFFFFFFFFLL; - tape.tape_bit = -1; + tape.tape_bit = 0xff; + pulse_iterator = NULL; + delete tape_instance; + tape_instance = NULL; } +#ifdef USE_LEGACY_TAPE_COMPARISON #define align_by(a,b) (((dword)(a) + ((b)-1)) & ~((b)-1)) //============================================================================= @@ -239,6 +287,9 @@ void eTape::MakeBlock(const byte* data, dword size, dword pilot_t, dword s1_t, //----------------------------------------------------------------------------- void eTape::Desc(const byte* data, dword size, char* dst) { +#ifdef USE_MU_SIMPLIFICATIONS + dst[0] = 0; +#else byte crc = 0; char prg[10]; dword i; //Alone Coder 0.36.7 @@ -259,6 +310,7 @@ void eTape::Desc(const byte* data, dword size, char* dst) else sprintf(dst, "#%02X block, %d bytes", *data, size - 2); sprintf(dst + strlen(dst), ", crc %s", crc ? "bad" : "ok"); +#endif } //============================================================================= // eTape::AllocInfocell @@ -270,55 +322,53 @@ void eTape::AllocInfocell() tapeinfo[tape_infosize].pos = tape_imagesize; appendable = 0; } -//============================================================================= -// eTape::NamedCell -//----------------------------------------------------------------------------- -void eTape::NamedCell(const void* nm, dword sz) -{ - AllocInfocell(); - if(sz) - memcpy(tapeinfo[tape_infosize].desc, nm, sz), tapeinfo[tape_infosize].desc[sz] - = 0; - else - strcpy(tapeinfo[tape_infosize].desc, (const char*)nm); - tape_infosize++; -} +#endif //============================================================================= // eTape::Open //----------------------------------------------------------------------------- -bool eTape::Open(const char* type, const void* data, size_t data_size) +#ifdef USE_STREAM +bool eTape::Open(const char* type, struct stream *stream) { if(!strcmp(type, "tap")) - return ParseTAP(data, data_size); + return OpenTAP(stream); +#ifndef NO_USE_CSW else if(!strcmp(type, "csw")) - return ParseCSW(data, data_size); + return ParseCSW(stream); +#endif +#ifndef NO_USE_TZX else if(!strcmp(type, "tzx")) - return ParseTZX(data, data_size); + return ParseTZX(stream); +#endif return false; } +#endif //============================================================================= // eTape::ParseTAP //----------------------------------------------------------------------------- -bool eTape::ParseTAP(const void* data, size_t data_size) +bool eTape::OpenTAP(struct stream *stream) { - const byte* ptr = (const byte*)data; CloseTape(); - while(ptr < (const byte*)data + data_size) - { - dword size = Word(ptr); - ptr += 2; - if(!size) - break; - AllocInfocell(); - Desc(ptr, size, tapeinfo[tape_infosize].desc); - tape_infosize++; - MakeBlock(ptr, size, 2168, 667, 735, 855, 1710, (*ptr < 4) ? 8064 - : 3220, 1000); - ptr += size; - } - FindTapeSizes(); - return (ptr == (const byte*)data + data_size); + tape_instance = new eTapeInstanceTAP(stream); + +#ifndef NDEBUG + do { + if (!stream_peek(stream, 1)) break; // nothing left + const uint8_t *size_ptr = stream_peek(stream, 2); + if (!size_ptr) { + return false; + } + int size = Word(size_ptr); + if (!size) break; + if (!stream_skip(stream, size + 2)) { + return false; + } + } while (true); + stream_reset(stream); +#endif + return true; } + +#ifndef NO_USE_CSW //============================================================================= // eTape::ParseCSW //----------------------------------------------------------------------------- @@ -350,6 +400,8 @@ bool eTape::ParseCSW(const void* data, size_t data_size) FindTapeSizes(); return true; } +#endif +#ifndef NO_USE_TZX //============================================================================= // eTape::CreateAppendableBlock //----------------------------------------------------------------------------- @@ -361,197 +413,6 @@ void eTape::CreateAppendableBlock() appendable = 1; } //============================================================================= -// eTape::ParseHardware -//----------------------------------------------------------------------------- -void eTape::ParseHardware(const byte* ptr) -{ - dword n = *ptr++; - if(!n) - return; - NamedCell("- HARDWARE TYPE "); - static const char ids[] = "computer\0" - "ZX Spectrum 16k\0" - "ZX Spectrum 48k, Plus\0" - "ZX Spectrum 48k ISSUE 1\0" - "ZX Spectrum 128k (Sinclair)\0" - "ZX Spectrum 128k +2 (Grey case)\0" - "ZX Spectrum 128k +2A, +3\0" - "Timex Sinclair TC-2048\0" - "Timex Sinclair TS-2068\0" - "Pentagon 128\0" - "Sam Coupe\0" - "Didaktik M\0" - "Didaktik Gama\0" - "ZX-81 or TS-1000 with 1k RAM\0" - "ZX-81 or TS-1000 with 16k RAM or more\0" - "ZX Spectrum 128k, Spanish version\0" - "ZX Spectrum, Arabic version\0" - "TK 90-X\0" - "TK 95\0" - "Byte\0" - "Elwro\0" - "ZS Scorpion\0" - "Amstrad CPC 464\0" - "Amstrad CPC 664\0" - "Amstrad CPC 6128\0" - "Amstrad CPC 464+\0" - "Amstrad CPC 6128+\0" - "Jupiter ACE\0" - "Enterprise\0" - "Commodore 64\0" - "Commodore 128\0" - "\0" - "ext. storage\0" - "Microdrive\0" - "Opus Discovery\0" - "Disciple\0" - "Plus-D\0" - "Rotronics Wafadrive\0" - "TR-DOS (BetaDisk)\0" - "Byte Drive\0" - "Watsford\0" - "FIZ\0" - "Radofin\0" - "Didaktik disk drives\0" - "BS-DOS (MB-02)\0" - "ZX Spectrum +3 disk drive\0" - "JLO (Oliger) disk interface\0" - "FDD3000\0" - "Zebra disk drive\0" - "Ramex Millenia\0" - "Larken\0" - "\0" - "ROM/RAM type add-on\0" - "Sam Ram\0" - "Multiface\0" - "Multiface 128k\0" - "Multiface +3\0" - "MultiPrint\0" - "MB-02 ROM/RAM expansion\0" - "\0" - "sound device\0" - "Classic AY hardware\0" - "Fuller Box AY sound hardware\0" - "Currah microSpeech\0" - "SpecDrum\0" - "AY ACB stereo; Melodik\0" - "AY ABC stereo\0" - "\0" - "joystick\0" - "Kempston\0" - "Cursor, Protek, AGF\0" - "Sinclair 2\0" - "Sinclair 1\0" - "Fuller\0" - "\0" - "mice\0" - "AMX mouse\0" - "Kempston mouse\0" - "\0" - "other controller\0" - "Trickstick\0" - "ZX Light Gun\0" - "Zebra Graphics Tablet\0" - "\0" - "serial port\0" - "ZX Interface 1\0" - "ZX Spectrum 128k\0" - "\0" - "parallel port\0" - "Kempston S\0" - "Kempston E\0" - "ZX Spectrum 128k +2A, +3\0" - "Tasman\0" - "DK'Tronics\0" - "Hilderbay\0" - "INES Printerface\0" - "ZX LPrint Interface 3\0" - "MultiPrint\0" - "Opus Discovery\0" - "Standard 8255 chip with ports 31,63,95\0" - "\0" - "printer\0" - "ZX Printer, Alphacom 32 & compatibles\0" - "Generic Printer\0" - "EPSON Compatible\0" - "\0" - "modem\0" - "VTX 5000\0" - "T/S 2050 or Westridge 2050\0" - "\0" - "digitaiser\0" - "RD Digital Tracer\0" - "DK'Tronics Light Pen\0" - "British MicroGraph Pad\0" - "\0" - "network adapter\0" - "ZX Interface 1\0" - "\0" - "keyboard / keypad\0" - "Keypad for ZX Spectrum 128k\0" - "\0" - "AD/DA converter\0" - "Harley Systems ADC 8.2\0" - "Blackboard Electronics\0" - "\0" - "EPROM Programmer\0" - "Orme Electronics\0" - "\0" - "\0"; - for(dword i = 0; i < n; i++) - { - byte type_n = *ptr++; - byte id_n = *ptr++; - byte value_n = *ptr++; - const char* type = ids, *id, *value; - dword j; //Alone Coder 0.36.7 - for(/*dword*/j = 0; j < type_n; j++) - { - if(!*type) - break; - while(Word((byte*)type)) - type++; - type += 2; - } - if(!*type) - type = id = "??"; - else - { - id = type + strlen(type) + 1; - for(j = 0; j < id_n; j++) - { - if(!*id) - { - id = "??"; - break; - } - id += strlen(id) + 1; - } - } - switch(value_n) - { - case 0: - value = "compatible with"; - break; - case 1: - value = "uses"; - break; - case 2: - value = "compatible, but doesn't use"; - break; - case 3: - value = "incompatible with"; - break; - default: - value = "??"; - } - char bf[512]; - sprintf(bf, "%s %s: %s", value, type, id); - NamedCell(bf); - } - NamedCell("-"); -} -//============================================================================= // eTape::ParseTZX //----------------------------------------------------------------------------- bool eTape::ParseTZX(const void* data, size_t data_size) @@ -567,69 +428,78 @@ bool eTape::ParseTZX(const void* data, size_t data_size) { switch(*ptr++) { - case 0x10: // normal block - AllocInfocell(); - size = Word(ptr + 2); - pause = Word(ptr); - ptr += 4; - Desc(ptr, size, tapeinfo[tape_infosize].desc); - tape_infosize++; - MakeBlock(ptr, size, 2168, 667, 735, 855, 1710, (*ptr < 4) ? 8064 - : 3220, pause); - ptr += size; - break; - case 0x11: // turbo block - AllocInfocell(); - size = 0xFFFFFF & Dword(ptr + 0x0F); - Desc(ptr + 0x12, size, tapeinfo[tape_infosize].desc); - tape_infosize++; - MakeBlock(ptr + 0x12, size, Word(ptr), Word(ptr + 2), - Word(ptr + 4), Word(ptr + 6), Word(ptr + 8), - Word(ptr + 10), Word(ptr + 13), ptr[12]); - // todo: test used bits - ptr+12 - ptr += size + 0x12; - break; - case 0x12: // pure tone - CreateAppendableBlock(); - pl = FindPulse(Word(ptr)); - n = Word(ptr + 2); - Reserve(n); - for(i = 0; i < n; i++) - tape_image[tape_imagesize++] = pl; - ptr += 4; - break; - case 0x13: // sequence of pulses of different lengths - CreateAppendableBlock(); - n = *ptr++; - Reserve(n); - for(i = 0; i < n; i++, ptr += 2) - tape_image[tape_imagesize++] = FindPulse(Word(ptr)); - break; - case 0x14: // pure data block - CreateAppendableBlock(); - size = 0xFFFFFF & Dword(ptr + 7); - MakeBlock(ptr + 0x0A, size, 0, 0, 0, Word(ptr), - Word(ptr + 2), -1, Word(ptr + 5), ptr[4]); - ptr += size + 0x0A; - break; - case 0x15: // direct recording - size = 0xFFFFFF & Dword(ptr + 5); - t0 = Word(ptr); - pause = Word(ptr + 2); - last = ptr[4]; - NamedCell("direct recording"); - ptr += 8; - pl = 0; - n = 0; - for(i = 0; i < size; i++) // count number of pulses - for(j = 0x80; j; j >>= 1) - if((ptr[i] ^ pl) & j) - n++, pl ^= -1; - t = 0; - pl = 0; - Reserve(n + 2); - for(i = 1; i < size; i++, ptr++) // find pulses - for(j = 0x80; j; j >>= 1) + case 0x10: // normal block + AllocInfocell(); + size = Word(ptr + 2); + pause = Word(ptr); + ptr += 4; + tape_infosize++; + MakeBlock(ptr, size, 2168, 667, 735, 855, 1710, (*ptr < 4) ? 8064 + : 3220, pause); + ptr += size; + break; + case 0x11: // turbo block + AllocInfocell(); + size = 0xFFFFFF & Dword(ptr + 0x0F); + tape_infosize++; + MakeBlock(ptr + 0x12, size, Word(ptr), Word(ptr + 2), + Word(ptr + 4), Word(ptr + 6), Word(ptr + 8), + Word(ptr + 10), Word(ptr + 13), ptr[12]); + // todo: test used bits - ptr+12 + ptr += size + 0x12; + break; + case 0x12: // pure tone + CreateAppendableBlock(); + pl = FindPulse(Word(ptr)); + n = Word(ptr + 2); + Reserve(n); + for(i = 0; i < n; i++) + tape_image[tape_imagesize++] = pl; + ptr += 4; + break; + case 0x13: // sequence of pulses of different lengths + CreateAppendableBlock(); + n = *ptr++; + Reserve(n); + for(i = 0; i < n; i++, ptr += 2) + tape_image[tape_imagesize++] = FindPulse(Word(ptr)); + break; + case 0x14: // pure data block + CreateAppendableBlock(); + size = 0xFFFFFF & Dword(ptr + 7); + MakeBlock(ptr + 0x0A, size, 0, 0, 0, Word(ptr), + Word(ptr + 2), -1, Word(ptr + 5), ptr[4]); + ptr += size + 0x0A; + break; + case 0x15: // direct recording + size = 0xFFFFFF & Dword(ptr + 5); + t0 = Word(ptr); + pause = Word(ptr + 2); + last = ptr[4]; + NamedCell("direct recording"); + ptr += 8; + pl = 0; + n = 0; + for(i = 0; i < size; i++) // count number of pulses + for(j = 0x80; j; j >>= 1) + if((ptr[i] ^ pl) & j) + n++, pl ^= -1; + t = 0; + pl = 0; + Reserve(n + 2); + for(i = 1; i < size; i++, ptr++) // find pulses + for(j = 0x80; j; j >>= 1) + { + t += t0; + if((*ptr ^ pl) & j) + { + tape_image[tape_imagesize++] = FindPulse(t); + pl ^= -1; + t = 0; + } + } + // find pulses - last byte + for(j = 0x80; j != (byte)(0x80 >> last); j >>= 1) { t += t0; if((*ptr ^ pl) & j) @@ -639,233 +509,215 @@ bool eTape::ParseTZX(const void* data, size_t data_size) t = 0; } } - // find pulses - last byte - for(j = 0x80; j != (byte)(0x80 >> last); j >>= 1) - { - t += t0; - if((*ptr ^ pl) & j) - { - tape_image[tape_imagesize++] = FindPulse(t); - pl ^= -1; - t = 0; + ptr++; + tape_image[tape_imagesize++] = FindPulse(t); // last pulse ??? + if(pause) + tape_image[tape_imagesize++] = FindPulse(pause * 3500); + break; + case 0x20: // pause (silence) or 'stop the tape' command + pause = Word(ptr); + sprintf(nm, pause ? "pause %d ms" : "stop the tape", pause); + NamedCell(nm); + Reserve(2); + ptr += 2; + if(!pause) + { // at least 1ms pulse as specified in TZX 1.13 + tape_image[tape_imagesize++] = FindPulse(3500); + pause = -1; } - } - ptr++; - tape_image[tape_imagesize++] = FindPulse(t); // last pulse ??? - if(pause) - tape_image[tape_imagesize++] = FindPulse(pause * 3500); - break; - case 0x20: // pause (silence) or 'stop the tape' command - pause = Word(ptr); - sprintf(nm, pause ? "pause %d ms" : "stop the tape", pause); - NamedCell(nm); - Reserve(2); - ptr += 2; - if(!pause) - { // at least 1ms pulse as specified in TZX 1.13 - tape_image[tape_imagesize++] = FindPulse(3500); - pause = -1; - } - else - pause *= 3500; - tape_image[tape_imagesize++] = FindPulse(pause); - break; - case 0x21: // group start - n = *ptr++; - NamedCell(ptr, n); - ptr += n; - appendable = 1; - break; - case 0x22: // group end - break; - case 0x23: // jump to block - NamedCell("* jump"); - ptr += 2; - break; - case 0x24: // loop start - loop_n = Word(ptr); - loop_p = tape_imagesize; - ptr += 2; - break; - case 0x25: // loop end - if(!loop_n) + else + pause *= 3500; + tape_image[tape_imagesize++] = FindPulse(pause); break; - size = tape_imagesize - loop_p; - Reserve((loop_n - 1) * size); - for(i = 1; i < loop_n; i++) - memcpy(tape_image + loop_p + i * size, tape_image + loop_p, - size); - tape_imagesize += (loop_n - 1) * size; - loop_n = 0; - break; - case 0x26: // call - NamedCell("* call"); - ptr += 2 + 2 * Word(ptr); - break; - case 0x27: // ret - NamedCell("* return"); - break; - case 0x28: // select block - sprintf(nm, "* choice: "); - n = ptr[2]; - p = ptr + 3; - for(i = 0; i < n; i++) - { - if(i) - strcat(nm, " / "); - char *q = nm + strlen(nm); - size = *(p + 2); - memcpy(q, p + 3, size); - q[size] = 0; - p += size + 3; - } - NamedCell(nm); - ptr += 2 + Word(ptr); - break; - case 0x2A: // stop if 48k - NamedCell("* stop if 48K"); - ptr += 4 + Dword(ptr); - break; - case 0x30: // text description - n = *ptr++; - NamedCell(ptr, n); - ptr += n; - appendable = 1; - break; - case 0x31: // message block - NamedCell("- MESSAGE BLOCK "); - end = ptr + 2 + ptr[1]; - pl = *end; - *end = 0; - for(p = ptr + 2; p < end; p++) - if(*p == 0x0D) - *p = 0; - for(p = ptr + 2; p < end; p += strlen((char*)p) + 1) - NamedCell(p); - *end = pl; - ptr = end; - NamedCell("-"); - break; - case 0x32: // archive info - NamedCell("- ARCHIVE INFO "); - p = ptr + 3; - for(i = 0; i < ptr[2]; i++) - { - const char *info; - switch(*p++) - { - case 0: - info = "Title"; - break; - case 1: - info = "Publisher"; - break; - case 2: - info = "Author"; - break; - case 3: - info = "Year"; - break; - case 4: - info = "Language"; - break; - case 5: - info = "Type"; - break; - case 6: - info = "Price"; - break; - case 7: - info = "Protection"; - break; - case 8: - info = "Origin"; - break; - case 0xFF: - info = "Comment"; - break; - default: - info = "info"; + case 0x21: // group start + n = *ptr++; + NamedCell(ptr, n); + ptr += n; + appendable = 1; + break; + case 0x22: // group end + break; + case 0x23: // jump to block + NamedCell("* jump"); + ptr += 2; + break; + case 0x24: // loop start + loop_n = Word(ptr); + loop_p = tape_imagesize; + ptr += 2; + break; + case 0x25: // loop end + if(!loop_n) break; + size = tape_imagesize - loop_p; + Reserve((loop_n - 1) * size); + for(i = 1; i < loop_n; i++) + memcpy(tape_image + loop_p + i * size, tape_image + loop_p, + size); + tape_imagesize += (loop_n - 1) * size; + loop_n = 0; + break; + case 0x26: // call + NamedCell("* call"); + ptr += 2 + 2 * Word(ptr); + break; + case 0x27: // ret + NamedCell("* return"); + break; + case 0x28: // select block + sprintf(nm, "* choice: "); + n = ptr[2]; + p = ptr + 3; + for(i = 0; i < n; i++) + { + if(i) + strcat(nm, " / "); + char *q = nm + strlen(nm); + size = *(p + 2); + memcpy(q, p + 3, size); + q[size] = 0; + p += size + 3; } - dword size = *p + 1; - char tmp = p[size]; - p[size] = 0; - sprintf(nm, "%s: %s", info, p + 1); - p[size] = tmp; - p += size; NamedCell(nm); - } - NamedCell("-"); - ptr += 2 + Word(ptr); - break; - case 0x33: // hardware type - ParseHardware(ptr); - ptr += 1 + 3 * *ptr; - break; - case 0x34: // emulation info - NamedCell("* emulation info"); - ptr += 8; - break; - case 0x35: // custom info - if(!memcmp(ptr, "POKEs ", 16)) - { - NamedCell("- POKEs block "); - NamedCell(ptr + 0x15, ptr[0x14]); - p = ptr + 0x15 + ptr[0x14]; - n = *p++; - for(i = 0; i < n; i++) + ptr += 2 + Word(ptr); + break; + case 0x2A: // stop if 48k + NamedCell("* stop if 48K"); + ptr += 4 + Dword(ptr); + break; + case 0x30: // text description + n = *ptr++; + NamedCell(ptr, n); + ptr += n; + appendable = 1; + break; + case 0x31: // message block + NamedCell("- MESSAGE BLOCK "); + end = ptr + 2 + ptr[1]; + pl = *end; + *end = 0; + for(p = ptr + 2; p < end; p++) + if(*p == 0x0D) + *p = 0; + for(p = ptr + 2; p < end; p += strlen((char*)p) + 1) + NamedCell(p); + *end = pl; + ptr = end; + NamedCell("-"); + break; + case 0x32: // archive info + NamedCell("- ARCHIVE INFO "); + p = ptr + 3; + for(i = 0; i < ptr[2]; i++) { - NamedCell(p + 1, *p); - p += *p + 1; - t = *p++; - strcpy(nm, "POKE "); - for(j = 0; j < t; j++) + const char *info; + switch(*p++) { - sprintf(nm + strlen(nm), "%d,", Word(p + 1)); - sprintf(nm + strlen(nm), *p & 0x10 ? "nn" : "%d", - *(byte*)(p + 3)); - if(!(*p & 0x08)) - sprintf(nm + strlen(nm), "(page %d)", *p & 7); - strcat(nm, "; "); - p += 5; + case 0: + info = "Title"; + break; + case 1: + info = "Publisher"; + break; + case 2: + info = "Author"; + break; + case 3: + info = "Year"; + break; + case 4: + info = "Language"; + break; + case 5: + info = "Type"; + break; + case 6: + info = "Price"; + break; + case 7: + info = "Protection"; + break; + case 8: + info = "Origin"; + break; + case 0xFF: + info = "Comment"; + break; + default: + info = "info"; + break; } + dword size = *p + 1; + char tmp = p[size]; + p[size] = 0; + sprintf(nm, "%s: %s", info, p + 1); + p[size] = tmp; + p += size; NamedCell(nm); } - nm[0] = '-'; - nm[1] = 0; - nm[2] = 0; - nm[3] = 0; - } - else - sprintf(nm, "* custom info: %s", ptr), nm[15 + 16] = 0; - NamedCell(nm); - ptr += 0x14 + Dword(ptr + 0x10); - break; - case 0x40: // snapshot - NamedCell("* snapshot"); - ptr += 4 + (0xFFFFFF & Dword(ptr + 1)); - break; - case 0x5A: // 'Z' - ptr += 9; - break; - default: - ptr += data_size; + NamedCell("-"); + ptr += 2 + Word(ptr); + break; + case 0x33: // hardware type + ptr += 1 + 3 * *ptr; + break; + case 0x34: // emulation info + NamedCell("* emulation info"); + ptr += 8; + break; + case 0x35: // custom info + if(!memcmp(ptr, "POKEs ", 16)) + { + NamedCell("- POKEs block "); + NamedCell(ptr + 0x15, ptr[0x14]); + p = ptr + 0x15 + ptr[0x14]; + n = *p++; + for(i = 0; i < n; i++) + { + NamedCell(p + 1, *p); + p += *p + 1; + t = *p++; + strcpy(nm, "POKE "); + for(j = 0; j < t; j++) + { + sprintf(nm + strlen(nm), "%d,", Word(p + 1)); + sprintf(nm + strlen(nm), *p & 0x10 ? "nn" : "%d", + *(byte*)(p + 3)); + if(!(*p & 0x08)) + sprintf(nm + strlen(nm), "(page %d)", *p & 7); + strcat(nm, "; "); + p += 5; + } + NamedCell(nm); + } + nm[0] = '-'; + nm[1] = 0; + nm[2] = 0; + nm[3] = 0; + } + else + sprintf(nm, "* custom info: %s", ptr), nm[15 + 16] = 0; + NamedCell(nm); + ptr += 0x14 + Dword(ptr + 0x10); + break; + case 0x40: // snapshot + NamedCell("* snapshot"); + ptr += 4 + (0xFFFFFF & Dword(ptr + 1)); + break; + case 0x5A: // 'Z' + ptr += 9; + break; + default: + ptr += data_size; } } - for(i = 0; i < tape_infosize; i++) - { - if(tapeinfo[i].desc[0] == '*' && tapeinfo[i].desc[1] == ' ') - strcat(tapeinfo[i].desc, " [UNSUPPORTED]"); - if(*tapeinfo[i].desc == '-') - while(strlen(tapeinfo[i].desc) < sizeof(tapeinfo[i].desc) - 1) - strcat(tapeinfo[i].desc, "-"); - } + if(tape_imagesize && tape_pulse[tape_image[tape_imagesize - 1]] < 350000) Reserve(1), tape_image[tape_imagesize++] = FindPulse(350000); // small pause [rqd for 3ddeathchase] FindTapeSizes(); return (ptr == (const byte*)data + data_size); } +#endif //============================================================================= // eTape::TapeBit //----------------------------------------------------------------------------- @@ -877,277 +729,348 @@ byte eTape::TapeBit(int tact) dword t = (dword)(tape.edge_change - speccy->T()); if((int)t >= 0) { - const short vol = 1000; - short mono = tape.tape_bit ? vol : 0; +// const short vol = 4096;//1000; // a little loader than the default +// short mono = tape.tape_bit ? vol : 0; +// mono = (mono * 182 * xPlatform::OpTapeVolume()) >> 16; //(0->255) + // max out at 160/256 + uint8_t mono = tape.tape_bit ? (xPlatform::OpTapeVolume() << 4) : 0; Update(tact, mono, mono); } dword pulse; - tape.tape_bit ^= -1; - if(tape.play_pointer >= tape.end_of_tape || - (pulse = tape_pulse[*tape.play_pointer++]) == (dword)-1) - StopTape(); - else + tape.tape_bit ^= 0xff; + if ((pulse = NextPulseOrStop()) != (dword)-1) { tape.edge_change += pulse; + } } return (byte)tape.tape_bit; } +dword eTape::NextPulseOrStop() +{ + dword pulse; + assert(pulse_iterator && tape.playing); + pulse = (pulse_iterator && tape.playing) ? pulse_iterator->next_pulse_length() : -1; +#ifdef USE_LEGACY_TAPE_COMPARISON + dword pulse2; + if(tape.play_pointer >= tape.end_of_tape || + (pulse2 = tape_pulse[*tape.play_pointer++]) == (dword)-1) { + assert(pulse == -1); + } + else + { + assert(pulse == pulse2); + } +#endif + if (pulse == (dword)-1) + { + StopTape(); + } + return pulse; +} + +void eTape::SkipPulse() { + assert(pulse_iterator); + pulse_iterator->next_pulse_length(); +#ifdef USE_LEGACY_TAPE_COMPARISON + tape.play_pointer++; +#endif +} + +#ifndef NO_USE_FAST_TAPE namespace xZ80 { //***************************************************************************** // eZ80_FastTape //----------------------------------------------------------------------------- -class eZ80_FastTape: public xZ80::eZ80 -{ -public: - void StepEdge(); - void StepTrap(); - void Step() + class eZ80_FastTape: public xZ80::eZ80 { - StepTrap(); - StepEdge(); - } -}; + public: + void StepEdge(); + void StepTrap(); + void Step() + { +// static int foo = 0; +// printf("Tape step %d %04x\n", foo++, get_caller_pc()); + StepTrap(); + StepEdge(); + } + void FindEdge(byte mask, int tstates) { + FindEdge(mask, tstates, 0xff, 1); + } + void FindEdge(byte mask, int tstates, byte endb, int delta_b); + bool CompareMemory(word addr, byte *cmp, int len) + { + while (len--) + { + if (*(cmp++) != memory->Read(addr++)) + return false; + } + return true; + } + }; + //============================================================================= // eZ80_FastTape::StepEdge //----------------------------------------------------------------------------- -void eZ80_FastTape::StepEdge() -{ - byte p0 = memory->Read(pc + 0); - byte p1 = memory->Read(pc + 1); - byte p2 = memory->Read(pc + 2); - byte p3 = memory->Read(pc + 3); - if(p0 == 0x3D && p1 == 0x20 && p2 == 0xFD && p3 == 0xA7) - { // dec a:jr nz,$-1 - t += ((byte)(a - 1)) * 16; - a = 1; - return; - } - if(p0 == 0x10 && p1 == 0xFE) - { // djnz $ - t += ((byte)(b - 1)) * 13; - b = 1; - return; - } - if(p0 == 0x3D && p1 == 0xC2 && pc == dword(p3) * 0x100 + p2) - { // dec a:jp nz,$-1 - t += ((byte)(a - 1)) * 14; - a = 1; - return; - } - if(p0 == 0x04 && p1 == 0xC8 && p2 == 0x3E) + void eZ80_FastTape::StepEdge() { - byte p04 = memory->Read(pc + 4); - byte p05 = memory->Read(pc + 5); - byte p06 = memory->Read(pc + 6); - byte p07 = memory->Read(pc + 7); - byte p08 = memory->Read(pc + 8); - byte p09 = memory->Read(pc + 9); - byte p10 = memory->Read(pc + 10); - byte p11 = memory->Read(pc + 11); - byte p12 = memory->Read(pc + 12); - if(p04 == 0xDB && p05 == 0xFE && p06 == 0x1F && p07 == 0xD0 && p08 - == 0xA9 && p09 == 0xE6 && p10 == 0x20 && p11 == 0x28 && p12 - == 0xF3) - { // find edge (rom routine) - eTape* tape = devices->Get(); - for(;;) - { - if(b == 0xFF) - return; - if((tape->TapeBit(T()) ^ c) & 0x20) - return; - b++; - t += 59; - } + const word pc = get_caller_pc(); + byte p0 = memory->Read(pc + 0); + byte p1 = memory->Read(pc + 1); + byte p2 = memory->Read(pc + 2); + byte p3 = memory->Read(pc + 3); + if(p0 == 0x3D && p1 == 0x20 && p2 == 0xFD && p3 == 0xA7) + { // dec a:jr nz,$-1 + delta_caller_t(((byte)(get_caller_a() - 1)) * 16); + set_caller_a(1); + return; } - if(p04 == 0xDB && p05 == 0xFE && p06 == 0xCB && p07 == 0x1F && p08 - == 0xA9 && p09 == 0xE6 && p10 == 0x20 && p11 == 0x28 && p12 - == 0xF3) - { // rra,ret nc => rr a (popeye2) - eTape* tape = devices->Get(); - for(;;) - { - if(b == 0xFF) - return; - if((tape->TapeBit(T()) ^ c) & 0x20) - return; - b++; - t += 58; - } + if(p0 == 0x10 && p1 == 0xFE) + { // djnz $ + delta_caller_t(((byte)(get_caller_b() - 1)) * 13); + set_caller_b(1); + return; } - if(p04 == 0xDB && p05 == 0xFE && p06 == 0x1F && p07 == 0x00 && p08 - == 0xA9 && p09 == 0xE6 && p10 == 0x20 && p11 == 0x28 && p12 - == 0xF3) - { // ret nc nopped (some bleep loaders) - eTape* tape = devices->Get(); - for(;;) - { - if(b == 0xFF) - return; - if((tape->TapeBit(T()) ^ c) & 0x20) - return; - b++; - t += 58; + if(p0 == 0x3D && p1 == 0xC2 && pc == dword(p3) * 0x100 + p2) + { // dec a:jp nz,$-1 + delta_caller_t(((byte)(get_caller_a() - 1)) * 14); + set_caller_a(1); + return; + } + if(p0 == 0x04 && p1 == 0xC8 && p2 == 0x3E) + { + byte p04 = memory->Read(pc + 4); + byte p05 = memory->Read(pc + 5); + byte p06 = memory->Read(pc + 6); + byte p07 = memory->Read(pc + 7); + byte p08 = memory->Read(pc + 8); + byte p09 = memory->Read(pc + 9); + byte p10 = memory->Read(pc + 10); + byte p11 = memory->Read(pc + 11); + byte p12 = memory->Read(pc + 12); + if(p04 == 0xDB && p05 == 0xFE && p06 == 0x1F && p07 == 0xD0 && p08 + == 0xA9 && p09 == 0xE6 && p10 == 0x20 && p11 == 0x28 && p12 + == 0xF3) + { // find edge (rom routine) + FindEdge(0x20, 59); + return; + } + if(p04 == 0xDB && p05 == 0xFE && p06 == 0xCB && p07 == 0x1F && p08 + == 0xA9 && p09 == 0xE6 && p10 == 0x20 && p11 == 0x28 && p12 + == 0xF3) + { // rra,ret nc => rr a (popeye2) + FindEdge(0x20, 58); + return; + } + if(p04 == 0xDB && p05 == 0xFE && p06 == 0x1F && p07 == 0x00 && p08 + == 0xA9 && p09 == 0xE6 && p10 == 0x20 && p11 == 0x28 && p12 + == 0xF3) + { // ret nc nopped (some bleep loaders) + FindEdge(0x20, 58); + return; + } + if(p04 == 0xDB && p05 == 0xFE && p06 == 0xA9 && p07 == 0xE6 && p08 + == 0x40 && p09 == 0xD8 && p10 == 0x00 && p11 == 0x28 && p12 + == 0xF3) + { // no rra, no break check (rana rama) + FindEdge(0x40, 59); + return; + } + if(p04 == 0xDB && p05 == 0xFE && p06 == 0x1F && p07 == 0xA9 && p08 + == 0xE6 && p09 == 0x20 && p10 == 0x28 && p11 == 0xF4) + { // ret nc skipped: routine without BREAK checking (ZeroMusic & JSW) + FindEdge(0x20, 54); + return; } } - if(p04 == 0xDB && p05 == 0xFE && p06 == 0xA9 && p07 == 0xE6 && p08 - == 0x40 && p09 == 0xD8 && p10 == 0x00 && p11 == 0x28 && p12 - == 0xF3) - { // no rra, no break check (rana rama) - eTape* tape = devices->Get(); - for(;;) - { - if(b == 0xFF) - return; - if((tape->TapeBit(T()) ^ c) & 0x40) - return; - b++; - t += 59; + if(p0 == 0x04 && p1 == 0x20 && p2 == 0x03) + { + byte p06 = memory->Read(pc + 6); + byte p08 = memory->Read(pc + 8); + byte p09 = memory->Read(pc + 9); + byte p10 = memory->Read(pc + 10); + byte p11 = memory->Read(pc + 11); + byte p12 = memory->Read(pc + 12); + byte p13 = memory->Read(pc + 13); + byte p14 = memory->Read(pc + 14); + if(p06 == 0xDB && p08 == 0x1F && p09 == 0xC8 && p10 == 0xA9 && p11 + == 0xE6 && p12 == 0x20 && p13 == 0x28 && p14 == 0xF1) + { // find edge from Donkey Kong + FindEdge(0x20, 59); + return; } } - if(p04 == 0xDB && p05 == 0xFE && p06 == 0x1F && p07 == 0xA9 && p08 - == 0xE6 && p09 == 0x20 && p10 == 0x28 && p11 == 0xF4) - { // ret nc skipped: routine without BREAK checking (ZeroMusic & JSW) - eTape* tape = devices->Get(); - for(;;) - { - if(b == 0xFF) - return; - if((tape->TapeBit(T()) ^ c) & 0x20) - return; - b++; - t += 54; + if(p0 == 0x3E && p2 == 0xDB && p3 == 0xFE) + { + byte p04 = memory->Read(pc + 4); + byte p05 = memory->Read(pc + 5); + byte p06 = memory->Read(pc + 6); + byte p07 = memory->Read(pc + 7); + byte p09 = memory->Read(pc + 9); + byte p10 = memory->Read(pc + 10); + byte p11 = memory->Read(pc + 11); + if(p04 == 0xA9 && p05 == 0xE6 && p06 == 0x40 && p07 == 0x20 && p09 + == 0x05 && p10 == 0x20 && p11 == 0xF4) + { // lode runner + FindEdge(0x40, 52, 1, -1); } } } - if(p0 == 0x04 && p1 == 0x20 && p2 == 0x03) + + void eZ80_FastTape::FindEdge(byte mask, int tstates, byte end_b, int delta_b) { - byte p06 = memory->Read(pc + 6); - byte p08 = memory->Read(pc + 8); - byte p09 = memory->Read(pc + 9); - byte p10 = memory->Read(pc + 10); - byte p11 = memory->Read(pc + 11); - byte p12 = memory->Read(pc + 12); - byte p13 = memory->Read(pc + 13); - byte p14 = memory->Read(pc + 14); - if(p06 == 0xDB && p08 == 0x1F && p09 == 0xC8 && p10 == 0xA9 && p11 - == 0xE6 && p12 == 0x20 && p13 == 0x28 && p14 == 0xF1) - { // find edge from Donkey Kong - eTape* tape = devices->Get(); - for(;;) - { - if(b == 0xFF) - return; - if((tape->TapeBit(T()) ^ c) & 0x20) - return; - b++; - t += 59; - } + eTape *tape = devices->Get(); + for (;;) + { + if (get_caller_b() == end_b) + return; + if ((tape->TapeBit(T()) ^ get_caller_c()) & mask) + return; + set_caller_b(get_caller_b() + delta_b); + delta_caller_t(tstates); } } - if(p0 == 0x3E && p2 == 0xDB && p3 == 0xFE) + +//============================================================================= +// eZ80_FastTape::StepTrap +//----------------------------------------------------------------------------- + void eZ80_FastTape::StepTrap() { - byte p04 = memory->Read(pc + 4); - byte p05 = memory->Read(pc + 5); - byte p06 = memory->Read(pc + 6); - byte p07 = memory->Read(pc + 7); - byte p09 = memory->Read(pc + 9); - byte p10 = memory->Read(pc + 10); - byte p11 = memory->Read(pc + 11); - if(p04 == 0xA9 && p05 == 0xE6 && p06 == 0x40 && p07 == 0x20 && p09 - == 0x05 && p10 == 0x20 && p11 == 0xF4) - { // lode runner - eTape* tape = devices->Get(); - for(;;) +#if 1 + const word pc = get_caller_pc(); + if ((pc & 0xFFFF) != 0x056B) return; + eTape* tape = devices->Get(); + dword pulse; + do + { + if ((pulse = tape->NextPulseOrStop()) == (dword)-1) { + return; + } + } + while(pulse > 770); + tape->SkipPulse(); + + // loading header + byte l = 0; + for(dword bit = 0x80; bit; bit >>= 1) + { + if ((pulse = tape->NextPulseOrStop()) == (dword)-1) { + set_caller_l(l); + set_caller_pc(0x05E2); + return; + } + l |= (pulse > 1240) ? bit : 0; + tape->SkipPulse(); + } + + // loading data + word de = get_caller_de(); + do + { + l = 0; + for(dword bit = 0x80; bit; bit >>= 1) { - if(b == 1) + if ((pulse = tape->NextPulseOrStop()) == (dword)-1) { + set_caller_l(l); + set_caller_pc(0x05E2); return; - if((tape->TapeBit(T()) ^ c) & 0x40) - return; - t += 52; - b--; + } + l |= (pulse > 1240) ? bit : 0; + tape->SkipPulse(); } + word ix = get_caller_ix(); + memory->Write(ix++, l); + set_caller_ix(ix); + --de; + set_caller_de(de); } - } -} -//============================================================================= -// eZ80_FastTape::StepTrap -//----------------------------------------------------------------------------- -void eZ80_FastTape::StepTrap() -{ + while(de & 0xFFFF); + + // loading CRC + l = 0; + for(dword bit = 0x80; bit; bit >>= 1) + { + if ((pulse = tape->NextPulseOrStop()) == (dword)-1) { + set_caller_l(l); + set_caller_pc(0x05E2); + return; + } + l |= (pulse > 1240) ? bit : 0; + tape->SkipPulse(); + } + set_caller_pc(0x05DF); + set_caller_flag(CF); + set_caller_bc(0xB001); + set_caller_h(0); + set_caller_l(l); +#endif +#if 0 + const word pc = get_caller_pc(); if((pc & 0xFFFF) != 0x056B) return; eTape* tape = devices->Get(); dword pulse; do { - if(tape->tape.play_pointer >= tape->tape.end_of_tape || - (pulse = tape->tape_pulse[*tape->tape.play_pointer++]) == (dword)-1) - { - tape->Stop(); + if ((pulse = tape->NextPulseOrStop()) == (dword)-1) { return; } } while(pulse > 770); - ++tape->tape.play_pointer; + tape->SkipPulse(); // loading header - l = 0; + set_caller_l(0); for(dword bit = 0x80; bit; bit >>= 1) { - if(tape->tape.play_pointer >= tape->tape.end_of_tape || - (pulse = tape->tape_pulse[*tape->tape.play_pointer++]) == (dword)-1) - { - tape->Stop(); - pc = 0x05E2; + if ((pulse = tape->NextPulseOrStop()) == (dword)-1) { + set_caller_pc(0x05E2); return; } - l |= (pulse > 1240) ? bit : 0; - ++tape->tape.play_pointer; + set_caller_l(get_caller_l() | (pulse > 1240) ? bit : 0); + tape->SkipPulse(); } // loading data do { - l = 0; + set_caller_l(0); for(dword bit = 0x80; bit; bit >>= 1) { - if(tape->tape.play_pointer >= tape->tape.end_of_tape || - (pulse = tape->tape_pulse[*tape->tape.play_pointer++]) == (dword)-1) - { - tape->Stop(); - pc = 0x05E2; + if ((pulse = tape->NextPulseOrStop()) == (dword)-1) { + set_caller_pc(0x05E2); return; } - l |= (pulse > 1240) ? bit : 0; - ++tape->tape.play_pointer; + set_caller_l(get_caller_l() | (pulse > 1240) ? bit : 0); + tape->SkipPulse(); } - memory->Write(ix++, l); - --de; + word ix = get_caller_ix(); + memory->Write(ix, get_caller_l()); + set_caller_ix(ix + 1); + set_caller_de(get_caller_de()-1); } - while(de & 0xFFFF); + while(get_caller_de() & 0xFFFF); // loading CRC - l = 0; + set_caller_l(0); for(dword bit = 0x80; bit; bit >>= 1) { - if(tape->tape.play_pointer >= tape->tape.end_of_tape || - (pulse = tape->tape_pulse[*tape->tape.play_pointer++]) == (dword)-1) - { - tape->Stop(); - pc = 0x05E2; + if ((pulse = tape->NextPulseOrStop()) == (dword)-1) { + set_caller_pc(0x05E2); return; } - l |= (pulse > 1240) ? bit : 0; - ++tape->tape.play_pointer; + set_caller_l(get_caller_l() | (pulse > 1240) ? bit : 0); + tape->SkipPulse(); } - pc = 0x05DF; - f |= CF; - bc = 0xB001; - h = 0; -} + set_caller_pc(0x05DF); + set_caller_f(get_caller_f() | CF); + set_caller_bc(0xB001); + set_caller_h(0); + +#endif + } + } //namespace xZ80 @@ -1161,3 +1084,5 @@ static class eFastTapeEmul : public xZ80::eZ80::eHandlerStep } fte; xZ80::eZ80::eHandlerStep* fast_tape_emul = &fte; +#endif +#endif \ No newline at end of file diff --git a/devices/input/tape.h b/devices/input/tape.h index 7e692c4..5419f2f 100644 --- a/devices/input/tape.h +++ b/devices/input/tape.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -19,74 +20,310 @@ along with this program. If not, see . #ifndef __TAPE_H__ #define __TAPE_H__ +#ifndef NO_USE_TAPE +#include "../../std.h" #include "../sound/device_sound.h" #include "../../z80/z80.h" +#include "stream.h" #pragma once +//#define USE_LEGACY_TAPE_COMPARISON class eSpeccy; namespace xZ80 { class eZ80_FastTape; } +class eTapePulseIterator { +public: + // return -1 for none + virtual int32_t next_pulse_length() = 0; +}; + +class eTapeBlockPulseIterator : public eTapePulseIterator { +public: + static const int CACHE_SIZE = 32; + eTapeBlockPulseIterator() { set_needs_reset(); } + + void set_needs_reset() { + state = DONE; + } + + inline void reset(struct stream *stream, dword size, dword pilot_t, dword s1_t, + dword s2_t, dword zero_t, dword one_t, dword pilot_len, dword pause, + byte last = 8) { + this->stream = stream; + this->size = this->remaining_bytes = size; + this->pilot_t = pilot_t; + this->s1_t = s1_t; + this->s2_t = s2_t; + this->zero_t = zero_t; + this->one_t = one_t; + this->pilot_len = pilot_len; + this->pause = pause; + this->last = last; + this->state = RESET; + recache(); + } + virtual int32_t next_pulse_length() { + while (true) { + switch (state) { + case RESET: + if (pilot_len != (dword)-1) { + counter = pilot_len; + state = PILOT; + } else { + state = DATA; + counter = 0; + } + break; + case DONE: + return -1; + case PILOT: + if (counter--) { + return pilot_t; + } else { + state = S1; + } + break; + case S1: + state = S2; + return s1_t; + case S2: + state = DATA; + counter = 0; + intra_byte = 0x180; + return s2_t; + case DATA: + if (counter < size) { + int32_t len = data_cache[data_cache_pos] & intra_byte ? one_t : zero_t; + if (intra_byte & 0x100) { + // need second half of pulse + intra_byte = intra_byte & 0xff; + } else + { + intra_byte >>= 1; + if (counter == size -1) { + if (intra_byte == (0x80 >> last)) { + state = END_PAUSE; + } + } + if (!intra_byte) { + intra_byte = 0x180; + counter++; + data_cache_pos++; + if (data_cache_pos == data_cache_size) { + recache(); + } + } else + { + intra_byte |= 0x100; + } + } + return len; + } else { + state = END_PAUSE; + } + break; + case END_PAUSE: + state = DONE; + if (pause) + { + return pause * 3500; + } + break; + default: + assert(false); + } + } + } +protected: + struct stream *stream; + dword size; + dword remaining_bytes; + dword data_cache_size; + dword data_cache_pos; + dword pilot_t; + dword s1_t; + dword s2_t; + dword zero_t; + dword one_t; + dword pilot_len; + dword pause; + byte data_cache[CACHE_SIZE]; + byte last; + + void recache() { + data_cache_size = MIN(remaining_bytes, CACHE_SIZE); + stream_read(stream, data_cache, data_cache_size, true); + data_cache_pos = 0; + remaining_bytes -= data_cache_size; + assert(data_cache); + } + + enum State { + RESET, + PILOT, + S1, + S2, + DATA, + DONE, + END_PAUSE + } state; + // for use by state + dword counter; + int intra_byte; // +/i +}; + +class eTapeInstance { +public: + eTapeInstance(struct stream *stream) : stream(stream) {} + + // start at beginning of tape (note the instance owns the iterator) + virtual eTapePulseIterator *reset() = 0; + virtual ~eTapeInstance() { + stream_close(stream); + } +protected: + struct stream *stream; +}; + +class eTapeInstanceTAP : public eTapeInstance, eTapePulseIterator { +public: + eTapeInstanceTAP(struct stream *stream) : eTapeInstance(stream) {} + eTapePulseIterator *reset() override { + stream_reset(stream); + done = false; + block_pulse_iterator.set_needs_reset(); + return this; + } +protected: + eTapeBlockPulseIterator block_pulse_iterator; + bool done; + + int32_t next_pulse_length() override + { + while (!done) { + int32_t r = block_pulse_iterator.next_pulse_length(); + if (r != -1) { + return r; + } + next_block(); + } + return -1; + } + + void next_block() { + const uint8_t *size_ptr = stream_peek(stream, 2); + if (!size_ptr) { + done = true; + } else { + dword size = Word(size_ptr); + if (size) + { + stream_skip(stream, 2); + const uint8_t *ptr = stream_peek(stream, 1); + // this should read the full contents of the block + block_pulse_iterator.reset(stream, size, 2168, 667, 735, 855, 1710, (*ptr < 4) ? 8064 : 3220, + 1000); + } else { + done = true; + } + } + } +}; + class eTape : public eDeviceSound { typedef eDeviceSound eInherited; +#ifndef NO_USE_FAST_TAPE friend class xZ80::eZ80_FastTape; +#endif public: eTape(eSpeccy* s) : speccy(s) {} +#ifndef NO_USE_DESTRUCTORS virtual ~eTape() { CloseTape(); } +#endif virtual void Init(); virtual void Reset(); - virtual bool IoRead(word port) const; - virtual void IoRead(word port, byte* v, int tact); - bool Open(const char* type, const void* data, size_t data_size); +#ifndef USE_HACKED_DEVICE_ABSTRACTION + virtual bool IoRead(word port) const final; + virtual dword IoNeed() const { return ION_READ; } +#endif + virtual void IoRead(word port, byte* v, int tact) final; + +#ifndef USE_STREAM + bool Open(const char* type, const void* data, size_t data_size) { + assert(false); + return false; + } +#else + bool Open(const char* type, struct stream *stream); +#endif void Start(); void Stop(); + void Rewind() { ResetTape(); } bool Started() const; bool Inserted() const; static eDeviceId Id() { return D_TAPE; } - virtual dword IoNeed() const { return ION_READ; } byte TapeBit(int tact); protected: - bool ParseTAP(const void* data, size_t data_size); - bool ParseCSW(const void* data, size_t data_size); - bool ParseTZX(const void* data, size_t data_size); + bool OpenTAP(struct stream *stream); +#ifndef NO_USE_CSW + bool ParseCSW(struct stream *stream); +#endif +#ifndef NO_USE_TZX + bool ParseTZX(struct stream *stream); +#endif - dword FindPulse(dword t); - void FindTapeIndex(); - void FindTapeSizes(); void StopTape(); void ResetTape(); void StartTape(); +public: void CloseTape(); +protected: + dword NextPulseOrStop(); + void SkipPulse(); + +#ifdef USE_LEGACY_TAPE_COMPARISON + dword FindPulse(dword t); + void FindTapeIndex(); +#ifndef USE_MU_SIMPLIFICATIONS + void FindTapeSizes(); +#endif void Reserve(dword datasize); void MakeBlock(const byte* data, dword size, dword pilot_t, - dword s1_t, dword s2_t, dword zero_t, dword one_t, - dword pilot_len, dword pause, byte last = 8); + dword s1_t, dword s2_t, dword zero_t, dword one_t, + dword pilot_len, dword pause, byte last = 8); void Desc(const byte* data, dword size, char* dst); void AllocInfocell(); - void NamedCell(const void *nm, dword sz = 0); - void CreateAppendableBlock(); - void ParseHardware(const byte* ptr); - +#endif protected: eSpeccy* speccy; + eTapeInstance *tape_instance; + eTapePulseIterator *pulse_iterator; + struct eTapeState { qword edge_change; +#ifdef USE_LEGACY_TAPE_COMPARISON byte* play_pointer; // or NULL if tape stopped byte* end_of_tape; // where to stop tape dword index; // current tape block - dword tape_bit; +#endif + byte tape_bit; + bool playing; }; eTapeState tape; +#ifdef USE_LEGACY_TAPE_COMPARISON struct TAPEINFO { +#ifndef NO_USE_TAPEINFO_DESC char desc[280]; +#endif dword pos; dword t_size; }; @@ -102,8 +339,12 @@ class eTape : public eDeviceSound dword tape_infosize; dword appendable; +#endif }; +#ifndef NO_USE_FAST_TAPE extern xZ80::eZ80::eHandlerStep* fast_tape_emul; +#endif +#endif #endif//__TAPE_H__ diff --git a/devices/memory.cpp b/devices/memory.cpp index dd2dfa0..62f0a7d 100644 --- a/devices/memory.cpp +++ b/devices/memory.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -21,11 +22,15 @@ along with this program. If not, see . #include "memory.h" #ifdef USE_EMBEDDED_RESOURCES +#ifndef NO_USE_128K #include "res/rom/sos128_0.h" #include "res/rom/sos128_1.h" -#include "res/rom/sos48.h" #include "res/rom/service.h" +#endif +#ifndef NO_USE_DOS #include "res/rom/dos513f.h" +#endif +#include "res/rom/sos48.h" #endif//USE_EMBEDDED_RESOURCES #ifdef USE_EXTERN_RESOURCES @@ -36,35 +41,77 @@ extern byte service[]; extern byte dos513f[]; #endif//USE_EXTERN_RESOURCES +#ifdef NO_FLASH +#define USE_COMPRESSED_ROM +#endif + +#ifdef USE_COMPRESSED_ROM +#include "stream.h" +#include "roms/roms.h" +#endif + +#ifdef USE_SINGLE_64K_MEMORY +#ifndef USE_COMPRESSED_ROM +#include "ram64k.h" +#endif + +#ifndef NO_USE_128K +#error cant handle 128K ROM with single 64K memory buffer +#endif +#endif + //============================================================================= // eMemory::eMemory //----------------------------------------------------------------------------- eMemory::eMemory() : memory(NULL) { +#ifndef USE_SINGLE_64K_MEMORY memory = new byte[SIZE]; memset(memory, 0, SIZE); +#else +#ifdef USE_COMPRESSED_ROM + memory =(uint8_t *) calloc(1, 65536); +#else + memory = ram64k; +#endif + memset(memory + PAGE_SIZE, 0, 3 * PAGE_SIZE); +#endif } //============================================================================= // eMemory::~eMemory -//----------------------------------------------------------------------------- +//----------------- +// ------------------------------------------------------------ +#ifndef NO_USE_DESTRUCTORS eMemory::~eMemory() { +#ifndef USE_SINGLE_64K_MEMORY SAFE_DELETE_ARRAY(memory); +#endif } +#endif //============================================================================= // eMemory::SetPage //----------------------------------------------------------------------------- void eMemory::SetPage(int idx, int page) { +#ifdef USE_BANKED_MEMORY_ACCESS byte* addr = Get(page); + addr -= 0x4000 * idx; bank_read[idx] = addr; - bank_write[idx] = idx ? addr : NULL; + if (idx) + { + bank_write[idx] = addr; + } +#else + assert(false); +#endif } //============================================================================= // eMemory::Page //----------------------------------------------------------------------------- int eMemory::Page(int idx) { +#ifdef USE_BANKED_MEMORY_ACCESS byte* addr = bank_read[idx]; for(int p = 0; p < P_AMOUNT; ++p) { @@ -73,6 +120,10 @@ int eMemory::Page(int idx) } assert(false); return -1; +#else + assert(false); + return 0; +#endif } //============================================================================= @@ -80,38 +131,126 @@ int eMemory::Page(int idx) //----------------------------------------------------------------------------- void eRom::LoadRom(int page, const char* rom) { +#ifdef USE_SINGLE_64K_MEMORY + panic("Warning should not be loading ROM for 64K\n"); +#endif +#if !defined(USE_COMPRESSED_ROM) && !defined(USE_EMBEDDED_RESOURCES) && !defined(USE_EXTERN_RESOURCES) && !defined(USE_EMBEDDED_FILES) FILE* f = fopen(rom, "rb"); assert(f); size_t s = fread(memory->Get(page), 1, eMemory::PAGE_SIZE, f); assert(s == eMemory::PAGE_SIZE); fclose(f); +#else + assert(false); +#endif } + +#ifdef USE_COMPRESSED_ROM +static void decompress_rom_to_memory(uint8_t *dest, const char *name) +{ + for(int i=0;iGet(ROM_128_0), rom128_0_z, rom128_0_z_len, rom128_0_len); + decompress_rom_to_memory(memory->Get(ROM_128_1), rom128_1_z, rom128_1_z_len, rom128_1_len); +#else + init_rom_contents((eMemory::ePage)ROM_128_0, ROM_128_0); + init_rom_contents((eMemory::ePage)ROM_128_1, ROM_128_1); +#endif +#endif +#endif +#else #if defined(USE_EMBEDDED_RESOURCES) || defined(USE_EXTERN_RESOURCES) +#ifndef NO_USE_128K memcpy(memory->Get(ROM_128_0), sos128_0, eMemory::PAGE_SIZE); memcpy(memory->Get(ROM_128_1), sos128_1, eMemory::PAGE_SIZE); +#endif memcpy(memory->Get(ROM_48), sos48, eMemory::PAGE_SIZE); +#ifndef NO_USE_128K memcpy(memory->Get(ROM_SYS), service, eMemory::PAGE_SIZE); +#endif +#ifndef NO_USE_DOS memcpy(memory->Get(ROM_DOS), dos513f, eMemory::PAGE_SIZE); +#endif #else//USE_EMBEDDED_RESOURCES +#ifndef NO_USE_128K LoadRom(ROM_128_0, xIo::ResourcePath("res/rom/sos128_0.rom")); LoadRom(ROM_128_1, xIo::ResourcePath("res/rom/sos128_1.rom")); +#endif LoadRom(ROM_48, xIo::ResourcePath("res/rom/sos48.rom")); +#ifndef NO_USE_128K +#ifndef NO_USE_SERVICE_ROM LoadRom(ROM_SYS, xIo::ResourcePath("res/rom/service.rom")); +#endif +#endif +#ifndef NO_USE_DOS LoadRom(ROM_DOS, xIo::ResourcePath("res/rom/dos513f.rom")); +#endif #endif//USE_EMBEDDED_RESOURCES +#endif } + +#ifdef USE_BANKED_MEMORY_ACCESS //============================================================================= // eRom::Reset //----------------------------------------------------------------------------- void eRom::Reset() { +#ifndef NO_USE_128K +#ifndef NO_USE_SERVICE_ROM SelectPage(mode_48k ? ROM_48 : ROM_SYS); +#else + SelectPage(ROM_SOS()); +#endif +#else + SelectPage(ROM_48); +#endif } +#endif + +#ifdef USE_MU +void eRom::init_rom_contents(eMemory::ePage memory_page, ePage rom_page) { +#if USE_COMPRESSED_ROM + switch (rom_page) { + case ROM_48: + decompress_rom_to_memory(memory->Get(memory_page), "ROM_48"); + break; +#ifndef NO_USE_128K + case ROM_128_0: + decompress_rom_to_memory(memory->Get(memory_page), "ROM_128_0"); + break; + case ROM_128_1: + decompress_rom_to_memory(memory->Get(memory_page), "ROM_128_1"); + break; +#endif + default: + assert(false); + } +#endif +} +#endif +#ifndef NO_USE_128K +#ifndef USE_HACKED_DEVICE_ABSTRACTION //============================================================================= // eRom::IoWrite //----------------------------------------------------------------------------- @@ -120,13 +259,23 @@ bool eRom::IoWrite(word port) const return !mode_48k && !(port & 2) && !(port & 0x8000); // zx128 port } //============================================================================= +// eRam::IoWrite +//----------------------------------------------------------------------------- +bool eRam::IoWrite(word port) const +{ + return !mode_48k && !(port & 2) && !(port & 0x8000); // zx128 port +} +#endif +//============================================================================= // eRom::IoWrite //----------------------------------------------------------------------------- void eRom::IoWrite(word port, byte v, int tact) { - SelectPage((page_selected & ~1) + ((v >> 4) & 1)); + SelectPage((ePage)((page_selected & ~1) + ((v >> 4) & 1))); } +#endif +#ifdef USE_BANKED_MEMORY_ACCESS //============================================================================= // eRam::Reset //----------------------------------------------------------------------------- @@ -136,13 +285,8 @@ void eRam::Reset() memory->SetPage(2, eMemory::P_RAM2); memory->SetPage(3, eMemory::P_RAM0); } -//============================================================================= -// eRam::IoWrite -//----------------------------------------------------------------------------- -bool eRam::IoWrite(word port) const -{ - return !mode_48k && !(port & 2) && !(port & 0x8000); // zx128 port -} +#endif +#ifndef NO_USE_128K //============================================================================= // eRam::IoWrite //----------------------------------------------------------------------------- @@ -151,3 +295,4 @@ void eRam::IoWrite(word port, byte v, int tact) int page = eMemory::P_RAM0 + (v & 7); memory->SetPage(3, page); } +#endif diff --git a/devices/memory.h b/devices/memory.h index 0a09354..5ddd5e6 100644 --- a/devices/memory.h +++ b/devices/memory.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -25,6 +26,15 @@ along with this program. If not, see . #undef PAGE_SIZE +#if !defined(NO_USE_128K) +#ifdef USE_SINGLE_64K_MEMORY +#error cant use 64k memory for 128 +#endif +#if !defined(USE_BANKED_MEMORY_ACCESS) +#define USE_BANKED_MEMORY_ACCESS +#endif +#endif + //***************************************************************************** // eMemory //----------------------------------------------------------------------------- @@ -32,36 +42,71 @@ class eMemory { public: eMemory(); +#ifndef NO_USE_DESTRUCTORS virtual ~eMemory(); +#endif byte Read(word addr) const { - byte* a = bank_read[(addr >> 14) & 3] + (addr & (PAGE_SIZE - 1)); +#ifndef USE_SINGLE_64K_MEMORY + byte* a = bank_read[(addr >> 14) & 3] + (addr & 0xffffu); return *a; +#else + return memory[addr]; +#endif } void Write(word addr, byte v) { +#ifndef USE_SINGLE_64K_MEMORY byte* a = bank_write[(addr >> 14) & 3]; if(!a) //rom write prevent return; - a += (addr & (PAGE_SIZE - 1)); + a += addr & 0xffffu; *a = v; +#else + if (addr>>14) { + memory[addr] = v; + } +#endif } byte* Get(int page) { return memory + page * PAGE_SIZE; } enum ePage { - P_ROM0 = 0, P_ROM1, P_ROM2, P_ROM3, P_ROM4, +#ifndef NO_USE_128K +#ifndef USE_OVERLAPPED_ROMS + P_ROM0 = 0, + P_ROM1, +#ifndef NO_USE_SERVICE_ROM + P_ROM2, +#endif +#ifndef NO_USE_DOS + P_ROM3, +#endif +#ifndef USE_OVERLAPPED_ROMS + P_ROM4, +#endif +#else + P_ROM_ALT0 = 0, P_ROM_ALT1, +#endif P_RAM0, P_RAM1, P_RAM2, P_RAM3, P_RAM4, P_RAM5, P_RAM6, P_RAM7, +#else + P_ROM4 = 0, P_RAM5, P_RAM2, P_RAM0, +#endif P_AMOUNT }; void SetPage(int idx, int page); int Page(int idx); - enum { BANKS_AMOUNT = 4, PAGE_SIZE = 0x4000, SIZE = P_AMOUNT * PAGE_SIZE }; +#ifdef USE_BANKED_MEMORY_ACCESS + byte **GetBankReads() { return bank_read; } + byte **GetBankWrites() { return bank_write; } +#endif protected: +#ifdef USE_BANKED_MEMORY_ACCESS byte* bank_read[BANKS_AMOUNT]; byte* bank_write[BANKS_AMOUNT]; +#endif byte* memory; }; @@ -71,13 +116,46 @@ class eMemory class eRom : public eDevice { public: - eRom(eMemory* m) : memory(m), page_selected(0), mode_48k(false) {} + eRom(eMemory* m) : memory(m), page_selected((ePage)-1), mode_48k(false) {} + virtual void Init(); +#ifdef USE_BANKED_MEMORY_ACCESS virtual void Reset(); +#endif +#ifndef NO_USE_128K +#ifndef USE_HACKED_DEVICE_ABSTRACTION virtual bool IoWrite(word port) const; + virtual dword IoNeed() const { return ION_WRITE; } +#endif virtual void IoWrite(word port, byte v, int tact); +#endif + + enum ePage + { +#ifndef USE_OVERLAPPED_ROMS +#if !defined(NO_USE_128K) + ROM_128_1 = eMemory::P_ROM0, + ROM_128_0 = eMemory::P_ROM1, +#endif +#ifndef NO_USE_128K +#ifndef NO_USE_SERVICE_ROM + ROM_SYS = eMemory::P_ROM2, +#endif +#endif +#ifndef NO_USE_DOS + ROM_DOS = eMemory::P_ROM3, +#endif + ROM_48 = eMemory::P_ROM4, +#else + ROM_128_1 = (eMemory::P_AMOUNT + 1) & ~1, // must be even + ROM_128_0, + ROM_48, +#endif + }; + void Read(word addr) { +#ifndef NO_USE_DOS byte pc_h = addr >> 8; if(page_selected == ROM_SOS() && (pc_h == 0x3d)) { @@ -87,28 +165,65 @@ class eRom : public eDevice { SelectPage(ROM_SOS()); } +#endif + } + bool DosSelected() const { +#ifndef NO_USE_DOS + return page_selected == ROM_DOS; +#else + return false; +#endif } - void SelectPage(int page) { page_selected = page; memory->SetPage(0, page_selected); } - bool DosSelected() const { return page_selected == ROM_DOS; } +#ifndef NO_USE_128K void Mode48k(bool on) { mode_48k = on; } - int ROM_SOS() const { return mode_48k ? ROM_48 : ROM_128_0; } + ePage ROM_SOS() const { return mode_48k ? ROM_48 : ROM_128_0; } +#else + ePage ROM_SOS() const { return ROM_48; } +#endif static eDeviceId Id() { return D_ROM; } - virtual dword IoNeed() const { return ION_WRITE; } - enum ePage - { - ROM_128_1 = eMemory::P_ROM0, - ROM_128_0 = eMemory::P_ROM1, - ROM_SYS = eMemory::P_ROM2, - ROM_DOS = eMemory::P_ROM3, - ROM_48 = eMemory::P_ROM4, - }; +#ifdef USE_MU + void init_rom_contents(eMemory::ePage memory_page, ePage rom_page); +#endif + void SelectPage(ePage page) { +#ifdef USE_BANKED_MEMORY_ACCESS +#ifdef USE_OVERLAPPED_ROMS + if (page != page_selected) { + if ((page & ~1) != (page_selected & ~1)) { +// printf("Switching ROM contents for %d\n", page); + switch (page) { + case ROM_128_1: + case ROM_128_0: + init_rom_contents(eMemory::ePage::P_ROM_ALT0, ROM_128_1); + init_rom_contents(eMemory::ePage::P_ROM_ALT1, ROM_128_0); + break; + case ROM_48: + init_rom_contents(eMemory::ePage::P_ROM_ALT0, ROM_48); + break; + default: + assert(false); + + } + } +// printf("Switching ROM page to %d\n", page); + page_selected = page; + memory->SetPage(0, page & 1 ? eMemory::ePage::P_ROM_ALT1 : eMemory::ePage::P_ROM_ALT0); + } +#else + assert(page < eMemory::P_RAM0); + page_selected = page; memory->SetPage(0, page_selected); +#endif +#else + assert(page == ROM_48); + page_selected = page; +#endif + } protected: void LoadRom(int page, const char* rom); protected: eMemory* memory; - int page_selected; + ePage page_selected; bool mode_48k; }; @@ -119,13 +234,20 @@ class eRam : public eDevice { public: eRam(eMemory* m) : memory(m), mode_48k(false) {} +#ifdef USE_BANKED_MEMORY_ACCESS virtual void Reset(); - virtual bool IoWrite(word port) const; - virtual void IoWrite(word port, byte v, int tact); +#endif +#ifndef NO_USE_128K void Mode48k(bool on) { mode_48k = on; } + virtual void IoWrite(word port, byte v, int tact); +#ifndef USE_HACKED_DEVICE_ABSTRACTION + virtual bool IoWrite(word port) const; + virtual dword IoNeed() const { return ION_WRITE; } +#endif +#endif bool Mode48k() const { return mode_48k; } static eDeviceId Id() { return D_RAM; } - virtual dword IoNeed() const { return ION_WRITE; } + protected: eMemory* memory; bool mode_48k; diff --git a/devices/sound/ay.cpp b/devices/sound/ay.cpp index d4243ba..fd2bb59 100644 --- a/devices/sound/ay.cpp +++ b/devices/sound/ay.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -16,25 +17,76 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#ifndef NO_USE_AY #include "../../std.h" #include "../../z80/z80.h" #include "ay.h" +#if KHAN128_I2S +#include "pico/audio_i2s.h" +#else +#include "pico/audio_pwm.h" +#endif +#include "hardware/gpio.h" +#include "../../options_common.h" + +CU_REGISTER_DEBUG_PINS(generate) + +// ---- select at most one --- +//CU_SELECT_DEBUG_PINS(generate) + +// set to 2 just to prove that 22050 is acceptable sounding +//#define HALVEIT 2 +#define HALVEIT 1 +#if PICO_ON_DEVICE +#define DOUBLEO_O 2 +#else +// 2 is more oversampling +#define DOUBLEO_O 2 +//#define DOUBLEO_O 1 +#endif + +#if HALVEIT == 2 +#define DOUBLEO (DOUBLEO_O + 1) +#else +#define DOUBLEO DOUBLEO_O +#endif +#ifdef USE_FAST_AY +static struct fast_ay_state ays; +#endif + +#define RATIO_MAJOR (SNDR_DEFAULT_AY_RATE / 8) +#define RATIO_MINOR ((SNDR_DEFAULT_SAMPLE_RATE << DOUBLEO) / HALVEIT) //============================================================================= // eAY::eAY //----------------------------------------------------------------------------- -eAY::eAY() : t(0), ta(0), tb(0), tc(0), tn(0), te(0), env(0), denv(0) - ,bitA(0), bitB(0), bitC(0), bitN(0), ns(0) +eAY::eAY() : t(0) +#ifndef USE_FAST_AY + ,ta(0), tb(0), tc(0), tn(0), te(0) + ,env(0), denv(0) + ,bitA(0), bitB(0), bitC(0), bitN(0),ns(0) ,bit0(0), bit1(0), bit2(0), bit3(0), bit4(0), bit5(0) ,ea(0), eb(0), ec(0), va(0), vb(0), vc(0) - ,fa(0), fb(0), fc(0), fn(0), fe(0) - ,activereg(0) +#endif + ,fa(0), fb(0), fc(0), fn(0), fe(0) + ,activereg(0) { +#ifdef USE_FAST_AY + ays.v10001 = 0x10001; + ays.frac_major = RATIO_MAJOR; + ays.frac_minor = RATIO_MINOR; + static_assert(RATIO_MAJOR > RATIO_MINOR); +#endif + SetChip(CHIP_AY); SetTimings(SNDR_DEFAULT_SYSTICK_RATE, SNDR_DEFAULT_AY_RATE, SNDR_DEFAULT_SAMPLE_RATE); +#ifndef USE_FAST_AY SetVolumes(0x7FFF, SNDR_VOL_AY, SNDR_PAN_ABC); +#endif + _Reset(); } +#ifndef USE_HACKED_DEVICE_ABSTRACTION //============================================================================= // eAY::IoRead //----------------------------------------------------------------------------- @@ -47,12 +99,12 @@ bool eAY::IoRead(word port) const //----------------------------------------------------------------------------- bool eAY::IoWrite(word port) const { - if(port&2) + if(port&2) return false; - if((port & 0xC0FF) == 0xC0FD) + if((port & 0xC0FF) == 0xC0FD) + return true; + if((port & 0xC000) == 0x8000) return true; - if((port & 0xC000) == 0x8000) - return true; return false; } //============================================================================= @@ -76,6 +128,7 @@ void eAY::IoWrite(word port, byte v, int tact) Write(tact, v); } } +#endif const dword MULT_C_1 = 14; // fixed point precision for 'system tick -> ay tick' // b = 1+ln2(max_ay_tick/8) = 1+ln2(max_ay_fq/8 / min_intfq) = 1+ln2(10000000/(10*8)) = 17.9 @@ -89,64 +142,226 @@ void eAY::FrameStart(dword tacts) t = tacts * chip_clock_rate / system_clock_rate; eInherited::FrameStart(t); } +#ifdef USE_MUx +#define mult_const ((dword)(((qword)SNDR_DEFAULT_SYSTICK_RATE << (MULT_C_1 - 4)) / SNDR_DEFAULT_SYSTICK_RATE)) +#endif + //============================================================================= // eAY::FrameEnd //----------------------------------------------------------------------------- void eAY::FrameEnd(dword tacts) { //adjusting 't' with whole history will fix accumulation of rounding errors - qword end_chip_tick = ((passed_clk_ticks + tacts) * chip_clock_rate) / system_clock_rate; - Flush((dword)(end_chip_tick - passed_chip_ticks)); +// qword end_chip_tick = ((passed_clk_ticks + tacts) * chip_clock_rate) / system_clock_rate; + + dword end_chip_tick = ((passed_clk_ticks + tacts) * mult_const) >> MULT_C_1; + Flush(end_chip_tick - passed_chip_ticks, true); eInherited::FrameEnd(t); passed_clk_ticks += tacts; passed_chip_ticks += t; } + +#define DOUBLEO_MASK ((1u<sample_count = 0; + } + int16_t *samples = (int16_t *) buffer->buffer->bytes; +#ifdef USE_FAST_AY + int count = chiptick - t; + if (count < 0 || count > 4434) { + static bool pants; + if (!pants) { + printf("warn out of range %d\n", count); + pants = true; + } + if (count < 0) count = 0; + else count = 4434; + } + + + uint v = xPlatform::OpSoundVolume(); +// printf("vol %d\n", v); + ays.master_volume = v * 13; // now 4 fractional bits in fast_ay.S) MIN(v, 8);// ? (1 + ((v*11)>>4)) : 0; + //ays.wakeups = 0; + bool print = false; + assert(count < 65531); + if (count > 0) + { + ays.tt = count; + if (print) printf("-> %d %d %d %d %d %d\n", count, ays.tt, (uint)buffer->sample_count, (uint)buffer->max_sample_count, (uint)(ays.over_c_value >> 30u), (uint)ays.over_rem); + DEBUG_PINS_SET(generate, 2); + // printf("count %d\n", count); +// assert(!ays.over_rem); // should not be any remaining samples +// assert(!buffer->sample_count); +#if 0 + ays.a_vol = 28; + ays.fa = 50; + if (!ays.ta) ays.ta = ays.fa + 1; + ays.bits_a03x |= 2; + ays.bits_a03x &= ~4; + static bool warble; +// if (warble) __breakpoint(); +#endif +#if 1 + uint32_t written = fast_ay(&ays, samples + buffer->sample_count, + buffer->max_sample_count - buffer->sample_count); +#else + uint32_t written = 0; + printf("and...\n"); + int e = ays.tt; + for(int j=0; jsample_count + written, + buffer->max_sample_count - buffer->sample_count - written); + printf("%d %d\n", j, written); + } +#endif + static_assert(PICO_AUDIO_BUFFER_SAMPLE_LENGTH >= (PICO_SAMPLE_RATE + 49) / 50, ""); + if (ays.over_rem) { + printf("OOPS overflow %d\n", ays.over_rem); + } + assert(!ays.over_rem); // should not be any remaining samples + DEBUG_PINS_CLR(generate, 2); + buffer->sample_count += written; + assert(buffer->sample_count <= buffer->max_sample_count); + if (print) printf(" %d %d %d %d %d\n", (int)written, ays.tt, (uint)buffer->sample_count, (uint)ays.over_rem, ays.te); +#if 0 + printf("--\n"); + for(int i=0;isample_count;i += 16) { + for(int j=i; jsample_count, i+ 16); j++) { + //printf("%04x ", samples[j]); + printf("%d\n", samples[j]); + } + //printf("\n"); + } +#endif +// if (samples[0]) warble = true; + } else { + assert(count == 0); + assert(eof); // only time we should have no ticks + } + t = chiptick; + if (buffer->sample_count && eof) + { + if (print) printf("%d\n", (uint)buffer->sample_count); +// int x = buffer->sample_count; +// int y = buffer->max_sample_count; + DEBUG_PINS_SET(generate, 1); + give_audio_buffer(producer_pool, buffer); + DEBUG_PINS_CLR(generate, 1); +// printf("%d %d\n", x, y); + buffer = 0; + } + +#else + //const int32_t RATIO_MAJOR = chip_clock_rate; + int32_t ratio = RATIO_MAJOR / 2; + static bool flip; + if (!flip) { + printf("%f %d %d\n", ((float)(RATIO_MAJOR)) / RATIO_MINOR, RATIO_MAJOR, SNDR_DEFAULT_SAMPLE_RATE * 2); + flip = 1; + } + uint alt = DOUBLEO_MASK; + dword en, mix_l = 0, mix_r = 0; +// dword x = 100000; +// if (fa) x = MIN(x, fa); +// if (fb) x = MIN(x, fb); +// if (fc) x = MIN(x, fc); +// if (fn) x = MIN(x, fn); +// if (fe) x = MIN(x, fe); +// printf("%d %d %d %d\nn", x, wakeups, wobble, chiptick - t); + DEBUG_PINS_SET(generate, 1); while (t < chiptick) { t++; - if(++ta >= fa) ta = 0, bitA ^= -1; - if(++tb >= fb) tb = 0, bitB ^= -1; - if(++tc >= fc) tc = 0, bitC ^= -1; - if(++tn >= fn) + if (++ta >= fa) ta = 0, bitA ^= -1; + if (++tb >= fb) tb = 0, bitB ^= -1; + if (++tc >= fc) tc = 0, bitC ^= -1; + if (++tn >= fn) tn = 0, - ns = (ns*2+1) ^ (((ns>>16)^(ns>>13)) & 1), + ns = (ns * 2 + 1) ^ (((ns >> 16) ^ (ns >> 13)) & 1), bitN = 0 - ((ns >> 16) & 1); - if(++te >= fe) + if (++te >= fe) { te = 0, env += denv; - if(env & ~31) + if (env & ~31) { - dword mask = (1< DOUBLEO_MASK) + { + mix_l = mix_r = 0; + alt = 0; + } +#else + mix_l = mix_r = 0; +#endif en = ((ea & env) | va) & ((bitA | bit0) & (bitN | bit3)); - mix_l = vols[0][en]; mix_r = vols[1][en]; + mix_l += vols[0][en]; + mix_r += vols[1][en]; en = ((eb & env) | vb) & ((bitB | bit1) & (bitN | bit4)); - mix_l += vols[2][en]; mix_r += vols[3][en]; + mix_l += vols[2][en]; + mix_r += vols[3][en]; en = ((ec & env) | vc) & ((bitC | bit2) & (bitN | bit5)); - mix_l += vols[4][en]; mix_r += vols[5][en]; + mix_l += vols[4][en]; + mix_r += vols[5][en]; + - if((mix_l ^ eInherited::mix_l) | (mix_r ^ eInherited::mix_r)) // similar check inside update() - Update(t, mix_l, mix_r); + static_assert(RATIO_MAJOR >= RATIO_MINOR, ""); +// if((mix_l ^ eInherited::mix_l) | (mix_r ^ eInherited::mix_r)) // similar check inside update() +// Update(t, mix_l, mix_r); +#if DOUBLEO + if (alt == DOUBLEO_MASK) + { + ratio -= RATIO_MINOR; +#else + ratio -= SNDR_DEFAULT_SAMPLE_RATE; +#endif + if (ratio <= 0) + { + samples[buffer->sample_count++] = (mix_l + mix_r) / (2 << DOUBLEO); +#if HALVEIT == 2 + samples[buffer->sample_count++] = (mix_l + mix_r) / (2 << DOUBLEO); +#endif + assert(buffer->sample_count <= buffer->max_sample_count); + if (buffer->sample_count == buffer->max_sample_count) + { + give_audio_buffer(producer_pool, buffer); + buffer = take_audio_buffer(producer_pool, true); + samples = (int16_t *) buffer->buffer->bytes; + buffer->sample_count = 0; + } + ratio += RATIO_MAJOR; + } +#if DOUBLEO } +#endif + } + DEBUG_PINS_CLR(generate, 1); +#endif } //============================================================================= // eAY::Select @@ -155,7 +370,32 @@ void eAY::Select(byte nreg) { if(chiptype == CHIP_AY) nreg &= 0x0F; activereg = nreg; +#ifdef USE_FAST_AY + extern uint16_t *current_voltab; + extern uint16_t voltab_ay; + extern uint16_t voltab_ym; + current_voltab = chiptype == CHIP_AY ? &voltab_ay : &voltab_ym; +#endif +} + +#ifdef USE_FAST_AY +static inline uint16_t bswap_and_adjust(uint16_t v) { + v = SwapWord(v); + if (v < 3) v = 3; // seems like a reasonable minimum freq + return v; +} + +void adjust_tx(uint16_t new_fx, uint16_t &fx, uint16_t &tx) { + new_fx &= 0xfff; + if (!fx) tx = 1; // we should have been ready anyway + fx = new_fx; + if (!fx) tx = 0xffff; // hack to avoid waking up all the time } + +void update_channel_bits(uint8_t &cb, uint8_t x0, uint8_t x3) { + cb = (cb & 0x9) | (x0 << 2) | (x3 << 1); +} +#endif //============================================================================= // eAY::Write //----------------------------------------------------------------------------- @@ -173,8 +413,70 @@ void eAY::Write(dword timestamp, byte val) reg[activereg] = val; if(timestamp) - Flush((timestamp * mult_const) >> MULT_C_1); // cputick * ( (chip_clock_rate/8) / system_clock_rate ); + Flush((timestamp * mult_const) >> MULT_C_1, false); // cputick * ( (chip_clock_rate/8) / system_clock_rate ); +#ifdef USE_FAST_AY + switch(activereg) + { + case 0: + case 1: + adjust_tx(bswap_and_adjust(r.fA), ays.fa, ays.ta); + break; + case 2: + case 3: + adjust_tx(bswap_and_adjust(r.fB), ays.fb, ays.tb); + break; + case 4: + case 5: + adjust_tx(bswap_and_adjust(r.fC), ays.fc, ays.tc); + break; + case 6: + { + // todo noise freq needs more work (with oversampling) +// uint fn = ((val&0x1f) * 9)/4; + //if (fn == 1) fn = 2; + //uint fn = (val&0x1f)*2 + 1; + uint fn = (val&0x1f)*2; + adjust_tx(fn, ays.fn, ays.tn); + break; + } + case 7: + update_channel_bits(ays.bits_a03x, (val >> 0u) & 1u, (val >> 3u) & 1u); + update_channel_bits(ays.bits_b14x, (val >> 1u) & 1u, (val >> 4u) & 1u); + update_channel_bits(ays.bits_c25x, (val >> 2u) & 1u, (val >> 5u) & 1u); + break; + case 8: + ays.bits_a03x &= ~0x10; + ays.bits_a03x |= (val&0x10); + ays.a_vol = ((val & 0x0F)*2+1); + break; + case 9: + ays.bits_b14x &= ~0x10; + ays.bits_b14x |= (val&0x10); + ays.b_vol = ((val & 0x0F)*2+1); + break; + case 10: + ays.bits_c25x &= ~0x10; + ays.bits_c25x |= (val&0x10); + ays.c_vol = ((val & 0x0F)*2+1); + break; + case 11: + case 12: + adjust_tx( bswap_and_adjust(r.envT), ays.fe, ays.te); + break; + case 13: + { + ays.te = ays.fe; // reset timer + if (ays.te <= 1) ays.te = 0xffff; + if (r.env & 4) ays.e_vol = 0, ays.de_vol = 1; // attack + else ays.e_vol = 31, ays.de_vol = -1; // decay + extern uint32_t envelope_switch[]; + extern uint32_t envelope_handler; + envelope_handler = envelope_switch[r.env & 0xf]; + break; + } + } +#else switch(activereg) { case 0: @@ -222,6 +524,7 @@ void eAY::Write(dword timestamp, byte val) else env = 31, denv = -1; // decay break; } +#endif } //============================================================================= // eAY::Read @@ -237,16 +540,26 @@ byte eAY::Read() //----------------------------------------------------------------------------- void eAY::SetTimings(dword _system_clock_rate, dword _chip_clock_rate, dword _sample_rate) { - _chip_clock_rate /= 8; +#ifndef USE_MU + _chip_clock_rate /= 8; system_clock_rate = _system_clock_rate; chip_clock_rate = _chip_clock_rate; - mult_const = (dword)(((qword)chip_clock_rate << MULT_C_1) / system_clock_rate); +#endif + +#ifndef USE_MU eInherited::SetTimings(_chip_clock_rate, _sample_rate); +#endif passed_chip_ticks = passed_clk_ticks = 0; - t = 0; ns = 0xFFFF; + t = 0; +#ifndef USE_FAST_AY + ns = 0xFFFF; +#else + ays.ns = 0xffff; +#endif } +#ifndef USE_FAST_AY //============================================================================= // eAY::SetVolumes //----------------------------------------------------------------------------- @@ -256,6 +569,9 @@ void eAY::SetVolumes(dword global_vol, const SNDCHIP_VOLTAB *voltab, const SNDCH for (int i = 0; i < 32; i++) vols[j][i] = (dword)(((qword)global_vol * voltab->v[i] * stereo->raw[j])/(65535*100*3)); } +#endif +//#pragma GCC push_options +//#pragma GCC optimize("Os") //============================================================================= // eAY::Reset //----------------------------------------------------------------------------- @@ -265,6 +581,7 @@ void eAY::_Reset(dword timestamp) reg[i] = 0; ApplyRegs(timestamp); } +//#pragma GCC pop_options //============================================================================= // eAY::ApplyRegs //----------------------------------------------------------------------------- @@ -287,6 +604,7 @@ const char * const ay_chips[] = { "AY-3-8910", "YM2149F" }; const char* eAY::GetChipName(CHIP_TYPE i) { return ay_chips[i]; } +#ifndef USE_FAST_AY const SNDCHIP_VOLTAB SNDR_VOL_AY_S = { { 0x0000,0x0000,0x0340,0x0340,0x04C0,0x04C0,0x06F2,0x06F2,0x0A44,0x0A44,0x0F13,0x0F13,0x1510,0x1510,0x227E,0x227E, 0x289F,0x289F,0x414E,0x414E,0x5B21,0x5B21,0x7258,0x7258,0x905E,0x905E,0xB550,0xB550,0xD7A0,0xD7A0,0xFFFF,0xFFFF } }; @@ -312,3 +630,5 @@ const SNDCHIP_PANTAB* SNDR_PAN_BAC = &SNDR_PAN_BAC_S; const SNDCHIP_PANTAB* SNDR_PAN_BCA = &SNDR_PAN_BCA_S; const SNDCHIP_PANTAB* SNDR_PAN_CAB = &SNDR_PAN_CAB_S; const SNDCHIP_PANTAB* SNDR_PAN_CBA = &SNDR_PAN_CBA_S; +#endif +#endif \ No newline at end of file diff --git a/devices/sound/ay.h b/devices/sound/ay.h index e5f7b4b..42d07bd 100644 --- a/devices/sound/ay.h +++ b/devices/sound/ay.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -19,10 +20,14 @@ along with this program. If not, see . #ifndef __AY_H__ #define __AY_H__ +#ifndef NO_USE_AY #include "device_sound.h" - +#include "pico/audio.h" #pragma once + +extern struct audio_buffer_pool *producer_pool; + namespace xZ80 { class eZ80; } const dword SNDR_DEFAULT_AY_RATE = 1774400; // original ZX-Spectrum soundchip clock fq @@ -74,12 +79,16 @@ class eAY : public eDeviceSound typedef eDeviceSound eInherited; public: eAY(); +#ifndef NO_USE_DESTRUCTORS virtual ~eAY() {} +#endif +#ifndef USE_HACKED_DEVICE_ABSTRACTION virtual bool IoRead(word port) const; virtual bool IoWrite(word port) const; virtual void IoRead(word port, byte* v, int tact); virtual void IoWrite(word port, byte v, int tact); +#endif enum CHIP_TYPE { CHIP_AY, CHIP_YM, CHIP_MAX }; static const char* GetChipName(CHIP_TYPE i); @@ -95,24 +104,36 @@ class eAY : public eDeviceSound virtual void FrameEnd(dword tacts); static eDeviceId Id() { return D_AY; } +#ifndef USE_HACKED_DEVICE_ABSTRACTION virtual dword IoNeed() const { return ION_WRITE|ION_READ; } +#endif -protected: void Write(dword timestamp, byte val); byte Read(); +protected: private: - dword t, ta, tb, tc, tn, te, env; + dword t; +#ifndef USE_FAST_AY + dword ta, tb, tc, tn, te; + dword env; int denv; dword bitA, bitB, bitC, bitN, ns; dword bit0, bit1, bit2, bit3, bit4, bit5; dword ea, eb, ec, va, vb, vc; +#endif dword fa, fb, fc, fn, fe; +#ifndef USE_MU dword mult_const; +#else + const dword mult_const = 1013; +#endif byte activereg; +#ifndef USE_FAST_AY dword vols[6][32]; +#endif CHIP_TYPE chiptype; union { @@ -120,12 +141,74 @@ class eAY : public eDeviceSound struct AYREGS r; }; +#ifndef USE_MU dword chip_clock_rate, system_clock_rate; +#else + const dword chip_clock_rate = SNDR_DEFAULT_AY_RATE / 8; + const dword system_clock_rate = SNDR_DEFAULT_SYSTICK_RATE; +#endif qword passed_chip_ticks, passed_clk_ticks; void _Reset(dword timestamp = 0); // call with default parameter, when context outside start_frame/end_frame block - void Flush(dword chiptick); + void Flush(dword chiptick, bool eof); void ApplyRegs(dword timestamp = 0); }; +#ifdef USE_FAST_AY +extern "C" { +// do not change order of this, as it is used by fast_ay.S +struct fast_ay_state { + // note these are 5 bit values, with bit 4 being channel envelope flag + uint8_t bits_a03x; + uint8_t bits_b14x; + uint8_t bits_c25x; + uint8_t bit_n; + + uint8_t _pad[2]; + + uint16_t fa, fb, fc, fn, fe; + + uint16_t a_output; + uint16_t b_output; + uint16_t c_output; + uint16_t a_vol; + uint16_t b_vol; + uint16_t c_vol; + uint16_t e_vol; + uint16_t de_vol; + + uint32_t master_volume; + uint32_t ns; + uint32_t pad4[2]; + + // mutable channel state + // unlike original code, these tx count down not up, so must be adjust when fx changes + uint16_t ta; + uint16_t tb; + uint16_t tc; + uint16_t tn; + uint16_t te; + uint16_t tt; + + uint32_t v10001; // always 0x10001 + // sample state + uint32_t frac_major; + uint32_t frac_minor; + // mutable sample state + uint32_t frac_accum; + uint32_t over_c_value; + uint32_t over_rem; // number of over_samples remaining + uint32_t over_accum; + + uint32_t wakeups; +}; + +static_assert(offsetof(struct fast_ay_state, fe) == 14, ""); +static_assert(offsetof(struct fast_ay_state, ta) == 48, ""); +static_assert(offsetof(struct fast_ay_state, frac_major) == 64, ""); +static_assert(offsetof(struct fast_ay_state, wakeups) == 88, ""); +extern uint32_t fast_ay(struct fast_ay_state *state, int16_t *samples, uint32_t max_samples); +} +#endif +#endif #endif//__AY_H__ diff --git a/devices/sound/beeper.cpp b/devices/sound/beeper.cpp index b944180..75af9f9 100644 --- a/devices/sound/beeper.cpp +++ b/devices/sound/beeper.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -16,25 +17,59 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#ifndef NO_USE_BEEPER #include "../../std.h" #include "beeper.h" +#include "khan_lib.h" +#include "../../options_common.h" +eBeeper::eBeeper() { + // need this to create vtable +} //============================================================================= // eBeeper::IoWrite //----------------------------------------------------------------------------- +#ifndef USE_HACKED_DEVICE_ABSTRACTION bool eBeeper::IoWrite(word port) const { return !(port&1); } +#endif //============================================================================= // eBeeper::IoWrite //----------------------------------------------------------------------------- void eBeeper::IoWrite(word port, byte v, int tact) { - const short spk_vol = 8192; - const short mic_vol = 1000; - short spk = (v & 0x10) ? spk_vol : 0; - short mic = (v & 0x08) ? mic_vol : 0; - short mono = spk + mic; - Update(tact, mono, mono); +// const short spk_vol = 8192; +// const short mic_vol = 1000; +// short spk = (v & 0x10) ? spk_vol : 0; +// short mic = (v & 0x08) ? mic_vol : 0; +// short mono = spk + mic; +// mono = (mono * 182 * xPlatform::OpSoundVolume()) >> 16; //(0->255) + if (!(port&1)) { + uint8_t mono = (((v & 0x10) ? 21 : 0) + ((v & 0x08) ? 4 : 0)) * xPlatform::OpSoundVolume(); + Update(tact, mono, mono); + } +} + +void eBeeper::Reset() +{ + khan_beeper_reset(); +} +//============================================================================= +// eDeviceSound::FrameStart +//----------------------------------------------------------------------------- +void eBeeper::FrameStart(dword tacts) +{ + static int frame_number = 0; + khan_beeper_begin_frame(tacts, frame_number++); +} + +//============================================================================= +// eDeviceSound::FrameEnd +//----------------------------------------------------------------------------- +void eBeeper::FrameEnd(dword tacts) +{ + khan_beeper_end_frame(tacts); } +#endif diff --git a/devices/sound/beeper.h b/devices/sound/beeper.h index e02a733..4150f6a 100644 --- a/devices/sound/beeper.h +++ b/devices/sound/beeper.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -29,10 +30,16 @@ along with this program. If not, see . class eBeeper : public eDeviceSound { public: - virtual bool IoWrite(word port) const; - virtual void IoWrite(word port, byte v, int tact); + eBeeper(); + void IoWrite(word port, byte v, int tact) override; static eDeviceId Id() { return D_BEEPER; } + void Reset() override; +#ifndef USE_HACKED_DEVICE_ABSTRACTION + virtual bool IoWrite(word port) const; virtual dword IoNeed() const { return ION_WRITE; } +#endif + void FrameStart(dword tacts) override; + void FrameEnd(dword tacts) override; }; #endif//__BEEPER_H__ diff --git a/devices/sound/device_sound.cpp b/devices/sound/device_sound.cpp index 14c0dff..88f0c75 100644 --- a/devices/sound/device_sound.cpp +++ b/devices/sound/device_sound.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -18,28 +19,28 @@ along with this program. If not, see . #include "../../std.h" #include "device_sound.h" +#include "../../platform/platform.h" +#include "../../speccy.h" + +#include "khan_lib.h" + +#if defined(NO_USE_BEEPER) && defined(NO_USE_AY) +#define NO_USE_SOUND +#endif //============================================================================= // eDeviceSound::eDeviceSound //----------------------------------------------------------------------------- -eDeviceSound::eDeviceSound() : mix_l(0), mix_r(0), s1_l(0), s1_r(0), s2_l(0), s2_r(0) +eDeviceSound::eDeviceSound() : mix_l(0), mix_r(0) { - SetTimings(SNDR_DEFAULT_SYSTICK_RATE, SNDR_DEFAULT_SAMPLE_RATE); +// SetTimings(SNDR_DEFAULT_SYSTICK_RATE, SNDR_DEFAULT_SAMPLE_RATE); } -const dword TICK_FF = 6; // oversampling ratio: 2^6 = 64 -const dword TICK_F = (1< sound tick' -// b = 1+ln2(max_sndtick) = 1+ln2((max_sndfq*TICK_F)/min_intfq) = 1+ln2(48000*64/10) ~= 19.2; -// assert(b+MULT_C <= 32) - //============================================================================= // eDeviceSound::FrameStart //----------------------------------------------------------------------------- void eDeviceSound::FrameStart(dword tacts) { - dword endtick = (tacts * (qword)sample_rate * TICK_F) / clock_rate; //prev frame rest - base_tick = tick - endtick; } //============================================================================= // eDeviceSound::Update @@ -48,183 +49,27 @@ void eDeviceSound::Update(dword tact, dword l, dword r) { if(!((l ^ mix_l) | (r ^ mix_r))) return; - dword endtick = (tact * (qword)sample_rate * TICK_F) / clock_rate; - Flush(base_tick + endtick); mix_l = l; mix_r = r; + Flush(tact); + // note we flush with the new level } //============================================================================= // eDeviceSound::FrameEnd //----------------------------------------------------------------------------- void eDeviceSound::FrameEnd(dword tacts) { - dword endtick = (tacts * (qword)sample_rate * TICK_F) / clock_rate; - Flush(base_tick + endtick); -} -//============================================================================= -// eDeviceSound::AudioData -//----------------------------------------------------------------------------- -void* eDeviceSound::AudioData() -{ - return buffer; -} -//============================================================================= -// eDeviceSound::AudioDataReady -//----------------------------------------------------------------------------- -dword eDeviceSound::AudioDataReady() -{ - return (dstpos - buffer)*sizeof(SNDSAMPLE); +// dword endtick = (tacts * (qword)sample_rate * TICK_F) / clock_rate; +// Flush(base_tick + endtick); } -//============================================================================= -// eDeviceSound::AudioDataUse -//----------------------------------------------------------------------------- -void eDeviceSound::AudioDataUse(dword size) -{ - assert(size == AudioDataReady()); - dstpos = buffer; -} -//============================================================================= -// eDeviceSound::SetTimings -//----------------------------------------------------------------------------- -void eDeviceSound::SetTimings(dword _clock_rate, dword _sample_rate) -{ - clock_rate = _clock_rate; - sample_rate = _sample_rate; - - tick = base_tick = 0; - dstpos = buffer; -} - -static dword filter_diff[TICK_F*2]; -const double filter_sum_full = 1.0, filter_sum_half = 0.5; -const dword filter_sum_full_u = (dword)(filter_sum_full * 0x10000); -const dword filter_sum_half_u = (dword)(filter_sum_half * 0x10000); //============================================================================= // eDeviceSound::Flush //----------------------------------------------------------------------------- -void eDeviceSound::Flush(dword endtick) -{ - dword scale; - if(!((endtick ^ tick) & ~(TICK_F-1))) - { - //same discrete as before - scale = filter_diff[(endtick & (TICK_F-1)) + TICK_F] - filter_diff[(tick & (TICK_F-1)) + TICK_F]; - s2_l += mix_l * scale; - s2_r += mix_r * scale; - - scale = filter_diff[endtick & (TICK_F-1)] - filter_diff[tick & (TICK_F-1)]; - s1_l += mix_l * scale; - s1_r += mix_r * scale; - - tick = endtick; - } - else - { - scale = filter_sum_full_u - filter_diff[(tick & (TICK_F-1)) + TICK_F]; - - dword sample_value; - sample_value = ((mix_l*scale + s2_l) >> 16) + - ((mix_r*scale + s2_r) & 0xFFFF0000); - - dstpos->sample = sample_value; - dstpos++; - if(dstpos - buffer >= BUFFER_LEN) - { - dstpos = buffer; - } - - scale = filter_sum_half_u - filter_diff[tick & (TICK_F-1)]; - s2_l = s1_l + mix_l * scale; - s2_r = s1_r + mix_r * scale; - - tick = (tick | (TICK_F-1))+1; - - if((endtick ^ tick) & ~(TICK_F-1)) - { - // assume filter_coeff is symmetric - dword val_l = mix_l * filter_sum_half_u; - dword val_r = mix_r * filter_sum_half_u; - do - { - dword sample_value; - sample_value = ((s2_l + val_l) >> 16) + - ((s2_r + val_r) & 0xFFFF0000); // save s2+val - - dstpos->sample = sample_value; - dstpos++; - if(dstpos - buffer >= BUFFER_LEN) - { - dstpos = buffer; - } - - tick += TICK_F; - s2_l = val_l; - s2_r = val_r; // s2=s1, s1=0; - - } while ((endtick ^ tick) & ~(TICK_F-1)); - } - - tick = endtick; - - scale = filter_diff[(endtick & (TICK_F-1)) + TICK_F] - filter_sum_half_u; - s2_l += mix_l * scale; - s2_r += mix_r * scale; - - scale = filter_diff[endtick & (TICK_F-1)]; - s1_l = mix_l * scale; - s1_r = mix_r * scale; - } -} - -const double filter_coeff[TICK_F*2] = -{ - // filter designed with Matlab's DSP toolbox - 0.000797243121022152, 0.000815206499600866, 0.000844792477531490, 0.000886460636664257, - 0.000940630171246217, 0.001007677515787512, 0.001087934129054332, 0.001181684445143001, - 0.001289164001921830, 0.001410557756409498, 0.001545998595893740, 0.001695566052785407, - 0.001859285230354019, 0.002037125945605404, 0.002229002094643918, 0.002434771244914945, - 0.002654234457752337, 0.002887136343664226, 0.003133165351783907, 0.003391954293894633, - 0.003663081102412781, 0.003946069820687711, 0.004240391822953223, 0.004545467260249598, - 0.004860666727631453, 0.005185313146989532, 0.005518683858848785, 0.005860012915564928, - 0.006208493567431684, 0.006563280932335042, 0.006923494838753613, 0.007288222831108771, - 0.007656523325719262, 0.008027428904915214, 0.008399949736219575, 0.008773077102914008, - 0.009145787031773989, 0.009517044003286715, 0.009885804729257883, 0.010251021982371376, - 0.010611648461991030, 0.010966640680287394, 0.011314962852635887, 0.011655590776166550, - 0.011987515680350414, 0.012309748033583185, 0.012621321289873522, 0.012921295559959939, - 0.013208761191466523, 0.013482842243062109, 0.013742699838008606, 0.013987535382970279, - 0.014216593638504731, 0.014429165628265581, 0.014624591374614174, 0.014802262449059521, - 0.014961624326719471, 0.015102178534818147, 0.015223484586101132, 0.015325161688957322, - 0.015406890226980602, 0.015468413001680802, 0.015509536233058410, 0.015530130313785910, - 0.015530130313785910, 0.015509536233058410, 0.015468413001680802, 0.015406890226980602, - 0.015325161688957322, 0.015223484586101132, 0.015102178534818147, 0.014961624326719471, - 0.014802262449059521, 0.014624591374614174, 0.014429165628265581, 0.014216593638504731, - 0.013987535382970279, 0.013742699838008606, 0.013482842243062109, 0.013208761191466523, - 0.012921295559959939, 0.012621321289873522, 0.012309748033583185, 0.011987515680350414, - 0.011655590776166550, 0.011314962852635887, 0.010966640680287394, 0.010611648461991030, - 0.010251021982371376, 0.009885804729257883, 0.009517044003286715, 0.009145787031773989, - 0.008773077102914008, 0.008399949736219575, 0.008027428904915214, 0.007656523325719262, - 0.007288222831108771, 0.006923494838753613, 0.006563280932335042, 0.006208493567431684, - 0.005860012915564928, 0.005518683858848785, 0.005185313146989532, 0.004860666727631453, - 0.004545467260249598, 0.004240391822953223, 0.003946069820687711, 0.003663081102412781, - 0.003391954293894633, 0.003133165351783907, 0.002887136343664226, 0.002654234457752337, - 0.002434771244914945, 0.002229002094643918, 0.002037125945605404, 0.001859285230354019, - 0.001695566052785407, 0.001545998595893740, 0.001410557756409498, 0.001289164001921830, - 0.001181684445143001, 0.001087934129054332, 0.001007677515787512, 0.000940630171246217, - 0.000886460636664257, 0.000844792477531490, 0.000815206499600866, 0.000797243121022152 -}; - -//============================================================================= -// eFilterDiffInit -//----------------------------------------------------------------------------- -static struct eFilterDiffInit +void eDeviceSound::Flush(int64_t endtick) { - eFilterDiffInit() - { - double sum = 0; - for(int i = 0; i < (int)TICK_F*2; i++) - { - filter_diff[i] = (int)(sum * 0x10000); - sum += filter_coeff[i]; - } - } -} fdi; +#ifndef NO_USE_SOUND +#ifndef NO_USE_BEEPER + khan_beeper_level_change(endtick, (mix_l + mix_r)/2); +#endif +#endif +} \ No newline at end of file diff --git a/devices/sound/device_sound.h b/devices/sound/device_sound.h index 92323e8..2b43266 100644 --- a/devices/sound/device_sound.h +++ b/devices/sound/device_sound.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -23,47 +24,47 @@ along with this program. If not, see . #pragma once -union SNDSAMPLE -{ - dword sample; // left/right channels in low/high WORDs - struct { word left, right; } ch; // or left/right separately -}; - +//union SNDSAMPLE +//{ +// dword sample; // left/right channels in low/high WORDs +// struct { word left, right; } ch; // or left/right separately +//}; +// const dword SNDR_DEFAULT_SYSTICK_RATE = 71680 * 50; // ZX-Spectrum Z80 clock -const dword SNDR_DEFAULT_SAMPLE_RATE = 44100; -//============================================================================= -// eDeviceSound -//----------------------------------------------------------------------------- +#if defined(USE_MU) && PICO_ON_DEVICE +#if KHAN128_I2S +#define PICO_SAMPLE_RATE 24000 // some I2S chips seem unhappy with 22050 +#else +// PWM must be 22058 +#define PICO_SAMPLE_RATE 22058 +#endif +#else +#define PICO_SAMPLE_RATE 44100 +#endif + +const dword SNDR_DEFAULT_SAMPLE_RATE = PICO_SAMPLE_RATE; + +//============================================================================= +// eDeviceSound +//----------------------------------------------------------------------------- class eDeviceSound : public eDevice { public: eDeviceSound(); - void SetTimings(dword clock_rate, dword sample_rate); +// void SetTimings(dword clock_rate, dword sample_rate); virtual void FrameStart(dword tacts); virtual void FrameEnd(dword tacts); virtual void Update(dword tact, dword l, dword r); - enum { BUFFER_LEN = 16384 }; - - void* AudioData(); - dword AudioDataReady(); - void AudioDataUse(dword size); - protected: dword mix_l, mix_r; - SNDSAMPLE* dstpos; - dword clock_rate, sample_rate; - - SNDSAMPLE buffer[BUFFER_LEN]; +// dword clock_rate, sample_rate; private: - dword tick, base_tick; - dword s1_l, s1_r; - dword s2_l, s2_r; - - void Flush(dword endtick); + void Flush(int64_t endtick); }; #endif//__DEVICE_SOUND_H__ + diff --git a/devices/ula.cpp b/devices/ula.cpp index c1b70a7..26d23c0 100644 --- a/devices/ula.cpp +++ b/devices/ula.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -24,140 +25,63 @@ along with this program. If not, see . #define Max(o, p) (o > p ? o : p) #define Min(o, p) (o < p ? o : p) +#define line_tacts 224 +#define paper_start 17989 +#define mid_lines SZX_HEIGHT +#define buf_mid SZX_WIDTH +#define b_top ((S_HEIGHT - mid_lines) / 2) +#define b_left ((S_WIDTH - buf_mid) / 2) +#define first_line_t (paper_start - b_top * line_tacts - b_left / 2) + +// this produces very nice code on ARM! +#define hideous_divide_by_224(x) (((((x)*0x1249) + (((x)*0x1249)>>15)) + 255)>>20) + +#define speccy_y_to_normal(y) (((y) & 0xc0) + (((y)&7) << 3) + (((y) >> 3) & 7)) +// it is self inverse +#define normal_to_speccy_y(y) speccy_y_to_normal(y) //============================================================================= // eUla::eUla //----------------------------------------------------------------------------- -eUla::eUla(eMemory* m) : memory(m), border_color(0), first_screen(true), base(NULL) - , colortab(NULL), timing(NULL), prev_t(0), frame(0), mode_48k(false) +eUla::eUla(eMemory* m) : memory(m), border_color(0), first_screen(true), base(NULL), + prev_t(0), border_y(0), in_paper(false), frame(0) +#ifndef NO_USE_128K + ,mode_48k(false) +#endif { - screen = new byte[S_WIDTH * S_HEIGHT]; - memset(screen, 0, S_WIDTH * S_HEIGHT); } //============================================================================= // eUla::~eUla //----------------------------------------------------------------------------- +#ifndef NO_USE_DESTRUCTORS eUla::~eUla() { +#ifndef NO_USE_SCREEN SAFE_DELETE_ARRAY(screen); +#endif } +#endif //============================================================================= // eUla::Init //----------------------------------------------------------------------------- void eUla::Init() { - // pentagon timings - line_tacts = 224; - paper_start = 17989; - prev_t = 0; - timing = timings; + border_y = prev_t = 0; + in_paper = false; +#ifndef NO_USE_SCREEN colortab = colortab1; - CreateTables(); - CreateTimings(); +#endif base = memory->Get(eMemory::P_RAM5); } //============================================================================= -// eUla::CreateTables -//----------------------------------------------------------------------------- -void eUla::CreateTables() -{ - int i = 0; // calc screen addresses - for(int p = 0; p < 4; p++) - { - for(int y = 0; y < 8; y++) - { - for(int o = 0; o < 8; o++, i++) - { - scrtab[i] = p*0x800 + y*0x20 + o*0x100, - atrtab[i] = 0x1800 + (p*8+y)*32; - } - } - } - - // make colortab: zx-attr -> pc-attr - for(int a = 0; a < 0x100; a++) - { - byte ink = a & 7; - byte paper = (a >> 3) & 7; - byte bright = (a >> 6) & 1; - byte flash = (a >> 7) & 1; - if(ink) - ink |= bright << 3; // no bright for 0th color - if(paper) - paper |= bright << 3; // no bright for 0th color - byte c1 = (paper << 4) | ink; - if(flash) - { - byte t = ink; - ink = paper; - paper = t; - } - byte c2 = (paper << 4) | ink; - colortab1[a] = c1; - colortab2[a] = c2; - } -} -//============================================================================= -// eUla::CreateTimings -//----------------------------------------------------------------------------- -// each cpu tact ula painted 2pix -// while painted border ula read color value on each 2pix, on paper each 8pix -//----------------------------------------------------------------------------- -void eUla::CreateTimings() -{ - int b_bottom, b_top, b_left, b_right; - int mid_lines = SZX_HEIGHT, buf_mid = SZX_WIDTH; - b_top = b_bottom = (S_HEIGHT - mid_lines) / 2; - b_left = b_right = (S_WIDTH - buf_mid) / 2; - - int scr_width = S_WIDTH; - int idx = 0; - - timings[idx++].Set(0, eTiming::Z_SHADOW); // to skip non visible area - int line_t = paper_start - b_top * line_tacts - b_left / 2; - for(int i = 0; i < b_top; ++i) // top border - { - byte* dst = screen + scr_width * i; - timings[idx++].Set(Max(line_t, 0), eTiming::Z_BORDER, dst); - - int t = Max(line_t + (b_left + buf_mid + b_right) / 2, 0); - timings[idx++].Set(t, eTiming::Z_SHADOW); - line_t += line_tacts; - } - for(int i = 0; i < mid_lines; ++i) // screen + border - { - byte* dst = screen + scr_width * (i + b_top); - timings[idx++].Set(Max(line_t, 0), eTiming::Z_BORDER, dst); - - int t = Max(line_t + b_left / 2, 0); - dst = screen + scr_width * (i + b_top) + b_left; - timings[idx++].Set(t, eTiming::Z_PAPER, dst, scrtab[i], atrtab[i]); - - t = Max(line_t + (b_left + buf_mid) / 2, 0); - dst = screen + scr_width * (i + b_top) + b_left + buf_mid; - timings[idx++].Set(t, eTiming::Z_BORDER, dst); - - t = Max(line_t + (b_left + buf_mid + b_right) / 2, 0); - timings[idx++].Set(t, eTiming::Z_SHADOW); - line_t += line_tacts; - } - for(int i = 0; i < b_bottom; ++i) // bottom border - { - byte* dst = screen + scr_width * (i + b_top + mid_lines); - timings[idx++].Set(Max(line_t, 0), eTiming::Z_BORDER, dst); - - int t = Max(line_t + (b_left + buf_mid + b_right) / 2, 0); - timings[idx++].Set(t, eTiming::Z_SHADOW); - line_t += line_tacts; - } - timings[idx].Set(0x7fffffff, eTiming::Z_SHADOW); // shadow area rest -} -//============================================================================= // eUla::Reset //----------------------------------------------------------------------------- void eUla::Reset() { +#ifndef NO_USE_128K SwitchScreen(true, 0); +#endif } +#ifndef NO_USE_128K //============================================================================= // eUla::SwitchScreen //----------------------------------------------------------------------------- @@ -170,6 +94,8 @@ void eUla::SwitchScreen(bool first, int tact) int page = first_screen ? eMemory::P_RAM5: eMemory::P_RAM7; base = memory->Get(page); } +#endif +#ifndef USE_HACKED_DEVICE_ABSTRACTION //============================================================================= // eUla::IoRead //----------------------------------------------------------------------------- @@ -184,20 +110,19 @@ bool eUla::IoWrite(word port) const { return !(port&1) || (!mode_48k && !(port & 2) && !(port & 0x8000)); } +#endif //============================================================================= // eUla::IoRead //----------------------------------------------------------------------------- void eUla::IoRead(word port, byte* v, int tact) { UpdateRay(tact); - if(timing->zone != eTiming::Z_PAPER) // ray is not in paper + if(!in_paper) // ray is not in paper { *v = 0xff; return; } - int t = tact; - int offs = (t - timing->t) / 4; - byte* atr = base + timing->attr_offs + offs; + byte* atr = base + 0x1800 + (paper_y / 8) * 32 + paper_x / 4; *v = *atr; } //============================================================================= @@ -213,10 +138,12 @@ void eUla::IoWrite(word port, byte v, int tact) border_color = v & 7; } } +#ifndef NO_USE_128K if(!(port & 2) && !(port & 0x8000)) // zx128 port { SwitchScreen(!(v & 0x08), tact); } +#endif } //============================================================================= // eUla::FrameUpdate @@ -224,118 +151,55 @@ void eUla::IoWrite(word port, byte v, int tact) void eUla::FrameUpdate() { UpdateRay(0x7fff0000); - prev_t = 0; - timing = timings; + in_paper = false; + border_y = 0; if(++frame >= 15) { frame = 0; - colortab = colortab == colortab1 ? colortab2 : colortab1; } } + + + //============================================================================= // UpdateRay //----------------------------------------------------------------------------- void eUla::UpdateRay(int tact) { - int t = prev_t; - while(t < tact) - { - switch(timing->zone) - { - case eTiming::Z_SHADOW: - t = (timing + 1)->t; - break; - case eTiming::Z_BORDER: - UpdateRayBorder(t, tact); - break; - case eTiming::Z_PAPER: - UpdateRayPaper(t, tact); - break; - } - if(t == (timing + 1)->t) - { - timing++; - } - } - prev_t = t; -} -//============================================================================= -// eUla::UpdateRayBorder -//----------------------------------------------------------------------------- -void eUla::UpdateRayBorder(int& t, int last_t) -{ - int offs = (t - timing->t) * 2; - byte* dst = timing->dst + offs; - int end = Min(last_t, (timing + 1)->t); - for(; t < end; ++t) - { - *dst++ = border_color; - *dst++ = border_color; - } -} -//============================================================================= -// eUla::UpdateRayPaper -//----------------------------------------------------------------------------- -void eUla::UpdateRayPaper(int& t, int last_t) -{ - int offs = (t - timing->t) / 4; - byte* scr = base + timing->scr_offs + offs; - byte* atr = base + timing->attr_offs + offs; - byte* dst = timing->dst + offs * 8; - int end = Min(last_t, (timing + 1)->t); - for(int i = 0; t < end; ++i) - { - byte pix = scr[i]; - byte ink = colortab[atr[i]]; - byte paper = ink >> 4; - ink &= 0x0f; - for(int b = 0; b < 8; ++b) - { - *dst++ = ((pix << b) & 0x80) ? ink : paper; + int t = tact - first_line_t; + if (t > 0) { + // y = t / 224; + int32_t y = hideous_divide_by_224(t); + for(; border_y < Min(y, S_HEIGHT); border_y++) { + // todo left/right border or indeed changing mid line + border_colors[border_y] = border_color; } - t += 4; - } -} -//============================================================================= -// eUla::FlushScreen -//----------------------------------------------------------------------------- -void eUla::FlushScreen() -{ - int page = first_screen ? eMemory::P_RAM5: eMemory::P_RAM7; - byte* src = memory->Get(page); - byte* dst = screen; - - int border_half_width = (S_WIDTH - SZX_WIDTH) / 2; - int border_half_height = (S_HEIGHT - SZX_HEIGHT) / 2; - - for(int i = 0; i < border_half_height * S_WIDTH; ++i) - { - *dst++ = border_color; - } - for(int y = 0; y < SZX_HEIGHT; ++y) - { - for(int x = 0; x < border_half_width; ++x) - { - *dst++ = border_color; - } - for(int x = 0; x < SZX_WIDTH / 8; x++) - { - byte pix = *(src + scrtab[y] + x); - byte ink = colortab[*(src + atrtab[y] + x)]; - byte paper = ink >> 4; - ink &= 0x0f; - for(int b = 0; b < 8; ++b) - { - *dst++ = ((pix << b) & 0x80) ? ink : paper; + in_paper = y >= b_top && y < (b_top + mid_lines); + if (in_paper) { + paper_x = t - y * 224 - b_left / 2; + paper_x = paper_x * 2; + if (paper_x < 0 || paper_x >= buf_mid) { + in_paper = false; + } else { + y -= b_top; + paper_y = speccy_y_to_normal(y); } } - for(int x = 0; x < border_half_width; ++x) - { - *dst++ = border_color; - } } - for(int i = 0; i < border_half_height * S_WIDTH; ++i) - { - *dst++ = border_color; + prev_t = tact; +} + +// note l is 0 based for top of display area, negative for top border +bool eUla::GetLineInfo(int l, byte& border, const byte *& attr, const byte *& pixels) { + int sl = l + (S_HEIGHT - SZX_HEIGHT) / 2; + if (sl < 0) sl = 0; + if (sl >= S_HEIGHT) sl = S_HEIGHT - 1; + border = border_colors[sl]; + if (l >= 0 && l < SZX_HEIGHT) { + pixels = base + normal_to_speccy_y(l) * 32; + attr = base + 0x1800 + (l >> 3) * 32; + } else { + pixels = attr = nullptr; } -} + return true; +} \ No newline at end of file diff --git a/devices/ula.h b/devices/ula.h index d82a1ed..ef82c89 100644 --- a/devices/ula.h +++ b/devices/ula.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -32,52 +33,39 @@ class eUla : public eDevice { public: eUla(eMemory* m); +#ifndef NO_USE_DESTRUCTORS virtual ~eUla(); +#endif virtual void Init(); virtual void Reset(); virtual void FrameUpdate(); - virtual bool IoRead(word port) const; - virtual bool IoWrite(word port) const; - virtual void IoRead(word port, byte* v, int tact); - virtual void IoWrite(word port, byte v, int tact); - void Write(int tact) { if(prev_t < tact) UpdateRay(tact); } - void* Screen() const { return screen; } +#ifndef USE_HACKED_DEVICE_ABSTRACTION + virtual bool IoRead(word port) const final; + virtual bool IoWrite(word port) const final; + virtual dword IoNeed() const { return ION_WRITE|ION_READ; } +#endif + virtual void IoRead(word port, byte* v, int tact) final; + virtual void IoWrite(word port, byte v, int tact) final; + void Write(int tact) { + if(prev_t < tact) UpdateRay(tact); + } byte BorderColor() const { return border_color; } bool FirstScreen() const { return first_screen; } +#ifndef NO_USE_128K void Mode48k(bool on) { mode_48k = on; } +#endif static eDeviceId Id() { return D_ULA; } - virtual dword IoNeed() const { return ION_WRITE|ION_READ; } -protected: - void CreateTables(); - void CreateTimings(); + bool GetLineInfo(int l, byte& border, const byte *& attr, const byte *& pixels); +#ifndef NO_USE_128K void SwitchScreen(bool first, int tact); +#endif +protected: void UpdateRay(int tact); - void UpdateRayBorder(int& t, int last_t); - void UpdateRayPaper(int& t, int last_t); - void FlushScreen(); enum eScreen { S_WIDTH = 320, S_HEIGHT = 240, SZX_WIDTH = 256, SZX_HEIGHT = 192 }; - struct eTiming - { - enum eZone { Z_SHADOW, Z_BORDER, Z_PAPER }; - void Set(int _t, eZone _zone = Z_SHADOW, byte* _dst = NULL - , int _scr_offs = 0, int _attr_offs = 0) - { - dst = _dst; - t = _t; - zone = _zone; - scr_offs = _scr_offs; - attr_offs = _attr_offs; - } - byte* dst; // screen raster pointer - int t; // start zone tact - eZone zone; // what are drawing: shadow/border/screen - int scr_offs; - int attr_offs; - }; protected: eMemory* memory; int line_tacts; // t-states per line @@ -85,18 +73,19 @@ class eUla : public eDevice byte border_color; bool first_screen; byte* base; - byte* screen; - int scrtab[256]; // offset to start of line - int atrtab[256]; // offset to start of attribute line - byte colortab1[256]; // map zx attributes to pc attributes - byte colortab2[256]; - byte* colortab; - eTiming timings[4 * S_HEIGHT]; - eTiming* timing; - int prev_t; // last drawn pixel's tact + int prev_t; + int border_y; // last update ray y pos + bool in_paper; int frame; + // only valid if in_paper = true; + int paper_x, paper_y; +#ifndef NO_USE_128K bool mode_48k; +#else + static const bool mode_48k = false; +#endif + byte border_colors[S_HEIGHT]; }; #endif//__ULA_H__ diff --git a/embed_tool/CMakeLists.txt b/embed_tool/CMakeLists.txt new file mode 100644 index 0000000..93af8b5 --- /dev/null +++ b/embed_tool/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.13) +project(embed_tool C CXX) +set(CMAKE_CXX_STANDARD 17) + +set(SRC_ROOT ..) + +add_executable(embed_tool + main.cpp + ${SRC_ROOT}/3rdparty/miniz/miniz.c + ${SRC_ROOT}/3rdparty/miniz/miniz_tinfl.c + ${SRC_ROOT}/3rdparty/miniz/miniz_tdef.c + ) + +target_include_directories(embed_tool PRIVATE + ${SRC_ROOT} + ${SRC_ROOT}/3rdparty/miniz +) diff --git a/embed_tool/main.cpp b/embed_tool/main.cpp new file mode 100644 index 0000000..e2acd65 --- /dev/null +++ b/embed_tool/main.cpp @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MINIZ_NO_STDIO +#define MINIZ_NO_ARCHIVE_APIS +#define MINIZ_NO_TIME +#define MINIZ_NO_ZLIB_APIS +#define MINIZ_NO_MALLOC +#include "miniz.h" +#include "khan/games/games.h" + +#define ERROR_ARGS -1 +#define ERROR_UNKNOWN -2 +#define ERROR_SYNTAX -3 +#define ERROR_FILE -3 + +using std::string; +using std::vector; +using std::filesystem::path; + +string root_dir; +bool is_game; +bool is_rom; + +vector compress(const vector& raw); + +static string hex_string(int value, int width = 8, bool prefix = true) { + std::stringstream ss; + if (prefix) ss << "0x"; + ss << std::setfill('0') << std::setw(width) << std::hex << value; + return ss.str(); +} + +struct failure : std::exception { + failure(int code, string s) : c(code), s(std::move(s)) {} + + const char *what() const noexcept override { + return s.c_str(); + } + + int code() const { return c; } + +private: + int c; + string s; +}; + +static int usage() { + fprintf(stderr, "Usage: embed_tool [-d | -r] -p \n"); + return ERROR_ARGS; +} + +string trim_left(const string &str) { + const string pattern = " \f\n\r\t\v"; + auto n = str.find_first_not_of(pattern); + if (n == string::npos) return str; + return str.substr(n); +} + +string trim_right(const string &str) { + const string pattern = " \f\n\r\t\v"; + auto n = str.find_last_not_of(pattern); + if (n == string::npos) return str; + return str.substr(0, n+ 1); +} + +string trim(const string &str) { + return trim_left(trim_right(str)); +} + +void doit(std::ifstream &in, std::ofstream &out) { + string entity = is_game ? "game" : "rom"; + + string line; + auto dump = [&](vector data) { + for (uint i = 0; i < data.size(); i += 32) { + out << " "; + for (uint j = i; j < std::min(i + 32, (uint) data.size()); j++) { + out << hex_string(data[j], 2) << ", "; + } + out << "\n"; + } + }; + + std::vector> games; + + out << "#include \n"; + out << "#include \"../std.h\"\n"; + out << "#include \"" << entity << "s/" << entity << "s.h\"\n"; + + int default_index = -1; + for (int lnum = 1; std::getline(in, line); lnum++) { + line = trim(line); + if (!line.empty()) { + if (line.find_first_of('#') == 0) continue; + if (line.find_first_of('>') == 0) { + default_index = games.size(); + line = line.substr(1); + } + uint flags = 0; + auto starts_with_and_skip = [](string& line, string to_match) { + if (line.find(to_match) == 0) { + line = line.substr(to_match.length()); + return true; + } + return false; + }; + while (true) { + line = trim(line); + if (starts_with_and_skip(line, "SLOW_TAPE")) { + flags |= GF_SLOW_TAPE; + continue; + } + if (starts_with_and_skip(line, "NO_WAIT_VBLANK")) { + flags |= GF_NO_WAIT_VBLANK; + continue; + } + break; + } + size_t split = line.find_first_of('='); + if (split == string::npos) { + std::stringstream ss; + ss << "Expected to find = at line " << lnum << ": '" << line << "'"; + throw failure(ERROR_SYNTAX, ss.str()); + } + string name = trim(line.substr(0, split)); + string file = trim(line.substr(split + 1)); + if (name.empty() || file.empty()) { + std::stringstream ss; + ss << "Expected to find name = value at line " << lnum; + throw failure(ERROR_SYNTAX, ss.str()); + } + int index = games.size(); + if (!root_dir.empty()) { + path fs_path = root_dir; + fs_path /= file; + file = fs_path; + } + FILE *fp; + const char *fn = file.c_str(); + const char *ext; + ext = strrchr(fn, '.'); + if (!ext) { + std::stringstream ss; + ss << "Expected file extension on " << fn; + throw failure(ERROR_ARGS, ss.str()); + } + if ((fp = fopen(fn, "rb"))) { + const struct sdf_geometry *geo; + vector contents; + fseek(fp, 0, SEEK_END); + size_t size = ftell(fp); + fseek(fp, 0, SEEK_SET); + games.emplace_back(name, ext, size, flags); + contents.resize(size); + if (1 != fread(contents.data(), size, 1, fp)) { + throw failure(ERROR_FILE, "Failed to read file\n"); + } + auto compressed = compress(contents); + fclose(fp); + out << "static unsigned char __game_data " << entity << "_" << index << "_data_z[" << compressed.size() + << "] = {\n"; + dump(compressed); + out << "};\n"; + } else { + std::stringstream ss; + ss << "Can't open " << file; + throw failure(ERROR_FILE, ss.str()); + } + } + + } + + out << "embedded_" << entity << "_t embedded_" << entity << "s[" << games.size() << "] = {\n"; + int i = 0; + for (const auto &e : games) { + out << " { \"" << std::get<0>(e) << "\","; + if (is_game) out << " \"" << std::get<1>(e) << "\","; + out << entity << "_" << i << "_data_z, sizeof(" << entity << "_" << i << "_data_z), " << std::get<2>(e) << ""; + if (is_game) out << ", " << std::get<3>(e); + out << " },\n"; + i++; + } + out << "};\n"; + out << "int embedded_" << entity << "_count = " << games.size() << ";\n"; + out << "int embedded_" << entity << "_default = " << default_index << ";\n"; +} + +int main(int argc, char **argv) { + string in_filename, out_filename; + int rc = 0; + for (int i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'p': + if (i != argc - 1) { + root_dir = argv[++i]; + } + break; + case 'r': + is_rom = true; + break; + case 'g': + is_game = true; + break; + } + } else if (in_filename.empty()) { + in_filename = argv[i]; + } else if (out_filename.empty()) { + out_filename = argv[i]; + } else { + rc = ERROR_ARGS; + } + } + if (!is_rom && !is_game) { + printf("ERROR: must specify -r or -g\n"); + rc = ERROR_ARGS; + } + if (rc) { + usage(); + } else { + try { + std::ifstream ifile(in_filename); + if (!ifile.good()) { + std::stringstream ss; + ss << "Can't open " << in_filename; + throw failure(ERROR_FILE, ss.str()); + } + std::ofstream ofile(out_filename); + if (!ofile.good()) { + std::stringstream ss; + ss << "Can't open " << out_filename; + throw failure(ERROR_FILE, ss.str()); + } + doit(ifile, ofile); + } catch (failure &e) { + std::cerr << "ERROR: " << e.what(); + rc = e.code(); + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what(); + rc = ERROR_UNKNOWN; + } + if (rc) { + std::filesystem::remove(out_filename.c_str()); + } + } + return rc; +} + + +vector compress(const vector& raw) { + tdefl_status status; + static tdefl_compressor deflator; + + mz_uint comp_flags = TDEFL_WRITE_ZLIB_HEADER | 1500; + // Initialize the low-level compressor. + status = tdefl_init(&deflator, NULL, NULL, comp_flags); + if (status != TDEFL_STATUS_OKAY) { + throw failure(ERROR_UNKNOWN, "tdefl_init() failed!"); + } + + // just a hacky one pass compression without hsassling with growing buffers; + vector compressed(raw.size() * 2); + const void *next_in = raw.data(); + void *next_out = compressed.data(); + size_t in_bytes = raw.size(); + size_t out_bytes = compressed.size(); + status = tdefl_compress(&deflator, next_in, &in_bytes, next_out, &out_bytes, TDEFL_FINISH); + compressed.resize(out_bytes); + if (status != TDEFL_STATUS_DONE) { + throw failure(ERROR_UNKNOWN, "tdefl_compress failed!"); + } + return compressed; +} + diff --git a/file_type.cpp b/file_type.cpp index 3fb2e3e..44c77a8 100644 --- a/file_type.cpp +++ b/file_type.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2012 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -32,7 +33,8 @@ const eFileType* eFileType::FindByName(const char* name) while(ext >= name && *ext != '.') --ext; ++ext; - char type[xIo::MAX_PATH_LEN]; + assert(name + l + 1 - ext < 8); + char type[name + l + 1 - ext]; char* t = type; while(*ext) { @@ -47,6 +49,7 @@ const eFileType* eFileType::FindByName(const char* name) bool eFileType::Open(const char* name) const { +#ifndef USE_MU_SIMPLIFICATIONS for(const eFileType* t = First(); t; t = t->Next()) { char contain_path[xIo::MAX_PATH_LEN]; @@ -56,13 +59,15 @@ bool eFileType::Open(const char* name) const return t->Open(name); } } +#endif +#ifndef USE_EMBEDDED_FILES FILE* f = fopen(name, "rb"); if(f) { fseek(f, 0, SEEK_END); size_t size = ftell(f); fseek(f, 0, SEEK_SET); - byte* buf = new byte[size]; + byte* buf = (byte*)malloc(size); size_t r = fread(buf, 1, size, f); fclose(f); if(r != size) @@ -70,10 +75,18 @@ bool eFileType::Open(const char* name) const SAFE_DELETE_ARRAY(buf); return false; } +#ifndef USE_STREAM bool ok = Open(buf, size); - SAFE_DELETE_ARRAY(buf); +#else + struct stream *stream = memory_stream_open(buf, size, true); + bool ok = Open(stream); +#endif + free(buf); return ok; } +#else + assert(false); +#endif return false; } diff --git a/file_type.h b/file_type.h index 4ac5c40..bf2255c 100644 --- a/file_type.h +++ b/file_type.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2012 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -23,6 +24,9 @@ along with this program. If not, see . #pragma once +#ifdef USE_STREAM +#include "stream.h" +#endif namespace xIo { class eFileSelect; @@ -35,7 +39,11 @@ namespace xPlatform struct eFileType : public eList { static const eFileType* First() { return _First(); } +#ifndef USE_STREAM virtual bool Open(const void* data, size_t data_size) const = 0; +#else + virtual bool Open(struct stream *stream) const = 0; +#endif virtual bool Open(const char* name) const; virtual bool Store(const char* name) const { return false; } virtual bool AbleOpen() const { return true; } diff --git a/khan/CMakeLists.txt b/khan/CMakeLists.txt new file mode 100644 index 0000000..5d2ca2e --- /dev/null +++ b/khan/CMakeLists.txt @@ -0,0 +1,306 @@ +add_library(khan_common INTERFACE) + +# this doesn't work +OPTION(KHAN128_I2S "(doesn't work) Use I2S rather than PWM for non beeper audio (khan128)") + +function(create_embed_file TARGET SOURCE_FILE TARGET_FILE) + if (NOT EmbedTool_FOUND) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_LIST_DIR}) + find_package(EmbedTool) + endif() + + cmake_parse_arguments(embed "DISC;ROM" "PREFIX" "" ${ARGN} ) + + set(EMBED_TARGET "${TARGET}_embed") + + set(EMBED_ARGS "") + if (embed_ROM) + LIST(APPEND EMBED_ARGS "-r") + STRING(APPEND EMBED_TARGET "_roms") + else() + LIST(APPEND EMBED_ARGS "-g") + STRING(APPEND EMBED_TARGET "_games") + endif() + if (embed_PREFIX) + LIST(APPEND EMBED_ARGS "-p" "${embed_PREFIX}") + endif() + add_custom_target(${EMBED_TARGET} DEPENDS ${TARGET_FILE}) + + add_custom_command(OUTPUT ${TARGET_FILE} + DEPENDS ${SOURCE_FILE} + COMMAND EmbedTool ${EMBED_ARGS} ${SOURCE_FILE} ${TARGET_FILE} + ) + add_dependencies(${TARGET} ${EMBED_TARGET}) + + message("COMMAND EmbedTool ${EMBED_ARGS} ${SOURCE_FILE} ${TARGET_FILE}") + target_sources(${TARGET} PRIVATE ${TARGET_FILE}) + target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +endfunction() + +# NEED CUSTOM ALLOCATOR for USB memory + +pico_generate_pio_header(khan_common ${CMAKE_CURRENT_LIST_DIR}/khan.pio) + +target_sources(khan_common INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/khan.c + ${CMAKE_CURRENT_LIST_DIR}/khan_init.cpp + ${CMAKE_CURRENT_LIST_DIR}/khan_lib.cpp + ${CMAKE_CURRENT_LIST_DIR}/ui_font.cpp + ${CMAKE_CURRENT_LIST_DIR}/../file_type.cpp + ${CMAKE_CURRENT_LIST_DIR}/../options_common.cpp + ${CMAKE_CURRENT_LIST_DIR}/../speccy.cpp + ${CMAKE_CURRENT_LIST_DIR}/../speccy_handler.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/device.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/memory.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/ula.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/input/kempston_joy.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/input/keyboard.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/input/tape.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/sound/ay.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/sound/beeper.cpp + ${CMAKE_CURRENT_LIST_DIR}/../devices/sound/device_sound.cpp + ${CMAKE_CURRENT_LIST_DIR}/../platform/io.cpp + ${CMAKE_CURRENT_LIST_DIR}/../platform/platform.cpp + ${CMAKE_CURRENT_LIST_DIR}/../snapshot/snapshot.cpp + ${CMAKE_CURRENT_LIST_DIR}/../tools/options.cpp + ) + +if (PICO_ON_DEVICE) + target_sources(khan_common INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/z80arm.cpp + ${CMAKE_CURRENT_LIST_DIR}/z80khan.S + ${CMAKE_CURRENT_LIST_DIR}/fast_ay.S + ) + target_link_libraries(khan_common INTERFACE + hardware_interp + ) +else() + target_sources(khan_common INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/spoon.cpp + ${CMAKE_CURRENT_LIST_DIR}/../z80/z80.cpp + ${CMAKE_CURRENT_LIST_DIR}/../z80/z80_opcodes.cpp + ${CMAKE_CURRENT_LIST_DIR}/../z80/z80_op_tables.cpp + ${CMAKE_CURRENT_LIST_DIR}/z80t.cpp + ) +endif() + +# ------------------------------------------------------------------------------- +# system wide definitions +# ------------------------------------------------------------------------------- +target_compile_definitions(khan_common INTERFACE + #COLLAPSE_IRQS + #NO_USE_SIM + PICO_SCANVIDEO_SCANLINE_BUFFER_COUNT=4 + PICO_SCANVIDEO_MAX_SCANLINE_BUFFER_WORDS=76 + PICO_SCANVIDEO_MAX_SCANLINE_BUFFER2_WORDS=48 + PICO_SCANVIDEO_48MHZ=1 + PICO_HEAP_SIZE=0x400 + PICO_STACK_SIZE=0x380 + ENABLE_COMPRESSED_STREAM + $<$:NDEBUG> + #PICO_DEBUG_MALLOC + PICO_USE_OPTIMISTIC_SBRK + + PICO_SCANVIDEO_PLANE_COUNT=2 + +) + +add_library(khan128_core INTERFACE) + +if (KHAN128_I2S) + target_compile_definitions(khan128_core INTERFACE + PICO_AUDIO_I2S_PIO=1 + PICO_AUDIO_I2S_DMA_IRQ=1 + PICO_AUDIO_I2S_MONO_INPUT=1 + PICO_AUDIO_I2S_MONO_OUTPUT=1 + KHAN128_I2S=1 + # this is a little over enough for 1/50s of audio at 24000Hz + PICO_AUDIO_BUFFER_SAMPLE_LENGTH=484 + ) +else() + target_compile_definitions(khan128_core INTERFACE + PICO_AUDIO_PWM_PIO=1 + PICO_AUDIO_PWM_INTERP_SAVE=1 + # todo trying to shrink stuff down a bit - may not work + PICO_AUDIO_BUFFERS_PER_CHANNEL=2 + # this is a little over enough for 1/50s of audio at 22058Hz + PICO_AUDIO_BUFFER_SAMPLE_LENGTH=448 + ) +endif() +target_compile_definitions(khan128_core INTERFACE + # for use without NO_USE_BEEPER + #PIO_PWM_IRQ_FUNC_NAME=delegate_pwm_irq + BLOCKING_GIVE_SYNCHRONIZE_BUFFERS + + PICO_PRINTF_SUPPORT_FLOAT=0 + PICO_PRINTF_SUPPORT_LONG_LONG=0 + PICO_PRINTF_SUPPORT_PTRDIFF_T=0 + + PICO_SCANVIDEO_MISSING_SCANLINE_COLOR=0 + + PICO_TIME_DEFAULT_ALARM_POOL_DISABLED=1 +) + +if (PICO_ON_DEVICE) + target_compile_definitions(khan128_core INTERFACE + PICO_BUFFER_USB_ALLOC_HACK=1 + ) +endif() + +# ------------------------------------------------------------------------------- +# khan specifics +# ------------------------------------------------------------------------------- +target_compile_definitions(khan_common INTERFACE + USE_MU + USE_KHAN_GPIO + USE_Z80_ARM_OFFSET_T + USE_STREAM + USE_MU_SIMPLIFICATIONS + USE_HACKED_DEVICE_ABSTRACTION + USE_LARGER_FASTER_CB + # we still want to be able to load off disk for native + $<$:USE_EMBEDDED_FILES> + +# ENABLE_BREAKPOINT_IN_DEBUG +# NO_USE_TAPE +# NO_USE_FAST_TAPE +# NO_UPDATE_RLOW_IN_FETCH + + NO_USE_DESTRUCTORS + # thse two have to be used together actually! + NO_USE_SCREEN + + NO_USE_FDD + #NO_USE_KEMPSTON + NO_USE_REPLAY + NO_USE_DOS + NO_USE_SERVICE_ROM + NO_USE_SAVE + NO_USE_CSW + NO_USE_TZX + NO_USE_SZX +# NO_USE_Z80 +# NO_USE_SNA + ) + +target_compile_definitions(khan128_core INTERFACE + #for now don't use both + NO_USE_BEEPER + #$<$:NO_USE_AY> + $<$:USE_FAST_AY> + USE_OVERLAPPED_ROMS + # this simply refers to use of banks by the z80 runtime; it is required for NO_USE_128K + $<$:USE_BANKED_MEMORY_ACCESS> + ) + +if (NOT PICO_NO_FLASH) + target_compile_definitions(khan_common INTERFACE + USE_COMPRESSED_ROM + USE_XIP_STREAM + ) +endif() + +#target_compile_options(khan_common INTERFACE -Wall -Werror) + +add_library(miniz INTERFACE) + +set(MINIZ_DIR ${CMAKE_CURRENT_LIST_DIR}/../3rdparty/miniz) +target_sources(miniz INTERFACE + ${MINIZ_DIR}/miniz_tinfl.c + ) + +target_include_directories(miniz INTERFACE ${MINIZ_DIR}) + +target_link_libraries(khan_common INTERFACE pico_stdlib pico_scanvideo_dpi miniz stream pico_multicore) + +function(add_khan_exe NAME GAME_FILE ROM_FILE USE_USB) + set(USE_USB_KEYBOARD ${USE_USB}) + add_executable(${NAME}) + set(OVERRIDE_GAME_FILE ${NAME}_games) + if (DEFINED ${OVERRIDE_GAME_FILE}) + set(GAME_FILE ${OVERRIDE_GAME_FILE}) + endif() + set(OVERRIDE_ROM_FILE ${NAME}_roms) + if (DEFINED ${OVERRIDE_ROM_FILE}) + set(ROM_FILE ${OVERRIDE_ROM_FILE}) + endif() + pico_set_float_implementation(${NAME} none) + pico_set_double_implementation(${NAME} none) + pico_enable_stdio_uart(${NAME} 1) # just use raw uart + + get_filename_component(GAME_FILE ${GAME_FILE} ABSOLUTE) + + # todo override prefix + create_embed_file(${NAME} ${GAME_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${NAME}_games.cpp PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../unrealspeccyp/res) + get_filename_component(ROM_FILE ${ROM_FILE} ABSOLUTE) + create_embed_file(${NAME} ${ROM_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${NAME}_roms.cpp PREFIX ${CMAKE_CURRENT_LIST_DIR}/../../unrealspeccyp/res/rom ROM) + pico_add_extra_outputs(${NAME}) + + if (PICO_ON_DEVICE) + if (USE_USB_KEYBOARD OR USE_USB_MOUSE) + target_link_libraries(${NAME} PRIVATE tinyusb_host) + + target_compile_definitions(${NAME} PRIVATE + USB_DPRAM_MAX=768 # needs to be big + PICO_USB_HOST_INTERRUPT_ENDPOINTS=3 + ) + endif() + if (USE_USB_KEYBOARD) + target_compile_definitions(${NAME} PRIVATE USE_USB_KEYBOARD) + endif() + if (USE_USB_MOUSE) + target_compile_definitions(${NAME} PRIVATE USE_USB_MOUSE) + endif() + if (NOT PICO_NO_FLASH) + pico_set_linker_script(${NAME} ${CMAKE_CURRENT_LIST_DIR}/memmap_khan.ld) + endif() + endif() +endfunction() + +add_khan_exe(khan games/khan_games.txt roms/khan_roms.txt 0) +add_khan_exe(khan128 games/khan128_games.txt roms/khan128_roms.txt 0) +add_khan_exe(khan128_turbo games/khan128_turbo_games.txt roms/khan128_roms.txt 0) +add_khan_exe(khan_usb games/khan_games.txt roms/khan_roms.txt 1) +add_khan_exe(khan128_usb games/khan128_games.txt roms/khan128_roms.txt 1) +add_khan_exe(khan128_turbo_usb games/khan128_turbo_games.txt roms/khan128_roms.txt 1) + +target_compile_definitions(khan PRIVATE + USE_SINGLE_64K_MEMORY # note this is ignored with USE_COMPRESSED_ROM + NO_USE_128K + NO_USE_AY + ) +target_compile_definitions(khan_usb PRIVATE + USE_SINGLE_64K_MEMORY # note this is ignored with USE_COMPRESSED_ROM + NO_USE_128K + NO_USE_AY + ) + +if (PICO_ON_DEVICE) +# target_compile_definitions(khan_common INTERFACE USE_UART_KEYBOARD) # regular UART Input + target_compile_definitions(khan_common INTERFACE USE_SDL_EVENT_FORWARDER_INPUT) # support SDL event forwarder + target_compile_definitions(khan_common INTERFACE USE_USB_INPUT) # support SDL event forwarder + target_compile_definitions(khan_common INTERFACE USE_Z80_ARM) + # target_compile_definitions(khan_common INTERFACE USE_LEGACY_TAPE_COMPARISON) +endif() + +target_compile_options(khan_common INTERFACE -g) +target_link_libraries(khan PRIVATE khan_common) +target_link_libraries(khan_usb PRIVATE khan_common) + +if (KHAN128_I2S) + target_link_libraries(khan128_core INTERFACE khan_common pico_audio_i2s) +else() + target_link_libraries(khan128_core INTERFACE khan_common pico_audio_pwm) +endif() + +target_link_libraries(khan128 PRIVATE khan128_core) +target_link_libraries(khan128_turbo PRIVATE khan128_core) +target_link_libraries(khan128_usb PRIVATE khan128_core) +target_link_libraries(khan128_turbo_usb PRIVATE khan128_core) + +target_compile_definitions(khan128_turbo PRIVATE + SPEEDUP=3 +) +target_compile_definitions(khan128_turbo_usb PRIVATE + SPEEDUP=3 + ) diff --git a/khan/FindEmbedTool.cmake b/khan/FindEmbedTool.cmake new file mode 100644 index 0000000..7f5fde4 --- /dev/null +++ b/khan/FindEmbedTool.cmake @@ -0,0 +1,44 @@ +# Finds (or builds) the EmbedTool executable +# +# This will define the following variables +# +# EmbedTool_FOUND +# +# and the following imported targets +# +# EmbedTool +# + +if (NOT EmbedTool_FOUND) + # todo we would like to use pckgconfig to look for it first + # see https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/ + + include(ExternalProject) + + set(EmbedTool_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../embed_tool) + set(EmbedTool_BINARY_DIR ${CMAKE_BINARY_DIR}/embed_tool) + + set(EmbedTool_BUILD_TARGET EmbedToolBuild) + set(EmbedTool_TARGET EmbedTool) + + if (NOT TARGET ${EmbedTool_BUILD_TARGET}) + message("EmbedTool will need to be built") + ExternalProject_Add(${EmbedTool_BUILD_TARGET} + PREFIX embed_tool + SOURCE_DIR ${EmbedTool_SOURCE_DIR} + BINARY_DIR ${EmbedTool_BINARY_DIR} + BUILD_ALWAYS 1 # force dependency checking + INSTALL_COMMAND "" + ) + endif() + + set(EmbedTool_EXECUTABLE ${EmbedTool_BINARY_DIR}/embed_tool) + if(NOT TARGET ${EmbedTool_TARGET}) + add_executable(${EmbedTool_TARGET} IMPORTED) + endif() + set_property(TARGET ${EmbedTool_TARGET} PROPERTY IMPORTED_LOCATION + ${EmbedTool_EXECUTABLE}) + + add_dependencies(${EmbedTool_TARGET} ${EmbedTool_BUILD_TARGET}) + set(EmbedTool_FOUND 1) +endif() diff --git a/khan/fast_ay.S b/khan/fast_ay.S new file mode 100644 index 0000000..3120d6b --- /dev/null +++ b/khan/fast_ay.S @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +.cpu cortex-m0 +.thumb +.syntax divided +// todo corret volume ramps .. pretty cheap really + +#define OFFSET_BITS_A03X 0 +#define OFFSET_BITS_B14X 1 +#define OFFSET_BITS_C25X 2 +#define OFFSET_BIT_N 3 + +#define OFFSET_CH_F_A 6 +#define OFFSET_CH_F_B 8 +#define OFFSET_CH_F_C 10 +#define OFFSET_CH_F_N 12 +#define OFFSET_CH_F_E 14 + +#define OFFSET_A_OUTPUT 16 +#define OFFSET_B_OUTPUT 18 +#define OFFSET_C_OUTPUT 20 + +#define OFFSET_A_VOL 22 +#define OFFSET_B_VOL 24 +#define OFFSET_C_VOL 26 +#define OFFSET_E_VOL 28 +// must be next to e_vol +#define OFFSET_DE_VOL 30 + +#define OFFSET_MASTER_VOLUME 32 +#define OFFSET_NS 36 + +// counter state 16 bytes +#define OFFSET_COUNTER_STATE 48 +#define OFFSET_T_PAIRS 48 +#define OFFSET_TA 48 +#define OFFSET_TB 50 +#define OFFSET_TC 52 +#define OFFSET_TM 54 +#define OFFSET_TE 56 +#define OFFSET_TT 58 +#define OFFSET_10001 60 + +// sample state 16 bytes +#define OFFSET_SAMPLE_STATE 64 +#define OFFSET_FRAC_MAJOR 64 +#define OFFSET_FRAC_MINOR 68 +#define OFFSET_FRAC_ACCUM 72 +#define OFFSET_OVER_C_VALUE 76 +#define OFFSET_OVER_REM 80 +#define OFFSET_OVER_ACCUM 84 + +#define OFFSET_WAKEUPS 88 + +#define OFFSET_CH_VOL 0 +#define OFFSET_CH_VALUE 0 + + +#define r_state r0 + +#define r_t_ab r1 +#define r_sample_ptr r1 + +#define r_t_cn r2 +#define r_frac_major r2 + +#define r_t_et r3 +#define r_frac_minor r3 + +#define r_frac_accum r4 + +#define r_over_c_value r5 + +#define r_over_rem r6 + +#define r_hi_counter_loop_breakout r8 + +#define r_hi_saved_sample_ptr r10 +// r6, tmp +// r7 0x10001 +#.section .data_fast_ay,"x" + +//#define SANIT_AY + +.macro debug_break +// bkpt #0 +.endm + +.macro check_over_rem +#ifdef SANIT_AY + cmp r_over_rem, #0 + bge over_rem_ok\@ + bkpt #0 +over_rem_ok\@: +#endif +.endm + +buffer_full1: +#ifdef SANIT_AY + bkpt #0 // shouldn't happen any more (but still does very rarely atm :-( ) +#endif +// mov r10, r1 +// mov r1, r_state +// add r1, #OFFSET_SAMPLE_STATE + 8 +// stmia r1!, {r_frac_accum, r_over_c_value, r_over_rem} +buffer_full2: +#ifdef SANIT_AY + bkpt #0 // shouldn't happen any more (but still does very rarely atm :-( ) +#endif + + mov r0, #0 + str r0, [r1, #OFFSET_OVER_REM] // no more samples + +all_done: + debug_break + mov r0, r10 + pop {r1, r4-r7} + sub r0, r1 + lsr r0, #1 + mov r8, r4 + mov r9, r5 + mov r10, r6 + mov r11, r7 + pop {r4-r7, pc} + +.align 2 +.global fast_ay +.type fast_ay,%function +.thumb_func +fast_ay: + push {r4-r7, lr} + mov r4, r8 + mov r5, r9 + mov r6, r10 + mov r7, r11 + push {r1, r4-r7} + + add r2, r2 + add r2, r1 + mov r9, r2 + + // save original sample_ptr + mov r10, r1 + // note we now expect that over_rem is always 0 on entry +#ifdef SANIT_AY + ldr r7, [r_state, #OFFSET_OVER_REM] + cmp r7, #0 + beq 1f + bkpt #0 +1: +#endif + // so we jump straight to counter loop (kept code in + // this order because it makes branches in range) + b counter_loop_enter + +flush_samples: + debug_break + +// r0 - state +// r1 - sample_ptr +// r2 - frac_major +// r3 - frac_minor +// r4 - frac_accum +// r5 - oc_v (aa00 0000 0000 0000 bbbb bbbb bbbb bbbb - oc = oversample_count 0-3, v = value to write) +// r6 - over_rem +// r7 - +// r8 - next_value (or -1) +// r9 - end of sample buffer +// r10 - saved sample ptr +// r11 - saved done flag + + mov r7, r_state + add r7, #OFFSET_SAMPLE_STATE + ldmia r7!, {r_frac_major, r_frac_minor, r_frac_accum, r_over_c_value, r_over_rem} + check_over_rem +// bkpt #0 + + cmp r_over_rem, #0 + beq no_flush + +// debug_break + + lsr r7, r_over_c_value, #30 + beq sample_aligned + + + mov r8, r2 + ldr r2, [r_state, #OFFSET_OVER_ACCUM] + +2: + sub r_over_rem, #1 +1: + add r2, r_over_c_value + add r7, #1 + cmp r7, #4 + beq 2f + cmp r_over_rem, #0 + bne 2b +2: + lsl r_over_c_value, #2 + lsr r_over_c_value, #2 + lsl r7, #30 + orr r_over_c_value, r7 + cmp r7, #0 + bne no_full_sample + + lsr r2, #2 // (poorly) filtered sample + + check_over_rem + + sub r_frac_accum, r_frac_minor + bge 1f + // r2 is r_frac_major, so we need to reload into now spare reg r7 + mov r7, r8 + add r_frac_accum, r7 + strh r2, [r1] + add r1, #2 + cmp r1, r9 + bhs buffer_full1 +1: + + mov r2, #0 + str r2, [r_state, #OFFSET_OVER_ACCUM] + +no_full_sample: + mov r2, r8 +sample_aligned: +1: + sub r_over_rem, #4 + bmi 3f +2: + sub r_frac_accum, r_frac_minor + bge 1b + add r_frac_accum, r_frac_major + strh r_over_c_value, [r1] + add r1, #2 + check_over_rem + cmp r1, r9 + bhs buffer_full2 + sub r_over_rem, #4 + bpl 2b +3: + // save sample ptr + mov r10, r1 + + debug_break + + // save and mask off top 2 bits + lsr r7, r_over_c_value, #30 + lsl r7, r7, #30 + eor r_over_c_value, r7 + + // set r1 to % 4 (i.e. what is left) + lsl r_over_rem, #30 + lsr r1, r_over_rem, #30 + + beq no_fraction + +#ifdef SANIT_AY + push {r6} + uxth r6, r_over_c_value + cmp r6, r_over_c_value + beq 1f + bkpt #0 +1: + pop {r6} +#endif + // calculate accumulation based on remaining bits + mul r1, r_over_c_value + + // use r_over_c_value as temp here + ldr r_over_c_value, [r_state, #OFFSET_OVER_ACCUM] + add r1, r_over_c_value +#ifdef SANIT_AY + push {r6} + lsl r6, r1, #14 + lsr r6, r6, #14 + cmp r6, r1 + beq 1f + bkpt #0 +1: + pop {r6} +#endif + str r1, [r_state, #OFFSET_OVER_ACCUM] +no_fraction: + + ldr r3, =current_voltab + ldr r3, [r3] + + // recalculate sample value + ldr r1, [r_state, #OFFSET_A_OUTPUT] // AB + lsl r1, #1 + lsr r2, r1, #16 // B*2 + uxth r1, r1 // A*2 + ldrh r1, [r3, r1] + ldrh r2, [r3, r2] + add r1, r2 + ldrh r_over_c_value, [r_state, #OFFSET_C_OUTPUT] + lsl r_over_c_value, #1 + ldrh r_over_c_value, [r3, r_over_c_value] + add r_over_c_value, r1 + ldr r1, [r_state, #OFFSET_MASTER_VOLUME] + mul r_over_c_value, r1 + lsr r_over_c_value, #6 + 4 // todo could be part of volume table + + orr r_over_c_value, r7 + add r_over_c_value, r_over_rem +#ifdef SANIT_AY + bcc 1f + bkpt #0 // if there were enough remaining to fill a whole oversample bucket, we should have handled it at the top +1: +#endif + mov r_over_rem, #0 + + mov r1, r_state + add r1, #OFFSET_FRAC_ACCUM + check_over_rem + //bkpt #0 + stmia r1!, {r_frac_accum, r_over_c_value, r_over_rem} + +no_flush: + // tt + mov r6, r11 + cmp r6, #0 + beq all_done + + debug_break +counter_loop_enter: + // simple for now just load register bank + mov r6, r_state + add r6, #OFFSET_COUNTER_STATE + ldmia r6!, {r_t_ab, r_t_cn, r_t_et, r7} + + lsr r4, r_t_et, #16 +#ifdef SANIT_AY + bne 1f + bkpt #0 +1: +#endif + mov r_hi_counter_loop_breakout, r7 // set to 0x10001 which is bigger than samples + // r0 - state + // r1 - t_ab + // r2 - t_cn + // r3 - t_ey + // r4 - tt original + // r5 - scratch + // r6 - scratch + // r7 - 0x10001 + // r8 - breakout_flag + // r9 - end of sample buffer + // r10 - saved sample pltr + +counter_loop: + sub r_t_ab, r7 + lsl r6, r_t_ab, #16 + beq a_done +.thumb_func +check_b: + lsr r6, r_t_ab, #16 + beq b_done +.thumb_func +check_c: + sub r_t_cn, r7 + lsl r6, r_t_cn, #16 + beq c_done +.thumb_func +check_n: + lsr r6, r_t_cn, #16 + beq n_done +.thumb_func +check_e: + sub r_t_et, r7 + lsl r6, r_t_et, #16 + beq e_done +.thumb_func +check_t: + lsr r6, r_t_et, #16 + beq save_counter_state + cmp r6, r_hi_counter_loop_breakout + blo counter_loop + + // here we broke out because something changed + +save_counter_state: + debug_break + + // get number of cycles + lsr r7, r_t_et, #16 + sub r4, r7 +#ifdef SANIT_AY + bhs 1f + bkpt #0 +1: +#endif + + // save tt + mov r11, r6 + +#ifdef SANIT_AY + ldr r5, [r_state, #OFFSET_OVER_REM] + cmp r5, #0 + beq 1f + bkpt #0 // wrong +1: +#endif + str r4, [r_state, #OFFSET_OVER_REM] + + mov r5, r_state + add r5, #OFFSET_COUNTER_STATE + stmia r5!, {r_t_ab, r_t_cn, r_t_et} + + // load sample ptr + mov r1, r10 + + b flush_samples + +// add r4, r_state, #4 +// ldr r6, [r4, #OFFSET_WAKEUPS - 4] +// add r6, #1 +// str r6, [r4, #OFFSET_WAKEUPS - 4] + + +zero_fa: + // set ta to 0xffff + sub r6, r7, #2 + add r_t_ab, r6 + b check_b +a_done: + mov r8, r6 + ldrh r6, [r_state, #OFFSET_CH_F_A] + // if the frequency is 0 we do nothing - and we will wrap to + cmp r6, #0 + beq zero_fa + add r6, #1 + add r_t_ab, r6 + ldrb r6, [r_state, #OFFSET_BITS_A03X] + mov r5, #8 + eor r6, r5 + strb r6, [r_state, #OFFSET_BITS_A03X] + ldr r6, =check_b + mov lr, r6 + b update_a + +zero_fb: + // set tb to 0xffff + sub r_t_ab, r7 + add r_t_ab, #1 + b check_c +b_done: + mov r8, r6 + ldrh r6, [r_state, #OFFSET_CH_F_B] + // if the frequency is 0 we do nothing - and we will wrap to + lsl r6, #16 + beq zero_fb + add r_t_ab, r6 + add r_t_ab, r7 + sub r_t_ab, #1 + ldrb r6, [r_state, #OFFSET_BITS_B14X] + mov r5, #8 + eor r6, r5 + strb r6, [r_state, #OFFSET_BITS_B14X] + ldr r6, =check_c + mov lr, r6 + b update_b + +zero_fc: + // set tc to 0xffff + sub r6, r7, #2 + add r_t_cn, r6 + b check_n +c_done: + mov r8, r6 + ldrh r6, [r_state, #OFFSET_CH_F_C] + // if the frequency is 0 we do nothing - and we will wrap to + cmp r6, #0 + beq zero_fc + add r6, #1 + add r_t_cn, r6 + ldrb r6, [r_state, #OFFSET_BITS_C25X] + mov r5, #8 + eor r6, r5 + strb r6, [r_state, #OFFSET_BITS_C25X] + ldr r6, =check_n + mov lr, r6 + b update_c + +zero_fn: + // set tn to 0xffff + sub r_t_cn, r7 + add r_t_cn, #1 + b check_e +n_done: + mov r8, r6 + ldrh r6, [r_state, #OFFSET_CH_F_N] + // if the frequency is 0 we do nothing - and we will wrap to + lsl r6, #16 + beq zero_fn + add r_t_cn, r6 + add r_t_cn, r7 + sub r_t_cn, #1 + + // ns = (ns * 2 + 1) ^ (((ns >> 16) ^ (ns >> 13)) & 1), + // bitN = 0 - ((ns >> 16) & 1); + + ldr r5, [r_state, #OFFSET_NS] + lsl r6, r5, #3 + eor r6, r5 + lsl r6, #15 + lsr r6, #31 + add r5, r5 + add r5, #1 + eor r5, r6 + str r5, [r_state, #OFFSET_NS] + lsl r5, #15 + lsr r5, #31 + strb r5, [r_state, #OFFSET_BIT_N] + + // todo inline? + bl update_a + bl update_b + bl update_c + b check_e + +zero_fe: + // set te to 0xffff + sub r6, r7, #2 + add r_t_et, r6 + b check_t +e_done: + mov r8, r6 + ldrh r6, [r_state, #OFFSET_CH_F_E] + // if the frequency is 0 we do nothing - and we will wrap to + cmp r6, #0 + beq zero_fe + add r6, #1 + add r_t_et, r6 + + // update envelope + ldr r5, [r_state, #OFFSET_E_VOL] + lsr r6, r5, #16 // DE_VOL + add r5, r6 + lsl r5, #27 + lsr r5, #27 + beq 1f + ldr r6, =envelope_handler + ldr r6, [r6] + blx r6 + strh r5, [r_state, #OFFSET_E_VOL] +1: + + // todo inline? + bl update_a + bl update_b + bl update_c + b check_t + +// update net effect of channel a +update_a: + ldrb r6, [r_state, #OFFSET_BITS_A03X] + lsr r5, r6, #5 + bcs 1f + ldrb r5, [r_state, #OFFSET_BIT_N] + add r6, r5 + ldr r5, =channel_logic + ldrb r6, [r5, r6] + ldrh r5, [r_state, #OFFSET_A_VOL] + mul r6, r5 + strh r6, [r_state, #OFFSET_A_OUTPUT] + bx lr +1: + ldrb r5, [r_state, #OFFSET_BIT_N] + add r6, r5 + ldr r5, =channel_logic - 16 + ldrb r6, [r5, r6] + ldrh r5, [r_state, #OFFSET_E_VOL] + mul r6, r5 + strh r6, [r_state, #OFFSET_A_OUTPUT] + bx lr + +// update net effect of channel b +update_b: + ldrb r6, [r_state, #OFFSET_BITS_B14X] + lsr r5, r6, #5 + bcs 1f + ldrb r5, [r_state, #OFFSET_BIT_N] + add r6, r5 + ldr r5, =channel_logic + ldrb r6, [r5, r6] + ldrh r5, [r_state, #OFFSET_B_VOL] + mul r6, r5 + strh r6, [r_state, #OFFSET_B_OUTPUT] + bx lr +1: + ldrb r5, [r_state, #OFFSET_BIT_N] + add r6, r5 + ldr r5, =channel_logic - 16 + ldrb r6, [r5, r6] + ldrh r5, [r_state, #OFFSET_E_VOL] + mul r6, r5 + strh r6, [r_state, #OFFSET_B_OUTPUT] + bx lr + +// update net effect of channel c +update_c: + ldrb r6, [r_state, #OFFSET_BITS_C25X] + lsr r5, r6, #5 + bcs 1f + ldrb r5, [r_state, #OFFSET_BIT_N] + add r6, r5 + ldr r5, =channel_logic + ldrb r6, [r5, r6] + ldrh r5, [r_state, #OFFSET_C_VOL] + mul r6, r5 + strh r6, [r_state, #OFFSET_C_OUTPUT] + bx lr +1: + ldrb r5, [r_state, #OFFSET_BIT_N] + add r6, r5 + ldr r5, =channel_logic - 16 + ldrb r6, [r5, r6] + ldrh r5, [r_state, #OFFSET_E_VOL] + mul r6, r5 + strh r6, [r_state, #OFFSET_C_OUTPUT] + bx lr + +.thumb_func +envelope_wrap: + // already wrapped + bx lr + +.thumb_func +envelope_floor: + mov r5, #0 + strh r5, [r_state, #OFFSET_DE_VOL] + bx lr + +.thumb_func +envelope_ceil: + mov r5, #0 + strh r5, [r_state, #OFFSET_DE_VOL] + mov r5, #31 + bx lr + +.thumb_func +envelope_bounce: + ldrh r6, [r_state, #OFFSET_DE_VOL] + neg r6, r6 + strh r6, [r_state, #OFFSET_DE_VOL] + add r5, r6 + bx lr + + +.align 2 +channel_logic: + .byte 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 + + // dword mask = (1 << r.env); + // if (mask & + // ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 9) | + // (1 << 15))) + // env = denv = 0; + // else if (mask & ((1 << 8) | (1 << 12))) + // env &= 31; + // else if (mask & ((1 << 10) | (1 << 14))) + // denv = -denv, env = env + denv; + // else env = 31, denv = 0; //11,13 +.global envelope_switch +envelope_switch: + .word envelope_floor // 0 + .word envelope_floor // 1 + .word envelope_floor // 2 + .word envelope_floor // 3 + .word envelope_floor // 4 + .word envelope_floor // 5 + .word envelope_floor // 6 + .word envelope_floor // 7 + .word envelope_wrap // 8 + .word envelope_floor // 9 + .word envelope_bounce // 10 + .word envelope_ceil // 11 + .word envelope_wrap // 12 + .word envelope_ceil // 13 + .word envelope_bounce // 14 + .word envelope_floor // 15 + +.global voltab_ay +voltab_ay: + +.hword 0x0000,0x0000,0x0340,0x0340,0x04C0,0x04C0,0x06F2,0x06F2,0x0A44,0x0A44,0x0F13,0x0F13,0x1510,0x1510,0x227E,0x227E +.hword 0x289F,0x289F,0x414E,0x414E,0x5B21,0x5B21,0x7258,0x7258,0x905E,0x905E,0xB550,0xB550,0xD7A0,0xD7A0,0xFFFF,0xFFFF + +.global voltab_ym +voltab_ym: +.hword 0x0000,0x0000,0x00EF,0x01D0,0x0290,0x032A,0x03EE,0x04D2,0x0611,0x0782,0x0912,0x0A36,0x0C31,0x0EB6,0x1130,0x13A0 +.hword 0x1751,0x1BF5,0x20E2,0x2594,0x2CA1,0x357F,0x3E45,0x475E,0x5502,0x6620,0x7730,0x8844,0xA1D2,0xC102,0xE0A2,0xFFFF + +.global envelope_switch + +.section .data +.global current_voltab +current_voltab: +.word voltab_ay +.global envelope_handler +envelope_handler: +.word envelope_floor \ No newline at end of file diff --git a/khan/games/games.h b/khan/games/games.h new file mode 100644 index 0000000..0abe281 --- /dev/null +++ b/khan/games/games.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef GAMES_H +#define GAMES_H + +#define GF_SLOW_TAPE 1 +#define GF_NO_WAIT_VBLANK 2 + +typedef struct { + const char *name; + const char *ext; + const uint8_t *data_z; + const uint data_z_size; + const uint data_size; + uint8_t flags; +} embedded_game_t; + +extern embedded_game_t embedded_games[]; +extern int embedded_game_count; +extern int embedded_game_default; // can default to -1 +#endif diff --git a/khan/games/khan128_games.txt b/khan/games/khan128_games.txt new file mode 100644 index 0000000..810ea2f --- /dev/null +++ b/khan/games/khan128_games.txt @@ -0,0 +1,15 @@ +chron128 = CHRON128.sna +aykaramba = ay2.z80 +#doomdemo = DOOM_PD.Z80 +#tcats = thundercats.tap +#manic = Manic_Miner_1983_Software_Projects.z80 +#pac-3d = 3D_Pacman_1983_Freddy_Kristiansen.z80 +#k-lore = knightlore.sna +#jsw = JETSET.TAP +#starwars = Star_Wars_1987_Domark.z80 +hobbit = Hobbit_The_v1.2_1982_Melbourne_House_a.z80 +#aticatac = aticatac.tap +#dandare = dandare1.tap +bitbang = bitbang.tap + + diff --git a/khan/games/khan128_turbo_games.txt b/khan/games/khan128_turbo_games.txt new file mode 100644 index 0000000..09110e7 --- /dev/null +++ b/khan/games/khan128_turbo_games.txt @@ -0,0 +1,3 @@ +down = down_abc.tap +shock = shock.tap +NO_WAIT_VBLANK z80full = z80full.tap diff --git a/khan/games/khan_games.txt b/khan/games/khan_games.txt new file mode 100644 index 0000000..d3f6527 --- /dev/null +++ b/khan/games/khan_games.txt @@ -0,0 +1,13 @@ +# >, SLOW_TAPE, NO_WAIT_VBLANK +# todo no flash? +SLOW_TAPE zxpico = zxpico.tap +manic = Manic_Miner_1983_Software_Projects.z80 +pac-3d = 3D_Pacman_1983_Freddy_Kristiansen.z80 +k-lore = knightlore.sna +jsw = JETSET.TAP +starwars = Star_Wars_1987_Domark.z80 +hobbit = Hobbit_The_v1.2_1982_Melbourne_House_a.z80 +aticatac = aticatac.tap +dandare = dandare1.tap +bitbang = bitbang.tap + diff --git a/khan/khan.c b/khan/khan.c new file mode 100644 index 0000000..cb20f4a --- /dev/null +++ b/khan/khan.c @@ -0,0 +1,1767 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "pico/stdlib.h" +#include "pico/scanvideo.h" +#include "pico/multicore.h" +#include "khan_lib.h" +#if PICO_ON_DEVICE +#include "hardware/clocks.h" +#include "hardware/pio.h" +#include "hardware/dma.h" +#include "hardware/irq.h" +#include "sdl_keys.h" +#endif +#include "khan_init.h" +#include "khan.pio.h" +#include "hardware/sync.h" +#include "pico/binary_info.h" + +#if defined(USE_USB_KEYBOARD) || defined(USE_USB_MOUSE) +#define USE_USB 1 +#define USB_ON_CORE1 1 +#include "tusb.h" +#include "hardware/irq.h" +#endif + +#include "pico/binary_info.h" +#ifdef USE_USB_KEYBOARD +bi_decl(bi_program_feature("USB keyboard support")); +#endif +#ifdef USE_USB_MOUSE +bi_decl(bi_program_feature("USB mouse support")); +#endif +#ifdef USE_SDL_EVENT_FORWARDER_INPUT +bi_decl(bi_program_feature("SDL event forwarder support")); +#endif +#ifdef USE_UART_KEYBOARD +bi_decl(bi_program_feature("Raw UART keyboard support (limited)")); +#endif +//#define HACK_NON_CIRCULAR +#define ENABLE_MENU_OVERLAY +#if defined(ENABLE_MENU_OVERLAY) && PICO_SCANVIDEO_PLANE_COUNT > 1 +#define ENABLE_MENU_PLANE_RENDERING +#endif + +#ifdef PICO_SCANVIDEO_48MHZ +#define CLOCK_MHZ 48000000 +#else +#define CLOCK_MHZ 50000000 +#endif +// todo yikes IRQs take so long that we run out of time with audio +// rendering doesn't take that much time, so use core 0 which has interrupts on it, leaving core1 100% time for z80 +#define RENDER_ON_CORE0 +//#define RENDER_ON_CORE1 + +// other IRQs are there... save time by having it on core1 +//#define BEEPER_IRQ_ON_RENDER_THREAD + +CU_REGISTER_DEBUG_PINS(khan_timing, beeper, menu) + +// ---- select at most one --- +//CU_SELECT_DEBUG_PINS(khan_timing) +//CU_SELECT_DEBUG_PINS(beeper) +//CU_SELECT_DEBUG_PINS(menu) + +//#ifdef NDEBUG +#define DISABLE_BEEPER_ASSERTIONS +//#endif +#ifndef DISABLE_BEEPER_ASSERTIONS +#define beeper_assert(x) assert(x) +#else +#define beeper_assert(x) ((void)0) +#endif + +#if defined(USE_UART_KEYBOARD) || defined(USE_SDL_EVENT_FORWARDER_INPUT) +void khan_check_uart(); +#endif +#ifdef USE_USB +void khan_check_usb(); +#endif +#ifndef NO_USE_BEEPER +void khan_beeper_setup(); +void khan_beeper_enable(); +#endif + +typedef bool (*render_scanline_func)(struct scanvideo_scanline_buffer *dest, int core); +bool render_scanline_khan(struct scanvideo_scanline_buffer *dest, int core); + +#define the_video_mode video_mode_khan +//#define the_video_mode video_mode_khan_tft + +//#define the_video_mode video_mode_khan2 +//#define the_video_mode video_mode_khan3 +//#define the_video_mode video_mode_khan_small_border_50 +//#define video_mode video_mode_320x240_60 + +render_scanline_func render_scanline = render_scanline_khan; + +int vspeed += 1*1; +int hspeed = 1*-1; + +// to make sure only one core updates the state when the frame number changes +// todo note we should actually make sure here that the other core isn't still rendering (i.e. all must arrive before either can proceed - a la barrier) +static struct mutex frame_logic_mutex; + +#ifdef ENABLE_MENU_OVERLAY +static uint32_t menu_text_color; +static uint32_t menu_highlight_bg_color; +static uint32_t menu_highlight_fg_color; +static uint32_t menu_highlight_fg_color2; +#endif + +static void khan_update_menu_visuals(); + +//void __isr isr_siob_proc0() { +// gpio_put(24, 1); +//} + +#ifndef NO_USE_AY +#if KHAN128_I2S +#include "pico/audio_i2s.h" +#else +#include "pico/audio_pwm.h" +#endif + +struct audio_buffer_pool *producer_pool; + +bool ay_init() { + const struct audio_format *output_format; + +#if KHAN128_I2S +#if !PICO_NO_BINARY_INFO + bi_decl(bi_3pins_with_names(PICO_AUDIO_I2S_DATA_PIN, "I2S DATA", PICO_AUDIO_I2S_CLOCK_PIN_BASE, "I2S BCLK", + PICO_AUDIO_I2S_CLOCK_PIN_BASE+1, "I2S LRCLK")); +#endif + struct audio_i2s_config ay_config = { + .data_pin = PICO_AUDIO_I2S_DATA_PIN, + .clock_pin_base = PICO_AUDIO_I2S_CLOCK_PIN_BASE, + .dma_channel = 4, + .pio_sm = 0, + }; +#else +#ifdef NO_USE_BEEPER + bi_decl(bi_1pin_with_name(PICO_AUDIO_PWM_L_PIN, "AY sound (PWM)")); +#else + bi_decl(bi_1pin_with_name(PICO_AUDIO_PWM_R_PIN, "AY sound (PWM)")); +#endif + static struct audio_pwm_channel_config ay_config = { +#ifdef NO_USE_BEEPER + .core.base_pin = PICO_AUDIO_PWM_L_PIN, +#else + .core.base_pin = PICO_AUDIO_PWM_R_PIN, +#endif + .core.dma_channel = 4, + .core.pio_sm = 0, + }; +#endif + + static struct audio_format producer_audio_format = { + .format = AUDIO_BUFFER_FORMAT_PCM_S16, + .sample_freq = 0, + .channel_count = 1, + }; + producer_audio_format.sample_freq = ay_sample_rate(); + + static struct audio_buffer_format producer_format = { + .format = &producer_audio_format, + .sample_stride = 2 + }; + + bool __unused ok = false; + + producer_pool = audio_new_producer_pool(&producer_format, 3, PICO_AUDIO_BUFFER_SAMPLE_LENGTH); // we need 441-443 so this seems reasonable + +#if KHAN128_I2S + output_format = audio_i2s_setup(&producer_audio_format, &ay_config); +#else + output_format = audio_pwm_setup(&producer_audio_format, -1, &ay_config); +#endif + if (!output_format) { + printf("MuAudio: Unable to open audio device.\n"); + return false; + } else + { +#if KHAN128_I2S + audio_i2s_connect(producer_pool); +#else + audio_pwm_set_correction_mode(fixed_dither); + audio_pwm_default_connect(producer_pool, false); +#endif +#if defined(SPEEDUP) && PICO_ON_DEVICE && !KHAN128_I2S + // hack to slow down PWM + pio_sm_set_clkdiv_int_frac(pio1, 0, SPEEDUP, 0); +#endif +#if KHAN128_I2S + audio_i2s_set_enabled(true); +#else + audio_pwm_set_enabled(true); +#endif + return true; + } +} +#endif + +void main_loop() { + +// gpio_init(PIN_DBG1); +// gpio_dir_out_mask(1 << PIN_DBG1); // steal debug pin 1 for this core +// gpio_init(PIN_DBG1+1); +// gpio_init(PIN_DBG1+2); +// gpio_dir_out_mask(6 << PIN_DBG1); // steal debug pin 2,3 for this core + int hz = (the_video_mode.default_timing->clock_freq + (the_video_mode.default_timing->h_total * the_video_mode.default_timing->v_total) / 2) / + (the_video_mode.default_timing->h_total * the_video_mode.default_timing->v_total); +#ifdef NO_USE_128K + puts("\n===============\nWelcome to Khan\n"); +#else + puts("\n===================\nWelcome to Khan128\nnote: currently beeper sound is disabled in favor of AY (wip)\n"); +#endif + printf("Hz %d\n", hz); +#ifndef NO_USE_BEEPER +#ifndef BEEPER_IRQ_ON_RENDER_THREAD + khan_beeper_enable(); +#endif +#endif +#ifndef NO_USE_AY + // todo on its own we need to hook up ISR + ay_init(); +#endif +#ifdef USE_USB +#if USE_USB && USB_ON_CORE1 + tusb_init(); + irq_set_priority(USBCTRL_IRQ, 0xc0); +#endif +#endif + khan_go(hz); +} + +int render_loop() { +// gpio_init(PIN_DBG1); +// gpio_dir_out_mask(1 << PIN_DBG1); // steal debug pin 1 for this core +// gpio_init(PIN_DBG1+1); +// gpio_init(PIN_DBG1+2); +// gpio_dir_out_mask(6 << PIN_DBG1); // steal debug pin 2,3 for this core + +#ifndef NO_USE_BEEPER +#ifdef BEEPER_IRQ_ON_RENDER_THREAD + khan_beeper_enable(); +#endif +#endif + static uint32_t last_frame_num = 0; + int core_num = get_core_num(); + assert(core_num >=0 && core_num < 2); +// printf("khan rendering on core %d\r\n", core_num); + while (true) { + struct scanvideo_scanline_buffer *scanvideo_scanline_buffer = scanvideo_begin_scanline_generation(true); +// if (scanvideo_scanline_buffer->data_used) { +// // validate the previous scanline to make sure noone corrupted it +// validate_scanline(scanvideo_scanline_buffer->data, scanvideo_scanline_buffer->data_used, video_mode.width, video_mode.width); +// } + // do any frame related logic + mutex_enter_blocking(&frame_logic_mutex); + uint32_t frame_num = scanvideo_frame_number(scanvideo_scanline_buffer->scanline_id); + // note that with multiple cores we may have got here not for the first scanline, however one of the cores will do this logic first before either does the actual generation + if (frame_num != last_frame_num) { + DEBUG_PINS_SET(khan_timing, 4); + khan_menu_selection_change(MK_NONE); +#if defined(USE_UART_KEYBOARD) || defined(USE_SDL_EVENT_FORWARDER_INPUT) + khan_check_uart(); +#endif +#ifdef USE_USB + khan_check_usb(); +#endif + khan_update_menu_visuals(); + khan_idle_blanking(); + DEBUG_PINS_CLR(khan_timing, 4); + // this could should be during vblank as we try to create the next line + // todo should we ignore if we aren't attempting the next line + last_frame_num = frame_num; + } + mutex_exit(&frame_logic_mutex); + DEBUG_PINS_SET(khan_timing, 2); + render_scanline(scanvideo_scanline_buffer, core_num); + DEBUG_PINS_CLR(khan_timing, 2); +#ifdef ENABLE_VIDEO_PLANE3 + assert(false); +#endif + // release the scanline into the wild + scanvideo_end_scanline_generation(scanvideo_scanline_buffer); + } +} + +#ifndef USE_CONST_CMD_LOOKUP +uint32_t cmd_lookup[256]; +#else +const uint32_t cmd_lookup[256] = { + 0xb0000000, 0xf0000000, 0xb5000000, 0xf5000000, 0xb0500000, 0xf0500000, 0xb5500000, 0xf5500000, + 0xb0050000, 0xf0050000, 0xb5050000, 0xf5050000, 0xb0550000, 0xf0550000, 0xb5550000, 0xf5550000, + 0xb0005000, 0xf0005000, 0xb5005000, 0xf5005000, 0xb0505000, 0xf0505000, 0xb5505000, 0xf5505000, + 0xb0055000, 0xf0055000, 0xb5055000, 0xf5055000, 0xb0555000, 0xf0555000, 0xb5555000, 0xf5555000, + 0xb0000500, 0xf0000500, 0xb5000500, 0xf5000500, 0xb0500500, 0xf0500500, 0xb5500500, 0xf5500500, + 0xb0050500, 0xf0050500, 0xb5050500, 0xf5050500, 0xb0550500, 0xf0550500, 0xb5550500, 0xf5550500, + 0xb0005500, 0xf0005500, 0xb5005500, 0xf5005500, 0xb0505500, 0xf0505500, 0xb5505500, 0xf5505500, + 0xb0055500, 0xf0055500, 0xb5055500, 0xf5055500, 0xb0555500, 0xf0555500, 0xb5555500, 0xf5555500, + 0xb0000050, 0xf0000050, 0xb5000050, 0xf5000050, 0xb0500050, 0xf0500050, 0xb5500050, 0xf5500050, + 0xb0050050, 0xf0050050, 0xb5050050, 0xf5050050, 0xb0550050, 0xf0550050, 0xb5550050, 0xf5550050, + 0xb0005050, 0xf0005050, 0xb5005050, 0xf5005050, 0xb0505050, 0xf0505050, 0xb5505050, 0xf5505050, + 0xb0055050, 0xf0055050, 0xb5055050, 0xf5055050, 0xb0555050, 0xf0555050, 0xb5555050, 0xf5555050, + 0xb0000550, 0xf0000550, 0xb5000550, 0xf5000550, 0xb0500550, 0xf0500550, 0xb5500550, 0xf5500550, + 0xb0050550, 0xf0050550, 0xb5050550, 0xf5050550, 0xb0550550, 0xf0550550, 0xb5550550, 0xf5550550, + 0xb0005550, 0xf0005550, 0xb5005550, 0xf5005550, 0xb0505550, 0xf0505550, 0xb5505550, 0xf5505550, + 0xb0055550, 0xf0055550, 0xb5055550, 0xf5055550, 0xb0555550, 0xf0555550, 0xb5555550, 0xf5555550, + 0xb0000005, 0xf0000005, 0xb5000005, 0xf5000005, 0xb0500005, 0xf0500005, 0xb5500005, 0xf5500005, + 0xb0050005, 0xf0050005, 0xb5050005, 0xf5050005, 0xb0550005, 0xf0550005, 0xb5550005, 0xf5550005, + 0xb0005005, 0xf0005005, 0xb5005005, 0xf5005005, 0xb0505005, 0xf0505005, 0xb5505005, 0xf5505005, + 0xb0055005, 0xf0055005, 0xb5055005, 0xf5055005, 0xb0555005, 0xf0555005, 0xb5555005, 0xf5555005, + 0xb0000505, 0xf0000505, 0xb5000505, 0xf5000505, 0xb0500505, 0xf0500505, 0xb5500505, 0xf5500505, + 0xb0050505, 0xf0050505, 0xb5050505, 0xf5050505, 0xb0550505, 0xf0550505, 0xb5550505, 0xf5550505, + 0xb0005505, 0xf0005505, 0xb5005505, 0xf5005505, 0xb0505505, 0xf0505505, 0xb5505505, 0xf5505505, + 0xb0055505, 0xf0055505, 0xb5055505, 0xf5055505, 0xb0555505, 0xf0555505, 0xb5555505, 0xf5555505, + 0xb0000055, 0xf0000055, 0xb5000055, 0xf5000055, 0xb0500055, 0xf0500055, 0xb5500055, 0xf5500055, + 0xb0050055, 0xf0050055, 0xb5050055, 0xf5050055, 0xb0550055, 0xf0550055, 0xb5550055, 0xf5550055, + 0xb0005055, 0xf0005055, 0xb5005055, 0xf5005055, 0xb0505055, 0xf0505055, 0xb5505055, 0xf5505055, + 0xb0055055, 0xf0055055, 0xb5055055, 0xf5055055, 0xb0555055, 0xf0555055, 0xb5555055, 0xf5555055, + 0xb0000555, 0xf0000555, 0xb5000555, 0xf5000555, 0xb0500555, 0xf0500555, 0xb5500555, 0xf5500555, + 0xb0050555, 0xf0050555, 0xb5050555, 0xf5050555, 0xb0550555, 0xf0550555, 0xb5550555, 0xf5550555, + 0xb0005555, 0xf0005555, 0xb5005555, 0xf5005555, 0xb0505555, 0xf0505555, 0xb5505555, 0xf5505555, + 0xb0055555, 0xf0055555, 0xb5055555, 0xf5055555, 0xb0555555, 0xf0555555, 0xb5555555, 0xf5555555, +}; +#endif + +int video_main(void) { + mutex_init(&frame_logic_mutex); + // set 18-22 to RIO for debugging +// for (int i = PIN_DBG1; i < 22; ++i) +// gpio_init(i); + +// gpio_init(24); +// gpio_init(22); +// // just from this core +// gpio_dir_out_mask(0x01380000); +// gpio_dir_in_mask( 0x00400000); +// +// //gpio_funcsel(22, 0); +// +// // debug pin +// gpio_put(24, 0); + + // go for launch (debug pin) +#ifndef NO_USE_BEEPER + khan_beeper_setup(); +#endif + +#if !PICO_ON_DEVICE + void simulate_video_pio_video_khan(const uint32_t *dma_data, uint32_t dma_data_size, + uint16_t *pixel_buffer, int32_t max_pixels, int32_t expected_width, bool overlay); + scanvideo_set_simulate_scanvideo_pio_fn(VIDEO_KHAN_PROGRAM_NAME, simulate_video_pio_video_khan); +#endif + //video_setup_with_timing(&the_video_mode, &video_timing_648x480_60_alt1); + scanvideo_setup(&the_video_mode); + +#ifndef USE_CONST_CMD_LOOKUP + for(int i=0;i<256;i++) { + uint32_t c = 0; + for(int j=0; j<8;j++) { + uint b; + if (i & (1<<(7-j))) { + b = j==7 ? video_khan_offset_ink_end_of_block : video_khan_offset_ink; + } else { + b = j==7 ? video_khan_offset_paper_end_of_block : video_khan_offset_paper; + } + c |= (b << (j*4)); + } + cmd_lookup[i] = c; + } +#endif +// printf("static uint32_t cmd_lookup[256] = {\n"); +// for(int i=0;i<0x100;i+=8) { +// printf("\t"); +// for(int j=0;j<8;j++) { +// printf("0x%08x, ", cmd_lookup[i+j]); +// } +// printf("\n"); +// } +// printf("};\n"); + + khan_init(); + // start video after the init which can take about 70ms when decompressing from flash + scanvideo_timing_enable(true); + + // button here so we have video running (albeit with red bar) +#if PICO_ON_DEVICE +#if 0 + printf("Press button to start\r\n"); + // todo NOTE THAT ON STARTUP RIGHT NOW WITH RESET ISSUES ON FPGA, THIS CURRENTLY DOES NOT STOP!!! if you make last_input static, then it never releases instead :-( + uint8_t last_input = 0; + while (true) { + uint8_t new_input = gpio_get(input_pin0); + if (last_input && !new_input) { + break; + } + last_input = new_input; + yield(); + } +#endif +#endif +// gpio_put(24, 1); +// gpio_funcsel(input_pin0, GPIO_FUNC_PROC); + + +#if USE_USB && !USB_ON_CORE1 + tusb_init(); + irq_set_priority(USBCTRL_IRQ, 0xc0); +#endif + +#ifdef RENDER_ON_CORE1 + // todo it seems that maybe we get into trouble if this is too slow... first time after FPGA + // is power up after a while, we get consistant dump everything red error scanlines + platform_launch_core1(render_loop); + main_loop(); +#else + multicore_launch_core1(main_loop); + render_loop(); +#endif + __builtin_unreachable(); +} + +bool khan_cb_is_button_down() { + // todo default button +//#if !PICO_ON_DEVICE +// return gpio_get(input_pin0); +//#else + return false; +//#endif +} + +void __maybe_in_ram measure_text(struct text_element *element, int max) { + if (!element->width) + { + element->width = 0; + if (element->text) + { + const char *p = element->text; + uint8_t c; + while ((c = *p++)) + { + c -= MENU_GLYPH_MIN; + if (c < MENU_GLYPH_MAX) + { + element->width += menu_glyph_widths[c] + MENU_GLYPH_ADVANCE; + } + } + if (element->width > max) + element->width = max; + } + } +} + +void khan_hide_menu() { + kms.appearing = false; + if (kms.opacity) + { + kms.disappearing = true; + } +} + +void khan_menu_key(enum menu_key key) { + if (key == MK_ESCAPE) { + if (kms.appearing || kms.opacity) { + if (!khan_menu_selection_change(MK_ESCAPE)) { + khan_hide_menu(); + } + } else if (kms.disappearing || !kms.opacity) { + kms.disappearing = false; + kms.appearing = true; + khan_fill_main_menu(); + } + } else { + if (kms.opacity && !kms.disappearing) { + switch (key) { + case MK_UP: + case MK_DOWN: + case MK_LEFT: + case MK_RIGHT: + case MK_ENTER: + khan_menu_selection_change(key); + break; + default: + break; + } + } + } +} + +//#define MAKE_RGB(r,g,b) (0x8000u|(((r)>>3))|(((g)>>3)<<5)|(((b)>>3)<<10)) +//#define MAKE_BGR(b,g,r) MAKE_RGB(r,g,b) + +#ifdef ENABLE_MENU_OVERLAY +// doesn't need to be this big really, given the big gap in the middle, but this is simplest (for one color menu anyway) +// note we don't store the empty lines between glyphs +static uint8_t menu_bitmap[MENU_WIDTH_IN_BLOCKS * MENU_GLYPH_HEIGHT * MENU_MAX_LINES]; +#endif + +int render_font_line(const struct text_element *element, uint8_t *out, const uint8_t *bitmaps, int pos, int bits); + +uint32_t mix_frac16(uint32_t a, uint32_t b, int level) { + int rr = (PICO_SCANVIDEO_R5_FROM_PIXEL(a) * (16 - level) + PICO_SCANVIDEO_R5_FROM_PIXEL(b) * level) / 16; + int gg = (PICO_SCANVIDEO_G5_FROM_PIXEL(a) * (16 - level) + PICO_SCANVIDEO_G5_FROM_PIXEL(b) * level) / 16; + int bb = (PICO_SCANVIDEO_B5_FROM_PIXEL(a) * (16 - level) + PICO_SCANVIDEO_B5_FROM_PIXEL(b) * level) / 16; + return PICO_SCANVIDEO_ALPHA_MASK | PICO_SCANVIDEO_PIXEL_FROM_RGB5(rr, gg, bb); +} + +// called once per frame +void khan_update_menu_visuals() { + bool hide; + bool fill; + mutex_enter_blocking(&kms.mutex); + hide = kms.do_hide_menu; kms.do_hide_menu = false; + fill = kms.do_fill_menu; kms.do_fill_menu = false; + mutex_exit(&kms.mutex); + if (hide) khan_hide_menu(); + if (fill) khan_fill_main_menu(); +#ifdef ENABLE_MENU_OVERLAY + DEBUG_PINS_SET(menu, 1); + if (kms.opacity > MENU_OPACITY_THRESHOLD) { + menu_text_color = PICO_SCANVIDEO_ALPHA_MASK + PICO_SCANVIDEO_PIXEL_FROM_RGB5(1, 1, 1) * (kms.opacity + MENU_OPACITY_OFFSET); + menu_highlight_fg_color = PICO_SCANVIDEO_ALPHA_MASK + PICO_SCANVIDEO_PIXEL_FROM_RGB5(1, 1, 1) * (kms.opacity + MENU_OPACITY_OFFSET + 7); + + if (kms.error_level) { + menu_highlight_fg_color = mix_frac16(menu_highlight_fg_color, PICO_SCANVIDEO_PIXEL_FROM_RGB5(0x1f, 0, 0), kms.error_level); + } + menu_highlight_fg_color2 = menu_highlight_fg_color; + if (kms.flashing) { + int level; + if (kms.flash_pos < 8) + level = kms.flash_pos * 2; + else if ((MENU_FLASH_LENGTH - kms.flash_pos) < 8) + level = (MENU_FLASH_LENGTH - kms.flash_pos) * 2; + else + level = 16; + menu_highlight_fg_color2 = mix_frac16(PICO_SCANVIDEO_PIXEL_FROM_RGB5(0x10, 0x10, 0x10), menu_highlight_fg_color2, level); + } +// menu_highlight_bg_color = kms.opacity > MENU_OPACITY_OFFSET + MENU_OPACITY_MAX - 6 ? MAKE_RGB(0x40, 0x40, 0xc0) : 0; + menu_highlight_bg_color = kms.opacity > MENU_OPACITY_OFFSET + MENU_OPACITY_MAX - 6 ? PICO_SCANVIDEO_ALPHA_MASK + PICO_SCANVIDEO_PIXEL_FROM_RGB8(48,138,208) : 0; + } else { + menu_text_color = menu_highlight_bg_color = menu_highlight_fg_color = 0; + } + static int foobar; + foobar++; + + if (kms.opacity) + { + static int refresh_line = 0; + measure_text(&kms.lines[refresh_line].left_text, MENU_LEFT_WIDTH_IN_BLOCKS * 8); + measure_text(&kms.lines[refresh_line].right_text, MENU_RIGHT_WIDTH_IN_BLOCKS * 8); + + uint8_t *out = menu_bitmap + refresh_line * MENU_GLYPH_HEIGHT * MENU_WIDTH_IN_BLOCKS; + const uint8_t *bitmaps = menu_glypth_bitmap; + for (int j = 0; j < MENU_GLYPH_HEIGHT; j++) + { + int pos = 0; + int bits = 0; + if (kms.lines[refresh_line].left_text.text) + { + pos = render_font_line(&kms.lines[refresh_line].left_text, out, bitmaps, pos, bits); + } + while (pos < MENU_LEFT_WIDTH_IN_BLOCKS) + out[pos++] = 0; + pos = MENU_LEFT_WIDTH_IN_BLOCKS; // truncate + if (kms.lines[refresh_line].right_text.text) + { + int xoff = MENU_RIGHT_WIDTH_IN_BLOCKS * 8 - kms.lines[refresh_line].right_text.width; + int post = pos + (xoff >> 3); + while (pos < post) + out[pos++] = 0; + bits = xoff & 7; + pos = render_font_line(&kms.lines[refresh_line].right_text, out, bitmaps, pos, bits); + } + while (pos < MENU_WIDTH_IN_BLOCKS) + out[pos++] = 0; + bitmaps++; + out += pos; + } + refresh_line++; + if (refresh_line == MENU_MAX_LINES) refresh_line = 0; + } + int expected_top_pixel = kms.selected_line * MENU_LINE_HEIGHT; + int delta = expected_top_pixel - kms.selection_top_pixel; + if (delta > 1) { + kms.selection_top_pixel += 1 + (((delta + MENU_LINE_HEIGHT / 2)* ((1 << 16) / MENU_LINE_HEIGHT))>>16); + } else if (delta < -1) { + kms.selection_top_pixel -= 1 + (((MENU_LINE_HEIGHT/2 - delta) * ((1 << 16) / MENU_LINE_HEIGHT))>>16); + } else { + kms.selection_top_pixel = expected_top_pixel; + } + DEBUG_PINS_CLR(menu, 1); +#endif +} + +int render_font_line(const struct text_element *element, uint8_t *out, const uint8_t *bitmaps, int pos, int bits) { + const char *p = element->text; + uint32_t acc = 0; + uint8_t c; + while ((c = *p++)) { + c -= MENU_GLYPH_MIN; + if (c < MENU_GLYPH_MAX) { + int cbits = menu_glyph_widths[c] + 1; + if (cbits <= 8) { + acc = (acc << cbits) | ((bitmaps[c * MENU_GLYPH_HEIGHT] >> (8 - cbits))); + } else { + acc = (acc << cbits) | ((bitmaps[c * MENU_GLYPH_HEIGHT] << (cbits - 8))); + } + bits += cbits; + if (bits >= 8) { + bits -= 8; + out[pos++] = acc >> bits; + acc &= ((1 << bits) - 1); + } + } + } + if (bits) { + out[pos++] = acc << (8 - bits); + } + return pos; +} + +#pragma GCC push_options +#ifdef __arm__ +#pragma GCC optimize("O3") +#endif + +uint32_t *render_menu_line(int l, uint32_t *buf2); + +bool __time_critical_func(render_scanline_khan)(struct scanvideo_scanline_buffer *dest, int core) { + // 1 + line_num red, then white + uint32_t *buf = dest->data; + uint l = scanvideo_scanline_number(dest->scanline_id); + const uint8_t *attr = 0; + const uint8_t *pixels = 0; + uint16_t border = 0; + const uint32_t *attr_colors = 0; + const uint32_t *dark_attr_colors = 0; + int border_top = (the_video_mode.height - 192) / 2; + khan_get_scanline_info(l - border_top, &border, &attr, &pixels, &attr_colors, &dark_attr_colors); + +#if PICO_SCANVIDEO_PLANE_COUNT > 1 + uint32_t *buf2 = dest->data2; +#endif +#define MIN_COLOR_RUN 3 + if (attr) { + int border_width = (the_video_mode.width - 256) / 2; + if (border_width >= MIN_COLOR_RUN) { + *buf++ = border | ((border_width - MIN_COLOR_RUN) << 16u); + *buf++ = video_khan_offset_color_run; + } + l -= border_top + MENU_AREA_OFFSET_Y; + if (!(dark_attr_colors && l >= 0 && l <= MENU_LINE_HEIGHT * MENU_MAX_LINES + MENU_AREA_BORDER_HEIGHT * 2)) { + dark_attr_colors = attr_colors; + } +#define regular(n) *buf++ = attr_colors[attr[n]], *buf++ = cmd_lookup[pixels[n]] +#define maybe_dark(n) *buf++ = dark_attr_colors[attr[n]], *buf++ = cmd_lookup[pixels[n]] + + regular(0); + regular(1); + regular(2); + regular(3); + + maybe_dark(4); + maybe_dark(5); + maybe_dark(6); + maybe_dark(7); + + maybe_dark(8); + maybe_dark(9); + maybe_dark(10); + maybe_dark(11); + + maybe_dark(12); + maybe_dark(13); + maybe_dark(14); + maybe_dark(15); + + maybe_dark(16); + maybe_dark(17); + maybe_dark(18); + maybe_dark(19); + + maybe_dark(20); + maybe_dark(21); + maybe_dark(22); + maybe_dark(23); + + maybe_dark(24); + maybe_dark(25); + maybe_dark(26); + maybe_dark(27); + + regular(28); + regular(29); + regular(30); + regular(31); + + if (border_width >= MIN_COLOR_RUN) { + *buf++ = border | ((border_width - MIN_COLOR_RUN) << 16u); + *buf++ = video_khan_offset_color_run; + } +#ifdef ENABLE_MENU_PLANE_RENDERING + DEBUG_PINS_SET(menu, 2); + if (kms.opacity > MENU_OPACITY_THRESHOLD) { + l -= MENU_AREA_BORDER_HEIGHT; + if (l > 0 && l < MENU_LINE_HEIGHT * MENU_MAX_LINES) { + *buf2++ = 0 | ((border_width + ((256 - MENU_AREA_WIDTH) / 2) + MENU_LINE_BORDER_WIDTH - MIN_COLOR_RUN) + << 16); + *buf2++ = video_khan_offset_color_run; + buf2 = render_menu_line(l, buf2); + } + } + DEBUG_PINS_CLR(menu, 2); +#endif + } else { + *buf++ = border | ((the_video_mode.width - MIN_COLOR_RUN) << 16u); + *buf++ = video_khan_offset_color_run; + } + *buf++ = 0x0000u | (0x0000u << 16u); + *buf++ = video_khan_offset_end_of_line; + dest->status = SCANLINE_OK; + int pos = buf - dest->data; + assert(pos <= dest->data_max); + dest->data_used = (uint16_t) pos; +#if PICO_SCANVIDEO_PLANE_COUNT > 1 + *buf2++ = 0x0000u | (0x0000u << 16u); + *buf2++ = video_khan_offset_end_of_line; + pos = buf2 - dest->data2; + assert(pos <= dest->data2_max); + dest->data2_used = (uint16_t)pos; +#endif +// printf("%d %d\n", dest->data_used, dest->data2_used); + return true; +} +#pragma GCC pop_options + +uint32_t *render_menu_line(int l, uint32_t *buf2) { + int menu_line = (l * ((1 << 16) / MENU_LINE_HEIGHT))>>16; + int menu_line_offset = l - menu_line * MENU_LINE_HEIGHT; + uint32_t bg_color, fg_color; + if (l >= kms.selection_top_pixel && l < kms.selection_top_pixel + MENU_LINE_HEIGHT + 1) { + fg_color = menu_highlight_fg_color; + bg_color = menu_highlight_bg_color; + } else { + fg_color = menu_text_color; + bg_color = 0; + } + uint32_t combined_colors = (fg_color << 16) | bg_color; + if (menu_line_offset >= MENU_GLYPH_Y_OFFSET && menu_line_offset < MENU_GLYPH_Y_OFFSET + MENU_GLYPH_HEIGHT) { + *buf2++ = bg_color | ((MENU_LINE_TEXT_INDENT - MIN_COLOR_RUN) << 16); + *buf2++ = video_khan_offset_color_run; + uint8_t *pixels = &menu_bitmap[MENU_WIDTH_IN_BLOCKS * (menu_line * MENU_GLYPH_HEIGHT + menu_line_offset - MENU_GLYPH_Y_OFFSET)]; + +#define regular2(n) *buf2++ = combined_colors, *buf2++ = cmd_lookup[pixels[n]] + + static_assert(MENU_LEFT_WIDTH_IN_BLOCKS == 10, ""); + regular2(0); + regular2(1); + regular2(2); + regular2(3); + regular2(4); + regular2(5); + regular2(6); + regular2(7); + regular2(8); + regular2(9); + + *buf2++ = bg_color | ((MENU_AREA_WIDTH - (MENU_LINE_BORDER_WIDTH + MENU_LINE_TEXT_INDENT) * 2 - MENU_WIDTH - MIN_COLOR_RUN) << 16); + *buf2++ = video_khan_offset_color_run; + + static_assert(MENU_RIGHT_WIDTH_IN_BLOCKS == 7, ""); + if (kms.flashing && menu_line == kms.selected_line) { + combined_colors = (menu_highlight_fg_color2 << 16) | bg_color; + } + + regular2(10); + regular2(11); + regular2(12); + regular2(13); + regular2(14); + regular2(15); + regular2(16); + *buf2++ = bg_color | ((MENU_LINE_TEXT_INDENT - MIN_COLOR_RUN) << 16); + *buf2++ = video_khan_offset_color_run; + } else { + *buf2++ = bg_color | ((MENU_AREA_WIDTH - MENU_LINE_BORDER_WIDTH * 2 - MIN_COLOR_RUN) << 16); + *buf2++ = video_khan_offset_color_run; + } + return buf2; +} + +void go_core1(void (*execute)()) { + multicore_launch_core1(execute); +} + +#if !PICO_ON_DEVICE +void simulate_video_pio_video_khan(const uint32_t *dma_data, uint32_t dma_data_size, + uint16_t *pixel_buffer, int32_t max_pixels, int32_t expected_width, bool overlay) { + const uint16_t *it = (uint16_t *)dma_data; + assert(!(3&(intptr_t)dma_data)); + const __unused uint16_t *const dma_data_end = (uint16_t *)(dma_data + dma_data_size); +// const uint16_t *const pixels_end = (uint16_t *)(pixel_buffer + max_pixels); + uint16_t *pixels = pixel_buffer; +// bool ok = false; + bool done = false; + const uint16_t display_enable_bit = PICO_SCANVIDEO_ALPHA_MASK; // for now + do { + uint16_t a = *it++; + uint16_t b = *it++; + // note we align checked above + uint32_t c = *(uint32_t *) it; + it += 2; + bool had_eob = false; + for (int n = 0; n < 8 && !done && !had_eob; n++) { + uint cmd = c & 0xf; + c >>= 4; + switch (cmd) { + case video_khan_offset_paper: + if (!overlay || (a & display_enable_bit)) { + *pixels++ = a; + } else { + pixels++; + } + break; + case video_khan_offset_end_of_line: + done = true; + break; + case video_khan_offset_ink: + if (!overlay || (b & display_enable_bit)) { + *pixels++ = b; + } else { + pixels++; + } + break; + case video_khan_offset_color_run: + if (!overlay || (a & display_enable_bit)) { + for (uint i = 0; i < b + 3; i++) { + *pixels++ = a; + } + } else { + pixels += b + 3; + } + had_eob = true; + break; + case video_khan_offset_paper_end_of_block: + if (!overlay || (a & display_enable_bit)) { + *pixels++ = a; + } else { + pixels++; + } + had_eob = true; + break; + case video_khan_offset_ink_end_of_block: + if (!overlay || (b & display_enable_bit)) { + *pixels++ = b; + } else { + pixels++; + } + had_eob = true; + break; + } + } + assert(done || had_eob); + } while (!done); +// assert(ok); + assert(it == dma_data_end); + assert(!(3&(intptr_t)(it))); // should end on dword boundary +// assert(!expected_width || pixels == pixel_buffer + expected_width); // with the correct number of pixels (one more because we stick a black pixel on the end) +} +#endif + +#if PICO_ON_DEVICE +void __cxa_pure_virtual() { + __breakpoint(); +} + +#if 0 +void _fini() { + __breakpoint(); +} +#endif +#endif + +#include "miniz_tinfl.h" + +int main(void) { +#if PICO_SCANVIDEO_48MHZ + int base_khz = 48000; +#else + int base_khz = 50000; +#endif + +#ifndef SPEEDUP + set_sys_clock_khz(base_khz, true); +#else + set_sys_clock_khz(base_khz * SPEEDUP, true); +#endif +// gpio_put(27, 0); + setup_default_uart(); + +// size_t in_bytes = 0;//avail_in; +// size_t out_bytes = 0;//avail_out; +// tinfl_init(&inflator); +// +// mz_uint8 *s_outbuf = NULL, *next_out = NULL, *next_in = NULL; +// bool infile_remaining = false; +// tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, s_outbuf, (mz_uint8 *)next_out, &out_bytes, (infile_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0) | TINFL_FLAG_PARSE_ZLIB_HEADER); + + + return video_main(); +} + +void khan_cb_begin_frame() { + DEBUG_PINS_SET(khan_timing, 1); +} + +void khan_cb_end_frame() { + DEBUG_PINS_CLR(khan_timing, 1); +} + +#if PICO_ON_DEVICE +enum eKeyFlags { + KF_DOWN = 0x01, + KF_SHIFT = 0x02, + KF_CTRL = 0x04, + KF_ALT = 0x08, + + KF_KEMPSTON = 0x10, + KF_CURSOR = 0x20, + KF_QAOP = 0x40, + KF_SINCLAIR2 = 0x80, + +}; + +#ifdef USE_UART_KEYBOARD +static uint8_t UartTranslateKey(uint8_t _key, uint8_t esc, uint8_t *_flags) +{ + // todo lookup table + switch(_key) + { + case 27: + switch (esc) + { + case 'A': + *_flags = KF_CURSOR; + return 'u'; + case 'B': + *_flags = KF_CURSOR; + return 'd'; + case 'C': + *_flags = KF_CURSOR; + return 'r'; + case 'D': + *_flags = KF_CURSOR; + return 'l'; + } + break; + case '`': + return 'm'; +// case SDLK_LSHIFT: return 'c'; +// case SDLK_RSHIFT: return 'c'; +// case SDLK_LALT: return 's'; +// case SDLK_RALT: return 's'; + case '\r': + return 'e'; + case 8: + *_flags = KF_SHIFT; + return '0'; + case '\t': + *_flags = KF_ALT; + *_flags = KF_SHIFT; + return 0; +// case SDLK_LEFT: return 'l'; +// case SDLK_RIGHT: return 'r'; +// case SDLK_UP: return 'u'; +// case SDLK_DOWN: return 'd'; +// case SDLK_INSERT: +// case SDLK_RCTRL: +// case SDLK_LCTRL: return 'f'; + default: + break; + } + if(_key >= '0' && _key <= '9') + return _key; + if(_key >= 'A' && _key <= 'Z') { + *_flags = KF_SHIFT; + return _key; + } + if(_key >= 'a' && _key <= 'z') { + return _key + 'A' - 'a'; + } + if(_key == ' ') + return _key; + + const char alt_translate[] = "\"P'7T.M:Z;O/V?C-J_0=L+K"; + for(int i = 0; i < count_of(alt_translate); i+=2) { + if (_key == alt_translate[i]) { + *_flags |= KF_ALT; + return alt_translate[i+1]; + } + } + // todo escape codes + printf("Unknown key %d\n", _key); + *_flags = 0; + return 0; +} +#endif + +static bool skip_key; + +static uint16_t modifier_state; + +static const uint16_t mods[8] = { + KMOD_LCTRL, + KMOD_LSHIFT, + KMOD_LALT, + KMOD_LGUI, + KMOD_RCTRL, + KMOD_RSHIFT, + KMOD_RALT, + KMOD_RGUI, +}; + +bool update_modifiers(int scancode, bool down) { + if (scancode >= 224 && scancode < 224 + count_of(mods)) { + uint16_t mod = mods[scancode - 224]; + if (down) { + modifier_state |= mod; + } else { + modifier_state &= ~mod; + } + return true; + } + return false; +} + +#if defined(USE_UART_KEYBOARD) || defined(USE_SDL_EVENT_FORWARDER_INPUT) +void khan_check_uart() { +#ifdef USE_UART_KEYBOARD + static uint8_t last; + static uint8_t last_flags; + static int down_length = 0; +#endif + if (uart_is_readable(uart_default)) { + uint8_t flags = 0; + uint8_t b = uart_getc(uart_default); + if (skip_key) { + skip_key = false; + return; + } + uint8_t esc = 0; + bool menu_key = kms.opacity && !kms.disappearing; + if (b == 26 && uart_is_readable_within_us(uart_default, 80)) { + b = uart_getc(uart_default); + switch (b) { + case 0: + if (uart_is_readable_within_us(uart_default, 80)) { + uint scancode = (uint8_t)uart_getc(uart_default); + update_modifiers(scancode, true); + khan_key_down(scancode,0, modifier_state); + } + return; + case 1: + if (uart_is_readable_within_us(uart_default, 80)) { + uint scancode = (uint8_t)uart_getc(uart_default); + update_modifiers(scancode, false); + khan_key_up(scancode, 0, modifier_state); + } + return; + case 2: + case 3: + case 5: + if (uart_is_readable_within_us(uart_default, 80)) { + + uint state = (uint8_t)uart_getc(uart_default); +#ifndef NO_USE_KEMPSTON + uint8_t tstate = 0; + if (state & 0x03) tstate |= KEMPSTON_F; + if (state & 0x10) tstate |= KEMPSTON_U; + if (state & 0x20) tstate |= KEMPSTON_D; + if (state & 0x40) tstate |= KEMPSTON_L; + if (state & 0x80) tstate |= KEMPSTON_R; + //printf("Joystick state %02x\n", tstate); + khan_set_joystick_state(tstate); +#endif + } + return; + case 4: + if (uart_is_readable_within_us(uart_default, 80)) { + uint __unused scancode = (uint8_t)uart_getc(uart_default); + } + if (uart_is_readable_within_us(uart_default, 80)) { + uint __unused scancode = (uint8_t)uart_getc(uart_default); + } + return; + } + + uint8_t __unused state = uart_getc(uart_default); + return; + } +#if USE_UART_KEYBOARD + if (b == 27) { + if (uart_is_readable_within_us(uart_default, 80)) { + uint n = uart_getc(uart_default); + if (uart_is_readable_within_us(uart_default, 80)) { + if (n == 91) { + esc = uart_getc(uart_default); + if (menu_key) { + switch (esc) + { + case 'A': + khan_menu_key(MK_UP); + break; + case 'B': + khan_menu_key(MK_DOWN); + break; + case 'C': + khan_menu_key(MK_RIGHT); + break; + case 'D': + khan_menu_key(MK_LEFT); + break; + } + return; + } + } + } else { + skip_key = true; + } + } else { + khan_menu_key(MK_ESCAPE); + return; + } + } + if (!menu_key) + { + uint8_t translated = UartTranslateKey(b, esc, &flags); + if (translated) + { + if (down_length) + { + // can only have one key down + khan_zx_key_event(last, last_flags); + } + last = translated; + last_flags = flags; + down_length = 1; + flags |= KF_DOWN; + khan_zx_key_event(last, flags); + } + } else { + if (b == '\r') khan_menu_key(MK_ENTER); + } +#endif + } else { +#ifdef USE_UART_KEYBOARD + if (down_length) { + if (++down_length == 10) { + khan_zx_key_event(last, last_flags); + down_length = 0; + } + } +#endif + } +} +#endif +#endif + +#ifndef NO_USE_BEEPER +#if !PICO_ON_DEVICE +void khan_beeper_setup() {} +void khan_beeper_reset() {} +void khan_beeper_enable() {} +void khan_beeper_level_change(uint32_t t, uint8_t level) {} +void khan_beeper_begin_frame(uint32_t t, int32_t frame_num) {} +void khan_beeper_end_frame(uint32_t t) {} +#else + +#define BEEPER_ON_PIO1 + +#ifdef BEEPER_ON_PIO1 +#define beeper_pio pio1 +#define BEEPER_GPIO_FUNC GPIO_FUNC_PIO1 +// why not +#define BEEPER_SM 0 +#define BEEPER_DREQ_PIO_TX0 DREQ_PIO1_TX0 +#else +#define beeper_pio pio0 +#define BEEPER_GPIO_FUNC GPIO_FUNC_PIO0 +#define BEEPER_SM 2 +#define BEEPER_DREQ_PIO_TX0 DREQ_PIO0_TX0 +#endif + +#ifndef PICO_AUDIO_PWM_L_PIN +#error need a pin for the beeper +#endif +#define BEEPER_OUTPUT_PIN PICO_AUDIO_PWM_L_PIN +#define BEEPER_PATTERN_LENGTH 16 +#define BEEPER_DMA_CHANNEL 3 +bi_decl(bi_1pin_with_name(BEEPER_OUTPUT_PIN, "Beeper (PWM)")); + +#define BEEPER_MAKE_CYCLE_RUN(pattern, count) (((pattern)<<16)|((count)-1)) +void beeper_dma_complete(); + +#define BEEPER_FRAME_DELAY 2u +#define BEEPER_BUFFER_SIZE_LOG2 9u +#define BEEPER_BUFFER_SIZE (1u << BEEPER_BUFFER_SIZE_LOG2) +#define BEEPER_BUFFER_SIZE_MASK (BEEPER_BUFFER_SIZE - 1u) + +// we want two queued, one active, and some jitter due to 50/60 pulldown and one as a buffer +#define BEEPER_FRAMES 4u + +static struct { +#ifndef HACK_NON_CIRCULAR + uint32_t __attribute__ ((aligned (BEEPER_BUFFER_SIZE * 4))) circular_buffer[BEEPER_BUFFER_SIZE]; +#else + uint32_t __attribute__ ((aligned (BEEPER_BUFFER_SIZE * 2 * 4))) circular_buffer[BEEPER_BUFFER_SIZE*2]; +#endif + volatile spin_lock_t *spin_lock; + struct { + struct beeper_frame { + int32_t frame_number; + uint16_t start_pos; + uint16_t data_length; + } frames[BEEPER_FRAMES]; + struct beeper_frame *write_frame; // the frame being written, or about to be + struct beeper_frame *read_frame; // the frame being played, or the next to be + int32_t next_write_frame_number; + uint16_t write_limit_pos; // the location that may be written to (we need to start discarding data) + uint8_t queued_sequential_frame_count; // not including the playing frame + uint8_t playing_frame_count; // 0 or 1 for whether we have a frame in use + } locked; + struct { + uint32_t last_t; + uint32_t net_cycles_remainder; +#ifndef NDEBUG + int32_t frame_tstates; +#endif +#ifdef HACK_NON_CIRCULAR + int32_t hack_non_circular_offset; +#endif + uint16_t write_ptr; + // this is an unlocked copy + uint16_t write_limit_pos; // the location that may be written to (we need to start discarding data) + uint16_t last_level; + } gen_thread; +} beeper_state; + +inline static void khan_beeper_start_dma_locked() { + beeper_assert(beeper_state.locked.queued_sequential_frame_count > 0); + beeper_assert(!beeper_state.locked.playing_frame_count); + beeper_assert(beeper_state.locked.read_frame->data_length > 0); + DEBUG_PINS_SET(beeper, 4); + DEBUG_PINS_CLR(beeper, 1); +// printf("begin dma from read_buffer frame %d %p off %d len %d %d\n", beeper_state.locked.read_frame->frame_number, beeper_state.locked.read_frame, beeper_state.locked.read_frame->start_pos, beeper_state.locked.read_frame->data_length, CORE_NUM); + const uint32_t *buffer = beeper_state.circular_buffer + beeper_state.locked.read_frame->start_pos; +// printf("%08x %04x\n", (intptr_t)buffer, beeper_state.locked.read_frame->data_length); + dma_channel_transfer_from_buffer_now(BEEPER_DMA_CHANNEL, (void *)buffer, beeper_state.locked.read_frame->data_length); + beeper_state.locked.queued_sequential_frame_count--; + beeper_state.locked.playing_frame_count = 1; + DEBUG_PINS_SET(beeper, 1); + DEBUG_PINS_CLR(beeper, 4); +} + +void khan_beeper_reset() { + uint32_t save = spin_lock_blocking(beeper_state.spin_lock); + + beeper_state.locked.write_frame = beeper_state.locked.read_frame; + if (beeper_state.locked.playing_frame_count) { + beeper_state.locked.write_frame++; + if (beeper_state.locked.write_frame == beeper_state.locked.frames + BEEPER_FRAMES) { + beeper_state.locked.write_frame = beeper_state.locked.frames; + } + } + beeper_state.locked.queued_sequential_frame_count = 0; + spin_unlock(beeper_state.spin_lock, save); +} + +void khan_beeper_begin_frame(uint32_t t, int32_t frame_num) { + DEBUG_PINS_SET(beeper, 1); +// printf("Begin frame %d %d\n", frame_num, CORE_NUM); + uint32_t save = spin_lock_blocking(beeper_state.spin_lock); + // may be overkill, but right now if we are out of frames or out of order, we discard everything (except what is playing if anything) and start over + // removed this since it doesn't seem to make much difference, so why waste code... we shouldn't be out of whack anyway unless we are running unsynced + // note also that we only shift the wrong audio data a frame or two in either direction anyway +// if ((beeper_state.locked.queued_sequential_frame_count > 0 && frame_num != beeper_state.locked.next_write_frame_number) || (beeper_state.locked.queued_sequential_frame_count == BEEPER_FRAMES - beeper_state.locked.playing_frame_count)) { + if (beeper_state.locked.queued_sequential_frame_count == BEEPER_FRAMES - beeper_state.locked.playing_frame_count) { +// printf("Clearing frame queued = %d, wfn = %d\n", beeper_state.locked.queued_sequential_frame_count, beeper_state.locked.next_write_frame_number); + // discard the last frame we generated - this will give us the shortest gap + beeper_state.locked.write_frame--; + if (beeper_state.locked.write_frame < beeper_state.locked.frames) { + beeper_state.locked.write_frame += BEEPER_FRAMES; + } + beeper_assert(beeper_state.locked.queued_sequential_frame_count>0); + beeper_state.gen_thread.write_ptr = beeper_state.locked.write_frame->start_pos; + beeper_state.locked.queued_sequential_frame_count--; + } + beeper_state.locked.write_frame->frame_number = frame_num; + beeper_state.locked.write_frame->start_pos = beeper_state.gen_thread.write_ptr; +#ifdef HACK_NON_CIRCULAR + beeper_state.gen_thread.hack_non_circular_offset = 0; +#endif +#ifndef NDEBUG + beeper_state.locked.write_frame->data_length = 0; +#endif + // copy limit into gen thread + beeper_state.gen_thread.write_limit_pos = beeper_state.locked.write_limit_pos; + if (!beeper_state.locked.playing_frame_count && beeper_state.locked.queued_sequential_frame_count >= BEEPER_FRAME_DELAY) { + // dma underflowed (or hadn't started) and we now have enough frames worth of audio (again) + khan_beeper_start_dma_locked(); + } + spin_unlock(beeper_state.spin_lock, save); + DEBUG_PINS_CLR(beeper, 1); + beeper_state.gen_thread.last_t = t; +} + +void khan_beeper_end_frame(uint32_t t) { +#if 0 +// printf("End frame %d %d\n", t, CORE_NUM); + for(int i=1; i<16; i++) { + khan_beeper_level_change((t*i)/16, i&1?200:0); + } + khan_beeper_level_change(t, 0); +#else + khan_beeper_level_change(t, beeper_state.gen_thread.last_level); +#endif + uint32_t save = spin_lock_blocking(beeper_state.spin_lock); + // finish out the write frame + if (beeper_state.gen_thread.write_ptr != beeper_state.locked.write_frame->start_pos) { + beeper_state.locked.write_frame->data_length = + (BEEPER_BUFFER_SIZE + beeper_state.gen_thread.write_ptr - beeper_state.locked.write_frame->start_pos) & + BEEPER_BUFFER_SIZE_MASK; +// printf("%04x %04x %04x\n", beeper_state.gen_thread.write_ptr, beeper_state.locked.write_frame->start_pos, beeper_state.locked.write_frame->data_length); + beeper_state.locked.next_write_frame_number = beeper_state.locked.write_frame->frame_number + 1; + // and update + beeper_state.locked.write_frame++; + if (beeper_state.locked.write_frame == beeper_state.locked.frames + BEEPER_FRAMES) { + beeper_state.locked.write_frame = beeper_state.locked.frames; + } + beeper_state.locked.queued_sequential_frame_count++; + } else { + // we were totally full + } + spin_unlock(beeper_state.spin_lock, save); +} + +// called when dma is complete +void khan_beeper_next_frame_needed() { + DEBUG_PINS_SET(beeper, 2); + uint32_t save = spin_lock_blocking(beeper_state.spin_lock); +// printf("Begin DMA needed %d\n", CORE_NUM); + beeper_assert(beeper_state.locked.playing_frame_count); + beeper_state.locked.playing_frame_count = 0; + + // move past frame we just finished playing + beeper_state.locked.write_limit_pos = (beeper_state.locked.write_limit_pos + beeper_state.locked.read_frame->data_length) & BEEPER_BUFFER_SIZE_MASK; + beeper_state.locked.read_frame++; + if (beeper_state.locked.read_frame == beeper_state.locked.frames + BEEPER_FRAMES) { + beeper_state.locked.read_frame = beeper_state.locked.frames; + } + + // if we have any queued sequential frames, then play the next one + if (beeper_state.locked.queued_sequential_frame_count > 0) { + beeper_assert(beeper_state.locked.write_limit_pos == ((beeper_state.locked.read_frame->start_pos + BEEPER_BUFFER_SIZE_MASK) & BEEPER_BUFFER_SIZE_MASK)); + khan_beeper_start_dma_locked(); + } + spin_unlock(beeper_state.spin_lock, save); + DEBUG_PINS_CLR(beeper, 2); +} + +/** + * + * start of frame (play a buffered frame if we have two sequential) and we aren't playing... also discard any out of sequence frames + * dma complete (play a buffered frame if we have one in sequence) otherwise underflow + * end of frame ... finish book-keeping + */ + +void khan_beeper_level_change(uint32_t t, uint8_t level) { +// printf("Level %d %d %d\n", t, level, CORE_NUM); + // our thread is the only one writing into the circular buffer + // todo right now loading a .z80 may cause this - actually shouldn't any longer + int32_t delta_t = t - beeper_state.gen_thread.last_t; + beeper_assert(delta_t >= 0); + + // div 3200 because they are all multiples, and it makes 32bit math ok + static const uint32_t tstates_per_second_div_3200 = 71680 * 50 / 3200; + static const uint32_t net_clock_divisor = tstates_per_second_div_3200 * (BEEPER_PATTERN_LENGTH * 2 + 5); + static const uint32_t clocks_per_second_div_3200 = CLOCK_MHZ / 3200; + static_assert((CLOCK_MHZ / 3200) * (uint64_t)(CLOCK_MHZ / 3200) <= 0x100000000LL, ""); + + uint32_t net_cycles_dividend = clocks_per_second_div_3200 * delta_t + beeper_state.gen_thread.net_cycles_remainder; + int32_t net_cycles = net_cycles_dividend / net_clock_divisor; + // todo timing still a little off; we're betting off being a little short + beeper_state.gen_thread.net_cycles_remainder = net_cycles_dividend % net_clock_divisor; +// pantso = true; +// if (pantso) printf("%d %d %d %d %d %d\n", skinker, t, delta_t, net_cycles, level, beeper_state.gen_thread.last_level); + + beeper_state.gen_thread.last_t = t; +// uint32_t pattern = beeper_state.gen_thread.last_level ? 0xfff : 0; // todo volume 65535 >> (__builtin_clz(level)-16); + uint32_t pattern = (1 << (beeper_state.gen_thread.last_level >> 4)) - 1; + + while (net_cycles > 0) { + uint32_t count = (net_cycles >> 16) ? 0x10000 : net_cycles; + if (beeper_state.gen_thread.write_ptr != beeper_state.gen_thread.write_limit_pos) { +#ifndef HACK_NON_CIRCULAR + beeper_state.circular_buffer[beeper_state.gen_thread.write_ptr] = BEEPER_MAKE_CYCLE_RUN(pattern, count); + beeper_state.gen_thread.write_ptr = (beeper_state.gen_thread.write_ptr + 1) & BEEPER_BUFFER_SIZE_MASK; +#else + beeper_state.circular_buffer[beeper_state.gen_thread.write_ptr + beeper_state.gen_thread.hack_non_circular_offset] = BEEPER_MAKE_CYCLE_RUN(pattern, count); + beeper_state.gen_thread.write_ptr = beeper_state.gen_thread.write_ptr + 1; + if (beeper_state.gen_thread.write_ptr == BEEPER_BUFFER_SIZE) { + assert(!beeper_state.gen_thread.hack_non_circular_offset); + beeper_state.gen_thread.hack_non_circular_offset = BEEPER_BUFFER_SIZE; + beeper_state.gen_thread.write_ptr = 0; + } +#endif + net_cycles -= count; + } else { + // underflow + //printf("pants\n"); + break; + } + } + beeper_state.gen_thread.last_level = level; +#ifndef NDEBUG + beeper_state.gen_thread.frame_tstates += delta_t; +#endif +} + +void __maybe_in_ram khan_beeper_setup() { + beeper_state.spin_lock = spin_lock_init(spin_lock_claim_unused(true)); + gpio_set_function(BEEPER_OUTPUT_PIN, BEEPER_GPIO_FUNC); + + // todo did this need to be at 22? +// int beeper_load_offset = 22; + uint beeper_load_offset = pio_add_program(beeper_pio, &beeper_program); + + pio_sm_config smc = beeper_program_get_default_config(beeper_load_offset); + sm_config_set_out_shift(&smc, true, false, BEEPER_PATTERN_LENGTH); + // todo i don't think we need set pins, so I didn't but they were there before + sm_config_set_out_pins(&smc, BEEPER_OUTPUT_PIN, 1); + pio_sm_init(beeper_pio, BEEPER_SM, beeper_load_offset, &smc); + + pio_sm_set_consecutive_pindirs(beeper_pio, BEEPER_SM, BEEPER_OUTPUT_PIN, 1, true); + pio_sm_set_pins(beeper_pio, BEEPER_SM, 0); +// pio_sm_exec(beeper_pio, BEEPER_SM, pio_encode_set(pio_pindirs, 1)); // Raise the output enable on bottom pin +// pio_sm_exec(beeper_pio, BEEPER_SM, pio_encode_set(pio_pins, 0)); // clear pin +// pio_sm_exec(beeper_pio, BEEPER_SM, pio_encode_jmp(beeper_offset_entry_point + beeper_load_offset)); // jmp to ep + +// int ring_bits = 33 - __builtin_clz(BEEPER_BUFFER_SIZE); +#ifndef HACK_NON_CIRCULAR + int ring_bits = BEEPER_BUFFER_SIZE_LOG2 + 2; +#else + int ring_bits = 0; +#endif + + dma_channel_config dcc = dma_channel_get_default_config(BEEPER_DMA_CHANNEL); + channel_config_set_dreq(&dcc, BEEPER_DREQ_PIO_TX0 + BEEPER_SM); + channel_config_set_ring(&dcc, false, ring_bits); + dma_channel_set_write_addr(BEEPER_DMA_CHANNEL, &beeper_pio->txf[BEEPER_SM], false); + dma_channel_set_config(BEEPER_DMA_CHANNEL, &dcc, false); + + irq_set_priority(DMA_IRQ_1, 0x80); // lower priority by 2 + + beeper_state.locked.read_frame = beeper_state.locked.write_frame = beeper_state.locked.frames; + beeper_state.locked.write_limit_pos = BEEPER_BUFFER_SIZE_MASK; +} + +void khan_beeper_enable() { + dma_channel_set_irq1_enabled(BEEPER_DMA_CHANNEL, 1); + pio_sm_set_enabled(beeper_pio, BEEPER_SM, true); + irq_set_enabled(DMA_IRQ_1, true); +} + +extern void delegate_pwm_irq(); + +//void __isr __time_critical beeper_dma_complete() +void __maybe_in_ram __isr isr_dma_1() +{ + // note we don't loop so as not to steal time from video (we can certainly wait for another irq) + if (dma_hw->ints1 & (1u << BEEPER_DMA_CHANNEL)) { + dma_hw->ints1 = 1u << BEEPER_DMA_CHANNEL; + khan_beeper_next_frame_needed(); + } +#ifndef NO_USE_AY + delegate_pwm_irq(); +#endif +} +#endif +#endif + +#ifdef USE_KHAN_GPIO +bool gpio_allowed(int gpio) { + return gpio >= PICO_DEBUG_PIN_BASE; +} + +uint8_t khan_gpio_read(uint reg) { + int gpio = (reg&0x1f); + if (gpio_allowed(reg)) { + return gpio_get(gpio); + } + return 0; +} + +void khan_gpio_write(uint reg, uint8_t value) { + int gpio = (reg&0x1fu); + if (gpio_allowed(gpio)) { + if (reg & 0x80) { + gpio_init(gpio); + gpio_set_dir(gpio, GPIO_OUT); + gpio_set_function(gpio, GPIO_FUNC_SIO); + } else { + gpio_put(gpio, value); + } + } +} +#endif + +#if USE_USB + +#define MAX_REPORT 4 +#define debug_printf(fmt,...) ((void)0) +//#define debug_printf printf + +// Each HID instance can has multiple reports +static struct +{ + uint8_t report_count; + tuh_hid_report_info_t report_info[MAX_REPORT]; +}hid_info[CFG_TUH_HID]; + +static void process_kbd_report(hid_keyboard_report_t const *report); +static void process_mouse_report(hid_mouse_report_t const * report); +static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len); + +// Invoked when device with hid interface is mounted +// Report descriptor is also available for use. tuh_hid_parse_report_descriptor() +// can be used to parse common/simple enough descriptor. +// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped +// therefore report_desc = NULL, desc_len = 0 +void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) +{ + debug_printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance); + + // Interface protocol (hid_interface_protocol_enum_t) + const char* protocol_str[] = { "None", "Keyboard", "Mouse" }; + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + debug_printf("HID Interface Protocol = %s\r\n", protocol_str[itf_protocol]); +// printf("%d USB: device %d connected, protocol %s\n", time_us_32() - t0 , dev_addr, protocol_str[itf_protocol]); + + // By default host stack will use activate boot protocol on supported interface. + // Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser) + if ( itf_protocol == HID_ITF_PROTOCOL_NONE ) + { + hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len); + debug_printf("HID has %u reports \r\n", hid_info[instance].report_count); + } + + // request to receive report + // tuh_hid_report_received_cb() will be invoked when report is available + if ( !tuh_hid_receive_report(dev_addr, instance) ) + { + debug_printf("Error: cannot request to receive report\r\n"); + } +} + +// Invoked when device with hid interface is un-mounted +void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) +{ + debug_printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance); + printf("USB: device %d disconnected\n", dev_addr); +} + +// Invoked when received report from device via interrupt endpoint +void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) +{ + uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); + + switch (itf_protocol) + { + case HID_ITF_PROTOCOL_KEYBOARD: + TU_LOG2("HID receive boot keyboard report\r\n"); + process_kbd_report( (hid_keyboard_report_t const*) report ); + break; + +#ifdef USE_USB_MOUSE + case HID_ITF_PROTOCOL_MOUSE: + TU_LOG2("HID receive boot mouse report\r\n"); + process_mouse_report( (hid_mouse_report_t const*) report ); + break; +#endif + + default: + // Generic report requires matching ReportID and contents with previous parsed report info + process_generic_report(dev_addr, instance, report, len); + break; + } + + // continue to request to receive report + if ( !tuh_hid_receive_report(dev_addr, instance) ) + { + debug_printf("Error: cannot request to receive report\r\n"); + } +} + +//--------------------------------------------------------------------+ +// Keyboard +//--------------------------------------------------------------------+ + +// look up new key in previous keys +static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode) +{ + for(uint8_t i=0; i<6; i++) + { + if (report->keycode[i] == keycode) return true; + } + + return false; +} + +static void process_kbd_report(hid_keyboard_report_t const *report) +{ + static hid_keyboard_report_t prev_report = { 0, 0, {0} }; // previous report to check key released + + // ctrl seems not to be used, so assigning it to be ALT as well + modifier_state = (report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT) ? KMOD_LSHIFT : 0) | + (report->modifier & (KEYBOARD_MODIFIER_RIGHTSHIFT) ? KMOD_RSHIFT : 0) | + (report->modifier & (KEYBOARD_MODIFIER_LEFTALT | KEYBOARD_MODIFIER_LEFTCTRL) ? KMOD_LALT : 0) | + (report->modifier & (KEYBOARD_MODIFIER_RIGHTALT | KEYBOARD_MODIFIER_RIGHTCTRL) ? KMOD_RALT : 0); + //------------- example code ignore control (non-printable) key affects -------------// + for(uint8_t i=0; i<6; i++) + { + if ( report->keycode[i] ) + { + if ( find_key_in_report(&prev_report, report->keycode[i]) ) + { + // exist in previous report means the current key is holding + }else + { + // not existed in previous report means the current key is pressed + khan_key_down(report->keycode[i], 0, modifier_state); +// bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT); +// pico_key_down(report->keycode[i], 0, is_shift ? WITH_SHIFT : 0); + } + } + // Check for key depresses (i.e. was present in prev report but not here) + if (prev_report.keycode[i]) { + // If not present in the current report then depressed + if (!find_key_in_report(report, prev_report.keycode[i])) + { +// bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT); +// pico_key_up(prev_report.keycode[i], 0, is_shift ? WITH_SHIFT : 0); + khan_key_up(prev_report.keycode[i], 0, modifier_state); + } + } + } + prev_report = *report; +} + +//--------------------------------------------------------------------+ +// Mouse +//--------------------------------------------------------------------+ + +#ifdef USE_USB_MOUSE +static void process_mouse_report(hid_mouse_report_t const * report) +{ + static hid_mouse_report_t prev_report = { 0 }; + + uint8_t button_changed_mask = report->buttons ^ prev_report.buttons; + if ( button_changed_mask & report->buttons) + { + debug_printf(" %c%c%c ", + report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-', + report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-', + report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-'); + } + +// cursor_movement(report->x, report->y, report->wheel); +} +#endif + +//--------------------------------------------------------------------+ +// Generic Report +//--------------------------------------------------------------------+ +static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) +{ + (void) dev_addr; + + uint8_t const rpt_count = hid_info[instance].report_count; + tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info; + tuh_hid_report_info_t* rpt_info = NULL; + + if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0) + { + // Simple report without report ID as 1st byte + rpt_info = &rpt_info_arr[0]; + }else + { + // Composite report, 1st byte is report ID, data starts from 2nd byte + uint8_t const rpt_id = report[0]; + + // Find report id in the arrray + for(uint8_t i=0; iusage_page == HID_USAGE_PAGE_DESKTOP ) + { + switch (rpt_info->usage) + { +#ifdef USE_USB_KEYBOARD + case HID_USAGE_DESKTOP_KEYBOARD: + TU_LOG1("HID receive keyboard report\r\n"); + // Assume keyboard follow boot report layout + process_kbd_report( (hid_keyboard_report_t const*) report ); + break; +#endif +#ifdef USE_USB_MOUSE + case HID_USAGE_DESKTOP_MOUSE: + TU_LOG1("HID receive mouse report\r\n"); + // Assume mouse follow boot report layout + process_mouse_report( (hid_mouse_report_t const*) report ); + break; +#endif + + default: break; + } + } +} + +void khan_check_usb(void) { + // todo not needed every time perhaps + tuh_task(); +} +#endif \ No newline at end of file diff --git a/khan/khan.pio b/khan/khan.pio new file mode 100644 index 0000000..ab87a57 --- /dev/null +++ b/khan/khan.pio @@ -0,0 +1,84 @@ +; +; Copyright (c) 2023 Graham Sanderson +; +; SPDX-License-Identifier: BSD-3-Clause +; + +; format || a | b || c0-7 || ... +; +; c0-c7 are each 4 bit +; +; main display: each 2 x 32 block displays 8 pixels at 4 cycles per pixel +; a is paper color +; b is ink color +; c0-c6 are "jmp paper" or "jmp ink" +; c7 is "jmp paper_end_of_block" or "jmp ink_end_of_block" +; +; border segments: 2 x 32 block does a single run of pixels +; a is color +; b is count + 3 +; c0 is "jmp color_run" +; +; end of line: 2 x 32 block ends (inefficiently) a scanline (and blanks the pins) +; c0 is "jmp end_of_line" +; c1-c7 must be 0 as they are outed onto pins (strictly c1-c4 hold the pixel value) +; + +.program video_khan +.origin 0 +public paper: +public delay1: + mov pins, x [2] + out pc, 4 + +public end_of_line: + out pins, 28 ; required to flush OSR and clear pins +public entry_point: + wait irq 4 + jmp new_block + +public ink: +public delay2: + mov pins, y [2] + out pc, 4 + +public color_run: ; of y + 3 +public delay3: + mov pins, x +color_run_loop: +public delay4: + jmp y-- color_run_loop [3] + out null, 28 ; // discard rest + jmp new_block [2] + +public paper_end_of_block: + mov pins, x +.wrap_target +new_block: +public delay5: + out x, 16 + out y, 16 + out pc, 4 + +public ink_end_of_block: + mov pins, y + .wrap + +// cannot have autopull +// runs of 1 bit audio where the 1 bit has volume!! it is output as is an N+2 bit pattern where N is the bit threshold of the SM ... +// the carrier frequency = CLK / (2 * (N+2)) +// note zeros are included beyond the pattern for N > 16 +.program beeper +cycle_loop: + nop [2] +.wrap_target + mov osr, y +bit_loop: + out pins, 1 + jmp !osre bit_loop + jmp x-- cycle_loop +public entry_point: + pull + out x, 16 // cycle count + out y, 16 // bit pattern +.wrap diff --git a/khan/khan_init.cpp b/khan/khan_init.cpp new file mode 100644 index 0000000..dda12d5 --- /dev/null +++ b/khan/khan_init.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "khan_init.h" +#include "khan.pio.h" +#include "../platform/platform.h" +#include "../tools/options.h" + +const struct scanvideo_pio_program video_khan = { +#if PICO_ON_DEVICE + .program = &video_khan_program, + .adapt_for_mode = video_khan_adapt_for_mode, + .configure_pio = video_khan_configure_pio +#else + .id = VIDEO_KHAN_PROGRAM_NAME +#endif +}; + +const struct scanvideo_pio_program video_khan54 = { +#if PICO_ON_DEVICE + .program = &video_khan_program, + .adapt_for_mode = video_khan_adapt_for_mode54, + .configure_pio = video_khan_configure_pio +#else + .id = VIDEO_KHAN_PROGRAM_NAME +#endif +}; + +const struct scanvideo_mode video_mode_khan = + { + .default_timing = &vga_timing_640x480_60_default, + .pio_program = &video_khan, + .width = 320, + .height = 240, + .xscale = 2, + .yscale = 2, + }; + +const struct scanvideo_mode video_mode_khan_tft = + { + .default_timing = &vga_timing_wide_480_50, + .pio_program = &video_khan, + .width = 400, + .height = 240, + .xscale = 2, + .yscale = 2, + }; + + +// this one is good actually on dells for 640*576 (use 5/4 for 512x576) +const struct scanvideo_timing video_timing_khan2 = + { + + .clock_freq = 24000000, + + .h_active = 640, + .v_active = 579, + + .h_front_porch = 16, + .h_pulse = 64, + .h_total = 800, + .h_sync_polarity = 1, + + .v_front_porch = 3, + .v_pulse = 10, + .v_total = 600, + .v_sync_polarity = 1, + + .enable_clock = 0, + .clock_polarity = 0, + + .enable_den = 0 + }; + + +const struct scanvideo_mode video_mode_khan2 = + { + .default_timing = &video_timing_khan2, + .pio_program = &video_khan54, + .width = 256, + .height = 193, + .xscale = 2, + .yscale = 3, + }; + +// this one is ok too, though may need vertical positioning (it is always cropped - presumably at 480 +const struct scanvideo_timing video_timing_khan3 = + { + + .clock_freq = 24000000, + + .h_active = 704, + .v_active = 526, + + .h_front_porch = 18, + .h_pulse = 70, + .h_total = 880, + .h_sync_polarity = 1, + + .v_front_porch = 3, + .v_pulse = 10, + .v_total = 545, + .v_sync_polarity = 1, + + .enable_clock = 0, + .clock_polarity = 0, + + .enable_den = 0 + }; + + +const struct scanvideo_mode video_mode_khan3 = + { + .default_timing = &video_timing_khan3, + .pio_program = &video_khan, + .width = 352, + .height = 263, + .xscale = 2, + .yscale = 2, + }; + + +#if PICO_ON_DEVICE +static uint32_t missing_scanline_data[] = { + 0x801fu | (0x83ffu << 16u), + 0, // to be filled in + 0x0000u | (0x0000u << 16u), + video_khan_offset_end_of_line +}; + +pio_sm_config video_khan_configure_pio(PIO pio, uint sm, uint offset) { + const int SCANLINE_SM = 0; + pio_sm_config config = video_khan_program_get_default_config(offset); + scanvideo_default_configure_pio(pio, sm, offset, &config, sm != SCANLINE_SM); + return config; +} + +extern uint32_t cmd_lookup[256]; + +bool video_khan_adapt_for_mode(const struct scanvideo_pio_program *program, const struct scanvideo_mode *mode, + struct scanvideo_scanline_buffer *missing_scanvideo_scanline_buffer, uint16_t *modifiable_instructions) { + missing_scanline_data[1] = cmd_lookup[0xaa]; + missing_scanvideo_scanline_buffer->data = missing_scanline_data; + missing_scanvideo_scanline_buffer->data_used = missing_scanvideo_scanline_buffer->data_max = sizeof(missing_scanline_data) / 4; +#if PICO_SCANVIDEO_PLANE_COUNT > 1 + missing_scanvideo_scanline_buffer->data2 = missing_scanline_data; + missing_scanvideo_scanline_buffer->data2_used = missing_scanvideo_scanline_buffer->data2_max = sizeof(missing_scanline_data) / 4; +#endif + return true; +} + +bool video_khan_adapt_for_mode54(const struct scanvideo_pio_program *program, const struct scanvideo_mode *mode, + struct scanvideo_scanline_buffer *missing_scanvideo_scanline_buffer, uint16_t *modifiable_instructions) { + assert(false);// we need the delay values + missing_scanline_data[1] = cmd_lookup[0xaa]; + missing_scanvideo_scanline_buffer->data = missing_scanline_data; + missing_scanvideo_scanline_buffer->data_used = missing_scanvideo_scanline_buffer->data_max = sizeof(missing_scanline_data) / 4; + return true; +} +#endif \ No newline at end of file diff --git a/khan/khan_init.h b/khan/khan_init.h new file mode 100644 index 0000000..95901ef --- /dev/null +++ b/khan/khan_init.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef KHAN_INIT_H +#define KHAN_INIT_H + +#include "pico/scanvideo.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//extern struct khan_image { +// const char *name; +// void (*worker)(); +//} images[]; +//extern const uint image_count; + +extern bool no_wait_for_vblank; +extern const struct scanvideo_mode video_mode_khan; + +#define VIDEO_KHAN_PROGRAM_NAME "video_khan" + +#if PICO_ON_DEVICE +pio_sm_config video_khan_configure_pio(PIO pio, uint sm, uint offset); +bool video_khan_adapt_for_mode(const struct scanvideo_pio_program *program, const struct scanvideo_mode *mode, + struct scanvideo_scanline_buffer *missing_scanvideo_scanline_buffer, + uint16_t *modifiable_instructions); +bool video_khan_adapt_for_mode54(const struct scanvideo_pio_program *program, const struct scanvideo_mode *mode, + struct scanvideo_scanline_buffer *missing_scanvideo_scanline_buffer, + uint16_t *modifiable_instructions); +#endif + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/khan/khan_lib.cpp b/khan/khan_lib.cpp new file mode 100644 index 0000000..21c93e8 --- /dev/null +++ b/khan/khan_lib.cpp @@ -0,0 +1,807 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "khan_lib.h" +#include "pico/scanvideo.h" +#if PICO_ON_DEVICE +#include "z80khan.h" +#endif +#include "khan_init.h" + +#if PICO_ON_DEVICE +#include "sdl_keys.h" +#else +#include +#endif + +struct menu_state kms; + +#include "../platform/platform.h" +#include "../platform/io.h" +#include "../options_common.h" +#include "../speccy.h" +#include "../devices/memory.h" +#include "../devices/ula.h" +#include "../z80/z80.h" +#include "../file_type.h" +#include "../tools/options.h" +#include "games/games.h" + +#ifndef NO_USE_AY +#include "../devices/sound/ay.h" +#endif + +#include "khan_lib.h" + +bool no_wait_for_vblank; + + +extern void spoono(); +//eMemory memory; +//eDevices devices; +//xZ80::eZ80* cpu; + +int frame_tacts; + +void khan_reset() { +} + +void khan_zx_key_event(uint8_t key, uint8_t flags) { + //printf("onkey %02x %02x\n", key, flags); + xPlatform::Handler()->OnKey(key, flags); +} + +bool last_button_state; +static __maybe_in_ram void khan_button_state(bool down) { + if (down && !last_button_state) { + xPlatform::Handler()->OnKey('e', xPlatform::KF_DOWN); + } else if (!down && last_button_state) { + xPlatform::Handler()->OnKey('e', 0); + } + last_button_state = down; +} + +//#define REGENERATE_COLORS +#ifdef REGENERATE_COLORS +static uint32_t colors[512]; +static uint32_t dark_colors[512]; +#else +// todo const - need space for hobbit atm +static const uint32_t colors[512] = { + 0x00200020, 0xc8200020, 0x00390020, 0xc8390020, 0x06600020, 0xce600020, 0x06790020, 0xce790020, + 0x0020c820, 0xc820c820, 0x0039c820, 0xc839c820, 0x0660c820, 0xce60c820, 0x0679c820, 0xce79c820, + 0x00200039, 0xc8200039, 0x00390039, 0xc8390039, 0x06600039, 0xce600039, 0x06790039, 0xce790039, + 0x0020c839, 0xc820c839, 0x0039c839, 0xc839c839, 0x0660c839, 0xce60c839, 0x0679c839, 0xce79c839, + 0x00200660, 0xc8200660, 0x00390660, 0xc8390660, 0x06600660, 0xce600660, 0x06790660, 0xce790660, + 0x0020ce60, 0xc820ce60, 0x0039ce60, 0xc839ce60, 0x0660ce60, 0xce60ce60, 0x0679ce60, 0xce79ce60, + 0x00200679, 0xc8200679, 0x00390679, 0xc8390679, 0x06600679, 0xce600679, 0x06790679, 0xce790679, + 0x0020ce79, 0xc820ce79, 0x0039ce79, 0xc839ce79, 0x0660ce79, 0xce60ce79, 0x0679ce79, 0xce79ce79, + 0x00200020, 0xf8200020, 0x003f0020, 0xf83f0020, 0x07e00020, 0xffe00020, 0x07ff0020, 0xffff0020, + 0x0020f820, 0xf820f820, 0x003ff820, 0xf83ff820, 0x07e0f820, 0xffe0f820, 0x07fff820, 0xfffff820, + 0x0020003f, 0xf820003f, 0x003f003f, 0xf83f003f, 0x07e0003f, 0xffe0003f, 0x07ff003f, 0xffff003f, + 0x0020f83f, 0xf820f83f, 0x003ff83f, 0xf83ff83f, 0x07e0f83f, 0xffe0f83f, 0x07fff83f, 0xfffff83f, + 0x002007e0, 0xf82007e0, 0x003f07e0, 0xf83f07e0, 0x07e007e0, 0xffe007e0, 0x07ff07e0, 0xffff07e0, + 0x0020ffe0, 0xf820ffe0, 0x003fffe0, 0xf83fffe0, 0x07e0ffe0, 0xffe0ffe0, 0x07ffffe0, 0xffffffe0, + 0x002007ff, 0xf82007ff, 0x003f07ff, 0xf83f07ff, 0x07e007ff, 0xffe007ff, 0x07ff07ff, 0xffff07ff, + 0x0020ffff, 0xf820ffff, 0x003fffff, 0xf83fffff, 0x07e0ffff, 0xffe0ffff, 0x07ffffff, 0xffffffff, + 0x00200020, 0xc8200020, 0x00390020, 0xc8390020, 0x06600020, 0xce600020, 0x06790020, 0xce790020, + 0x0020c820, 0xc820c820, 0x0039c820, 0xc839c820, 0x0660c820, 0xce60c820, 0x0679c820, 0xce79c820, + 0x00200039, 0xc8200039, 0x00390039, 0xc8390039, 0x06600039, 0xce600039, 0x06790039, 0xce790039, + 0x0020c839, 0xc820c839, 0x0039c839, 0xc839c839, 0x0660c839, 0xce60c839, 0x0679c839, 0xce79c839, + 0x00200660, 0xc8200660, 0x00390660, 0xc8390660, 0x06600660, 0xce600660, 0x06790660, 0xce790660, + 0x0020ce60, 0xc820ce60, 0x0039ce60, 0xc839ce60, 0x0660ce60, 0xce60ce60, 0x0679ce60, 0xce79ce60, + 0x00200679, 0xc8200679, 0x00390679, 0xc8390679, 0x06600679, 0xce600679, 0x06790679, 0xce790679, + 0x0020ce79, 0xc820ce79, 0x0039ce79, 0xc839ce79, 0x0660ce79, 0xce60ce79, 0x0679ce79, 0xce79ce79, + 0x00200020, 0xf8200020, 0x003f0020, 0xf83f0020, 0x07e00020, 0xffe00020, 0x07ff0020, 0xffff0020, + 0x0020f820, 0xf820f820, 0x003ff820, 0xf83ff820, 0x07e0f820, 0xffe0f820, 0x07fff820, 0xfffff820, + 0x0020003f, 0xf820003f, 0x003f003f, 0xf83f003f, 0x07e0003f, 0xffe0003f, 0x07ff003f, 0xffff003f, + 0x0020f83f, 0xf820f83f, 0x003ff83f, 0xf83ff83f, 0x07e0f83f, 0xffe0f83f, 0x07fff83f, 0xfffff83f, + 0x002007e0, 0xf82007e0, 0x003f07e0, 0xf83f07e0, 0x07e007e0, 0xffe007e0, 0x07ff07e0, 0xffff07e0, + 0x0020ffe0, 0xf820ffe0, 0x003fffe0, 0xf83fffe0, 0x07e0ffe0, 0xffe0ffe0, 0x07ffffe0, 0xffffffe0, + 0x002007ff, 0xf82007ff, 0x003f07ff, 0xf83f07ff, 0x07e007ff, 0xffe007ff, 0x07ff07ff, 0xffff07ff, + 0x0020ffff, 0xf820ffff, 0x003fffff, 0xf83fffff, 0x07e0ffff, 0xffe0ffff, 0x07ffffff, 0xffffffff, + 0x00200020, 0xc8200020, 0x00390020, 0xc8390020, 0x06600020, 0xce600020, 0x06790020, 0xce790020, + 0x0020c820, 0xc820c820, 0x0039c820, 0xc839c820, 0x0660c820, 0xce60c820, 0x0679c820, 0xce79c820, + 0x00200039, 0xc8200039, 0x00390039, 0xc8390039, 0x06600039, 0xce600039, 0x06790039, 0xce790039, + 0x0020c839, 0xc820c839, 0x0039c839, 0xc839c839, 0x0660c839, 0xce60c839, 0x0679c839, 0xce79c839, + 0x00200660, 0xc8200660, 0x00390660, 0xc8390660, 0x06600660, 0xce600660, 0x06790660, 0xce790660, + 0x0020ce60, 0xc820ce60, 0x0039ce60, 0xc839ce60, 0x0660ce60, 0xce60ce60, 0x0679ce60, 0xce79ce60, + 0x00200679, 0xc8200679, 0x00390679, 0xc8390679, 0x06600679, 0xce600679, 0x06790679, 0xce790679, + 0x0020ce79, 0xc820ce79, 0x0039ce79, 0xc839ce79, 0x0660ce79, 0xce60ce79, 0x0679ce79, 0xce79ce79, + 0x00200020, 0xf8200020, 0x003f0020, 0xf83f0020, 0x07e00020, 0xffe00020, 0x07ff0020, 0xffff0020, + 0x0020f820, 0xf820f820, 0x003ff820, 0xf83ff820, 0x07e0f820, 0xffe0f820, 0x07fff820, 0xfffff820, + 0x0020003f, 0xf820003f, 0x003f003f, 0xf83f003f, 0x07e0003f, 0xffe0003f, 0x07ff003f, 0xffff003f, + 0x0020f83f, 0xf820f83f, 0x003ff83f, 0xf83ff83f, 0x07e0f83f, 0xffe0f83f, 0x07fff83f, 0xfffff83f, + 0x002007e0, 0xf82007e0, 0x003f07e0, 0xf83f07e0, 0x07e007e0, 0xffe007e0, 0x07ff07e0, 0xffff07e0, + 0x0020ffe0, 0xf820ffe0, 0x003fffe0, 0xf83fffe0, 0x07e0ffe0, 0xffe0ffe0, 0x07ffffe0, 0xffffffe0, + 0x002007ff, 0xf82007ff, 0x003f07ff, 0xf83f07ff, 0x07e007ff, 0xffe007ff, 0x07ff07ff, 0xffff07ff, + 0x0020ffff, 0xf820ffff, 0x003fffff, 0xf83fffff, 0x07e0ffff, 0xffe0ffff, 0x07ffffff, 0xffffffff, + 0x00200020, 0x0020c820, 0x00200039, 0x0020c839, 0x00200660, 0x0020ce60, 0x00200679, 0x0020ce79, + 0xc8200020, 0xc820c820, 0xc8200039, 0xc820c839, 0xc8200660, 0xc820ce60, 0xc8200679, 0xc820ce79, + 0x00390020, 0x0039c820, 0x00390039, 0x0039c839, 0x00390660, 0x0039ce60, 0x00390679, 0x0039ce79, + 0xc8390020, 0xc839c820, 0xc8390039, 0xc839c839, 0xc8390660, 0xc839ce60, 0xc8390679, 0xc839ce79, + 0x06600020, 0x0660c820, 0x06600039, 0x0660c839, 0x06600660, 0x0660ce60, 0x06600679, 0x0660ce79, + 0xce600020, 0xce60c820, 0xce600039, 0xce60c839, 0xce600660, 0xce60ce60, 0xce600679, 0xce60ce79, + 0x06790020, 0x0679c820, 0x06790039, 0x0679c839, 0x06790660, 0x0679ce60, 0x06790679, 0x0679ce79, + 0xce790020, 0xce79c820, 0xce790039, 0xce79c839, 0xce790660, 0xce79ce60, 0xce790679, 0xce79ce79, + 0x00200020, 0x0020f820, 0x0020003f, 0x0020f83f, 0x002007e0, 0x0020ffe0, 0x002007ff, 0x0020ffff, + 0xf8200020, 0xf820f820, 0xf820003f, 0xf820f83f, 0xf82007e0, 0xf820ffe0, 0xf82007ff, 0xf820ffff, + 0x003f0020, 0x003ff820, 0x003f003f, 0x003ff83f, 0x003f07e0, 0x003fffe0, 0x003f07ff, 0x003fffff, + 0xf83f0020, 0xf83ff820, 0xf83f003f, 0xf83ff83f, 0xf83f07e0, 0xf83fffe0, 0xf83f07ff, 0xf83fffff, + 0x07e00020, 0x07e0f820, 0x07e0003f, 0x07e0f83f, 0x07e007e0, 0x07e0ffe0, 0x07e007ff, 0x07e0ffff, + 0xffe00020, 0xffe0f820, 0xffe0003f, 0xffe0f83f, 0xffe007e0, 0xffe0ffe0, 0xffe007ff, 0xffe0ffff, + 0x07ff0020, 0x07fff820, 0x07ff003f, 0x07fff83f, 0x07ff07e0, 0x07ffffe0, 0x07ff07ff, 0x07ffffff, + 0xffff0020, 0xfffff820, 0xffff003f, 0xfffff83f, 0xffff07e0, 0xffffffe0, 0xffff07ff, 0xffffffff, +}; +static uint32_t dark_colors[512] = { + 0x00200020, 0xc8200020, 0x00390020, 0xc8390020, 0x06600020, 0xce600020, 0x06790020, 0xce790020, + 0x0020c820, 0xc820c820, 0x0039c820, 0xc839c820, 0x0660c820, 0xce60c820, 0x0679c820, 0xce79c820, + 0x00200039, 0xc8200039, 0x00390039, 0xc8390039, 0x06600039, 0xce600039, 0x06790039, 0xce790039, + 0x0020c839, 0xc820c839, 0x0039c839, 0xc839c839, 0x0660c839, 0xce60c839, 0x0679c839, 0xce79c839, + 0x00200660, 0xc8200660, 0x00390660, 0xc8390660, 0x06600660, 0xce600660, 0x06790660, 0xce790660, + 0x0020ce60, 0xc820ce60, 0x0039ce60, 0xc839ce60, 0x0660ce60, 0xce60ce60, 0x0679ce60, 0xce79ce60, + 0x00200679, 0xc8200679, 0x00390679, 0xc8390679, 0x06600679, 0xce600679, 0x06790679, 0xce790679, + 0x0020ce79, 0xc820ce79, 0x0039ce79, 0xc839ce79, 0x0660ce79, 0xce60ce79, 0x0679ce79, 0xce79ce79, + 0x00200020, 0xf8200020, 0x003f0020, 0xf83f0020, 0x07e00020, 0xffe00020, 0x07ff0020, 0xffff0020, + 0x0020f820, 0xf820f820, 0x003ff820, 0xf83ff820, 0x07e0f820, 0xffe0f820, 0x07fff820, 0xfffff820, + 0x0020003f, 0xf820003f, 0x003f003f, 0xf83f003f, 0x07e0003f, 0xffe0003f, 0x07ff003f, 0xffff003f, + 0x0020f83f, 0xf820f83f, 0x003ff83f, 0xf83ff83f, 0x07e0f83f, 0xffe0f83f, 0x07fff83f, 0xfffff83f, + 0x002007e0, 0xf82007e0, 0x003f07e0, 0xf83f07e0, 0x07e007e0, 0xffe007e0, 0x07ff07e0, 0xffff07e0, + 0x0020ffe0, 0xf820ffe0, 0x003fffe0, 0xf83fffe0, 0x07e0ffe0, 0xffe0ffe0, 0x07ffffe0, 0xffffffe0, + 0x002007ff, 0xf82007ff, 0x003f07ff, 0xf83f07ff, 0x07e007ff, 0xffe007ff, 0x07ff07ff, 0xffff07ff, + 0x0020ffff, 0xf820ffff, 0x003fffff, 0xf83fffff, 0x07e0ffff, 0xffe0ffff, 0x07ffffff, 0xffffffff, + 0x00200020, 0xc8200020, 0x00390020, 0xc8390020, 0x06600020, 0xce600020, 0x06790020, 0xce790020, + 0x0020c820, 0xc820c820, 0x0039c820, 0xc839c820, 0x0660c820, 0xce60c820, 0x0679c820, 0xce79c820, + 0x00200039, 0xc8200039, 0x00390039, 0xc8390039, 0x06600039, 0xce600039, 0x06790039, 0xce790039, + 0x0020c839, 0xc820c839, 0x0039c839, 0xc839c839, 0x0660c839, 0xce60c839, 0x0679c839, 0xce79c839, + 0x00200660, 0xc8200660, 0x00390660, 0xc8390660, 0x06600660, 0xce600660, 0x06790660, 0xce790660, + 0x0020ce60, 0xc820ce60, 0x0039ce60, 0xc839ce60, 0x0660ce60, 0xce60ce60, 0x0679ce60, 0xce79ce60, + 0x00200679, 0xc8200679, 0x00390679, 0xc8390679, 0x06600679, 0xce600679, 0x06790679, 0xce790679, + 0x0020ce79, 0xc820ce79, 0x0039ce79, 0xc839ce79, 0x0660ce79, 0xce60ce79, 0x0679ce79, 0xce79ce79, + 0x00200020, 0xf8200020, 0x003f0020, 0xf83f0020, 0x07e00020, 0xffe00020, 0x07ff0020, 0xffff0020, + 0x0020f820, 0xf820f820, 0x003ff820, 0xf83ff820, 0x07e0f820, 0xffe0f820, 0x07fff820, 0xfffff820, + 0x0020003f, 0xf820003f, 0x003f003f, 0xf83f003f, 0x07e0003f, 0xffe0003f, 0x07ff003f, 0xffff003f, + 0x0020f83f, 0xf820f83f, 0x003ff83f, 0xf83ff83f, 0x07e0f83f, 0xffe0f83f, 0x07fff83f, 0xfffff83f, + 0x002007e0, 0xf82007e0, 0x003f07e0, 0xf83f07e0, 0x07e007e0, 0xffe007e0, 0x07ff07e0, 0xffff07e0, + 0x0020ffe0, 0xf820ffe0, 0x003fffe0, 0xf83fffe0, 0x07e0ffe0, 0xffe0ffe0, 0x07ffffe0, 0xffffffe0, + 0x002007ff, 0xf82007ff, 0x003f07ff, 0xf83f07ff, 0x07e007ff, 0xffe007ff, 0x07ff07ff, 0xffff07ff, + 0x0020ffff, 0xf820ffff, 0x003fffff, 0xf83fffff, 0x07e0ffff, 0xffe0ffff, 0x07ffffff, 0xffffffff, + 0x00200020, 0xc8200020, 0x00390020, 0xc8390020, 0x06600020, 0xce600020, 0x06790020, 0xce790020, + 0x0020c820, 0xc820c820, 0x0039c820, 0xc839c820, 0x0660c820, 0xce60c820, 0x0679c820, 0xce79c820, + 0x00200039, 0xc8200039, 0x00390039, 0xc8390039, 0x06600039, 0xce600039, 0x06790039, 0xce790039, + 0x0020c839, 0xc820c839, 0x0039c839, 0xc839c839, 0x0660c839, 0xce60c839, 0x0679c839, 0xce79c839, + 0x00200660, 0xc8200660, 0x00390660, 0xc8390660, 0x06600660, 0xce600660, 0x06790660, 0xce790660, + 0x0020ce60, 0xc820ce60, 0x0039ce60, 0xc839ce60, 0x0660ce60, 0xce60ce60, 0x0679ce60, 0xce79ce60, + 0x00200679, 0xc8200679, 0x00390679, 0xc8390679, 0x06600679, 0xce600679, 0x06790679, 0xce790679, + 0x0020ce79, 0xc820ce79, 0x0039ce79, 0xc839ce79, 0x0660ce79, 0xce60ce79, 0x0679ce79, 0xce79ce79, + 0x00200020, 0xf8200020, 0x003f0020, 0xf83f0020, 0x07e00020, 0xffe00020, 0x07ff0020, 0xffff0020, + 0x0020f820, 0xf820f820, 0x003ff820, 0xf83ff820, 0x07e0f820, 0xffe0f820, 0x07fff820, 0xfffff820, + 0x0020003f, 0xf820003f, 0x003f003f, 0xf83f003f, 0x07e0003f, 0xffe0003f, 0x07ff003f, 0xffff003f, + 0x0020f83f, 0xf820f83f, 0x003ff83f, 0xf83ff83f, 0x07e0f83f, 0xffe0f83f, 0x07fff83f, 0xfffff83f, + 0x002007e0, 0xf82007e0, 0x003f07e0, 0xf83f07e0, 0x07e007e0, 0xffe007e0, 0x07ff07e0, 0xffff07e0, + 0x0020ffe0, 0xf820ffe0, 0x003fffe0, 0xf83fffe0, 0x07e0ffe0, 0xffe0ffe0, 0x07ffffe0, 0xffffffe0, + 0x002007ff, 0xf82007ff, 0x003f07ff, 0xf83f07ff, 0x07e007ff, 0xffe007ff, 0x07ff07ff, 0xffff07ff, + 0x0020ffff, 0xf820ffff, 0x003fffff, 0xf83fffff, 0x07e0ffff, 0xffe0ffff, 0x07ffffff, 0xffffffff, + 0x00200020, 0x0020c820, 0x00200039, 0x0020c839, 0x00200660, 0x0020ce60, 0x00200679, 0x0020ce79, + 0xc8200020, 0xc820c820, 0xc8200039, 0xc820c839, 0xc8200660, 0xc820ce60, 0xc8200679, 0xc820ce79, + 0x00390020, 0x0039c820, 0x00390039, 0x0039c839, 0x00390660, 0x0039ce60, 0x00390679, 0x0039ce79, + 0xc8390020, 0xc839c820, 0xc8390039, 0xc839c839, 0xc8390660, 0xc839ce60, 0xc8390679, 0xc839ce79, + 0x06600020, 0x0660c820, 0x06600039, 0x0660c839, 0x06600660, 0x0660ce60, 0x06600679, 0x0660ce79, + 0xce600020, 0xce60c820, 0xce600039, 0xce60c839, 0xce600660, 0xce60ce60, 0xce600679, 0xce60ce79, + 0x06790020, 0x0679c820, 0x06790039, 0x0679c839, 0x06790660, 0x0679ce60, 0x06790679, 0x0679ce79, + 0xce790020, 0xce79c820, 0xce790039, 0xce79c839, 0xce790660, 0xce79ce60, 0xce790679, 0xce79ce79, + 0x00200020, 0x0020f820, 0x0020003f, 0x0020f83f, 0x002007e0, 0x0020ffe0, 0x002007ff, 0x0020ffff, + 0xf8200020, 0xf820f820, 0xf820003f, 0xf820f83f, 0xf82007e0, 0xf820ffe0, 0xf82007ff, 0xf820ffff, + 0x003f0020, 0x003ff820, 0x003f003f, 0x003ff83f, 0x003f07e0, 0x003fffe0, 0x003f07ff, 0x003fffff, + 0xf83f0020, 0xf83ff820, 0xf83f003f, 0xf83ff83f, 0xf83f07e0, 0xf83fffe0, 0xf83f07ff, 0xf83fffff, + 0x07e00020, 0x07e0f820, 0x07e0003f, 0x07e0f83f, 0x07e007e0, 0x07e0ffe0, 0x07e007ff, 0x07e0ffff, + 0xffe00020, 0xffe0f820, 0xffe0003f, 0xffe0f83f, 0xffe007e0, 0xffe0ffe0, 0xffe007ff, 0xffe0ffff, + 0x07ff0020, 0x07fff820, 0x07ff003f, 0x07fff83f, 0x07ff07e0, 0x07ffffe0, 0x07ff07ff, 0x07ffffff, + 0xffff0020, 0xfffff820, 0xffff003f, 0xfffff83f, 0xffff07e0, 0xffffffe0, 0xffff07ff, 0xffffffff, +}; +#endif +static int color_toggle = 0; + +#define SPECTRUM_BRIGHTNESS(c) (200 + (((c)&8)?55:0)) +#define SPECTRUM_TO_RGB(c) MAKE_RGB(((c)&2) ? SPECTRUM_BRIGHTNESS(c) : 0, ((c)&4) ? SPECTRUM_BRIGHTNESS(c) : 0, ((c)&1) ? SPECTRUM_BRIGHTNESS(c) : 0) + +static struct eOptionImage : public xOptions::eOptionIntWithPending +{ + eOptionImage() { value = -1; } + virtual const char* Name() const override { +#ifndef USE_MU + return "image"; +#else + return "Image"; +#endif + } + + virtual int Order() const override { return 54; } + + const char *Value() const override { + int v = *this; + if (v >= 0 && v<(int)embedded_game_count) { + return embedded_games[v].name; + } + return "none"; + } + + virtual void Complete(bool accept) override + { + assert(is_change_pending); + if (accept && pending_value != value) { + SetNow(pending_value); + } + is_change_pending = false; + } + + void SetNow(const int &v) override + { + int old_value = value; + eOptionIntWithPending::SetNow(v); + if (value != old_value && value >= -1 && value<=(int)embedded_game_count) { + if (value == -1) { + xPlatform::Handler()->OnAction(xPlatform::A_RESET); + } else { + const embedded_game_t *g = embedded_games + value; +#ifndef NO_USE_FAST_TAPE + xOptions::eOptionBool::Find("Fast tape")->Set((g->flags & GF_SLOW_TAPE) == 0); +#endif + no_wait_for_vblank = (g->flags & GF_NO_WAIT_VBLANK) != 0; + xPlatform::Handler()->OnOpenFileCompressed(g->ext, g->data_z, g->data_z_size, g->data_size); + } + } + } + + void Change(bool next) override + { + eOptionInt::Change(-1, embedded_game_count, next); + } +} op_image; + +int khan_init() +{ + mutex_init(&kms.mutex); +#if !PICO_ON_DEVICE + static char buf[1024]; + if (!getcwd(buf, sizeof(buf))) + return false; + strcat(buf, "/"); + xIo::SetResourcePath(buf); +#endif +#ifndef NO_USE_128K + xOptions::eOptionBool::Find("48K mode")->Set(false); +#endif + xPlatform::Handler()->OnInit(); +// xPlatform::Handler()->Speccy()->CPU()->set_breakpoint(0xe7); //0x6EB1); //0x6D31); + // + op_image.SetNow(embedded_game_default); // actually select image + +#if !PICO_ON_DEVICE + +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/tests/z80tests.tap")); + +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/z80full.tap")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/Manic_Miner_1983_Software_Projects.z80")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/Jet Set Willy (R D Foord Software).tzx")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/knightlore.sna")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/g+g.sna")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/JETSET.TAP")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/3D_Pacman_1983_Freddy_Kristiansen.z80")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/Donkey_Kong_1986_Ocean.z80")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/Hobbit_The_v1.2_1982_Melbourne_House_a.z80")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/Star_Wars_1987_Domark.z80")); +// xPlatform::Handler()->OnOpenFile(xIo::ResourcePath("res/The Sentinel.tzx")); + platform_key_down = khan_key_down; + platform_key_up = khan_key_up; +#endif +#ifdef REGENERATE_COLORS + // make colortab: zx-attr -> pc-attr + for(int a = 0; a < 0x100; a++) { + byte ink = a & 7; + byte paper = (a >> 3) & 7; + byte bright = (a >> 6) & 1; + byte flash = (a >> 7) & 1; + if (ink) + ink |= bright << 3; // no bright for 0th color + if (paper) + paper |= bright << 3; // no bright for 0th color +// if (ink == paper) { +// ink = paper = 0; +// } + colors[a] = (SPECTRUM_TO_RGB(ink) << 16) | SPECTRUM_TO_RGB(paper); + if (flash) { + colors[a + 256] = SPECTRUM_TO_RGB(ink) | (SPECTRUM_TO_RGB(paper) << 16); + } else { + colors[a + 256] = colors[a]; + } + dark_colors[a] = colors[a]; + dark_colors[a + 256] = colors[a+256]; + } + printf("static uint32_t colors[512] = {\n"); + for(int i=0;i<0x200;i+=8) { + printf("\t"); + for(int j=0;j<8;j++) { + printf("0x%08x, ", colors[i+j]); + } + printf("\n"); + } + printf("};\n"); + printf("static uint32_t dark_colors[512] = {\n"); + for(int i=0;i<0x200;i+=8) { + printf("\t"); + for(int j=0;j<8;j++) { + printf("0x%08x, ", colors[i+j]); + } + printf("\n"); + } + printf("};\n"); +#endif +#if !PICO_ON_DEVICE + spoono(); +#endif + return 0; +} + + +void khan_done() { + xPlatform::Handler()->OnDone(); +} + +static int frame; +static int frame2; +static bool do_pulldown56; + +static bool option_next, option_complete; +static xOptions::eOptionB* option_needed; + +void __maybe_in_ram khan_loop() +{ +#if defined(USE_BANKED_MEMORY_ACCESS) && PICO_ON_DEVICE + xPlatform::Handler()->Speccy()->CPU()->InitMemoryBanks(); +#endif + while (true) { + if (!xPlatform::Handler()->IsVideoPaused()) { + if (do_pulldown56 && ++frame2 >= 6) { + frame2 = 0; + } else { + khan_cb_begin_frame(); + xPlatform::Handler()->OnLoop(); + if (++frame >= 15) { + frame = 0; + color_toggle ^= 256; + } + khan_cb_end_frame(); + } + } + if (!no_wait_for_vblank) scanvideo_wait_for_vblank(); + khan_button_state(khan_cb_is_button_down()); + xOptions::eOptionB *option; + bool next; + + mutex_enter_blocking(&kms.mutex); + option = option_needed; + next = option_next; + option_needed = NULL; + if (xPlatform::OpTapeRefresh()) { + kms.do_fill_menu = true; + } + if (option) { + if (option_complete) + { + // switch images is slow... we block this thread anyway, but releasing the mutex allows the video thread to still update the screen!! + mutex_exit(&kms.mutex); + option->Complete(); + mutex_enter_blocking(&kms.mutex); + kms.do_hide_menu = true; + kms.flashing = false; + } else { + const char *v = option->Value(); + option->Change(next); + // bit of a hack to detect unchangable values + if (v == option->Value()) { + kms.error_level = MENU_ERROR_LEVEL_MAX; + } + if (option->IsAction()) + { + kms.do_hide_menu = true; + kms.flashing = false; + } + else + { + kms.do_fill_menu = true; + } + } + } + mutex_exit(&kms.mutex); + } +} + +int __maybe_in_ram khan_go(int video_mode_hz) { + do_pulldown56 = video_mode_hz == 60; + if (do_pulldown56) { + OpVsyncRate(xPlatform::VR_5060); + } else if (video_mode_hz == 50) { + OpVsyncRate(xPlatform::VR_50); + } else { + OpVsyncRate(xPlatform::VR_WRONG); + } + khan_loop(); + khan_done(); + return 0; +} + +bool __maybe_in_ram khan_get_scanline_info(int l, uint16_t* border, const uint8_t ** attr, const uint8_t ** pixels, const uint32_t **attr_colors, const uint32_t **dark_attr_colors) { + eUla *ula = xPlatform::Handler()->Speccy()->Devices().Get(); + uint8_t bborder; + ula->GetLineInfo(l, bborder, *attr, *pixels); + *border = colors[bborder&0xf]>>16; + *attr_colors = colors + color_toggle; + if (kms.opacity) { + *dark_attr_colors = dark_colors + color_toggle; + } + return true; +} + +using xOptions::eOptionB; + +void __maybe_in_ram khan_fill_main_menu() { + kms.num_lines = 0; + for(eOptionB* o = eOptionB::First(); o; o = o->Next()) { + kms.lines[kms.num_lines].left_text.text = o->Name(); + kms.lines[kms.num_lines].right_text.text = o->Value(); + kms.lines[kms.num_lines].left_text.width = 0; + kms.lines[kms.num_lines].right_text.width = 0; + kms.num_lines++; + if (kms.num_lines == MENU_MAX_LINES) break; + } + for(int i=kms.num_lines;i 0) { + kms.error_level--; + } + if (kms.flashing) { + kms.flash_pos = (kms.flash_pos + 1); + if (kms.flash_pos == MENU_FLASH_LENGTH) kms.flash_pos = 0; + } + int next_opacity = kms.opacity; + if (kms.appearing) { + if (next_opacity < MENU_OPACITY_MAX) { + next_opacity = kms.opacity+2; + } else { + kms.appearing = false; + } + } else if (kms.disappearing) { + if (next_opacity > 0) { + next_opacity = kms.opacity-2; + } else { + kms.disappearing = false; + } + } + if (next_opacity != kms.opacity) { + int l = 32 - kms.opacity; + for(int i=0;i<512;i++) { + uint32_t cmask = 0x001f001f; + uint32_t r = (colors[i] >> PICO_SCANVIDEO_PIXEL_RSHIFT) & cmask; + uint32_t g = (colors[i] >> PICO_SCANVIDEO_PIXEL_GSHIFT) & cmask; + uint32_t b = (colors[i] >> PICO_SCANVIDEO_PIXEL_BSHIFT) & cmask; + uint32_t br = 4; + uint32_t bg = 2; + uint32_t bb = 7; + r = (((r * l)>>5) + ((br*kms.opacity)>>5)) & cmask; + g = (((g * l)>>5) + ((bg*kms.opacity)>>5)) & cmask; + b = (((b * l)>>5) + ((bb*kms.opacity)>>5)) & cmask; + dark_colors[i] = PICO_SCANVIDEO_PIXEL_FROM_RGB5(r, g, b); + } + kms.opacity = next_opacity; + } +} + +void __maybe_in_ram khan_defer_option(eOptionB *option, bool next, bool complete = false) { + mutex_enter_blocking(&kms.mutex); + option_needed = option; + option_next = next; + option_complete = complete; + mutex_exit(&kms.mutex); +} + +static inline void nav_error() { + kms.error_level = MENU_ERROR_LEVEL_MAX; +} + +// called to update the menu +bool __maybe_in_ram khan_menu_selection_change(enum menu_key key) +{ + int i = kms.selected_line; + eOptionB *o; + for (o = eOptionB::First(); o && i; o = o->Next(), i--) {} + if (o) { + switch (key) { + case MK_NONE: { + // bit of a hack.. just a safe place to do this once per frame + kms.flashing = o->IsChangePending(); + return false; + } + case MK_UP: + if (!o->IsChangePending()) + { + kms.selected_line = (kms.selected_line + kms.num_lines - 1) % kms.num_lines; + } else nav_error(); + return true; + case MK_DOWN: + if (!o->IsChangePending()) + { + kms.selected_line = (kms.selected_line + 1) % kms.num_lines; + } else nav_error(); + return true; + case MK_LEFT: + if (!o->IsAction()) { + // tell cpu thread to run the action + khan_defer_option(o, false); + } else nav_error(); + return true; + case MK_RIGHT: + if (!o->IsAction()) { + khan_defer_option(o, true); + } else nav_error(); + return true; + case MK_ENTER: + if (o->IsAction() || o->IsChangePending()) { + khan_defer_option(o, true, o->IsChangePending()); + } + return true; + case MK_ESCAPE: + if (o->IsChangePending()) { + o->Complete(false); + kms.do_fill_menu = true; + return true; + } + break; + default: + break; + } + } + return false; +} + +#ifndef NO_USE_KEMPSTON +#include "../devices/input/kempston_joy.h" + +void khan_set_joystick_state(uint8_t state) { + xPlatform::Handler()->Speccy()->Devices().Get()->setState(state); +} +#endif +namespace xIo { + +bool MkDir(const char* path) { + assert(false); + return false; +} + +} + +#if !PICO_ON_DEVICE +#include +#endif + +namespace xPlatform { +#if false && !PICO_ON_DEVICE + static bool PreProcessKey(SDL_Event& e) + { + if(e.key.keysym.mod) + return false; + if(e.type != SDL_KEYUP) + return false; + switch(e.key.keysym.sym) + { + case SDL_SCANCODE_F2: + { + using namespace xOptions; + eOptionB* o = eOptionB::Find("save state"); + SAFE_CALL(o)->Change(); + } + return true; + case SDL_SCANCODE_F3: + { + using namespace xOptions; + eOptionB* o = eOptionB::Find("load state"); + SAFE_CALL(o)->Change(); + } + return true; + case SDL_SCANCODE_F5: +#ifndef NO_USE_TAPE + Handler()->OnAction(A_TAPE_TOGGLE); +#endif + return true; + case SDL_SCANCODE_F7: + { + using namespace xOptions; + eOptionB* o = eOptionB::Find("pause"); + SAFE_CALL(o)->Change(); + } + return true; + case SDL_SCANCODE_F12: + Handler()->OnAction(A_RESET); + return true; + default: + return false; + } + } +#endif + + static byte TranslateScancode(SDL_Scancode _scancode, dword& _flags) + { + switch(_scancode) + { + case SDL_SCANCODE_GRAVE:return 'm'; + case SDL_SCANCODE_LSHIFT: return 'c'; + case SDL_SCANCODE_RSHIFT: return 'c'; + case SDL_SCANCODE_LALT: return 's'; + case SDL_SCANCODE_RALT: return 's'; + case SDL_SCANCODE_RETURN: return 'e'; + case SDL_SCANCODE_BACKSPACE: + _flags |= KF_SHIFT; + return '0'; + case SDL_SCANCODE_APOSTROPHE: + _flags |= KF_ALT; + if(_flags&KF_SHIFT) + { + _flags &= ~KF_SHIFT; + return 'P'; + } + else + return '7'; + case SDL_SCANCODE_COMMA: + _flags |= KF_ALT; + if(_flags&KF_SHIFT) + { + _flags &= ~KF_SHIFT; + return 'R'; + } + else + return 'N'; + case SDL_SCANCODE_PERIOD: + _flags |= KF_ALT; + if(_flags&KF_SHIFT) + { + _flags &= ~KF_SHIFT; + return 'T'; + } + else + return 'M'; + case SDL_SCANCODE_SEMICOLON: + _flags |= KF_ALT; + if(_flags&KF_SHIFT) + { + _flags &= ~KF_SHIFT; + return 'Z'; + } + else + return 'O'; + case SDL_SCANCODE_SLASH: + _flags |= KF_ALT; + if(_flags&KF_SHIFT) + { + _flags &= ~KF_SHIFT; + return 'C'; + } + else + return 'V'; + case SDL_SCANCODE_MINUS: + _flags |= KF_ALT; + if(_flags&KF_SHIFT) + { + _flags &= ~KF_SHIFT; + return '0'; + } + else + return 'J'; + case SDL_SCANCODE_EQUALS: + _flags |= KF_ALT; + if(_flags&KF_SHIFT) + { + _flags &= ~KF_SHIFT; + return 'K'; + } + else + return 'L'; + case SDL_SCANCODE_TAB: + _flags |= KF_ALT; + _flags |= KF_SHIFT; + return 0; + case SDL_SCANCODE_LEFT: return 'l'; + case SDL_SCANCODE_RIGHT: return 'r'; + case SDL_SCANCODE_UP: return 'u'; + case SDL_SCANCODE_DOWN: return 'd'; + case SDL_SCANCODE_INSERT: + case SDL_SCANCODE_RCTRL: + case SDL_SCANCODE_LCTRL: return 'f'; + case SDL_SCANCODE_0: return '0'; + default: + break; + } + if(_scancode >= SDL_SCANCODE_1 && _scancode <= SDL_SCANCODE_9) + return _scancode + '1' - SDL_SCANCODE_1; + if(_scancode >= SDL_SCANCODE_A && _scancode <= SDL_SCANCODE_Z) + return 'A' + _scancode - SDL_SCANCODE_A; + if(_scancode == SDL_SCANCODE_SPACE) + return ' '; + return 0; + } +} + +bool cursor_mode = true; + +void update_cursor_mode(int scan) { + cursor_mode = scan >= SDL_SCANCODE_RIGHT && scan <= SDL_SCANCODE_UP; +} + +void khan_key_down(int scan, int sym, int mod) { + if (scan == SDL_SCANCODE_ESCAPE) { + khan_menu_key(MK_ESCAPE); + return; + } + update_cursor_mode(scan); + if (!kms.opacity || kms.disappearing) + { + dword flags = xPlatform::KF_DOWN; + if (mod & KMOD_ALT) + flags |= xPlatform::KF_ALT; + if (mod & KMOD_SHIFT) + flags |= xPlatform::KF_SHIFT; + if (cursor_mode) + flags |= xPlatform::KF_CURSOR; + byte key = xPlatform::TranslateScancode((SDL_Scancode)scan, flags); + khan_zx_key_event(key, flags); + } else { + switch (scan) { + case SDL_SCANCODE_LEFT: + khan_menu_key(MK_LEFT); + return; + case SDL_SCANCODE_RIGHT: + khan_menu_key(MK_RIGHT); + return; + case SDL_SCANCODE_DOWN: + khan_menu_key(MK_DOWN); + return; + case SDL_SCANCODE_UP: + khan_menu_key(MK_UP); + return; + case SDL_SCANCODE_RETURN: + khan_menu_key(MK_ENTER); + return; + } + } +} + +void khan_key_up(int scan, int sym, int mod) { + if (sym == SDL_SCANCODE_ESCAPE) { + return; + } + dword flags = 0;//xPlatform::KF_DOWN; + if(mod&KMOD_ALT) + flags |= xPlatform::KF_ALT; + if(mod&KMOD_SHIFT) + flags |= xPlatform::KF_SHIFT; + update_cursor_mode(scan); + if (cursor_mode) + flags |= xPlatform::KF_CURSOR; + byte key = xPlatform::TranslateScancode((SDL_Scancode)scan, flags); + khan_zx_key_event(key, flags); +} + +#ifndef NO_USE_AY +int ay_sample_rate() { + return SNDR_DEFAULT_SAMPLE_RATE; +} +#endif \ No newline at end of file diff --git a/khan/khan_lib.h b/khan/khan_lib.h new file mode 100644 index 0000000..5d264c1 --- /dev/null +++ b/khan/khan_lib.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef KHAN_LIB_H +#define KHAN_LIB_H +#include "pico.h" +#include "pico/sync.h" + +//#if PICO_ON_DEVICE +//#define __maybe_in_ram __attribute__((section(".time_critical"))) +//#else +#define __maybe_in_ram +//#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern int khan_init(); +extern int khan_go(int video_mode_hz); + +void khan_key_down(int scan, int sym, int mod); +void khan_key_up(int scan, int sym, int mod); + +bool khan_get_scanline_info(int l, uint16_t *border, const uint8_t **attr, const uint8_t **pixels, const uint32_t **attr_colors, const uint32_t **dark_attr_colors); + +// these calls are into khan from the rendering thread + +// called from the rendering thread after the last visible line is rendered... a good time to do some per frame stuff +extern void khan_idle_blanking(); +extern void khan_fill_main_menu(); +extern void khan_zx_key_event(uint8_t key, uint8_t flags); +enum menu_key +{ + MK_NONE, + MK_ESCAPE, + MK_UP, + MK_DOWN, + MK_LEFT, + MK_RIGHT, + MK_ENTER +}; +extern void khan_menu_key(enum menu_key key); +extern void khan_hide_menu(); + +// callbacks from khan on the cpu thread +extern bool khan_cb_is_button_down(); +extern void khan_cb_begin_frame(); +extern void khan_cb_end_frame(); +// returns true if the key was consumed +extern bool khan_menu_selection_change(enum menu_key key); +#ifdef USE_KHAN_GPIO +extern uint8_t khan_gpio_read(uint reg); +extern void khan_gpio_write(uint reg, uint8_t value); +#endif + +#ifndef NO_USE_KEMPSTON +#define KEMPSTON_R 0x01 +#define KEMPSTON_L 0x02 +#define KEMPSTON_D 0x04 +#define KEMPSTON_U 0x08 +#define KEMPSTON_F 0x10 +extern void khan_set_joystick_state(uint8_t state); +#endif + +extern void khan_beeper_reset(); +extern void khan_beeper_begin_frame(uint32_t t, int32_t frame_num); +// t is within frame +extern void khan_beeper_level_change(uint32_t t, uint8_t level); +extern void khan_beeper_end_frame(uint32_t t); + +struct text_element +{ + const char *text; + int width; +}; + + +extern const uint8_t atlantis_glyph_bitmap[]; + +extern const uint8_t atlantis_glyph_widths[]; + +#define MENU_GLYPH_MIN 32 +#define MENU_GLYPH_COUNT 95 +#define MENU_GLYPH_MAX (MENU_GLYPH_MIN + MENU_GLYPH_COUNT - 1) +#define MENU_GLYPH_HEIGHT 9 +#define MENU_GLYPH_Y_OFFSET 2 +#define MENU_GLYPH_ADVANCE 1 + +// so we're offset a bit +#define MENU_WIDTH_IN_BLOCKS 17 +#define MENU_LEFT_WIDTH_IN_BLOCKS 10 +#define MENU_RIGHT_WIDTH_IN_BLOCKS (MENU_WIDTH_IN_BLOCKS - MENU_LEFT_WIDTH_IN_BLOCKS) + +#define MENU_LINE_HEIGHT 12 +#define MENU_LINE_BORDER_WIDTH 4 +#define MENU_LINE_TEXT_INDENT 6 +#ifndef NO_USE_128K +#define MENU_MAX_LINES 11 +#else +#define MENU_MAX_LINES 9 +#endif + +// must be <32 +#define MENU_AREA_WIDTH_IN_BLOCKS 24 +#define MENU_AREA_OFFSET_Y 4 +#define MENU_AREA_BORDER_HEIGHT 2 +// how opaque the bg is +//#define MENU_OPACITY_MAX 24 +//#define MENU_OPACITY_OFFSET 0 +#define MENU_OPACITY_MAX 20 +#define MENU_OPACITY_OFFSET 3 +// went the fg kicks in +#define MENU_OPACITY_THRESHOLD 10 + +#define MENU_AREA_WIDTH (MENU_AREA_WIDTH_IN_BLOCKS * 8) +#define MENU_WIDTH (MENU_WIDTH_IN_BLOCKS * 8) +#define MENU_BORDER_WIDTH_IN_BLOCKS ((MENU_AREA_WIDTH-MENU_WIDTH)/2) + +// note this is assumed to be 16 +#define MENU_ERROR_LEVEL_MAX 16 +#define MENU_FLASH_LENGTH 32 + +#define menu_glypth_bitmap atlantis_glyph_bitmap +#define menu_glyph_widths atlantis_glyph_widths + +struct menu_state +{ + struct mutex mutex; + struct + { + struct text_element left_text; + struct text_element right_text; + } lines[MENU_MAX_LINES]; + int16_t opacity; + bool appearing; + bool disappearing; + bool flashing; + + int8_t num_lines; + int8_t selected_line; + int8_t selection_top_pixel; + int8_t flash_pos; + int8_t error_level; + + // protected by mutex + bool do_hide_menu; + bool do_fill_menu; +}; + +extern struct menu_state kms; + +#ifndef NO_USE_AY +int ay_sample_rate(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/khan/memmap_khan.ld b/khan/memmap_khan.ld new file mode 100644 index 0000000..47238e9 --- /dev/null +++ b/khan/memmap_khan.ld @@ -0,0 +1,291 @@ +/* Based on GCC ARM embedded samples. + Defines the following symbols for use by code: + __exidx_start + __exidx_end + __etext + __data_start__ + __preinit_array_start + __preinit_array_end + __init_array_start + __init_array_end + __fini_array_start + __fini_array_end + __data_end__ + __bss_start__ + __bss_end__ + __end__ + end + __HeapLimit + __StackLimit + __StackTop + __stack (== StackTop) +*/ + +MEMORY +{ + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k +/* RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k + SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k + SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k*/ + + /* not currently using scratch except for stack, so shrink and have more ram */ + RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 262k + SCRATCH_X(rwx) : ORIGIN = 0x20041800, LENGTH = 1k + SCRATCH_Y(rwx) : ORIGIN = 0x20041c00, LENGTH = 1k +} + +ENTRY(_entry_point) + +SECTIONS +{ + /* Second stage bootloader is prepended to the image. It must be 256 bytes big + and checksummed. It is usually built by the boot_stage2 target + in the Pico SDK + */ + + .boot2 : { + __boot2_start__ = .; + KEEP (*(.boot2)) + __boot2_end__ = .; + } > FLASH + + ASSERT(__boot2_end__ - __boot2_start__ == 256, + "ERROR: Pico second stage bootloader must be 256 bytes in size") + + /* The second stage will always enter the image at the start of .text. + The debugger will use the ELF entry point, which is the _entry_point + symbol if present, otherwise defaults to start of .text. + This can be used to transfer control back to the bootrom on debugger + launches only, to perform proper flash setup. + */ + + .text : { + __logical_binary_start = .; + KEEP (*(.vectors)) + KEEP (*(.binary_info_header)) + __binary_info_header_end = .; + KEEP (*(.reset)) + *runtime.c.o*(.text*) + *pll.c.o*(.text*) + *pio.c.o*(.text*) + *dma.c.o*(.text*) + *irq.c.o*(.text*) + *stdio.c.o*(.text*) + *stdout.c.o*(.text*) + *stdlib.c.o*(.text*) + *lock_core.c.o*(.text*) + *sample_encoding.cpp.o*(.text*) + *printf.c.o*(.text*) + *multicore.c.o*(.text*) + *claim.c.o*(.text*) + *pico_malloc.c.o*(.text*) + *bootrom_helper.c.o*(.text*) + *lib_a*.o(.text*) + *options.cpp.obj(.text*) + *options_common.cpp.obj(.text*) + *speccy.cpp.obj(.text*) + *speccy_handler.cpp.obj(.text*) + *clocks.c.obj(.text*) + *runtime.c.obj(.text*) + *khan_init.cpp.obj(.text*) + *(.text.__wrap___aeabi_ldiv) + *(.text.process_enumeration) + *(.text._get_descriptor) + *(.text.scanvideo_setup_with_timing) + *(.text.process_set_config) + *(.text.hidh_open) + *(.text.hidh_set_config) + *(.text.usbh_driver_set_config_complete) + *(.text.audio_pwm_setup) + *(.text.audio_pwm_setup) + *(.text._Z21audio_new_buffer_poolP19audio_buffer_formatii) + *(.text._malloc_r) + *(.text._free_r) + *printf.c.obj(*) + /**platform_common.c.o*(.text*)*/ + *(.init) + /**(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)*/ + *(.fini) + /* Pull all c'tors into .text */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + /* Followed by destructors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + + *(.eh_frame*) + . = ALIGN(4); + } > FLASH + + .rodata : { + *(*.game_data) + /**(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a: *khan*) .rodata*)*/ + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*))) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + /* Machine inspectable binary information */ + . = ALIGN(4); + __binary_info_start = .; + .binary_info : + { + KEEP(*(.binary_info.keep.*)) + *(.binary_info.*) + } > FLASH + __binary_info_end = .; + . = ALIGN(4); + + /* End of .text-like segments */ + __etext = .; + + .ram_vector_table (COPY): { + *(.ram_vector_table) + } > RAM + + .data : { + __data_start__ = .; + *(vtable) + + *(.time_critical*) + + /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */ + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + + *(.data*) + + . = ALIGN(4); + *(.after_data.*) + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__mutex_array_start = .); + KEEP(*(SORT(.mutex_array.*))) + KEEP(*(.mutex_array)) + PROVIDE_HIDDEN (__mutex_array_end = .); + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(SORT(.preinit_array.*))) + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + *(SORT(.fini_array.*)) + *(.fini_array) + PROVIDE_HIDDEN (__fini_array_end = .); + + *(.jcr) + . = ALIGN(4); + __ram64k = .; + *(.ram64k) + + /* All data end */ + __data_end__ = .; + } > RAM AT> FLASH + + .uninitialized_data (COPY): { + . = ALIGN(4); + *(.uninitialized_data*) + } > RAM + + /* Start and end symbols must be word-aligned */ + .scratch_x : { + __scratch_x_start__ = .; + *(.scratch_x.*) + . = ALIGN(4); + __scratch_x_end__ = .; + } > SCRATCH_X AT > FLASH + __scratch_x_source__ = LOADADDR(.scratch_x); + + .scratch_y : { + __scratch_y_start__ = .; + *(.scratch_y.*) + . = ALIGN(4); + __scratch_y_end__ = .; + } > SCRATCH_Y AT > FLASH + __scratch_y_source__ = LOADADDR(.scratch_y); + + .bss : { + . = ALIGN(4); + __bss_start__ = .; + *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*))) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + } > RAM + + .heap (COPY): + { + __end__ = .; + end = __end__; + *(.heap*) + __HeapLimit = .; + } > RAM + + /* .stack*_dummy section doesn't contains any symbols. It is only + * used for linker to calculate size of stack sections, and assign + * values to stack symbols later + * + * stack1 section may be empty/missing if platform_launch_core1 is not used */ + + /* by default we put core 0 stack at the end of scratch Y, so that if core 1 + * stack is not used then all of SCRATCH_X is free. + */ + .stack1_dummy (COPY): + { + *(.stack1*) + } > SCRATCH_X + .stack_dummy (COPY): + { + *(.stack*) + } > SCRATCH_Y + + .flash_end : { + __flash_binary_end = .; + } > FLASH + + /* stack limit is poorly named, but historically is maximum heap ptr */ + __StackLimit = ORIGIN(RAM) + LENGTH(RAM); + __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X); + __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y); + __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy); + __StackBottom = __StackTop - SIZEOF(.stack_dummy); + PROVIDE(__stack = __StackTop); + + /* Check if data + heap + stack exceeds RAM limit */ + ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed") + + ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary") + /* todo assert on extra code */ +} + diff --git a/khan/ram64k.h b/khan/ram64k.h new file mode 100644 index 0000000..c21b4b1 --- /dev/null +++ b/khan/ram64k.h @@ -0,0 +1,1368 @@ +__attribute__((section(".ram64k"))) unsigned char ram64k[65536] = { + 0xf3, 0xaf, 0x11, 0xff, 0xff, 0xc3, 0xcb, 0x11, 0x2a, 0x5d, 0x5c, 0x22, + 0x5f, 0x5c, 0x18, 0x43, 0xc3, 0xf2, 0x15, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x2a, 0x5d, 0x5c, 0x7e, 0xcd, 0x7d, 0x00, 0xd0, 0xcd, 0x74, 0x00, 0x18, + 0xf7, 0xff, 0xff, 0xff, 0xc3, 0x5b, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc5, 0x2a, 0x61, 0x5c, 0xe5, 0xc3, 0x9e, 0x16, 0xf5, 0xe5, 0x2a, 0x78, + 0x5c, 0x23, 0x22, 0x78, 0x5c, 0x7c, 0xb5, 0x20, 0x03, 0xfd, 0x34, 0x40, + 0xc5, 0xd5, 0xcd, 0xbf, 0x02, 0xd1, 0xc1, 0xe1, 0xf1, 0xfb, 0xc9, 0xe1, + 0x6e, 0xfd, 0x75, 0x00, 0xed, 0x7b, 0x3d, 0x5c, 0xc3, 0xc5, 0x16, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xe5, 0x2a, 0xb0, 0x5c, 0x7c, + 0xb5, 0x20, 0x01, 0xe9, 0xe1, 0xf1, 0xed, 0x45, 0x2a, 0x5d, 0x5c, 0x23, + 0x22, 0x5d, 0x5c, 0x7e, 0xc9, 0xfe, 0x21, 0xd0, 0xfe, 0x0d, 0xc8, 0xfe, + 0x10, 0xd8, 0xfe, 0x18, 0x3f, 0xd8, 0x23, 0xfe, 0x16, 0x38, 0x01, 0x23, + 0x37, 0x22, 0x5d, 0x5c, 0xc9, 0xbf, 0x52, 0x4e, 0xc4, 0x49, 0x4e, 0x4b, + 0x45, 0x59, 0xa4, 0x50, 0xc9, 0x46, 0xce, 0x50, 0x4f, 0x49, 0x4e, 0xd4, + 0x53, 0x43, 0x52, 0x45, 0x45, 0x4e, 0xa4, 0x41, 0x54, 0x54, 0xd2, 0x41, + 0xd4, 0x54, 0x41, 0xc2, 0x56, 0x41, 0x4c, 0xa4, 0x43, 0x4f, 0x44, 0xc5, + 0x56, 0x41, 0xcc, 0x4c, 0x45, 0xce, 0x53, 0x49, 0xce, 0x43, 0x4f, 0xd3, + 0x54, 0x41, 0xce, 0x41, 0x53, 0xce, 0x41, 0x43, 0xd3, 0x41, 0x54, 0xce, + 0x4c, 0xce, 0x45, 0x58, 0xd0, 0x49, 0x4e, 0xd4, 0x53, 0x51, 0xd2, 0x53, + 0x47, 0xce, 0x41, 0x42, 0xd3, 0x50, 0x45, 0x45, 0xcb, 0x49, 0xce, 0x55, + 0x53, 0xd2, 0x53, 0x54, 0x52, 0xa4, 0x43, 0x48, 0x52, 0xa4, 0x4e, 0x4f, + 0xd4, 0x42, 0x49, 0xce, 0x4f, 0xd2, 0x41, 0x4e, 0xc4, 0x3c, 0xbd, 0x3e, + 0xbd, 0x3c, 0xbe, 0x4c, 0x49, 0x4e, 0xc5, 0x54, 0x48, 0x45, 0xce, 0x54, + 0xcf, 0x53, 0x54, 0x45, 0xd0, 0x44, 0x45, 0x46, 0x20, 0x46, 0xce, 0x43, + 0x41, 0xd4, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0xd4, 0x4d, 0x4f, 0x56, 0xc5, + 0x45, 0x52, 0x41, 0x53, 0xc5, 0x4f, 0x50, 0x45, 0x4e, 0x20, 0xa3, 0x43, + 0x4c, 0x4f, 0x53, 0x45, 0x20, 0xa3, 0x4d, 0x45, 0x52, 0x47, 0xc5, 0x56, + 0x45, 0x52, 0x49, 0x46, 0xd9, 0x42, 0x45, 0x45, 0xd0, 0x43, 0x49, 0x52, + 0x43, 0x4c, 0xc5, 0x49, 0x4e, 0xcb, 0x50, 0x41, 0x50, 0x45, 0xd2, 0x46, + 0x4c, 0x41, 0x53, 0xc8, 0x42, 0x52, 0x49, 0x47, 0x48, 0xd4, 0x49, 0x4e, + 0x56, 0x45, 0x52, 0x53, 0xc5, 0x4f, 0x56, 0x45, 0xd2, 0x4f, 0x55, 0xd4, + 0x4c, 0x50, 0x52, 0x49, 0x4e, 0xd4, 0x4c, 0x4c, 0x49, 0x53, 0xd4, 0x53, + 0x54, 0x4f, 0xd0, 0x52, 0x45, 0x41, 0xc4, 0x44, 0x41, 0x54, 0xc1, 0x52, + 0x45, 0x53, 0x54, 0x4f, 0x52, 0xc5, 0x4e, 0x45, 0xd7, 0x42, 0x4f, 0x52, + 0x44, 0x45, 0xd2, 0x43, 0x4f, 0x4e, 0x54, 0x49, 0x4e, 0x55, 0xc5, 0x44, + 0x49, 0xcd, 0x52, 0x45, 0xcd, 0x46, 0x4f, 0xd2, 0x47, 0x4f, 0x20, 0x54, + 0xcf, 0x47, 0x4f, 0x20, 0x53, 0x55, 0xc2, 0x49, 0x4e, 0x50, 0x55, 0xd4, + 0x4c, 0x4f, 0x41, 0xc4, 0x4c, 0x49, 0x53, 0xd4, 0x4c, 0x45, 0xd4, 0x50, + 0x41, 0x55, 0x53, 0xc5, 0x4e, 0x45, 0x58, 0xd4, 0x50, 0x4f, 0x4b, 0xc5, + 0x50, 0x52, 0x49, 0x4e, 0xd4, 0x50, 0x4c, 0x4f, 0xd4, 0x52, 0x55, 0xce, + 0x53, 0x41, 0x56, 0xc5, 0x52, 0x41, 0x4e, 0x44, 0x4f, 0x4d, 0x49, 0x5a, + 0xc5, 0x49, 0xc6, 0x43, 0x4c, 0xd3, 0x44, 0x52, 0x41, 0xd7, 0x43, 0x4c, + 0x45, 0x41, 0xd2, 0x52, 0x45, 0x54, 0x55, 0x52, 0xce, 0x43, 0x4f, 0x50, + 0xd9, 0x42, 0x48, 0x59, 0x36, 0x35, 0x54, 0x47, 0x56, 0x4e, 0x4a, 0x55, + 0x37, 0x34, 0x52, 0x46, 0x43, 0x4d, 0x4b, 0x49, 0x38, 0x33, 0x45, 0x44, + 0x58, 0x0e, 0x4c, 0x4f, 0x39, 0x32, 0x57, 0x53, 0x5a, 0x20, 0x0d, 0x50, + 0x30, 0x31, 0x51, 0x41, 0xe3, 0xc4, 0xe0, 0xe4, 0xb4, 0xbc, 0xbd, 0xbb, + 0xaf, 0xb0, 0xb1, 0xc0, 0xa7, 0xa6, 0xbe, 0xad, 0xb2, 0xba, 0xe5, 0xa5, + 0xc2, 0xe1, 0xb3, 0xb9, 0xc1, 0xb8, 0x7e, 0xdc, 0xda, 0x5c, 0xb7, 0x7b, + 0x7d, 0xd8, 0xbf, 0xae, 0xaa, 0xab, 0xdd, 0xde, 0xdf, 0x7f, 0xb5, 0xd6, + 0x7c, 0xd5, 0x5d, 0xdb, 0xb6, 0xd9, 0x5b, 0xd7, 0x0c, 0x07, 0x06, 0x04, + 0x05, 0x08, 0x0a, 0x0b, 0x09, 0x0f, 0xe2, 0x2a, 0x3f, 0xcd, 0xc8, 0xcc, + 0xcb, 0x5e, 0xac, 0x2d, 0x2b, 0x3d, 0x2e, 0x2c, 0x3b, 0x22, 0xc7, 0x3c, + 0xc3, 0x3e, 0xc5, 0x2f, 0xc9, 0x60, 0xc6, 0x3a, 0xd0, 0xce, 0xa8, 0xca, + 0xd3, 0xd4, 0xd1, 0xd2, 0xa9, 0xcf, 0x2e, 0x2f, 0x11, 0xff, 0xff, 0x01, + 0xfe, 0xfe, 0xed, 0x78, 0x2f, 0xe6, 0x1f, 0x28, 0x0e, 0x67, 0x7d, 0x14, + 0xc0, 0xd6, 0x08, 0xcb, 0x3c, 0x30, 0xfa, 0x53, 0x5f, 0x20, 0xf4, 0x2d, + 0xcb, 0x00, 0x38, 0xe6, 0x7a, 0x3c, 0xc8, 0xfe, 0x28, 0xc8, 0xfe, 0x19, + 0xc8, 0x7b, 0x5a, 0x57, 0xfe, 0x18, 0xc9, 0xcd, 0x8e, 0x02, 0xc0, 0x21, + 0x00, 0x5c, 0xcb, 0x7e, 0x20, 0x07, 0x23, 0x35, 0x2b, 0x20, 0x02, 0x36, + 0xff, 0x7d, 0x21, 0x04, 0x5c, 0xbd, 0x20, 0xee, 0xcd, 0x1e, 0x03, 0xd0, + 0x21, 0x00, 0x5c, 0xbe, 0x28, 0x2e, 0xeb, 0x21, 0x04, 0x5c, 0xbe, 0x28, + 0x27, 0xcb, 0x7e, 0x20, 0x04, 0xeb, 0xcb, 0x7e, 0xc8, 0x5f, 0x77, 0x23, + 0x36, 0x05, 0x23, 0x3a, 0x09, 0x5c, 0x77, 0x23, 0xfd, 0x4e, 0x07, 0xfd, + 0x56, 0x01, 0xe5, 0xcd, 0x33, 0x03, 0xe1, 0x77, 0x32, 0x08, 0x5c, 0xfd, + 0xcb, 0x01, 0xee, 0xc9, 0x23, 0x36, 0x05, 0x23, 0x35, 0xc0, 0x3a, 0x0a, + 0x5c, 0x77, 0x23, 0x7e, 0x18, 0xea, 0x42, 0x16, 0x00, 0x7b, 0xfe, 0x27, + 0xd0, 0xfe, 0x18, 0x20, 0x03, 0xcb, 0x78, 0xc0, 0x21, 0x05, 0x02, 0x19, + 0x7e, 0x37, 0xc9, 0x7b, 0xfe, 0x3a, 0x38, 0x2f, 0x0d, 0xfa, 0x4f, 0x03, + 0x28, 0x03, 0xc6, 0x4f, 0xc9, 0x21, 0xeb, 0x01, 0x04, 0x28, 0x03, 0x21, + 0x05, 0x02, 0x16, 0x00, 0x19, 0x7e, 0xc9, 0x21, 0x29, 0x02, 0xcb, 0x40, + 0x28, 0xf4, 0xcb, 0x5a, 0x28, 0x0a, 0xfd, 0xcb, 0x30, 0x5e, 0xc0, 0x04, + 0xc0, 0xc6, 0x20, 0xc9, 0xc6, 0xa5, 0xc9, 0xfe, 0x30, 0xd8, 0x0d, 0xfa, + 0x9d, 0x03, 0x20, 0x19, 0x21, 0x54, 0x02, 0xcb, 0x68, 0x28, 0xd3, 0xfe, + 0x38, 0x30, 0x07, 0xd6, 0x20, 0x04, 0xc8, 0xc6, 0x08, 0xc9, 0xd6, 0x36, + 0x04, 0xc8, 0xc6, 0xfe, 0xc9, 0x21, 0x30, 0x02, 0xfe, 0x39, 0x28, 0xba, + 0xfe, 0x30, 0x28, 0xb6, 0xe6, 0x07, 0xc6, 0x80, 0x04, 0xc8, 0xee, 0x0f, + 0xc9, 0x04, 0xc8, 0xcb, 0x68, 0x21, 0x30, 0x02, 0x20, 0xa4, 0xd6, 0x10, + 0xfe, 0x22, 0x28, 0x06, 0xfe, 0x20, 0xc0, 0x3e, 0x5f, 0xc9, 0x3e, 0x40, + 0xc9, 0xf3, 0x7d, 0xcb, 0x3d, 0xcb, 0x3d, 0x2f, 0xe6, 0x03, 0x4f, 0x06, + 0x00, 0xdd, 0x21, 0xd1, 0x03, 0xdd, 0x09, 0x3a, 0x48, 0x5c, 0xe6, 0x38, + 0x0f, 0x0f, 0x0f, 0xf6, 0x08, 0x00, 0x00, 0x00, 0x04, 0x0c, 0x0d, 0x20, + 0xfd, 0x0e, 0x3f, 0x05, 0xc2, 0xd6, 0x03, 0xee, 0x10, 0xd3, 0xfe, 0x44, + 0x4f, 0xcb, 0x67, 0x20, 0x09, 0x7a, 0xb3, 0x28, 0x09, 0x79, 0x4d, 0x1b, + 0xdd, 0xe9, 0x4d, 0x0c, 0xdd, 0xe9, 0xfb, 0xc9, 0xef, 0x31, 0x27, 0xc0, + 0x03, 0x34, 0xec, 0x6c, 0x98, 0x1f, 0xf5, 0x04, 0xa1, 0x0f, 0x38, 0x21, + 0x92, 0x5c, 0x7e, 0xa7, 0x20, 0x5e, 0x23, 0x4e, 0x23, 0x46, 0x78, 0x17, + 0x9f, 0xb9, 0x20, 0x54, 0x23, 0xbe, 0x20, 0x50, 0x78, 0xc6, 0x3c, 0xf2, + 0x25, 0x04, 0xe2, 0x6c, 0x04, 0x06, 0xfa, 0x04, 0xd6, 0x0c, 0x30, 0xfb, + 0xc6, 0x0c, 0xc5, 0x21, 0x6e, 0x04, 0xcd, 0x06, 0x34, 0xcd, 0xb4, 0x33, + 0xef, 0x04, 0x38, 0xf1, 0x86, 0x77, 0xef, 0xc0, 0x02, 0x31, 0x38, 0xcd, + 0x94, 0x1e, 0xfe, 0x0b, 0x30, 0x22, 0xef, 0xe0, 0x04, 0xe0, 0x34, 0x80, + 0x43, 0x55, 0x9f, 0x80, 0x01, 0x05, 0x34, 0x35, 0x71, 0x03, 0x38, 0xcd, + 0x99, 0x1e, 0xc5, 0xcd, 0x99, 0x1e, 0xe1, 0x50, 0x59, 0x7a, 0xb3, 0xc8, + 0x1b, 0xc3, 0xb5, 0x03, 0xcf, 0x0a, 0x89, 0x02, 0xd0, 0x12, 0x86, 0x89, + 0x0a, 0x97, 0x60, 0x75, 0x89, 0x12, 0xd5, 0x17, 0x1f, 0x89, 0x1b, 0x90, + 0x41, 0x02, 0x89, 0x24, 0xd0, 0x53, 0xca, 0x89, 0x2e, 0x9d, 0x36, 0xb1, + 0x89, 0x38, 0xff, 0x49, 0x3e, 0x89, 0x43, 0xff, 0x6a, 0x73, 0x89, 0x4f, + 0xa7, 0x00, 0x54, 0x89, 0x5c, 0x00, 0x00, 0x00, 0x89, 0x69, 0x14, 0xf6, + 0x24, 0x89, 0x76, 0xf1, 0x10, 0x05, 0xcd, 0xfb, 0x24, 0x3a, 0x3b, 0x5c, + 0x87, 0xfa, 0x8a, 0x1c, 0xe1, 0xd0, 0xe5, 0xcd, 0xf1, 0x2b, 0x62, 0x6b, + 0x0d, 0xf8, 0x09, 0xcb, 0xfe, 0xc9, 0x21, 0x3f, 0x05, 0xe5, 0x21, 0x80, + 0x1f, 0xcb, 0x7f, 0x28, 0x03, 0x21, 0x98, 0x0c, 0x08, 0x13, 0xdd, 0x2b, + 0xf3, 0x3e, 0x02, 0x47, 0x10, 0xfe, 0xd3, 0xfe, 0xee, 0x0f, 0x06, 0xa4, + 0x2d, 0x20, 0xf5, 0x05, 0x25, 0xf2, 0xd8, 0x04, 0x06, 0x2f, 0x10, 0xfe, + 0xd3, 0xfe, 0x3e, 0x0d, 0x06, 0x37, 0x10, 0xfe, 0xd3, 0xfe, 0x01, 0x0e, + 0x3b, 0x08, 0x6f, 0xc3, 0x07, 0x05, 0x7a, 0xb3, 0x28, 0x0c, 0xdd, 0x6e, + 0x00, 0x7c, 0xad, 0x67, 0x3e, 0x01, 0x37, 0xc3, 0x25, 0x05, 0x6c, 0x18, + 0xf4, 0x79, 0xcb, 0x78, 0x10, 0xfe, 0x30, 0x04, 0x06, 0x42, 0x10, 0xfe, + 0xd3, 0xfe, 0x06, 0x3e, 0x20, 0xef, 0x05, 0xaf, 0x3c, 0xcb, 0x15, 0xc2, + 0x14, 0x05, 0x1b, 0xdd, 0x23, 0x06, 0x31, 0x3e, 0x7f, 0xdb, 0xfe, 0x1f, + 0xd0, 0x7a, 0x3c, 0xc2, 0xfe, 0x04, 0x06, 0x3b, 0x10, 0xfe, 0xc9, 0xf5, + 0x3a, 0x48, 0x5c, 0xe6, 0x38, 0x0f, 0x0f, 0x0f, 0xd3, 0xfe, 0x3e, 0x7f, + 0xdb, 0xfe, 0x1f, 0xfb, 0x38, 0x02, 0xcf, 0x0c, 0xf1, 0xc9, 0x14, 0x08, + 0x15, 0xf3, 0x3e, 0x0f, 0xd3, 0xfe, 0x21, 0x3f, 0x05, 0xe5, 0xdb, 0xfe, + 0x1f, 0xe6, 0x20, 0xf6, 0x02, 0x4f, 0xbf, 0xc0, 0xcd, 0xe7, 0x05, 0x30, + 0xfa, 0x21, 0x15, 0x04, 0x10, 0xfe, 0x2b, 0x7c, 0xb5, 0x20, 0xf9, 0xcd, + 0xe3, 0x05, 0x30, 0xeb, 0x06, 0x9c, 0xcd, 0xe3, 0x05, 0x30, 0xe4, 0x3e, + 0xc6, 0xb8, 0x30, 0xe0, 0x24, 0x20, 0xf1, 0x06, 0xc9, 0xcd, 0xe7, 0x05, + 0x30, 0xd5, 0x78, 0xfe, 0xd4, 0x30, 0xf4, 0xcd, 0xe7, 0x05, 0xd0, 0x79, + 0xee, 0x03, 0x4f, 0x26, 0x00, 0x06, 0xb0, 0x18, 0x1f, 0x08, 0x20, 0x07, + 0x30, 0x0f, 0xdd, 0x75, 0x00, 0x18, 0x0f, 0xcb, 0x11, 0xad, 0xc0, 0x79, + 0x1f, 0x4f, 0x13, 0x18, 0x07, 0xdd, 0x7e, 0x00, 0xad, 0xc0, 0xdd, 0x23, + 0x1b, 0x08, 0x06, 0xb2, 0x2e, 0x01, 0xcd, 0xe3, 0x05, 0xd0, 0x3e, 0xcb, + 0xb8, 0xcb, 0x15, 0x06, 0xb0, 0xd2, 0xca, 0x05, 0x7c, 0xad, 0x67, 0x7a, + 0xb3, 0x20, 0xca, 0x7c, 0xfe, 0x01, 0xc9, 0xcd, 0xe7, 0x05, 0xd0, 0x3e, + 0x16, 0x3d, 0x20, 0xfd, 0xa7, 0x04, 0xc8, 0x3e, 0x7f, 0xdb, 0xfe, 0x1f, + 0xd0, 0xa9, 0xe6, 0x20, 0x28, 0xf3, 0x79, 0x2f, 0x4f, 0xe6, 0x07, 0xf6, + 0x08, 0xd3, 0xfe, 0x37, 0xc9, 0xf1, 0x3a, 0x74, 0x5c, 0xd6, 0xe0, 0x32, + 0x74, 0x5c, 0xcd, 0x8c, 0x1c, 0xcd, 0x30, 0x25, 0x28, 0x3c, 0x01, 0x11, + 0x00, 0x3a, 0x74, 0x5c, 0xa7, 0x28, 0x02, 0x0e, 0x22, 0xf7, 0xd5, 0xdd, + 0xe1, 0x06, 0x0b, 0x3e, 0x20, 0x12, 0x13, 0x10, 0xfc, 0xdd, 0x36, 0x01, + 0xff, 0xcd, 0xf1, 0x2b, 0x21, 0xf6, 0xff, 0x0b, 0x09, 0x03, 0x30, 0x0f, + 0x3a, 0x74, 0x5c, 0xa7, 0x20, 0x02, 0xcf, 0x0e, 0x78, 0xb1, 0x28, 0x0a, + 0x01, 0x0a, 0x00, 0xdd, 0xe5, 0xe1, 0x23, 0xeb, 0xed, 0xb0, 0xdf, 0xfe, + 0xe4, 0x20, 0x49, 0x3a, 0x74, 0x5c, 0xfe, 0x03, 0xca, 0x8a, 0x1c, 0xe7, + 0xcd, 0xb2, 0x28, 0xcb, 0xf9, 0x30, 0x0b, 0x21, 0x00, 0x00, 0x3a, 0x74, + 0x5c, 0x3d, 0x28, 0x15, 0xcf, 0x01, 0xc2, 0x8a, 0x1c, 0xcd, 0x30, 0x25, + 0x28, 0x18, 0x23, 0x7e, 0xdd, 0x77, 0x0b, 0x23, 0x7e, 0xdd, 0x77, 0x0c, + 0x23, 0xdd, 0x71, 0x0e, 0x3e, 0x01, 0xcb, 0x71, 0x28, 0x01, 0x3c, 0xdd, + 0x77, 0x00, 0xeb, 0xe7, 0xfe, 0x29, 0x20, 0xda, 0xe7, 0xcd, 0xee, 0x1b, + 0xeb, 0xc3, 0x5a, 0x07, 0xfe, 0xaa, 0x20, 0x1f, 0x3a, 0x74, 0x5c, 0xfe, + 0x03, 0xca, 0x8a, 0x1c, 0xe7, 0xcd, 0xee, 0x1b, 0xdd, 0x36, 0x0b, 0x00, + 0xdd, 0x36, 0x0c, 0x1b, 0x21, 0x00, 0x40, 0xdd, 0x75, 0x0d, 0xdd, 0x74, + 0x0e, 0x18, 0x4d, 0xfe, 0xaf, 0x20, 0x4f, 0x3a, 0x74, 0x5c, 0xfe, 0x03, + 0xca, 0x8a, 0x1c, 0xe7, 0xcd, 0x48, 0x20, 0x20, 0x0c, 0x3a, 0x74, 0x5c, + 0xa7, 0xca, 0x8a, 0x1c, 0xcd, 0xe6, 0x1c, 0x18, 0x0f, 0xcd, 0x82, 0x1c, + 0xdf, 0xfe, 0x2c, 0x28, 0x0c, 0x3a, 0x74, 0x5c, 0xa7, 0xca, 0x8a, 0x1c, + 0xcd, 0xe6, 0x1c, 0x18, 0x04, 0xe7, 0xcd, 0x82, 0x1c, 0xcd, 0xee, 0x1b, + 0xcd, 0x99, 0x1e, 0xdd, 0x71, 0x0b, 0xdd, 0x70, 0x0c, 0xcd, 0x99, 0x1e, + 0xdd, 0x71, 0x0d, 0xdd, 0x70, 0x0e, 0x60, 0x69, 0xdd, 0x36, 0x00, 0x03, + 0x18, 0x44, 0xfe, 0xca, 0x28, 0x09, 0xcd, 0xee, 0x1b, 0xdd, 0x36, 0x0e, + 0x80, 0x18, 0x17, 0x3a, 0x74, 0x5c, 0xa7, 0xc2, 0x8a, 0x1c, 0xe7, 0xcd, + 0x82, 0x1c, 0xcd, 0xee, 0x1b, 0xcd, 0x99, 0x1e, 0xdd, 0x71, 0x0d, 0xdd, + 0x70, 0x0e, 0xdd, 0x36, 0x00, 0x00, 0x2a, 0x59, 0x5c, 0xed, 0x5b, 0x53, + 0x5c, 0x37, 0xed, 0x52, 0xdd, 0x75, 0x0b, 0xdd, 0x74, 0x0c, 0x2a, 0x4b, + 0x5c, 0xed, 0x52, 0xdd, 0x75, 0x0f, 0xdd, 0x74, 0x10, 0xeb, 0x3a, 0x74, + 0x5c, 0xa7, 0xca, 0x70, 0x09, 0xe5, 0x01, 0x11, 0x00, 0xdd, 0x09, 0xdd, + 0xe5, 0x11, 0x11, 0x00, 0xaf, 0x37, 0xcd, 0x56, 0x05, 0xdd, 0xe1, 0x30, + 0xf2, 0x3e, 0xfe, 0xcd, 0x01, 0x16, 0xfd, 0x36, 0x52, 0x03, 0x0e, 0x80, + 0xdd, 0x7e, 0x00, 0xdd, 0xbe, 0xef, 0x20, 0x02, 0x0e, 0xf6, 0xfe, 0x04, + 0x30, 0xd9, 0x11, 0xc0, 0x09, 0xc5, 0xcd, 0x0a, 0x0c, 0xc1, 0xdd, 0xe5, + 0xd1, 0x21, 0xf0, 0xff, 0x19, 0x06, 0x0a, 0x7e, 0x3c, 0x20, 0x03, 0x79, + 0x80, 0x4f, 0x13, 0x1a, 0xbe, 0x23, 0x20, 0x01, 0x0c, 0xd7, 0x10, 0xf6, + 0xcb, 0x79, 0x20, 0xb3, 0x3e, 0x0d, 0xd7, 0xe1, 0xdd, 0x7e, 0x00, 0xfe, + 0x03, 0x28, 0x0c, 0x3a, 0x74, 0x5c, 0x3d, 0xca, 0x08, 0x08, 0xfe, 0x02, + 0xca, 0xb6, 0x08, 0xe5, 0xdd, 0x6e, 0xfa, 0xdd, 0x66, 0xfb, 0xdd, 0x5e, + 0x0b, 0xdd, 0x56, 0x0c, 0x7c, 0xb5, 0x28, 0x0d, 0xed, 0x52, 0x38, 0x26, + 0x28, 0x07, 0xdd, 0x7e, 0x00, 0xfe, 0x03, 0x20, 0x1d, 0xe1, 0x7c, 0xb5, + 0x20, 0x06, 0xdd, 0x6e, 0x0d, 0xdd, 0x66, 0x0e, 0xe5, 0xdd, 0xe1, 0x3a, + 0x74, 0x5c, 0xfe, 0x02, 0x37, 0x20, 0x01, 0xa7, 0x3e, 0xff, 0xcd, 0x56, + 0x05, 0xd8, 0xcf, 0x1a, 0xdd, 0x5e, 0x0b, 0xdd, 0x56, 0x0c, 0xe5, 0x7c, + 0xb5, 0x20, 0x06, 0x13, 0x13, 0x13, 0xeb, 0x18, 0x0c, 0xdd, 0x6e, 0xfa, + 0xdd, 0x66, 0xfb, 0xeb, 0x37, 0xed, 0x52, 0x38, 0x09, 0x11, 0x05, 0x00, + 0x19, 0x44, 0x4d, 0xcd, 0x05, 0x1f, 0xe1, 0xdd, 0x7e, 0x00, 0xa7, 0x28, + 0x3e, 0x7c, 0xb5, 0x28, 0x13, 0x2b, 0x46, 0x2b, 0x4e, 0x2b, 0x03, 0x03, + 0x03, 0xdd, 0x22, 0x5f, 0x5c, 0xcd, 0xe8, 0x19, 0xdd, 0x2a, 0x5f, 0x5c, + 0x2a, 0x59, 0x5c, 0x2b, 0xdd, 0x4e, 0x0b, 0xdd, 0x46, 0x0c, 0xc5, 0x03, + 0x03, 0x03, 0xdd, 0x7e, 0xfd, 0xf5, 0xcd, 0x55, 0x16, 0x23, 0xf1, 0x77, + 0xd1, 0x23, 0x73, 0x23, 0x72, 0x23, 0xe5, 0xdd, 0xe1, 0x37, 0x3e, 0xff, + 0xc3, 0x02, 0x08, 0xeb, 0x2a, 0x59, 0x5c, 0x2b, 0xdd, 0x22, 0x5f, 0x5c, + 0xdd, 0x4e, 0x0b, 0xdd, 0x46, 0x0c, 0xc5, 0xcd, 0xe5, 0x19, 0xc1, 0xe5, + 0xc5, 0xcd, 0x55, 0x16, 0xdd, 0x2a, 0x5f, 0x5c, 0x23, 0xdd, 0x4e, 0x0f, + 0xdd, 0x46, 0x10, 0x09, 0x22, 0x4b, 0x5c, 0xdd, 0x66, 0x0e, 0x7c, 0xe6, + 0xc0, 0x20, 0x0a, 0xdd, 0x6e, 0x0d, 0x22, 0x42, 0x5c, 0xfd, 0x36, 0x0a, + 0x00, 0xd1, 0xdd, 0xe1, 0x37, 0x3e, 0xff, 0xc3, 0x02, 0x08, 0xdd, 0x4e, + 0x0b, 0xdd, 0x46, 0x0c, 0xc5, 0x03, 0xf7, 0x36, 0x80, 0xeb, 0xd1, 0xe5, + 0xe5, 0xdd, 0xe1, 0x37, 0x3e, 0xff, 0xcd, 0x02, 0x08, 0xe1, 0xed, 0x5b, + 0x53, 0x5c, 0x7e, 0xe6, 0xc0, 0x20, 0x19, 0x1a, 0x13, 0xbe, 0x23, 0x20, + 0x02, 0x1a, 0xbe, 0x1b, 0x2b, 0x30, 0x08, 0xe5, 0xeb, 0xcd, 0xb8, 0x19, + 0xe1, 0x18, 0xec, 0xcd, 0x2c, 0x09, 0x18, 0xe2, 0x7e, 0x4f, 0xfe, 0x80, + 0xc8, 0xe5, 0x2a, 0x4b, 0x5c, 0x7e, 0xfe, 0x80, 0x28, 0x25, 0xb9, 0x28, + 0x08, 0xc5, 0xcd, 0xb8, 0x19, 0xc1, 0xeb, 0x18, 0xf0, 0xe6, 0xe0, 0xfe, + 0xa0, 0x20, 0x12, 0xd1, 0xd5, 0xe5, 0x23, 0x13, 0x1a, 0xbe, 0x20, 0x06, + 0x17, 0x30, 0xf7, 0xe1, 0x18, 0x03, 0xe1, 0x18, 0xe0, 0x3e, 0xff, 0xd1, + 0xeb, 0x3c, 0x37, 0xcd, 0x2c, 0x09, 0x18, 0xc4, 0x20, 0x10, 0x08, 0x22, + 0x5f, 0x5c, 0xeb, 0xcd, 0xb8, 0x19, 0xcd, 0xe8, 0x19, 0xeb, 0x2a, 0x5f, + 0x5c, 0x08, 0x08, 0xd5, 0xcd, 0xb8, 0x19, 0x22, 0x5f, 0x5c, 0x2a, 0x53, + 0x5c, 0xe3, 0xc5, 0x08, 0x38, 0x07, 0x2b, 0xcd, 0x55, 0x16, 0x23, 0x18, + 0x03, 0xcd, 0x55, 0x16, 0x23, 0xc1, 0xd1, 0xed, 0x53, 0x53, 0x5c, 0xed, + 0x5b, 0x5f, 0x5c, 0xc5, 0xd5, 0xeb, 0xed, 0xb0, 0xe1, 0xc1, 0xd5, 0xcd, + 0xe8, 0x19, 0xd1, 0xc9, 0xe5, 0x3e, 0xfd, 0xcd, 0x01, 0x16, 0xaf, 0x11, + 0xa1, 0x09, 0xcd, 0x0a, 0x0c, 0xfd, 0xcb, 0x02, 0xee, 0xcd, 0xd4, 0x15, + 0xdd, 0xe5, 0x11, 0x11, 0x00, 0xaf, 0xcd, 0xc2, 0x04, 0xdd, 0xe1, 0x06, + 0x32, 0x76, 0x10, 0xfd, 0xdd, 0x5e, 0x0b, 0xdd, 0x56, 0x0c, 0x3e, 0xff, + 0xdd, 0xe1, 0xc3, 0xc2, 0x04, 0x80, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, + 0x74, 0x61, 0x70, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, + 0xae, 0x0d, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x3a, 0xa0, 0x0d, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, + 0x3a, 0xa0, 0x0d, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, + 0x20, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3a, 0xa0, 0x0d, 0x42, 0x79, 0x74, + 0x65, 0x73, 0x3a, 0xa0, 0xcd, 0x03, 0x0b, 0xfe, 0x20, 0xd2, 0xd9, 0x0a, + 0xfe, 0x06, 0x38, 0x69, 0xfe, 0x18, 0x30, 0x65, 0x21, 0x0b, 0x0a, 0x5f, + 0x16, 0x00, 0x19, 0x5e, 0x19, 0xe5, 0xc3, 0x03, 0x0b, 0x4e, 0x57, 0x10, + 0x29, 0x54, 0x53, 0x52, 0x37, 0x50, 0x4f, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, + 0x5a, 0x54, 0x53, 0x0c, 0x3e, 0x22, 0xb9, 0x20, 0x11, 0xfd, 0xcb, 0x01, + 0x4e, 0x20, 0x09, 0x04, 0x0e, 0x02, 0x3e, 0x18, 0xb8, 0x20, 0x03, 0x05, + 0x0e, 0x21, 0xc3, 0xd9, 0x0d, 0x3a, 0x91, 0x5c, 0xf5, 0xfd, 0x36, 0x57, + 0x01, 0x3e, 0x20, 0xcd, 0x65, 0x0b, 0xf1, 0x32, 0x91, 0x5c, 0xc9, 0xfd, + 0xcb, 0x01, 0x4e, 0xc2, 0xcd, 0x0e, 0x0e, 0x21, 0xcd, 0x55, 0x0c, 0x05, + 0xc3, 0xd9, 0x0d, 0xcd, 0x03, 0x0b, 0x79, 0x3d, 0x3d, 0xe6, 0x10, 0x18, + 0x5a, 0x3e, 0x3f, 0x18, 0x6c, 0x11, 0x87, 0x0a, 0x32, 0x0f, 0x5c, 0x18, + 0x0b, 0x11, 0x6d, 0x0a, 0x18, 0x03, 0x11, 0x87, 0x0a, 0x32, 0x0e, 0x5c, + 0x2a, 0x51, 0x5c, 0x73, 0x23, 0x72, 0xc9, 0x11, 0xf4, 0x09, 0xcd, 0x80, + 0x0a, 0x2a, 0x0e, 0x5c, 0x57, 0x7d, 0xfe, 0x16, 0xda, 0x11, 0x22, 0x20, + 0x29, 0x44, 0x4a, 0x3e, 0x1f, 0x91, 0x38, 0x0c, 0xc6, 0x02, 0x4f, 0xfd, + 0xcb, 0x01, 0x4e, 0x20, 0x16, 0x3e, 0x16, 0x90, 0xda, 0x9f, 0x1e, 0x3c, + 0x47, 0x04, 0xfd, 0xcb, 0x02, 0x46, 0xc2, 0x55, 0x0c, 0xfd, 0xbe, 0x31, + 0xda, 0x86, 0x0c, 0xc3, 0xd9, 0x0d, 0x7c, 0xcd, 0x03, 0x0b, 0x81, 0x3d, + 0xe6, 0x1f, 0xc8, 0x57, 0xfd, 0xcb, 0x01, 0xc6, 0x3e, 0x20, 0xcd, 0x3b, + 0x0c, 0x15, 0x20, 0xf8, 0xc9, 0xcd, 0x24, 0x0b, 0xfd, 0xcb, 0x01, 0x4e, + 0x20, 0x1a, 0xfd, 0xcb, 0x02, 0x46, 0x20, 0x08, 0xed, 0x43, 0x88, 0x5c, + 0x22, 0x84, 0x5c, 0xc9, 0xed, 0x43, 0x8a, 0x5c, 0xed, 0x43, 0x82, 0x5c, + 0x22, 0x86, 0x5c, 0xc9, 0xfd, 0x71, 0x45, 0x22, 0x80, 0x5c, 0xc9, 0xfd, + 0xcb, 0x01, 0x4e, 0x20, 0x14, 0xed, 0x4b, 0x88, 0x5c, 0x2a, 0x84, 0x5c, + 0xfd, 0xcb, 0x02, 0x46, 0xc8, 0xed, 0x4b, 0x8a, 0x5c, 0x2a, 0x86, 0x5c, + 0xc9, 0xfd, 0x4e, 0x45, 0x2a, 0x80, 0x5c, 0xc9, 0xfe, 0x80, 0x38, 0x3d, + 0xfe, 0x90, 0x30, 0x26, 0x47, 0xcd, 0x38, 0x0b, 0xcd, 0x03, 0x0b, 0x11, + 0x92, 0x5c, 0x18, 0x47, 0x21, 0x92, 0x5c, 0xcd, 0x3e, 0x0b, 0xcb, 0x18, + 0x9f, 0xe6, 0x0f, 0x4f, 0xcb, 0x18, 0x9f, 0xe6, 0xf0, 0xb1, 0x0e, 0x04, + 0x77, 0x23, 0x0d, 0x20, 0xfb, 0xc9, 0xd6, 0xa5, 0x30, 0x09, 0xc6, 0x15, + 0xc5, 0xed, 0x4b, 0x7b, 0x5c, 0x18, 0x0b, 0xcd, 0x10, 0x0c, 0xc3, 0x03, + 0x0b, 0xc5, 0xed, 0x4b, 0x36, 0x5c, 0xeb, 0x21, 0x3b, 0x5c, 0xcb, 0x86, + 0xfe, 0x20, 0x20, 0x02, 0xcb, 0xc6, 0x26, 0x00, 0x6f, 0x29, 0x29, 0x29, + 0x09, 0xc1, 0xeb, 0x79, 0x3d, 0x3e, 0x21, 0x20, 0x0e, 0x05, 0x4f, 0xfd, + 0xcb, 0x01, 0x4e, 0x28, 0x06, 0xd5, 0xcd, 0xcd, 0x0e, 0xd1, 0x79, 0xb9, + 0xd5, 0xcc, 0x55, 0x0c, 0xd1, 0xc5, 0xe5, 0x3a, 0x91, 0x5c, 0x06, 0xff, + 0x1f, 0x38, 0x01, 0x04, 0x1f, 0x1f, 0x9f, 0x4f, 0x3e, 0x08, 0xa7, 0xfd, + 0xcb, 0x01, 0x4e, 0x28, 0x05, 0xfd, 0xcb, 0x30, 0xce, 0x37, 0xeb, 0x08, + 0x1a, 0xa0, 0xae, 0xa9, 0x12, 0x08, 0x38, 0x13, 0x14, 0x23, 0x3d, 0x20, + 0xf2, 0xeb, 0x25, 0xfd, 0xcb, 0x01, 0x4e, 0xcc, 0xdb, 0x0b, 0xe1, 0xc1, + 0x0d, 0x23, 0xc9, 0x08, 0x3e, 0x20, 0x83, 0x5f, 0x08, 0x18, 0xe6, 0x7c, + 0x0f, 0x0f, 0x0f, 0xe6, 0x03, 0xf6, 0x58, 0x67, 0xed, 0x5b, 0x8f, 0x5c, + 0x7e, 0xab, 0xa2, 0xab, 0xfd, 0xcb, 0x57, 0x76, 0x28, 0x08, 0xe6, 0xc7, + 0xcb, 0x57, 0x20, 0x02, 0xee, 0x38, 0xfd, 0xcb, 0x57, 0x66, 0x28, 0x08, + 0xe6, 0xf8, 0xcb, 0x6f, 0x20, 0x02, 0xee, 0x07, 0x77, 0xc9, 0xe5, 0x26, + 0x00, 0xe3, 0x18, 0x04, 0x11, 0x95, 0x00, 0xf5, 0xcd, 0x41, 0x0c, 0x38, + 0x09, 0x3e, 0x20, 0xfd, 0xcb, 0x01, 0x46, 0xcc, 0x3b, 0x0c, 0x1a, 0xe6, + 0x7f, 0xcd, 0x3b, 0x0c, 0x1a, 0x13, 0x87, 0x30, 0xf5, 0xd1, 0xfe, 0x48, + 0x28, 0x03, 0xfe, 0x82, 0xd8, 0x7a, 0xfe, 0x03, 0xd8, 0x3e, 0x20, 0xd5, + 0xd9, 0xd7, 0xd9, 0xd1, 0xc9, 0xf5, 0xeb, 0x3c, 0xcb, 0x7e, 0x23, 0x28, + 0xfb, 0x3d, 0x20, 0xf8, 0xeb, 0xf1, 0xfe, 0x20, 0xd8, 0x1a, 0xd6, 0x41, + 0xc9, 0xfd, 0xcb, 0x01, 0x4e, 0xc0, 0x11, 0xd9, 0x0d, 0xd5, 0x78, 0xfd, + 0xcb, 0x02, 0x46, 0xc2, 0x02, 0x0d, 0xfd, 0xbe, 0x31, 0x38, 0x1b, 0xc0, + 0xfd, 0xcb, 0x02, 0x66, 0x28, 0x16, 0xfd, 0x5e, 0x2d, 0x1d, 0x28, 0x5a, + 0x3e, 0x00, 0xcd, 0x01, 0x16, 0xed, 0x7b, 0x3f, 0x5c, 0xfd, 0xcb, 0x02, + 0xa6, 0xc9, 0xcf, 0x04, 0xfd, 0x35, 0x52, 0x20, 0x45, 0x3e, 0x18, 0x90, + 0x32, 0x8c, 0x5c, 0x2a, 0x8f, 0x5c, 0xe5, 0x3a, 0x91, 0x5c, 0xf5, 0x3e, + 0xfd, 0xcd, 0x01, 0x16, 0xaf, 0x11, 0xf8, 0x0c, 0xcd, 0x0a, 0x0c, 0xfd, + 0xcb, 0x02, 0xee, 0x21, 0x3b, 0x5c, 0xcb, 0xde, 0xcb, 0xae, 0xd9, 0xcd, + 0xd4, 0x15, 0xd9, 0xfe, 0x20, 0x28, 0x45, 0xfe, 0xe2, 0x28, 0x41, 0xf6, + 0x20, 0xfe, 0x6e, 0x28, 0x3b, 0x3e, 0xfe, 0xcd, 0x01, 0x16, 0xf1, 0x32, + 0x91, 0x5c, 0xe1, 0x22, 0x8f, 0x5c, 0xcd, 0xfe, 0x0d, 0xfd, 0x46, 0x31, + 0x04, 0x0e, 0x21, 0xc5, 0xcd, 0x9b, 0x0e, 0x7c, 0x0f, 0x0f, 0x0f, 0xe6, + 0x03, 0xf6, 0x58, 0x67, 0x11, 0xe0, 0x5a, 0x1a, 0x4e, 0x06, 0x20, 0xeb, + 0x12, 0x71, 0x13, 0x23, 0x10, 0xfa, 0xc1, 0xc9, 0x80, 0x73, 0x63, 0x72, + 0x6f, 0x6c, 0x6c, 0xbf, 0xcf, 0x0c, 0xfe, 0x02, 0x38, 0x80, 0xfd, 0x86, + 0x31, 0xd6, 0x19, 0xd0, 0xed, 0x44, 0xc5, 0x47, 0x2a, 0x8f, 0x5c, 0xe5, + 0x2a, 0x91, 0x5c, 0xe5, 0xcd, 0x4d, 0x0d, 0x78, 0xf5, 0x21, 0x6b, 0x5c, + 0x46, 0x78, 0x3c, 0x77, 0x21, 0x89, 0x5c, 0xbe, 0x38, 0x03, 0x34, 0x06, + 0x18, 0xcd, 0x00, 0x0e, 0xf1, 0x3d, 0x20, 0xe8, 0xe1, 0xfd, 0x75, 0x57, + 0xe1, 0x22, 0x8f, 0x5c, 0xed, 0x4b, 0x88, 0x5c, 0xfd, 0xcb, 0x02, 0x86, + 0xcd, 0xd9, 0x0d, 0xfd, 0xcb, 0x02, 0xc6, 0xc1, 0xc9, 0xaf, 0x2a, 0x8d, + 0x5c, 0xfd, 0xcb, 0x02, 0x46, 0x28, 0x04, 0x67, 0xfd, 0x6e, 0x0e, 0x22, + 0x8f, 0x5c, 0x21, 0x91, 0x5c, 0x20, 0x02, 0x7e, 0x0f, 0xae, 0xe6, 0x55, + 0xae, 0x77, 0xc9, 0xcd, 0xaf, 0x0d, 0x21, 0x3c, 0x5c, 0xcb, 0xae, 0xcb, + 0xc6, 0xcd, 0x4d, 0x0d, 0xfd, 0x46, 0x31, 0xcd, 0x44, 0x0e, 0x21, 0xc0, + 0x5a, 0x3a, 0x8d, 0x5c, 0x05, 0x18, 0x07, 0x0e, 0x20, 0x2b, 0x77, 0x0d, + 0x20, 0xfb, 0x10, 0xf7, 0xfd, 0x36, 0x31, 0x02, 0x3e, 0xfd, 0xcd, 0x01, + 0x16, 0x2a, 0x51, 0x5c, 0x11, 0xf4, 0x09, 0xa7, 0x73, 0x23, 0x72, 0x23, + 0x11, 0xa8, 0x10, 0x3f, 0x38, 0xf6, 0x01, 0x21, 0x17, 0x18, 0x2a, 0x21, + 0x00, 0x00, 0x22, 0x7d, 0x5c, 0xfd, 0xcb, 0x30, 0x86, 0xcd, 0x94, 0x0d, + 0x3e, 0xfe, 0xcd, 0x01, 0x16, 0xcd, 0x4d, 0x0d, 0x06, 0x18, 0xcd, 0x44, + 0x0e, 0x2a, 0x51, 0x5c, 0x11, 0xf4, 0x09, 0x73, 0x23, 0x72, 0xfd, 0x36, + 0x52, 0x01, 0x01, 0x21, 0x18, 0x21, 0x00, 0x5b, 0xfd, 0xcb, 0x01, 0x4e, + 0x20, 0x12, 0x78, 0xfd, 0xcb, 0x02, 0x46, 0x28, 0x05, 0xfd, 0x86, 0x31, + 0xd6, 0x18, 0xc5, 0x47, 0xcd, 0x9b, 0x0e, 0xc1, 0x3e, 0x21, 0x91, 0x5f, + 0x16, 0x00, 0x19, 0xc3, 0xdc, 0x0a, 0x06, 0x17, 0xcd, 0x9b, 0x0e, 0x0e, + 0x08, 0xc5, 0xe5, 0x78, 0xe6, 0x07, 0x78, 0x20, 0x0c, 0xeb, 0x21, 0xe0, + 0xf8, 0x19, 0xeb, 0x01, 0x20, 0x00, 0x3d, 0xed, 0xb0, 0xeb, 0x21, 0xe0, + 0xff, 0x19, 0xeb, 0x47, 0xe6, 0x07, 0x0f, 0x0f, 0x0f, 0x4f, 0x78, 0x06, + 0x00, 0xed, 0xb0, 0x06, 0x07, 0x09, 0xe6, 0xf8, 0x20, 0xdb, 0xe1, 0x24, + 0xc1, 0x0d, 0x20, 0xcd, 0xcd, 0x88, 0x0e, 0x21, 0xe0, 0xff, 0x19, 0xeb, + 0xed, 0xb0, 0x06, 0x01, 0xc5, 0xcd, 0x9b, 0x0e, 0x0e, 0x08, 0xc5, 0xe5, + 0x78, 0xe6, 0x07, 0x0f, 0x0f, 0x0f, 0x4f, 0x78, 0x06, 0x00, 0x0d, 0x54, + 0x5d, 0x36, 0x00, 0x13, 0xed, 0xb0, 0x11, 0x01, 0x07, 0x19, 0x3d, 0xe6, + 0xf8, 0x47, 0x20, 0xe5, 0xe1, 0x24, 0xc1, 0x0d, 0x20, 0xdc, 0xcd, 0x88, + 0x0e, 0x62, 0x6b, 0x13, 0x3a, 0x8d, 0x5c, 0xfd, 0xcb, 0x02, 0x46, 0x28, + 0x03, 0x3a, 0x48, 0x5c, 0x77, 0x0b, 0xed, 0xb0, 0xc1, 0x0e, 0x21, 0xc9, + 0x7c, 0x0f, 0x0f, 0x0f, 0x3d, 0xf6, 0x50, 0x67, 0xeb, 0x61, 0x68, 0x29, + 0x29, 0x29, 0x29, 0x29, 0x44, 0x4d, 0xc9, 0x3e, 0x18, 0x90, 0x57, 0x0f, + 0x0f, 0x0f, 0xe6, 0xe0, 0x6f, 0x7a, 0xe6, 0x18, 0xf6, 0x40, 0x67, 0xc9, + 0xf3, 0x06, 0xb0, 0x21, 0x00, 0x40, 0xe5, 0xc5, 0xcd, 0xf4, 0x0e, 0xc1, + 0xe1, 0x24, 0x7c, 0xe6, 0x07, 0x20, 0x0a, 0x7d, 0xc6, 0x20, 0x6f, 0x3f, + 0x9f, 0xe6, 0xf8, 0x84, 0x67, 0x10, 0xe7, 0x18, 0x0d, 0xf3, 0x21, 0x00, + 0x5b, 0x06, 0x08, 0xc5, 0xcd, 0xf4, 0x0e, 0xc1, 0x10, 0xf9, 0x3e, 0x04, + 0xd3, 0xfb, 0xfb, 0x21, 0x00, 0x5b, 0xfd, 0x75, 0x46, 0xaf, 0x47, 0x77, + 0x23, 0x10, 0xfc, 0xfd, 0xcb, 0x30, 0x8e, 0x0e, 0x21, 0xc3, 0xd9, 0x0d, + 0x78, 0xfe, 0x03, 0x9f, 0xe6, 0x02, 0xd3, 0xfb, 0x57, 0xcd, 0x54, 0x1f, + 0x38, 0x0a, 0x3e, 0x04, 0xd3, 0xfb, 0xfb, 0xcd, 0xdf, 0x0e, 0xcf, 0x0c, + 0xdb, 0xfb, 0x87, 0xf8, 0x30, 0xeb, 0x0e, 0x20, 0x5e, 0x23, 0x06, 0x08, + 0xcb, 0x12, 0xcb, 0x13, 0xcb, 0x1a, 0xdb, 0xfb, 0x1f, 0x30, 0xfb, 0x7a, + 0xd3, 0xfb, 0x10, 0xf0, 0x0d, 0x20, 0xe9, 0xc9, 0x2a, 0x3d, 0x5c, 0xe5, + 0x21, 0x7f, 0x10, 0xe5, 0xed, 0x73, 0x3d, 0x5c, 0xcd, 0xd4, 0x15, 0xf5, + 0x16, 0x00, 0xfd, 0x5e, 0xff, 0x21, 0xc8, 0x00, 0xcd, 0xb5, 0x03, 0xf1, + 0x21, 0x38, 0x0f, 0xe5, 0xfe, 0x18, 0x30, 0x31, 0xfe, 0x07, 0x38, 0x2d, + 0xfe, 0x10, 0x38, 0x3a, 0x01, 0x02, 0x00, 0x57, 0xfe, 0x16, 0x38, 0x0c, + 0x03, 0xfd, 0xcb, 0x37, 0x7e, 0xca, 0x1e, 0x10, 0xcd, 0xd4, 0x15, 0x5f, + 0xcd, 0xd4, 0x15, 0xd5, 0x2a, 0x5b, 0x5c, 0xfd, 0xcb, 0x07, 0x86, 0xcd, + 0x55, 0x16, 0xc1, 0x23, 0x70, 0x23, 0x71, 0x18, 0x0a, 0xfd, 0xcb, 0x07, + 0x86, 0x2a, 0x5b, 0x5c, 0xcd, 0x52, 0x16, 0x12, 0x13, 0xed, 0x53, 0x5b, + 0x5c, 0xc9, 0x5f, 0x16, 0x00, 0x21, 0x99, 0x0f, 0x19, 0x5e, 0x19, 0xe5, + 0x2a, 0x5b, 0x5c, 0xc9, 0x09, 0x66, 0x6a, 0x50, 0xb5, 0x70, 0x7e, 0xcf, + 0xd4, 0x2a, 0x49, 0x5c, 0xfd, 0xcb, 0x37, 0x6e, 0xc2, 0x97, 0x10, 0xcd, + 0x6e, 0x19, 0xcd, 0x95, 0x16, 0x7a, 0xb3, 0xca, 0x97, 0x10, 0xe5, 0x23, + 0x4e, 0x23, 0x46, 0x21, 0x0a, 0x00, 0x09, 0x44, 0x4d, 0xcd, 0x05, 0x1f, + 0xcd, 0x97, 0x10, 0x2a, 0x51, 0x5c, 0xe3, 0xe5, 0x3e, 0xff, 0xcd, 0x01, + 0x16, 0xe1, 0x2b, 0xfd, 0x35, 0x0f, 0xcd, 0x55, 0x18, 0xfd, 0x34, 0x0f, + 0x2a, 0x59, 0x5c, 0x23, 0x23, 0x23, 0x23, 0x22, 0x5b, 0x5c, 0xe1, 0xcd, + 0x15, 0x16, 0xc9, 0xfd, 0xcb, 0x37, 0x6e, 0x20, 0x08, 0x21, 0x49, 0x5c, + 0xcd, 0x0f, 0x19, 0x18, 0x6d, 0xfd, 0x36, 0x00, 0x10, 0x18, 0x1d, 0xcd, + 0x31, 0x10, 0x18, 0x05, 0x7e, 0xfe, 0x0d, 0xc8, 0x23, 0x22, 0x5b, 0x5c, + 0xc9, 0xcd, 0x31, 0x10, 0x01, 0x01, 0x00, 0xc3, 0xe8, 0x19, 0xcd, 0xd4, + 0x15, 0xcd, 0xd4, 0x15, 0xe1, 0xe1, 0xe1, 0x22, 0x3d, 0x5c, 0xfd, 0xcb, + 0x00, 0x7e, 0xc0, 0xf9, 0xc9, 0x37, 0xcd, 0x95, 0x11, 0xed, 0x52, 0x19, + 0x23, 0xc1, 0xd8, 0xc5, 0x44, 0x4d, 0x62, 0x6b, 0x23, 0x1a, 0xe6, 0xf0, + 0xfe, 0x10, 0x20, 0x09, 0x23, 0x1a, 0xd6, 0x17, 0xce, 0x00, 0x20, 0x01, + 0x23, 0xa7, 0xed, 0x42, 0x09, 0xeb, 0x38, 0xe6, 0xc9, 0xfd, 0xcb, 0x37, + 0x6e, 0xc0, 0x2a, 0x49, 0x5c, 0xcd, 0x6e, 0x19, 0xeb, 0xcd, 0x95, 0x16, + 0x21, 0x4a, 0x5c, 0xcd, 0x1c, 0x19, 0xcd, 0x95, 0x17, 0x3e, 0x00, 0xc3, + 0x01, 0x16, 0xfd, 0xcb, 0x37, 0x7e, 0x28, 0xa8, 0xc3, 0x81, 0x0f, 0xfd, + 0xcb, 0x30, 0x66, 0x28, 0xa1, 0xfd, 0x36, 0x00, 0xff, 0x16, 0x00, 0xfd, + 0x5e, 0xfe, 0x21, 0x90, 0x1a, 0xcd, 0xb5, 0x03, 0xc3, 0x30, 0x0f, 0xe5, + 0xcd, 0x90, 0x11, 0x2b, 0xcd, 0xe5, 0x19, 0x22, 0x5b, 0x5c, 0xfd, 0x36, + 0x07, 0x00, 0xe1, 0xc9, 0xfd, 0xcb, 0x02, 0x5e, 0xc4, 0x1d, 0x11, 0xa7, + 0xfd, 0xcb, 0x01, 0x6e, 0xc8, 0x3a, 0x08, 0x5c, 0xfd, 0xcb, 0x01, 0xae, + 0xf5, 0xfd, 0xcb, 0x02, 0x6e, 0xc4, 0x6e, 0x0d, 0xf1, 0xfe, 0x20, 0x30, + 0x52, 0xfe, 0x10, 0x30, 0x2d, 0xfe, 0x06, 0x30, 0x0a, 0x47, 0xe6, 0x01, + 0x4f, 0x78, 0x1f, 0xc6, 0x12, 0x18, 0x2a, 0x20, 0x09, 0x21, 0x6a, 0x5c, + 0x3e, 0x08, 0xae, 0x77, 0x18, 0x0e, 0xfe, 0x0e, 0xd8, 0xd6, 0x0d, 0x21, + 0x41, 0x5c, 0xbe, 0x77, 0x20, 0x02, 0x36, 0x00, 0xfd, 0xcb, 0x02, 0xde, + 0xbf, 0xc9, 0x47, 0xe6, 0x07, 0x4f, 0x3e, 0x10, 0xcb, 0x58, 0x20, 0x01, + 0x3c, 0xfd, 0x71, 0xd3, 0x11, 0x0d, 0x11, 0x18, 0x06, 0x3a, 0x0d, 0x5c, + 0x11, 0xa8, 0x10, 0x2a, 0x4f, 0x5c, 0x23, 0x23, 0x73, 0x23, 0x72, 0x37, + 0xc9, 0xcd, 0x4d, 0x0d, 0xfd, 0xcb, 0x02, 0x9e, 0xfd, 0xcb, 0x02, 0xae, + 0x2a, 0x8a, 0x5c, 0xe5, 0x2a, 0x3d, 0x5c, 0xe5, 0x21, 0x67, 0x11, 0xe5, + 0xed, 0x73, 0x3d, 0x5c, 0x2a, 0x82, 0x5c, 0xe5, 0x37, 0xcd, 0x95, 0x11, + 0xeb, 0xcd, 0x7d, 0x18, 0xeb, 0xcd, 0xe1, 0x18, 0x2a, 0x8a, 0x5c, 0xe3, + 0xeb, 0xcd, 0x4d, 0x0d, 0x3a, 0x8b, 0x5c, 0x92, 0x38, 0x26, 0x20, 0x06, + 0x7b, 0xfd, 0x96, 0x50, 0x30, 0x1e, 0x3e, 0x20, 0xd5, 0xcd, 0xf4, 0x09, + 0xd1, 0x18, 0xe9, 0x16, 0x00, 0xfd, 0x5e, 0xfe, 0x21, 0x90, 0x1a, 0xcd, + 0xb5, 0x03, 0xfd, 0x36, 0x00, 0xff, 0xed, 0x5b, 0x8a, 0x5c, 0x18, 0x02, + 0xd1, 0xe1, 0xe1, 0x22, 0x3d, 0x5c, 0xc1, 0xd5, 0xcd, 0xd9, 0x0d, 0xe1, + 0x22, 0x82, 0x5c, 0xfd, 0x36, 0x26, 0x00, 0xc9, 0x2a, 0x61, 0x5c, 0x2b, + 0xa7, 0xed, 0x5b, 0x59, 0x5c, 0xfd, 0xcb, 0x37, 0x6e, 0xc8, 0xed, 0x5b, + 0x61, 0x5c, 0xd8, 0x2a, 0x63, 0x5c, 0xc9, 0x7e, 0xfe, 0x0e, 0x01, 0x06, + 0x00, 0xcc, 0xe8, 0x19, 0x7e, 0x23, 0xfe, 0x0d, 0x20, 0xf1, 0xc9, 0xf3, + 0x3e, 0xff, 0xed, 0x5b, 0xb2, 0x5c, 0xd9, 0xed, 0x4b, 0xb4, 0x5c, 0xed, + 0x5b, 0x38, 0x5c, 0x2a, 0x7b, 0x5c, 0xd9, 0x47, 0x3e, 0x07, 0xd3, 0xfe, + 0x3e, 0x3f, 0xed, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x6b, + 0x36, 0x02, 0x2b, 0xbc, 0x20, 0xfa, 0xa7, 0xed, 0x52, 0x19, 0x23, 0x30, + 0x06, 0x35, 0x28, 0x03, 0x35, 0x28, 0xf3, 0x2b, 0xd9, 0xed, 0x43, 0xb4, + 0x5c, 0xed, 0x53, 0x38, 0x5c, 0x22, 0x7b, 0x5c, 0xd9, 0x04, 0x28, 0x19, + 0x22, 0xb4, 0x5c, 0x11, 0xaf, 0x3e, 0x01, 0xa8, 0x00, 0xeb, 0xed, 0xb8, + 0xeb, 0x23, 0x22, 0x7b, 0x5c, 0x2b, 0x01, 0x40, 0x00, 0xed, 0x43, 0x38, + 0x5c, 0x22, 0xb2, 0x5c, 0x21, 0x00, 0x3c, 0x22, 0x36, 0x5c, 0x2a, 0xb2, + 0x5c, 0x36, 0x3e, 0x2b, 0xf9, 0x2b, 0x2b, 0x22, 0x3d, 0x5c, 0xed, 0x56, + 0xfd, 0x21, 0x3a, 0x5c, 0xfb, 0x21, 0xb6, 0x5c, 0x22, 0x4f, 0x5c, 0x11, + 0xaf, 0x15, 0x01, 0x15, 0x00, 0xeb, 0xed, 0xb0, 0xeb, 0x2b, 0x22, 0x57, + 0x5c, 0x23, 0x22, 0x53, 0x5c, 0x22, 0x4b, 0x5c, 0x36, 0x80, 0x23, 0x22, + 0x59, 0x5c, 0x36, 0x0d, 0x23, 0x36, 0x80, 0x23, 0x22, 0x61, 0x5c, 0x22, + 0x63, 0x5c, 0x22, 0x65, 0x5c, 0x3e, 0x38, 0x32, 0x8d, 0x5c, 0x32, 0x8f, + 0x5c, 0x32, 0x48, 0x5c, 0x21, 0x23, 0x05, 0x22, 0x09, 0x5c, 0xfd, 0x35, + 0xc6, 0xfd, 0x35, 0xca, 0x21, 0xc6, 0x15, 0x11, 0x10, 0x5c, 0x01, 0x0e, + 0x00, 0xed, 0xb0, 0xfd, 0xcb, 0x01, 0xce, 0xcd, 0xdf, 0x0e, 0xfd, 0x36, + 0x31, 0x02, 0xcd, 0x6b, 0x0d, 0xaf, 0x11, 0x38, 0x15, 0xcd, 0x0a, 0x0c, + 0xfd, 0xcb, 0x02, 0xee, 0x18, 0x07, 0xfd, 0x36, 0x31, 0x02, 0xcd, 0x95, + 0x17, 0xcd, 0xb0, 0x16, 0x3e, 0x00, 0xcd, 0x01, 0x16, 0xcd, 0x2c, 0x0f, + 0xcd, 0x17, 0x1b, 0xfd, 0xcb, 0x00, 0x7e, 0x20, 0x12, 0xfd, 0xcb, 0x30, + 0x66, 0x28, 0x40, 0x2a, 0x59, 0x5c, 0xcd, 0xa7, 0x11, 0xfd, 0x36, 0x00, + 0xff, 0x18, 0xdd, 0x2a, 0x59, 0x5c, 0x22, 0x5d, 0x5c, 0xcd, 0xfb, 0x19, + 0x78, 0xb1, 0xc2, 0x5d, 0x15, 0xdf, 0xfe, 0x0d, 0x28, 0xc0, 0xfd, 0xcb, + 0x30, 0x46, 0xc4, 0xaf, 0x0d, 0xcd, 0x6e, 0x0d, 0x3e, 0x19, 0xfd, 0x96, + 0x4f, 0x32, 0x8c, 0x5c, 0xfd, 0xcb, 0x01, 0xfe, 0xfd, 0x36, 0x00, 0xff, + 0xfd, 0x36, 0x0a, 0x01, 0xcd, 0x8a, 0x1b, 0x76, 0xfd, 0xcb, 0x01, 0xae, + 0xfd, 0xcb, 0x30, 0x4e, 0xc4, 0xcd, 0x0e, 0x3a, 0x3a, 0x5c, 0x3c, 0xf5, + 0x21, 0x00, 0x00, 0xfd, 0x74, 0x37, 0xfd, 0x74, 0x26, 0x22, 0x0b, 0x5c, + 0x21, 0x01, 0x00, 0x22, 0x16, 0x5c, 0xcd, 0xb0, 0x16, 0xfd, 0xcb, 0x37, + 0xae, 0xcd, 0x6e, 0x0d, 0xfd, 0xcb, 0x02, 0xee, 0xf1, 0x47, 0xfe, 0x0a, + 0x38, 0x02, 0xc6, 0x07, 0xcd, 0xef, 0x15, 0x3e, 0x20, 0xd7, 0x78, 0x11, + 0x91, 0x13, 0xcd, 0x0a, 0x0c, 0xaf, 0x11, 0x36, 0x15, 0xcd, 0x0a, 0x0c, + 0xed, 0x4b, 0x45, 0x5c, 0xcd, 0x1b, 0x1a, 0x3e, 0x3a, 0xd7, 0xfd, 0x4e, + 0x0d, 0x06, 0x00, 0xcd, 0x1b, 0x1a, 0xcd, 0x97, 0x10, 0x3a, 0x3a, 0x5c, + 0x3c, 0x28, 0x1b, 0xfe, 0x09, 0x28, 0x04, 0xfe, 0x15, 0x20, 0x03, 0xfd, + 0x34, 0x0d, 0x01, 0x03, 0x00, 0x11, 0x70, 0x5c, 0x21, 0x44, 0x5c, 0xcb, + 0x7e, 0x28, 0x01, 0x09, 0xed, 0xb8, 0xfd, 0x36, 0x0a, 0xff, 0xfd, 0xcb, + 0x01, 0x9e, 0xc3, 0xac, 0x12, 0x80, 0x4f, 0xcb, 0x4e, 0x45, 0x58, 0x54, + 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x46, 0x4f, 0xd2, + 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6e, 0x6f, 0x74, + 0x20, 0x66, 0x6f, 0x75, 0x6e, 0xe4, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x20, 0x77, 0x72, 0x6f, 0x6e, 0xe7, 0x4f, 0x75, 0x74, + 0x20, 0x6f, 0x66, 0x20, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0xf9, 0x4f, 0x75, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x63, 0x72, 0x65, 0x65, 0xee, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x62, 0x69, + 0xe7, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4e, 0x20, 0x77, 0x69, 0x74, 0x68, + 0x6f, 0x75, 0x74, 0x20, 0x47, 0x4f, 0x53, 0x55, 0xc2, 0x45, 0x6e, 0x64, + 0x20, 0x6f, 0x66, 0x20, 0x66, 0x69, 0x6c, 0xe5, 0x53, 0x54, 0x4f, 0x50, + 0x20, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, 0xf4, 0x49, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, + 0x6e, 0xf4, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x20, 0x6f, 0x75, + 0x74, 0x20, 0x6f, 0x66, 0x20, 0x72, 0x61, 0x6e, 0x67, 0xe5, 0x4e, 0x6f, + 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, 0x42, 0x41, + 0x53, 0x49, 0xc3, 0x42, 0x52, 0x45, 0x41, 0x4b, 0x20, 0x2d, 0x20, 0x43, + 0x4f, 0x4e, 0x54, 0x20, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0xf3, 0x4f, + 0x75, 0x74, 0x20, 0x6f, 0x66, 0x20, 0x44, 0x41, 0x54, 0xc1, 0x49, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x6e, + 0x61, 0x6d, 0xe5, 0x4e, 0x6f, 0x20, 0x72, 0x6f, 0x6f, 0x6d, 0x20, 0x66, + 0x6f, 0x72, 0x20, 0x6c, 0x69, 0x6e, 0xe5, 0x53, 0x54, 0x4f, 0x50, 0x20, + 0x69, 0x6e, 0x20, 0x49, 0x4e, 0x50, 0x55, 0xd4, 0x46, 0x4f, 0x52, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x4e, 0x45, 0x58, 0xd4, + 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x20, 0x49, 0x2f, 0x4f, 0x20, + 0x64, 0x65, 0x76, 0x69, 0x63, 0xe5, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0xf2, 0x42, 0x52, 0x45, 0x41, + 0x4b, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0xed, 0x52, 0x41, 0x4d, 0x54, 0x4f, 0x50, 0x20, 0x6e, 0x6f, 0x20, + 0x67, 0x6f, 0x6f, 0xe4, 0x53, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x20, 0x6c, 0x6f, 0x73, 0xf4, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x20, 0x73, 0x74, 0x72, 0x65, 0x61, 0xed, 0x46, 0x4e, 0x20, 0x77, + 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x44, 0x45, 0xc6, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x20, 0x65, 0x72, 0x72, 0x6f, + 0xf2, 0x54, 0x61, 0x70, 0x65, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, + 0x67, 0x20, 0x65, 0x72, 0x72, 0x6f, 0xf2, 0x2c, 0xa0, 0x7f, 0x20, 0x31, + 0x39, 0x38, 0x32, 0x20, 0x53, 0x69, 0x6e, 0x63, 0x6c, 0x61, 0x69, 0x72, + 0x20, 0x52, 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, 0x4c, 0x74, + 0xe4, 0x3e, 0x10, 0x01, 0x00, 0x00, 0xc3, 0x13, 0x13, 0xed, 0x43, 0x49, + 0x5c, 0x2a, 0x5d, 0x5c, 0xeb, 0x21, 0x55, 0x15, 0xe5, 0x2a, 0x61, 0x5c, + 0x37, 0xed, 0x52, 0xe5, 0x60, 0x69, 0xcd, 0x6e, 0x19, 0x20, 0x06, 0xcd, + 0xb8, 0x19, 0xcd, 0xe8, 0x19, 0xc1, 0x79, 0x3d, 0xb0, 0x28, 0x28, 0xc5, + 0x03, 0x03, 0x03, 0x03, 0x2b, 0xed, 0x5b, 0x53, 0x5c, 0xd5, 0xcd, 0x55, + 0x16, 0xe1, 0x22, 0x53, 0x5c, 0xc1, 0xc5, 0x13, 0x2a, 0x61, 0x5c, 0x2b, + 0x2b, 0xed, 0xb8, 0x2a, 0x49, 0x5c, 0xeb, 0xc1, 0x70, 0x2b, 0x71, 0x2b, + 0x73, 0x2b, 0x72, 0xf1, 0xc3, 0xa2, 0x12, 0xf4, 0x09, 0xa8, 0x10, 0x4b, + 0xf4, 0x09, 0xc4, 0x15, 0x53, 0x81, 0x0f, 0xc4, 0x15, 0x52, 0xf4, 0x09, + 0xc4, 0x15, 0x50, 0x80, 0xcf, 0x12, 0x01, 0x00, 0x06, 0x00, 0x0b, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x06, 0x00, 0x10, 0x00, 0xfd, 0xcb, 0x02, 0x6e, + 0x20, 0x04, 0xfd, 0xcb, 0x02, 0xde, 0xcd, 0xe6, 0x15, 0xd8, 0x28, 0xfa, + 0xcf, 0x07, 0xd9, 0xe5, 0x2a, 0x51, 0x5c, 0x23, 0x23, 0x18, 0x08, 0x1e, + 0x30, 0x83, 0xd9, 0xe5, 0x2a, 0x51, 0x5c, 0x5e, 0x23, 0x56, 0xeb, 0xcd, + 0x2c, 0x16, 0xe1, 0xd9, 0xc9, 0x87, 0xc6, 0x16, 0x6f, 0x26, 0x5c, 0x5e, + 0x23, 0x56, 0x7a, 0xb3, 0x20, 0x02, 0xcf, 0x17, 0x1b, 0x2a, 0x4f, 0x5c, + 0x19, 0x22, 0x51, 0x5c, 0xfd, 0xcb, 0x30, 0xa6, 0x23, 0x23, 0x23, 0x23, + 0x4e, 0x21, 0x2d, 0x16, 0xcd, 0xdc, 0x16, 0xd0, 0x16, 0x00, 0x5e, 0x19, + 0xe9, 0x4b, 0x06, 0x53, 0x12, 0x50, 0x1b, 0x00, 0xfd, 0xcb, 0x02, 0xc6, + 0xfd, 0xcb, 0x01, 0xae, 0xfd, 0xcb, 0x30, 0xe6, 0x18, 0x04, 0xfd, 0xcb, + 0x02, 0x86, 0xfd, 0xcb, 0x01, 0x8e, 0xc3, 0x4d, 0x0d, 0xfd, 0xcb, 0x01, + 0xce, 0xc9, 0x01, 0x01, 0x00, 0xe5, 0xcd, 0x05, 0x1f, 0xe1, 0xcd, 0x64, + 0x16, 0x2a, 0x65, 0x5c, 0xeb, 0xed, 0xb8, 0xc9, 0xf5, 0xe5, 0x21, 0x4b, + 0x5c, 0x3e, 0x0e, 0x5e, 0x23, 0x56, 0xe3, 0xa7, 0xed, 0x52, 0x19, 0xe3, + 0x30, 0x09, 0xd5, 0xeb, 0x09, 0xeb, 0x72, 0x2b, 0x73, 0x23, 0xd1, 0x23, + 0x3d, 0x20, 0xe8, 0xeb, 0xd1, 0xf1, 0xa7, 0xed, 0x52, 0x44, 0x4d, 0x03, + 0x19, 0xeb, 0xc9, 0x00, 0x00, 0xeb, 0x11, 0x8f, 0x16, 0x7e, 0xe6, 0xc0, + 0x20, 0xf7, 0x56, 0x23, 0x5e, 0xc9, 0x2a, 0x63, 0x5c, 0x2b, 0xcd, 0x55, + 0x16, 0x23, 0x23, 0xc1, 0xed, 0x43, 0x61, 0x5c, 0xc1, 0xeb, 0x23, 0xc9, + 0x2a, 0x59, 0x5c, 0x36, 0x0d, 0x22, 0x5b, 0x5c, 0x23, 0x36, 0x80, 0x23, + 0x22, 0x61, 0x5c, 0x2a, 0x61, 0x5c, 0x22, 0x63, 0x5c, 0x2a, 0x63, 0x5c, + 0x22, 0x65, 0x5c, 0xe5, 0x21, 0x92, 0x5c, 0x22, 0x68, 0x5c, 0xe1, 0xc9, + 0xed, 0x5b, 0x59, 0x5c, 0xc3, 0xe5, 0x19, 0x23, 0x7e, 0xa7, 0xc8, 0xb9, + 0x23, 0x20, 0xf8, 0x37, 0xc9, 0xcd, 0x1e, 0x17, 0xcd, 0x01, 0x17, 0x01, + 0x00, 0x00, 0x11, 0xe2, 0xa3, 0xeb, 0x19, 0x38, 0x07, 0x01, 0xd4, 0x15, + 0x09, 0x4e, 0x23, 0x46, 0xeb, 0x71, 0x23, 0x70, 0xc9, 0xe5, 0x2a, 0x4f, + 0x5c, 0x09, 0x23, 0x23, 0x23, 0x4e, 0xeb, 0x21, 0x16, 0x17, 0xcd, 0xdc, + 0x16, 0x4e, 0x06, 0x00, 0x09, 0xe9, 0x4b, 0x05, 0x53, 0x03, 0x50, 0x01, + 0xe1, 0xc9, 0xcd, 0x94, 0x1e, 0xfe, 0x10, 0x38, 0x02, 0xcf, 0x17, 0xc6, + 0x03, 0x07, 0x21, 0x10, 0x5c, 0x4f, 0x06, 0x00, 0x09, 0x4e, 0x23, 0x46, + 0x2b, 0xc9, 0xef, 0x01, 0x38, 0xcd, 0x1e, 0x17, 0x78, 0xb1, 0x28, 0x16, + 0xeb, 0x2a, 0x4f, 0x5c, 0x09, 0x23, 0x23, 0x23, 0x7e, 0xeb, 0xfe, 0x4b, + 0x28, 0x08, 0xfe, 0x53, 0x28, 0x04, 0xfe, 0x50, 0x20, 0xcf, 0xcd, 0x5d, + 0x17, 0x73, 0x23, 0x72, 0xc9, 0xe5, 0xcd, 0xf1, 0x2b, 0x78, 0xb1, 0x20, + 0x02, 0xcf, 0x0e, 0xc5, 0x1a, 0xe6, 0xdf, 0x4f, 0x21, 0x7a, 0x17, 0xcd, + 0xdc, 0x16, 0x30, 0xf1, 0x4e, 0x06, 0x00, 0x09, 0xc1, 0xe9, 0x4b, 0x06, + 0x53, 0x08, 0x50, 0x0a, 0x00, 0x1e, 0x01, 0x18, 0x06, 0x1e, 0x06, 0x18, + 0x02, 0x1e, 0x10, 0x0b, 0x78, 0xb1, 0x20, 0xd5, 0x57, 0xe1, 0xc9, 0x18, + 0x90, 0xed, 0x73, 0x3f, 0x5c, 0xfd, 0x36, 0x02, 0x10, 0xcd, 0xaf, 0x0d, + 0xfd, 0xcb, 0x02, 0xc6, 0xfd, 0x46, 0x31, 0xcd, 0x44, 0x0e, 0xfd, 0xcb, + 0x02, 0x86, 0xfd, 0xcb, 0x30, 0xc6, 0x2a, 0x49, 0x5c, 0xed, 0x5b, 0x6c, + 0x5c, 0xa7, 0xed, 0x52, 0x19, 0x38, 0x22, 0xd5, 0xcd, 0x6e, 0x19, 0x11, + 0xc0, 0x02, 0xeb, 0xed, 0x52, 0xe3, 0xcd, 0x6e, 0x19, 0xc1, 0xc5, 0xcd, + 0xb8, 0x19, 0xc1, 0x09, 0x38, 0x0e, 0xeb, 0x56, 0x23, 0x5e, 0x2b, 0xed, + 0x53, 0x6c, 0x5c, 0x18, 0xed, 0x22, 0x6c, 0x5c, 0x2a, 0x6c, 0x5c, 0xcd, + 0x6e, 0x19, 0x28, 0x01, 0xeb, 0xcd, 0x33, 0x18, 0xfd, 0xcb, 0x02, 0xa6, + 0xc9, 0x3e, 0x03, 0x18, 0x02, 0x3e, 0x02, 0xfd, 0x36, 0x02, 0x00, 0xcd, + 0x30, 0x25, 0xc4, 0x01, 0x16, 0xdf, 0xcd, 0x70, 0x20, 0x38, 0x14, 0xdf, + 0xfe, 0x3b, 0x28, 0x04, 0xfe, 0x2c, 0x20, 0x06, 0xe7, 0xcd, 0x82, 0x1c, + 0x18, 0x08, 0xcd, 0xe6, 0x1c, 0x18, 0x03, 0xcd, 0xde, 0x1c, 0xcd, 0xee, + 0x1b, 0xcd, 0x99, 0x1e, 0x78, 0xe6, 0x3f, 0x67, 0x69, 0x22, 0x49, 0x5c, + 0xcd, 0x6e, 0x19, 0x1e, 0x01, 0xcd, 0x55, 0x18, 0xd7, 0xfd, 0xcb, 0x02, + 0x66, 0x28, 0xf6, 0x3a, 0x6b, 0x5c, 0xfd, 0x96, 0x4f, 0x20, 0xee, 0xab, + 0xc8, 0xe5, 0xd5, 0x21, 0x6c, 0x5c, 0xcd, 0x0f, 0x19, 0xd1, 0xe1, 0x18, + 0xe0, 0xed, 0x4b, 0x49, 0x5c, 0xcd, 0x80, 0x19, 0x16, 0x3e, 0x28, 0x05, + 0x11, 0x00, 0x00, 0xcb, 0x13, 0xfd, 0x73, 0x2d, 0x7e, 0xfe, 0x40, 0xc1, + 0xd0, 0xc5, 0xcd, 0x28, 0x1a, 0x23, 0x23, 0x23, 0xfd, 0xcb, 0x01, 0x86, + 0x7a, 0xa7, 0x28, 0x05, 0xd7, 0xfd, 0xcb, 0x01, 0xc6, 0xd5, 0xeb, 0xfd, + 0xcb, 0x30, 0x96, 0x21, 0x3b, 0x5c, 0xcb, 0x96, 0xfd, 0xcb, 0x37, 0x6e, + 0x28, 0x02, 0xcb, 0xd6, 0x2a, 0x5f, 0x5c, 0xa7, 0xed, 0x52, 0x20, 0x05, + 0x3e, 0x3f, 0xcd, 0xc1, 0x18, 0xcd, 0xe1, 0x18, 0xeb, 0x7e, 0xcd, 0xb6, + 0x18, 0x23, 0xfe, 0x0d, 0x28, 0x06, 0xeb, 0xcd, 0x37, 0x19, 0x18, 0xe0, + 0xd1, 0xc9, 0xfe, 0x0e, 0xc0, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x7e, + 0xc9, 0xd9, 0x2a, 0x8f, 0x5c, 0xe5, 0xcb, 0xbc, 0xcb, 0xfd, 0x22, 0x8f, + 0x5c, 0x21, 0x91, 0x5c, 0x56, 0xd5, 0x36, 0x00, 0xcd, 0xf4, 0x09, 0xe1, + 0xfd, 0x74, 0x57, 0xe1, 0x22, 0x8f, 0x5c, 0xd9, 0xc9, 0x2a, 0x5b, 0x5c, + 0xa7, 0xed, 0x52, 0xc0, 0x3a, 0x41, 0x5c, 0xcb, 0x07, 0x28, 0x04, 0xc6, + 0x43, 0x18, 0x16, 0x21, 0x3b, 0x5c, 0xcb, 0x9e, 0x3e, 0x4b, 0xcb, 0x56, + 0x28, 0x0b, 0xcb, 0xde, 0x3c, 0xfd, 0xcb, 0x30, 0x5e, 0x28, 0x02, 0x3e, + 0x43, 0xd5, 0xcd, 0xc1, 0x18, 0xd1, 0xc9, 0x5e, 0x23, 0x56, 0xe5, 0xeb, + 0x23, 0xcd, 0x6e, 0x19, 0xcd, 0x95, 0x16, 0xe1, 0xfd, 0xcb, 0x37, 0x6e, + 0xc0, 0x72, 0x2b, 0x73, 0xc9, 0x7b, 0xa7, 0xf8, 0x18, 0x0d, 0xaf, 0x09, + 0x3c, 0x38, 0xfc, 0xed, 0x42, 0x3d, 0x28, 0xf1, 0xc3, 0xef, 0x15, 0xcd, + 0x1b, 0x2d, 0x30, 0x30, 0xfe, 0x21, 0x38, 0x2c, 0xfd, 0xcb, 0x01, 0x96, + 0xfe, 0xcb, 0x28, 0x24, 0xfe, 0x3a, 0x20, 0x0e, 0xfd, 0xcb, 0x37, 0x6e, + 0x20, 0x16, 0xfd, 0xcb, 0x30, 0x56, 0x28, 0x14, 0x18, 0x0e, 0xfe, 0x22, + 0x20, 0x0a, 0xf5, 0x3a, 0x6a, 0x5c, 0xee, 0x04, 0x32, 0x6a, 0x5c, 0xf1, + 0xfd, 0xcb, 0x01, 0xd6, 0xd7, 0xc9, 0xe5, 0x2a, 0x53, 0x5c, 0x54, 0x5d, + 0xc1, 0xcd, 0x80, 0x19, 0xd0, 0xc5, 0xcd, 0xb8, 0x19, 0xeb, 0x18, 0xf4, + 0x7e, 0xb8, 0xc0, 0x23, 0x7e, 0x2b, 0xb9, 0xc9, 0x23, 0x23, 0x23, 0x22, + 0x5d, 0x5c, 0x0e, 0x00, 0x15, 0xc8, 0xe7, 0xbb, 0x20, 0x04, 0xa7, 0xc9, + 0x23, 0x7e, 0xcd, 0xb6, 0x18, 0x22, 0x5d, 0x5c, 0xfe, 0x22, 0x20, 0x01, + 0x0d, 0xfe, 0x3a, 0x28, 0x04, 0xfe, 0xcb, 0x20, 0x04, 0xcb, 0x41, 0x28, + 0xdf, 0xfe, 0x0d, 0x20, 0xe3, 0x15, 0x37, 0xc9, 0xe5, 0x7e, 0xfe, 0x40, + 0x38, 0x17, 0xcb, 0x6f, 0x28, 0x14, 0x87, 0xfa, 0xc7, 0x19, 0x3f, 0x01, + 0x05, 0x00, 0x30, 0x02, 0x0e, 0x12, 0x17, 0x23, 0x7e, 0x30, 0xfb, 0x18, + 0x06, 0x23, 0x23, 0x4e, 0x23, 0x46, 0x23, 0x09, 0xd1, 0xa7, 0xed, 0x52, + 0x44, 0x4d, 0x19, 0xeb, 0xc9, 0xcd, 0xdd, 0x19, 0xc5, 0x78, 0x2f, 0x47, + 0x79, 0x2f, 0x4f, 0x03, 0xcd, 0x64, 0x16, 0xeb, 0xe1, 0x19, 0xd5, 0xed, + 0xb0, 0xe1, 0xc9, 0x2a, 0x59, 0x5c, 0x2b, 0x22, 0x5d, 0x5c, 0xe7, 0x21, + 0x92, 0x5c, 0x22, 0x65, 0x5c, 0xcd, 0x3b, 0x2d, 0xcd, 0xa2, 0x2d, 0x38, + 0x04, 0x21, 0xf0, 0xd8, 0x09, 0xda, 0x8a, 0x1c, 0xc3, 0xc5, 0x16, 0xd5, + 0xe5, 0xaf, 0xcb, 0x78, 0x20, 0x20, 0x60, 0x69, 0x1e, 0xff, 0x18, 0x08, + 0xd5, 0x56, 0x23, 0x5e, 0xe5, 0xeb, 0x1e, 0x20, 0x01, 0x18, 0xfc, 0xcd, + 0x2a, 0x19, 0x01, 0x9c, 0xff, 0xcd, 0x2a, 0x19, 0x0e, 0xf6, 0xcd, 0x2a, + 0x19, 0x7d, 0xcd, 0xef, 0x15, 0xe1, 0xd1, 0xc9, 0xb1, 0xcb, 0xbc, 0xbf, + 0xc4, 0xaf, 0xb4, 0x93, 0x91, 0x92, 0x95, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x7f, 0x81, 0x2e, 0x6c, 0x6e, 0x70, 0x48, 0x94, 0x56, 0x3f, + 0x41, 0x2b, 0x17, 0x1f, 0x37, 0x77, 0x44, 0x0f, 0x59, 0x2b, 0x43, 0x2d, + 0x51, 0x3a, 0x6d, 0x42, 0x0d, 0x49, 0x5c, 0x44, 0x15, 0x5d, 0x01, 0x3d, + 0x02, 0x06, 0x00, 0x67, 0x1e, 0x06, 0xcb, 0x05, 0xf0, 0x1c, 0x06, 0x00, + 0xed, 0x1e, 0x00, 0xee, 0x1c, 0x00, 0x23, 0x1f, 0x04, 0x3d, 0x06, 0xcc, + 0x06, 0x05, 0x03, 0x1d, 0x04, 0x00, 0xab, 0x1d, 0x05, 0xcd, 0x1f, 0x05, + 0x89, 0x20, 0x05, 0x02, 0x2c, 0x05, 0xb2, 0x1b, 0x00, 0xb7, 0x11, 0x03, + 0xa1, 0x1e, 0x05, 0xf9, 0x17, 0x08, 0x00, 0x80, 0x1e, 0x03, 0x4f, 0x1e, + 0x00, 0x5f, 0x1e, 0x03, 0xac, 0x1e, 0x00, 0x6b, 0x0d, 0x09, 0x00, 0xdc, + 0x22, 0x06, 0x00, 0x3a, 0x1f, 0x05, 0xed, 0x1d, 0x05, 0x27, 0x1e, 0x03, + 0x42, 0x1e, 0x09, 0x05, 0x82, 0x23, 0x00, 0xac, 0x0e, 0x05, 0xc9, 0x1f, + 0x05, 0xf5, 0x17, 0x0b, 0x0b, 0x0b, 0x0b, 0x08, 0x00, 0xf8, 0x03, 0x09, + 0x05, 0x20, 0x23, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x00, 0x7a, + 0x1e, 0x06, 0x00, 0x94, 0x22, 0x05, 0x60, 0x1f, 0x06, 0x2c, 0x0a, 0x00, + 0x36, 0x17, 0x06, 0x00, 0xe5, 0x16, 0x0a, 0x00, 0x93, 0x17, 0x0a, 0x2c, + 0x0a, 0x00, 0x93, 0x17, 0x0a, 0x00, 0x93, 0x17, 0x00, 0x93, 0x17, 0xfd, + 0xcb, 0x01, 0xbe, 0xcd, 0xfb, 0x19, 0xaf, 0x32, 0x47, 0x5c, 0x3d, 0x32, + 0x3a, 0x5c, 0x18, 0x01, 0xe7, 0xcd, 0xbf, 0x16, 0xfd, 0x34, 0x0d, 0xfa, + 0x8a, 0x1c, 0xdf, 0x06, 0x00, 0xfe, 0x0d, 0x28, 0x7a, 0xfe, 0x3a, 0x28, + 0xeb, 0x21, 0x76, 0x1b, 0xe5, 0x4f, 0xe7, 0x79, 0xd6, 0xce, 0xda, 0x8a, + 0x1c, 0x4f, 0x21, 0x48, 0x1a, 0x09, 0x4e, 0x09, 0x18, 0x03, 0x2a, 0x74, + 0x5c, 0x7e, 0x23, 0x22, 0x74, 0x5c, 0x01, 0x52, 0x1b, 0xc5, 0x4f, 0xfe, + 0x20, 0x30, 0x0c, 0x21, 0x01, 0x1c, 0x06, 0x00, 0x09, 0x4e, 0x09, 0xe5, + 0xdf, 0x05, 0xc9, 0xdf, 0xb9, 0xc2, 0x8a, 0x1c, 0xe7, 0xc9, 0xcd, 0x54, + 0x1f, 0x38, 0x02, 0xcf, 0x14, 0xfd, 0xcb, 0x0a, 0x7e, 0x20, 0x71, 0x2a, + 0x42, 0x5c, 0xcb, 0x7c, 0x28, 0x14, 0x21, 0xfe, 0xff, 0x22, 0x45, 0x5c, + 0x2a, 0x61, 0x5c, 0x2b, 0xed, 0x5b, 0x59, 0x5c, 0x1b, 0x3a, 0x44, 0x5c, + 0x18, 0x33, 0xcd, 0x6e, 0x19, 0x3a, 0x44, 0x5c, 0x28, 0x19, 0xa7, 0x20, + 0x43, 0x47, 0x7e, 0xe6, 0xc0, 0x78, 0x28, 0x0f, 0xcf, 0xff, 0xc1, 0xcd, + 0x30, 0x25, 0xc8, 0x2a, 0x55, 0x5c, 0x3e, 0xc0, 0xa6, 0xc0, 0xaf, 0xfe, + 0x01, 0xce, 0x00, 0x56, 0x23, 0x5e, 0xed, 0x53, 0x45, 0x5c, 0x23, 0x5e, + 0x23, 0x56, 0xeb, 0x19, 0x23, 0x22, 0x55, 0x5c, 0xeb, 0x22, 0x5d, 0x5c, + 0x57, 0x1e, 0x00, 0xfd, 0x36, 0x0a, 0xff, 0x15, 0xfd, 0x72, 0x0d, 0xca, + 0x28, 0x1b, 0x14, 0xcd, 0x8b, 0x19, 0x28, 0x08, 0xcf, 0x16, 0xcd, 0x30, + 0x25, 0xc0, 0xc1, 0xc1, 0xdf, 0xfe, 0x0d, 0x28, 0xba, 0xfe, 0x3a, 0xca, + 0x28, 0x1b, 0xc3, 0x8a, 0x1c, 0x0f, 0x1d, 0x4b, 0x09, 0x67, 0x0b, 0x7b, + 0x8e, 0x71, 0xb4, 0x81, 0xcf, 0xcd, 0xde, 0x1c, 0xbf, 0xc1, 0xcc, 0xee, + 0x1b, 0xeb, 0x2a, 0x74, 0x5c, 0x4e, 0x23, 0x46, 0xeb, 0xc5, 0xc9, 0xcd, + 0xb2, 0x28, 0xfd, 0x36, 0x37, 0x00, 0x30, 0x08, 0xfd, 0xcb, 0x37, 0xce, + 0x20, 0x18, 0xcf, 0x01, 0xcc, 0x96, 0x29, 0xfd, 0xcb, 0x01, 0x76, 0x20, + 0x0d, 0xaf, 0xcd, 0x30, 0x25, 0xc4, 0xf1, 0x2b, 0x21, 0x71, 0x5c, 0xb6, + 0x77, 0xeb, 0xed, 0x43, 0x72, 0x5c, 0x22, 0x4d, 0x5c, 0xc9, 0xc1, 0xcd, + 0x56, 0x1c, 0xcd, 0xee, 0x1b, 0xc9, 0x3a, 0x3b, 0x5c, 0xf5, 0xcd, 0xfb, + 0x24, 0xf1, 0xfd, 0x56, 0x01, 0xaa, 0xe6, 0x40, 0x20, 0x24, 0xcb, 0x7a, + 0xc2, 0xff, 0x2a, 0xc9, 0xcd, 0xb2, 0x28, 0xf5, 0x79, 0xf6, 0x9f, 0x3c, + 0x20, 0x14, 0xf1, 0x18, 0xa9, 0xe7, 0xcd, 0x82, 0x1c, 0xfe, 0x2c, 0x20, + 0x09, 0xe7, 0xcd, 0xfb, 0x24, 0xfd, 0xcb, 0x01, 0x76, 0xc0, 0xcf, 0x0b, + 0xcd, 0xfb, 0x24, 0xfd, 0xcb, 0x01, 0x76, 0xc8, 0x18, 0xf4, 0xfd, 0xcb, + 0x01, 0x7e, 0xfd, 0xcb, 0x02, 0x86, 0xc4, 0x4d, 0x0d, 0xf1, 0x3a, 0x74, + 0x5c, 0xd6, 0x13, 0xcd, 0xfc, 0x21, 0xcd, 0xee, 0x1b, 0x2a, 0x8f, 0x5c, + 0x22, 0x8d, 0x5c, 0x21, 0x91, 0x5c, 0x7e, 0x07, 0xae, 0xe6, 0xaa, 0xae, + 0x77, 0xc9, 0xcd, 0x30, 0x25, 0x28, 0x13, 0xfd, 0xcb, 0x02, 0x86, 0xcd, + 0x4d, 0x0d, 0x21, 0x90, 0x5c, 0x7e, 0xf6, 0xf8, 0x77, 0xfd, 0xcb, 0x57, + 0xb6, 0xdf, 0xcd, 0xe2, 0x21, 0x18, 0x9f, 0xc3, 0x05, 0x06, 0xfe, 0x0d, + 0x28, 0x04, 0xfe, 0x3a, 0x20, 0x9c, 0xcd, 0x30, 0x25, 0xc8, 0xef, 0xa0, + 0x38, 0xc9, 0xcf, 0x08, 0xc1, 0xcd, 0x30, 0x25, 0x28, 0x0a, 0xef, 0x02, + 0x38, 0xeb, 0xcd, 0xe9, 0x34, 0xda, 0xb3, 0x1b, 0xc3, 0x29, 0x1b, 0xfe, + 0xcd, 0x20, 0x09, 0xe7, 0xcd, 0x82, 0x1c, 0xcd, 0xee, 0x1b, 0x18, 0x06, + 0xcd, 0xee, 0x1b, 0xef, 0xa1, 0x38, 0xef, 0xc0, 0x02, 0x01, 0xe0, 0x01, + 0x38, 0xcd, 0xff, 0x2a, 0x22, 0x68, 0x5c, 0x2b, 0x7e, 0xcb, 0xfe, 0x01, + 0x06, 0x00, 0x09, 0x07, 0x38, 0x06, 0x0e, 0x0d, 0xcd, 0x55, 0x16, 0x23, + 0xe5, 0xef, 0x02, 0x02, 0x38, 0xe1, 0xeb, 0x0e, 0x0a, 0xed, 0xb0, 0x2a, + 0x45, 0x5c, 0xeb, 0x73, 0x23, 0x72, 0xfd, 0x56, 0x0d, 0x14, 0x23, 0x72, + 0xcd, 0xda, 0x1d, 0xd0, 0xfd, 0x46, 0x38, 0x2a, 0x45, 0x5c, 0x22, 0x42, + 0x5c, 0x3a, 0x47, 0x5c, 0xed, 0x44, 0x57, 0x2a, 0x5d, 0x5c, 0x1e, 0xf3, + 0xc5, 0xed, 0x4b, 0x55, 0x5c, 0xcd, 0x86, 0x1d, 0xed, 0x43, 0x55, 0x5c, + 0xc1, 0x38, 0x11, 0xe7, 0xf6, 0x20, 0xb8, 0x28, 0x03, 0xe7, 0x18, 0xe8, + 0xe7, 0x3e, 0x01, 0x92, 0x32, 0x44, 0x5c, 0xc9, 0xcf, 0x11, 0x7e, 0xfe, + 0x3a, 0x28, 0x18, 0x23, 0x7e, 0xe6, 0xc0, 0x37, 0xc0, 0x46, 0x23, 0x4e, + 0xed, 0x43, 0x42, 0x5c, 0x23, 0x4e, 0x23, 0x46, 0xe5, 0x09, 0x44, 0x4d, + 0xe1, 0x16, 0x00, 0xc5, 0xcd, 0x8b, 0x19, 0xc1, 0xd0, 0x18, 0xe0, 0xfd, + 0xcb, 0x37, 0x4e, 0xc2, 0x2e, 0x1c, 0x2a, 0x4d, 0x5c, 0xcb, 0x7e, 0x28, + 0x1f, 0x23, 0x22, 0x68, 0x5c, 0xef, 0xe0, 0xe2, 0x0f, 0xc0, 0x02, 0x38, + 0xcd, 0xda, 0x1d, 0xd8, 0x2a, 0x68, 0x5c, 0x11, 0x0f, 0x00, 0x19, 0x5e, + 0x23, 0x56, 0x23, 0x66, 0xeb, 0xc3, 0x73, 0x1e, 0xcf, 0x00, 0xef, 0xe1, + 0xe0, 0xe2, 0x36, 0x00, 0x02, 0x01, 0x03, 0x37, 0x00, 0x04, 0x38, 0xa7, + 0xc9, 0x38, 0x37, 0xc9, 0xe7, 0xcd, 0x1f, 0x1c, 0xcd, 0x30, 0x25, 0x28, + 0x29, 0xdf, 0x22, 0x5f, 0x5c, 0x2a, 0x57, 0x5c, 0x7e, 0xfe, 0x2c, 0x28, + 0x09, 0x1e, 0xe4, 0xcd, 0x86, 0x1d, 0x30, 0x02, 0xcf, 0x0d, 0xcd, 0x77, + 0x00, 0xcd, 0x56, 0x1c, 0xdf, 0x22, 0x57, 0x5c, 0x2a, 0x5f, 0x5c, 0xfd, + 0x36, 0x26, 0x00, 0xcd, 0x78, 0x00, 0xdf, 0xfe, 0x2c, 0x28, 0xc9, 0xcd, + 0xee, 0x1b, 0xc9, 0xcd, 0x30, 0x25, 0x20, 0x0b, 0xcd, 0xfb, 0x24, 0xfe, + 0x2c, 0xc4, 0xee, 0x1b, 0xe7, 0x18, 0xf5, 0x3e, 0xe4, 0x47, 0xed, 0xb9, + 0x11, 0x00, 0x02, 0xc3, 0x8b, 0x19, 0xcd, 0x99, 0x1e, 0x60, 0x69, 0xcd, + 0x6e, 0x19, 0x2b, 0x22, 0x57, 0x5c, 0xc9, 0xcd, 0x99, 0x1e, 0x78, 0xb1, + 0x20, 0x04, 0xed, 0x4b, 0x78, 0x5c, 0xed, 0x43, 0x76, 0x5c, 0xc9, 0x2a, + 0x6e, 0x5c, 0xfd, 0x56, 0x36, 0x18, 0x0c, 0xcd, 0x99, 0x1e, 0x60, 0x69, + 0x16, 0x00, 0x7c, 0xfe, 0xf0, 0x30, 0x2c, 0x22, 0x42, 0x5c, 0xfd, 0x72, + 0x0a, 0xc9, 0xcd, 0x85, 0x1e, 0xed, 0x79, 0xc9, 0xcd, 0x85, 0x1e, 0x02, + 0xc9, 0xcd, 0xd5, 0x2d, 0x38, 0x15, 0x28, 0x02, 0xed, 0x44, 0xf5, 0xcd, + 0x99, 0x1e, 0xf1, 0xc9, 0xcd, 0xd5, 0x2d, 0x18, 0x03, 0xcd, 0xa2, 0x2d, + 0x38, 0x01, 0xc8, 0xcf, 0x0a, 0xcd, 0x67, 0x1e, 0x01, 0x00, 0x00, 0xcd, + 0x45, 0x1e, 0x18, 0x03, 0xcd, 0x99, 0x1e, 0x78, 0xb1, 0x20, 0x04, 0xed, + 0x4b, 0xb2, 0x5c, 0xc5, 0xed, 0x5b, 0x4b, 0x5c, 0x2a, 0x59, 0x5c, 0x2b, + 0xcd, 0xe5, 0x19, 0xcd, 0x6b, 0x0d, 0x2a, 0x65, 0x5c, 0x11, 0x32, 0x00, + 0x19, 0xd1, 0xed, 0x52, 0x30, 0x08, 0x2a, 0xb4, 0x5c, 0xa7, 0xed, 0x52, + 0x30, 0x02, 0xcf, 0x15, 0xeb, 0x22, 0xb2, 0x5c, 0xd1, 0xc1, 0x36, 0x3e, + 0x2b, 0xf9, 0xc5, 0xed, 0x73, 0x3d, 0x5c, 0xeb, 0xe9, 0xd1, 0xfd, 0x66, + 0x0d, 0x24, 0xe3, 0x33, 0xed, 0x4b, 0x45, 0x5c, 0xc5, 0xe5, 0xed, 0x73, + 0x3d, 0x5c, 0xd5, 0xcd, 0x67, 0x1e, 0x01, 0x14, 0x00, 0x2a, 0x65, 0x5c, + 0x09, 0x38, 0x0a, 0xeb, 0x21, 0x50, 0x00, 0x19, 0x38, 0x03, 0xed, 0x72, + 0xd8, 0x2e, 0x03, 0xc3, 0x55, 0x00, 0x01, 0x00, 0x00, 0xcd, 0x05, 0x1f, + 0x44, 0x4d, 0xc9, 0xc1, 0xe1, 0xd1, 0x7a, 0xfe, 0x3e, 0x28, 0x0b, 0x3b, + 0xe3, 0xeb, 0xed, 0x73, 0x3d, 0x5c, 0xc5, 0xc3, 0x73, 0x1e, 0xd5, 0xe5, + 0xcf, 0x06, 0xcd, 0x99, 0x1e, 0x76, 0x0b, 0x78, 0xb1, 0x28, 0x0c, 0x78, + 0xa1, 0x3c, 0x20, 0x01, 0x03, 0xfd, 0xcb, 0x01, 0x6e, 0x28, 0xee, 0xfd, + 0xcb, 0x01, 0xae, 0xc9, 0x3e, 0x7f, 0xdb, 0xfe, 0x1f, 0xd8, 0x3e, 0xfe, + 0xdb, 0xfe, 0x1f, 0xc9, 0xcd, 0x30, 0x25, 0x28, 0x05, 0x3e, 0xce, 0xc3, + 0x39, 0x1e, 0xfd, 0xcb, 0x01, 0xf6, 0xcd, 0x8d, 0x2c, 0x30, 0x16, 0xe7, + 0xfe, 0x24, 0x20, 0x05, 0xfd, 0xcb, 0x01, 0xb6, 0xe7, 0xfe, 0x28, 0x20, + 0x3c, 0xe7, 0xfe, 0x29, 0x28, 0x20, 0xcd, 0x8d, 0x2c, 0xd2, 0x8a, 0x1c, + 0xeb, 0xe7, 0xfe, 0x24, 0x20, 0x02, 0xeb, 0xe7, 0xeb, 0x01, 0x06, 0x00, + 0xcd, 0x55, 0x16, 0x23, 0x23, 0x36, 0x0e, 0xfe, 0x2c, 0x20, 0x03, 0xe7, + 0x18, 0xe0, 0xfe, 0x29, 0x20, 0x13, 0xe7, 0xfe, 0x3d, 0x20, 0x0e, 0xe7, + 0x3a, 0x3b, 0x5c, 0xf5, 0xcd, 0xfb, 0x24, 0xf1, 0xfd, 0xae, 0x01, 0xe6, + 0x40, 0xc2, 0x8a, 0x1c, 0xcd, 0xee, 0x1b, 0xcd, 0x30, 0x25, 0xe1, 0xc8, + 0xe9, 0x3e, 0x03, 0x18, 0x02, 0x3e, 0x02, 0xcd, 0x30, 0x25, 0xc4, 0x01, + 0x16, 0xcd, 0x4d, 0x0d, 0xcd, 0xdf, 0x1f, 0xcd, 0xee, 0x1b, 0xc9, 0xdf, + 0xcd, 0x45, 0x20, 0x28, 0x0d, 0xcd, 0x4e, 0x20, 0x28, 0xfb, 0xcd, 0xfc, + 0x1f, 0xcd, 0x4e, 0x20, 0x28, 0xf3, 0xfe, 0x29, 0xc8, 0xcd, 0xc3, 0x1f, + 0x3e, 0x0d, 0xd7, 0xc9, 0xdf, 0xfe, 0xac, 0x20, 0x0d, 0xcd, 0x79, 0x1c, + 0xcd, 0xc3, 0x1f, 0xcd, 0x07, 0x23, 0x3e, 0x16, 0x18, 0x10, 0xfe, 0xad, + 0x20, 0x12, 0xe7, 0xcd, 0x82, 0x1c, 0xcd, 0xc3, 0x1f, 0xcd, 0x99, 0x1e, + 0x3e, 0x17, 0xd7, 0x79, 0xd7, 0x78, 0xd7, 0xc9, 0xcd, 0xf2, 0x21, 0xd0, + 0xcd, 0x70, 0x20, 0xd0, 0xcd, 0xfb, 0x24, 0xcd, 0xc3, 0x1f, 0xfd, 0xcb, + 0x01, 0x76, 0xcc, 0xf1, 0x2b, 0xc2, 0xe3, 0x2d, 0x78, 0xb1, 0x0b, 0xc8, + 0x1a, 0x13, 0xd7, 0x18, 0xf7, 0xfe, 0x29, 0xc8, 0xfe, 0x0d, 0xc8, 0xfe, + 0x3a, 0xc9, 0xdf, 0xfe, 0x3b, 0x28, 0x14, 0xfe, 0x2c, 0x20, 0x0a, 0xcd, + 0x30, 0x25, 0x28, 0x0b, 0x3e, 0x06, 0xd7, 0x18, 0x06, 0xfe, 0x27, 0xc0, + 0xcd, 0xf5, 0x1f, 0xe7, 0xcd, 0x45, 0x20, 0x20, 0x01, 0xc1, 0xbf, 0xc9, + 0xfe, 0x23, 0x37, 0xc0, 0xe7, 0xcd, 0x82, 0x1c, 0xa7, 0xcd, 0xc3, 0x1f, + 0xcd, 0x94, 0x1e, 0xfe, 0x10, 0xd2, 0x0e, 0x16, 0xcd, 0x01, 0x16, 0xa7, + 0xc9, 0xcd, 0x30, 0x25, 0x28, 0x08, 0x3e, 0x01, 0xcd, 0x01, 0x16, 0xcd, + 0x6e, 0x0d, 0xfd, 0x36, 0x02, 0x01, 0xcd, 0xc1, 0x20, 0xcd, 0xee, 0x1b, + 0xed, 0x4b, 0x88, 0x5c, 0x3a, 0x6b, 0x5c, 0xb8, 0x38, 0x03, 0x0e, 0x21, + 0x47, 0xed, 0x43, 0x88, 0x5c, 0x3e, 0x19, 0x90, 0x32, 0x8c, 0x5c, 0xfd, + 0xcb, 0x02, 0x86, 0xcd, 0xd9, 0x0d, 0xc3, 0x6e, 0x0d, 0xcd, 0x4e, 0x20, + 0x28, 0xfb, 0xfe, 0x28, 0x20, 0x0e, 0xe7, 0xcd, 0xdf, 0x1f, 0xdf, 0xfe, + 0x29, 0xc2, 0x8a, 0x1c, 0xe7, 0xc3, 0xb2, 0x21, 0xfe, 0xca, 0x20, 0x11, + 0xe7, 0xcd, 0x1f, 0x1c, 0xfd, 0xcb, 0x37, 0xfe, 0xfd, 0xcb, 0x01, 0x76, + 0xc2, 0x8a, 0x1c, 0x18, 0x0d, 0xcd, 0x8d, 0x2c, 0xd2, 0xaf, 0x21, 0xcd, + 0x1f, 0x1c, 0xfd, 0xcb, 0x37, 0xbe, 0xcd, 0x30, 0x25, 0xca, 0xb2, 0x21, + 0xcd, 0xbf, 0x16, 0x21, 0x71, 0x5c, 0xcb, 0xb6, 0xcb, 0xee, 0x01, 0x01, + 0x00, 0xcb, 0x7e, 0x20, 0x0b, 0x3a, 0x3b, 0x5c, 0xe6, 0x40, 0x20, 0x02, + 0x0e, 0x03, 0xb6, 0x77, 0xf7, 0x36, 0x0d, 0x79, 0x0f, 0x0f, 0x30, 0x05, + 0x3e, 0x22, 0x12, 0x2b, 0x77, 0x22, 0x5b, 0x5c, 0xfd, 0xcb, 0x37, 0x7e, + 0x20, 0x2c, 0x2a, 0x5d, 0x5c, 0xe5, 0x2a, 0x3d, 0x5c, 0xe5, 0x21, 0x3a, + 0x21, 0xe5, 0xfd, 0xcb, 0x30, 0x66, 0x28, 0x04, 0xed, 0x73, 0x3d, 0x5c, + 0x2a, 0x61, 0x5c, 0xcd, 0xa7, 0x11, 0xfd, 0x36, 0x00, 0xff, 0xcd, 0x2c, + 0x0f, 0xfd, 0xcb, 0x01, 0xbe, 0xcd, 0xb9, 0x21, 0x18, 0x03, 0xcd, 0x2c, + 0x0f, 0xfd, 0x36, 0x22, 0x00, 0xcd, 0xd6, 0x21, 0x20, 0x0a, 0xcd, 0x1d, + 0x11, 0xed, 0x4b, 0x82, 0x5c, 0xcd, 0xd9, 0x0d, 0x21, 0x71, 0x5c, 0xcb, + 0xae, 0xcb, 0x7e, 0xcb, 0xbe, 0x20, 0x1c, 0xe1, 0xe1, 0x22, 0x3d, 0x5c, + 0xe1, 0x22, 0x5f, 0x5c, 0xfd, 0xcb, 0x01, 0xfe, 0xcd, 0xb9, 0x21, 0x2a, + 0x5f, 0x5c, 0xfd, 0x36, 0x26, 0x00, 0x22, 0x5d, 0x5c, 0x18, 0x17, 0x2a, + 0x63, 0x5c, 0xed, 0x5b, 0x61, 0x5c, 0x37, 0xed, 0x52, 0x44, 0x4d, 0xcd, + 0xb2, 0x2a, 0xcd, 0xff, 0x2a, 0x18, 0x03, 0xcd, 0xfc, 0x1f, 0xcd, 0x4e, + 0x20, 0xca, 0xc1, 0x20, 0xc9, 0x2a, 0x61, 0x5c, 0x22, 0x5d, 0x5c, 0xdf, + 0xfe, 0xe2, 0x28, 0x0c, 0x3a, 0x71, 0x5c, 0xcd, 0x59, 0x1c, 0xdf, 0xfe, + 0x0d, 0xc8, 0xcf, 0x0b, 0xcd, 0x30, 0x25, 0xc8, 0xcf, 0x10, 0x2a, 0x51, + 0x5c, 0x23, 0x23, 0x23, 0x23, 0x7e, 0xfe, 0x4b, 0xc9, 0xe7, 0xcd, 0xf2, + 0x21, 0xd8, 0xdf, 0xfe, 0x2c, 0x28, 0xf6, 0xfe, 0x3b, 0x28, 0xf2, 0xc3, + 0x8a, 0x1c, 0xfe, 0xd9, 0xd8, 0xfe, 0xdf, 0x3f, 0xd8, 0xf5, 0xe7, 0xf1, + 0xd6, 0xc9, 0xf5, 0xcd, 0x82, 0x1c, 0xf1, 0xa7, 0xcd, 0xc3, 0x1f, 0xf5, + 0xcd, 0x94, 0x1e, 0x57, 0xf1, 0xd7, 0x7a, 0xd7, 0xc9, 0xd6, 0x11, 0xce, + 0x00, 0x28, 0x1d, 0xd6, 0x02, 0xce, 0x00, 0x28, 0x56, 0xfe, 0x01, 0x7a, + 0x06, 0x01, 0x20, 0x04, 0x07, 0x07, 0x06, 0x04, 0x4f, 0x7a, 0xfe, 0x02, + 0x30, 0x16, 0x79, 0x21, 0x91, 0x5c, 0x18, 0x38, 0x7a, 0x06, 0x07, 0x38, + 0x05, 0x07, 0x07, 0x07, 0x06, 0x38, 0x4f, 0x7a, 0xfe, 0x0a, 0x38, 0x02, + 0xcf, 0x13, 0x21, 0x8f, 0x5c, 0xfe, 0x08, 0x38, 0x0b, 0x7e, 0x28, 0x07, + 0xb0, 0x2f, 0xe6, 0x24, 0x28, 0x01, 0x78, 0x4f, 0x79, 0xcd, 0x6c, 0x22, + 0x3e, 0x07, 0xba, 0x9f, 0xcd, 0x6c, 0x22, 0x07, 0x07, 0xe6, 0x50, 0x47, + 0x3e, 0x08, 0xba, 0x9f, 0xae, 0xa0, 0xae, 0x77, 0x23, 0x78, 0xc9, 0x9f, + 0x7a, 0x0f, 0x06, 0x80, 0x20, 0x03, 0x0f, 0x06, 0x40, 0x4f, 0x7a, 0xfe, + 0x08, 0x28, 0x04, 0xfe, 0x02, 0x30, 0xbd, 0x79, 0x21, 0x8f, 0x5c, 0xcd, + 0x6c, 0x22, 0x79, 0x0f, 0x0f, 0x0f, 0x18, 0xd8, 0xcd, 0x94, 0x1e, 0xfe, + 0x08, 0x30, 0xa9, 0xd3, 0xfe, 0x07, 0x07, 0x07, 0xcb, 0x6f, 0x20, 0x02, + 0xee, 0x07, 0x32, 0x48, 0x5c, 0xc9, 0x3e, 0xaf, 0x90, 0xda, 0xf9, 0x24, + 0x47, 0xa7, 0x1f, 0x37, 0x1f, 0xa7, 0x1f, 0xa8, 0xe6, 0xf8, 0xa8, 0x67, + 0x79, 0x07, 0x07, 0x07, 0xa8, 0xe6, 0xc7, 0xa8, 0x07, 0x07, 0x6f, 0x79, + 0xe6, 0x07, 0xc9, 0xcd, 0x07, 0x23, 0xcd, 0xaa, 0x22, 0x47, 0x04, 0x7e, + 0x07, 0x10, 0xfd, 0xe6, 0x01, 0xc3, 0x28, 0x2d, 0xcd, 0x07, 0x23, 0xcd, + 0xe5, 0x22, 0xc3, 0x4d, 0x0d, 0xed, 0x43, 0x7d, 0x5c, 0xcd, 0xaa, 0x22, + 0x47, 0x04, 0x3e, 0xfe, 0x0f, 0x10, 0xfd, 0x47, 0x7e, 0xfd, 0x4e, 0x57, + 0xcb, 0x41, 0x20, 0x01, 0xa0, 0xcb, 0x51, 0x20, 0x02, 0xa8, 0x2f, 0x77, + 0xc3, 0xdb, 0x0b, 0xcd, 0x14, 0x23, 0x47, 0xc5, 0xcd, 0x14, 0x23, 0x59, + 0xc1, 0x51, 0x4f, 0xc9, 0xcd, 0xd5, 0x2d, 0xda, 0xf9, 0x24, 0x0e, 0x01, + 0xc8, 0x0e, 0xff, 0xc9, 0xdf, 0xfe, 0x2c, 0xc2, 0x8a, 0x1c, 0xe7, 0xcd, + 0x82, 0x1c, 0xcd, 0xee, 0x1b, 0xef, 0x2a, 0x3d, 0x38, 0x7e, 0xfe, 0x81, + 0x30, 0x05, 0xef, 0x02, 0x38, 0x18, 0xa1, 0xef, 0xa3, 0x38, 0x36, 0x83, + 0xef, 0xc5, 0x02, 0x38, 0xcd, 0x7d, 0x24, 0xc5, 0xef, 0x31, 0xe1, 0x04, + 0x38, 0x7e, 0xfe, 0x80, 0x30, 0x08, 0xef, 0x02, 0x02, 0x38, 0xc1, 0xc3, + 0xdc, 0x22, 0xef, 0xc2, 0x01, 0xc0, 0x02, 0x03, 0x01, 0xe0, 0x0f, 0xc0, + 0x01, 0x31, 0xe0, 0x01, 0x31, 0xe0, 0xa0, 0xc1, 0x02, 0x38, 0xfd, 0x34, + 0x62, 0xcd, 0x94, 0x1e, 0x6f, 0xe5, 0xcd, 0x94, 0x1e, 0xe1, 0x67, 0x22, + 0x7d, 0x5c, 0xc1, 0xc3, 0x20, 0x24, 0xdf, 0xfe, 0x2c, 0x28, 0x06, 0xcd, + 0xee, 0x1b, 0xc3, 0x77, 0x24, 0xe7, 0xcd, 0x82, 0x1c, 0xcd, 0xee, 0x1b, + 0xef, 0xc5, 0xa2, 0x04, 0x1f, 0x31, 0x30, 0x30, 0x00, 0x06, 0x02, 0x38, + 0xc3, 0x77, 0x24, 0xc0, 0x02, 0xc1, 0x02, 0x31, 0x2a, 0xe1, 0x01, 0xe1, + 0x2a, 0x0f, 0xe0, 0x05, 0x2a, 0xe0, 0x01, 0x3d, 0x38, 0x7e, 0xfe, 0x81, + 0x30, 0x07, 0xef, 0x02, 0x02, 0x38, 0xc3, 0x77, 0x24, 0xcd, 0x7d, 0x24, + 0xc5, 0xef, 0x02, 0xe1, 0x01, 0x05, 0xc1, 0x02, 0x01, 0x31, 0xe1, 0x04, + 0xc2, 0x02, 0x01, 0x31, 0xe1, 0x04, 0xe2, 0xe5, 0xe0, 0x03, 0xa2, 0x04, + 0x31, 0x1f, 0xc5, 0x02, 0x20, 0xc0, 0x02, 0xc2, 0x02, 0xc1, 0xe5, 0x04, + 0xe0, 0xe2, 0x04, 0x0f, 0xe1, 0x01, 0xc1, 0x02, 0xe0, 0x04, 0xe2, 0xe5, + 0x04, 0x03, 0xc2, 0x2a, 0xe1, 0x2a, 0x0f, 0x02, 0x38, 0x1a, 0xfe, 0x81, + 0xc1, 0xda, 0x77, 0x24, 0xc5, 0xef, 0x01, 0x38, 0x3a, 0x7d, 0x5c, 0xcd, + 0x28, 0x2d, 0xef, 0xc0, 0x0f, 0x01, 0x38, 0x3a, 0x7e, 0x5c, 0xcd, 0x28, + 0x2d, 0xef, 0xc5, 0x0f, 0xe0, 0xe5, 0x38, 0xc1, 0x05, 0x28, 0x3c, 0x18, + 0x14, 0xef, 0xe1, 0x31, 0xe3, 0x04, 0xe2, 0xe4, 0x04, 0x03, 0xc1, 0x02, + 0xe4, 0x04, 0xe2, 0xe3, 0x04, 0x0f, 0xc2, 0x02, 0x38, 0xc5, 0xef, 0xc0, + 0x02, 0xe1, 0x0f, 0x31, 0x38, 0x3a, 0x7d, 0x5c, 0xcd, 0x28, 0x2d, 0xef, + 0x03, 0xe0, 0xe2, 0x0f, 0xc0, 0x01, 0xe0, 0x38, 0x3a, 0x7e, 0x5c, 0xcd, + 0x28, 0x2d, 0xef, 0x03, 0x38, 0xcd, 0xb7, 0x24, 0xc1, 0x10, 0xc6, 0xef, + 0x02, 0x02, 0x01, 0x38, 0x3a, 0x7d, 0x5c, 0xcd, 0x28, 0x2d, 0xef, 0x03, + 0x01, 0x38, 0x3a, 0x7e, 0x5c, 0xcd, 0x28, 0x2d, 0xef, 0x03, 0x38, 0xcd, + 0xb7, 0x24, 0xc3, 0x4d, 0x0d, 0xef, 0x31, 0x28, 0x34, 0x32, 0x00, 0x01, + 0x05, 0xe5, 0x01, 0x05, 0x2a, 0x38, 0xcd, 0xd5, 0x2d, 0x38, 0x06, 0xe6, + 0xfc, 0xc6, 0x04, 0x30, 0x02, 0x3e, 0xfc, 0xf5, 0xcd, 0x28, 0x2d, 0xef, + 0xe5, 0x01, 0x05, 0x31, 0x1f, 0xc4, 0x02, 0x31, 0xa2, 0x04, 0x1f, 0xc1, + 0x01, 0xc0, 0x02, 0x31, 0x04, 0x31, 0x0f, 0xa1, 0x03, 0x1b, 0xc3, 0x02, + 0x38, 0xc1, 0xc9, 0xcd, 0x07, 0x23, 0x79, 0xb8, 0x30, 0x06, 0x69, 0xd5, + 0xaf, 0x5f, 0x18, 0x07, 0xb1, 0xc8, 0x68, 0x41, 0xd5, 0x16, 0x00, 0x60, + 0x78, 0x1f, 0x85, 0x38, 0x03, 0xbc, 0x38, 0x07, 0x94, 0x4f, 0xd9, 0xc1, + 0xc5, 0x18, 0x04, 0x4f, 0xd5, 0xd9, 0xc1, 0x2a, 0x7d, 0x5c, 0x78, 0x84, + 0x47, 0x79, 0x3c, 0x85, 0x38, 0x0d, 0x28, 0x0d, 0x3d, 0x4f, 0xcd, 0xe5, + 0x22, 0xd9, 0x79, 0x10, 0xd9, 0xd1, 0xc9, 0x28, 0xf3, 0xcf, 0x0a, 0xdf, + 0x06, 0x00, 0xc5, 0x4f, 0x21, 0x96, 0x25, 0xcd, 0xdc, 0x16, 0x79, 0xd2, + 0x84, 0x26, 0x06, 0x00, 0x4e, 0x09, 0xe9, 0xcd, 0x74, 0x00, 0x03, 0xfe, + 0x0d, 0xca, 0x8a, 0x1c, 0xfe, 0x22, 0x20, 0xf3, 0xcd, 0x74, 0x00, 0xfe, + 0x22, 0xc9, 0xe7, 0xfe, 0x28, 0x20, 0x06, 0xcd, 0x79, 0x1c, 0xdf, 0xfe, + 0x29, 0xc2, 0x8a, 0x1c, 0xfd, 0xcb, 0x01, 0x7e, 0xc9, 0xcd, 0x07, 0x23, + 0x2a, 0x36, 0x5c, 0x11, 0x00, 0x01, 0x19, 0x79, 0x0f, 0x0f, 0x0f, 0xe6, + 0xe0, 0xa8, 0x5f, 0x79, 0xe6, 0x18, 0xee, 0x40, 0x57, 0x06, 0x60, 0xc5, + 0xd5, 0xe5, 0x1a, 0xae, 0x28, 0x04, 0x3c, 0x20, 0x1a, 0x3d, 0x4f, 0x06, + 0x07, 0x14, 0x23, 0x1a, 0xae, 0xa9, 0x20, 0x0f, 0x10, 0xf7, 0xc1, 0xc1, + 0xc1, 0x3e, 0x80, 0x90, 0x01, 0x01, 0x00, 0xf7, 0x12, 0x18, 0x0a, 0xe1, + 0x11, 0x08, 0x00, 0x19, 0xd1, 0xc1, 0x10, 0xd3, 0x48, 0xc3, 0xb2, 0x2a, + 0xcd, 0x07, 0x23, 0x79, 0x0f, 0x0f, 0x0f, 0x4f, 0xe6, 0xe0, 0xa8, 0x6f, + 0x79, 0xe6, 0x03, 0xee, 0x58, 0x67, 0x7e, 0xc3, 0x28, 0x2d, 0x22, 0x1c, + 0x28, 0x4f, 0x2e, 0xf2, 0x2b, 0x12, 0xa8, 0x56, 0xa5, 0x57, 0xa7, 0x84, + 0xa6, 0x8f, 0xc4, 0xe6, 0xaa, 0xbf, 0xab, 0xc7, 0xa9, 0xce, 0x00, 0xe7, + 0xc3, 0xff, 0x24, 0xdf, 0x23, 0xe5, 0x01, 0x00, 0x00, 0xcd, 0x0f, 0x25, + 0x20, 0x1b, 0xcd, 0x0f, 0x25, 0x28, 0xfb, 0xcd, 0x30, 0x25, 0x28, 0x11, + 0xf7, 0xe1, 0xd5, 0x7e, 0x23, 0x12, 0x13, 0xfe, 0x22, 0x20, 0xf8, 0x7e, + 0x23, 0xfe, 0x22, 0x28, 0xf2, 0x0b, 0xd1, 0x21, 0x3b, 0x5c, 0xcb, 0xb6, + 0xcb, 0x7e, 0xc4, 0xb2, 0x2a, 0xc3, 0x12, 0x27, 0xe7, 0xcd, 0xfb, 0x24, + 0xfe, 0x29, 0xc2, 0x8a, 0x1c, 0xe7, 0xc3, 0x12, 0x27, 0xc3, 0xbd, 0x27, + 0xcd, 0x30, 0x25, 0x28, 0x28, 0xed, 0x4b, 0x76, 0x5c, 0xcd, 0x2b, 0x2d, + 0xef, 0xa1, 0x0f, 0x34, 0x37, 0x16, 0x04, 0x34, 0x80, 0x41, 0x00, 0x00, + 0x80, 0x32, 0x02, 0xa1, 0x03, 0x31, 0x38, 0xcd, 0xa2, 0x2d, 0xed, 0x43, + 0x76, 0x5c, 0x7e, 0xa7, 0x28, 0x03, 0xd6, 0x10, 0x77, 0x18, 0x09, 0xcd, + 0x30, 0x25, 0x28, 0x04, 0xef, 0xa3, 0x38, 0x34, 0xe7, 0xc3, 0xc3, 0x26, + 0x01, 0x5a, 0x10, 0xe7, 0xfe, 0x23, 0xca, 0x0d, 0x27, 0x21, 0x3b, 0x5c, + 0xcb, 0xb6, 0xcb, 0x7e, 0x28, 0x1f, 0xcd, 0x8e, 0x02, 0x0e, 0x00, 0x20, + 0x13, 0xcd, 0x1e, 0x03, 0x30, 0x0e, 0x15, 0x5f, 0xcd, 0x33, 0x03, 0xf5, + 0x01, 0x01, 0x00, 0xf7, 0xf1, 0x12, 0x0e, 0x01, 0x06, 0x00, 0xcd, 0xb2, + 0x2a, 0xc3, 0x12, 0x27, 0xcd, 0x22, 0x25, 0xc4, 0x35, 0x25, 0xe7, 0xc3, + 0xdb, 0x25, 0xcd, 0x22, 0x25, 0xc4, 0x80, 0x25, 0xe7, 0x18, 0x48, 0xcd, + 0x22, 0x25, 0xc4, 0xcb, 0x22, 0xe7, 0x18, 0x3f, 0xcd, 0x88, 0x2c, 0x30, + 0x56, 0xfe, 0x41, 0x30, 0x3c, 0xcd, 0x30, 0x25, 0x20, 0x23, 0xcd, 0x9b, + 0x2c, 0xdf, 0x01, 0x06, 0x00, 0xcd, 0x55, 0x16, 0x23, 0x36, 0x0e, 0x23, + 0xeb, 0x2a, 0x65, 0x5c, 0x0e, 0x05, 0xa7, 0xed, 0x42, 0x22, 0x65, 0x5c, + 0xed, 0xb0, 0xeb, 0x2b, 0xcd, 0x77, 0x00, 0x18, 0x0e, 0xdf, 0x23, 0x7e, + 0xfe, 0x0e, 0x20, 0xfa, 0x23, 0xcd, 0xb4, 0x33, 0x22, 0x5d, 0x5c, 0xfd, + 0xcb, 0x01, 0xf6, 0x18, 0x14, 0xcd, 0xb2, 0x28, 0xda, 0x2e, 0x1c, 0xcc, + 0x96, 0x29, 0x3a, 0x3b, 0x5c, 0xfe, 0xc0, 0x38, 0x04, 0x23, 0xcd, 0xb4, + 0x33, 0x18, 0x33, 0x01, 0xdb, 0x09, 0xfe, 0x2d, 0x28, 0x27, 0x01, 0x18, + 0x10, 0xfe, 0xae, 0x28, 0x20, 0xd6, 0xaf, 0xda, 0x8a, 0x1c, 0x01, 0xf0, + 0x04, 0xfe, 0x14, 0x28, 0x14, 0xd2, 0x8a, 0x1c, 0x06, 0x10, 0xc6, 0xdc, + 0x4f, 0xfe, 0xdf, 0x30, 0x02, 0xcb, 0xb1, 0xfe, 0xee, 0x38, 0x02, 0xcb, + 0xb9, 0xc5, 0xe7, 0xc3, 0xff, 0x24, 0xdf, 0xfe, 0x28, 0x20, 0x0c, 0xfd, + 0xcb, 0x01, 0x76, 0x20, 0x17, 0xcd, 0x52, 0x2a, 0xe7, 0x18, 0xf0, 0x06, + 0x00, 0x4f, 0x21, 0x95, 0x27, 0xcd, 0xdc, 0x16, 0x30, 0x06, 0x4e, 0x21, + 0xed, 0x26, 0x09, 0x46, 0xd1, 0x7a, 0xb8, 0x38, 0x3a, 0xa7, 0xca, 0x18, + 0x00, 0xc5, 0x21, 0x3b, 0x5c, 0x7b, 0xfe, 0xed, 0x20, 0x06, 0xcb, 0x76, + 0x20, 0x02, 0x1e, 0x99, 0xd5, 0xcd, 0x30, 0x25, 0x28, 0x09, 0x7b, 0xe6, + 0x3f, 0x47, 0xef, 0x3b, 0x38, 0x18, 0x09, 0x7b, 0xfd, 0xae, 0x01, 0xe6, + 0x40, 0xc2, 0x8a, 0x1c, 0xd1, 0x21, 0x3b, 0x5c, 0xcb, 0xf6, 0xcb, 0x7b, + 0x20, 0x02, 0xcb, 0xb6, 0xc1, 0x18, 0xc1, 0xd5, 0x79, 0xfd, 0xcb, 0x01, + 0x76, 0x20, 0x15, 0xe6, 0x3f, 0xc6, 0x08, 0x4f, 0xfe, 0x10, 0x20, 0x04, + 0xcb, 0xf1, 0x18, 0x08, 0x38, 0xd7, 0xfe, 0x17, 0x28, 0x02, 0xcb, 0xf9, + 0xc5, 0xe7, 0xc3, 0xff, 0x24, 0x2b, 0xcf, 0x2d, 0xc3, 0x2a, 0xc4, 0x2f, + 0xc5, 0x5e, 0xc6, 0x3d, 0xce, 0x3e, 0xcc, 0x3c, 0xcd, 0xc7, 0xc9, 0xc8, + 0xca, 0xc9, 0xcb, 0xc5, 0xc7, 0xc6, 0xc8, 0x00, 0x06, 0x08, 0x08, 0x0a, + 0x02, 0x03, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0xcd, 0x30, 0x25, + 0x20, 0x35, 0xe7, 0xcd, 0x8d, 0x2c, 0xd2, 0x8a, 0x1c, 0xe7, 0xfe, 0x24, + 0xf5, 0x20, 0x01, 0xe7, 0xfe, 0x28, 0x20, 0x12, 0xe7, 0xfe, 0x29, 0x28, + 0x10, 0xcd, 0xfb, 0x24, 0xdf, 0xfe, 0x2c, 0x20, 0x03, 0xe7, 0x18, 0xf5, + 0xfe, 0x29, 0xc2, 0x8a, 0x1c, 0xe7, 0x21, 0x3b, 0x5c, 0xcb, 0xb6, 0xf1, + 0x28, 0x02, 0xcb, 0xf6, 0xc3, 0x12, 0x27, 0xe7, 0xe6, 0xdf, 0x47, 0xe7, + 0xd6, 0x24, 0x4f, 0x20, 0x01, 0xe7, 0xe7, 0xe5, 0x2a, 0x53, 0x5c, 0x2b, + 0x11, 0xce, 0x00, 0xc5, 0xcd, 0x86, 0x1d, 0xc1, 0x30, 0x02, 0xcf, 0x18, + 0xe5, 0xcd, 0xab, 0x28, 0xe6, 0xdf, 0xb8, 0x20, 0x08, 0xcd, 0xab, 0x28, + 0xd6, 0x24, 0xb9, 0x28, 0x0c, 0xe1, 0x2b, 0x11, 0x00, 0x02, 0xc5, 0xcd, + 0x8b, 0x19, 0xc1, 0x18, 0xd7, 0xa7, 0xcc, 0xab, 0x28, 0xd1, 0xd1, 0xed, + 0x53, 0x5d, 0x5c, 0xcd, 0xab, 0x28, 0xe5, 0xfe, 0x29, 0x28, 0x42, 0x23, + 0x7e, 0xfe, 0x0e, 0x16, 0x40, 0x28, 0x07, 0x2b, 0xcd, 0xab, 0x28, 0x23, + 0x16, 0x00, 0x23, 0xe5, 0xd5, 0xcd, 0xfb, 0x24, 0xf1, 0xfd, 0xae, 0x01, + 0xe6, 0x40, 0x20, 0x2b, 0xe1, 0xeb, 0x2a, 0x65, 0x5c, 0x01, 0x05, 0x00, + 0xed, 0x42, 0x22, 0x65, 0x5c, 0xed, 0xb0, 0xeb, 0x2b, 0xcd, 0xab, 0x28, + 0xfe, 0x29, 0x28, 0x0d, 0xe5, 0xdf, 0xfe, 0x2c, 0x20, 0x0d, 0xe7, 0xe1, + 0xcd, 0xab, 0x28, 0x18, 0xbe, 0xe5, 0xdf, 0xfe, 0x29, 0x28, 0x02, 0xcf, + 0x19, 0xd1, 0xeb, 0x22, 0x5d, 0x5c, 0x2a, 0x0b, 0x5c, 0xe3, 0x22, 0x0b, + 0x5c, 0xd5, 0xe7, 0xe7, 0xcd, 0xfb, 0x24, 0xe1, 0x22, 0x5d, 0x5c, 0xe1, + 0x22, 0x0b, 0x5c, 0xe7, 0xc3, 0x12, 0x27, 0x23, 0x7e, 0xfe, 0x21, 0x38, + 0xfa, 0xc9, 0xfd, 0xcb, 0x01, 0xf6, 0xdf, 0xcd, 0x8d, 0x2c, 0xd2, 0x8a, + 0x1c, 0xe5, 0xe6, 0x1f, 0x4f, 0xe7, 0xe5, 0xfe, 0x28, 0x28, 0x28, 0xcb, + 0xf1, 0xfe, 0x24, 0x28, 0x11, 0xcb, 0xe9, 0xcd, 0x88, 0x2c, 0x30, 0x0f, + 0xcd, 0x88, 0x2c, 0x30, 0x16, 0xcb, 0xb1, 0xe7, 0x18, 0xf6, 0xe7, 0xfd, + 0xcb, 0x01, 0xb6, 0x3a, 0x0c, 0x5c, 0xa7, 0x28, 0x06, 0xcd, 0x30, 0x25, + 0xc2, 0x51, 0x29, 0x41, 0xcd, 0x30, 0x25, 0x20, 0x08, 0x79, 0xe6, 0xe0, + 0xcb, 0xff, 0x4f, 0x18, 0x37, 0x2a, 0x4b, 0x5c, 0x7e, 0xe6, 0x7f, 0x28, + 0x2d, 0xb9, 0x20, 0x22, 0x17, 0x87, 0xf2, 0x3f, 0x29, 0x38, 0x30, 0xd1, + 0xd5, 0xe5, 0x23, 0x1a, 0x13, 0xfe, 0x20, 0x28, 0xfa, 0xf6, 0x20, 0xbe, + 0x28, 0xf4, 0xf6, 0x80, 0xbe, 0x20, 0x06, 0x1a, 0xcd, 0x88, 0x2c, 0x30, + 0x15, 0xe1, 0xc5, 0xcd, 0xb8, 0x19, 0xeb, 0xc1, 0x18, 0xce, 0xcb, 0xf8, + 0xd1, 0xdf, 0xfe, 0x28, 0x28, 0x09, 0xcb, 0xe8, 0x18, 0x0d, 0xd1, 0xd1, + 0xd1, 0xe5, 0xdf, 0xcd, 0x88, 0x2c, 0x30, 0x03, 0xe7, 0x18, 0xf8, 0xe1, + 0xcb, 0x10, 0xcb, 0x70, 0xc9, 0x2a, 0x0b, 0x5c, 0x7e, 0xfe, 0x29, 0xca, + 0xef, 0x28, 0x7e, 0xf6, 0x60, 0x47, 0x23, 0x7e, 0xfe, 0x0e, 0x28, 0x07, + 0x2b, 0xcd, 0xab, 0x28, 0x23, 0xcb, 0xa8, 0x78, 0xb9, 0x28, 0x12, 0x23, + 0x23, 0x23, 0x23, 0x23, 0xcd, 0xab, 0x28, 0xfe, 0x29, 0xca, 0xef, 0x28, + 0xcd, 0xab, 0x28, 0x18, 0xd9, 0xcb, 0x69, 0x20, 0x0c, 0x23, 0xed, 0x5b, + 0x65, 0x5c, 0xcd, 0xc0, 0x33, 0xeb, 0x22, 0x65, 0x5c, 0xd1, 0xd1, 0xaf, + 0x3c, 0xc9, 0xaf, 0x47, 0xcb, 0x79, 0x20, 0x4b, 0xcb, 0x7e, 0x20, 0x0e, + 0x3c, 0x23, 0x4e, 0x23, 0x46, 0x23, 0xeb, 0xcd, 0xb2, 0x2a, 0xdf, 0xc3, + 0x49, 0x2a, 0x23, 0x23, 0x23, 0x46, 0xcb, 0x71, 0x28, 0x0a, 0x05, 0x28, + 0xe8, 0xeb, 0xdf, 0xfe, 0x28, 0x20, 0x61, 0xeb, 0xeb, 0x18, 0x24, 0xe5, + 0xdf, 0xe1, 0xfe, 0x2c, 0x28, 0x20, 0xcb, 0x79, 0x28, 0x52, 0xcb, 0x71, + 0x20, 0x06, 0xfe, 0x29, 0x20, 0x3c, 0xe7, 0xc9, 0xfe, 0x29, 0x28, 0x6c, + 0xfe, 0xcc, 0x20, 0x32, 0xdf, 0x2b, 0x22, 0x5d, 0x5c, 0x18, 0x5e, 0x21, + 0x00, 0x00, 0xe5, 0xe7, 0xe1, 0x79, 0xfe, 0xc0, 0x20, 0x09, 0xdf, 0xfe, + 0x29, 0x28, 0x51, 0xfe, 0xcc, 0x28, 0xe5, 0xc5, 0xe5, 0xcd, 0xee, 0x2a, + 0xe3, 0xeb, 0xcd, 0xcc, 0x2a, 0x38, 0x19, 0x0b, 0xcd, 0xf4, 0x2a, 0x09, + 0xd1, 0xc1, 0x10, 0xb3, 0xcb, 0x79, 0x20, 0x66, 0xe5, 0xcb, 0x71, 0x20, + 0x13, 0x42, 0x4b, 0xdf, 0xfe, 0x29, 0x28, 0x02, 0xcf, 0x02, 0xe7, 0xe1, + 0x11, 0x05, 0x00, 0xcd, 0xf4, 0x2a, 0x09, 0xc9, 0xcd, 0xee, 0x2a, 0xe3, + 0xcd, 0xf4, 0x2a, 0xc1, 0x09, 0x23, 0x42, 0x4b, 0xeb, 0xcd, 0xb1, 0x2a, + 0xdf, 0xfe, 0x29, 0x28, 0x07, 0xfe, 0x2c, 0x20, 0xdb, 0xcd, 0x52, 0x2a, + 0xe7, 0xfe, 0x28, 0x28, 0xf8, 0xfd, 0xcb, 0x01, 0xb6, 0xc9, 0xcd, 0x30, + 0x25, 0xc4, 0xf1, 0x2b, 0xe7, 0xfe, 0x29, 0x28, 0x50, 0xd5, 0xaf, 0xf5, + 0xc5, 0x11, 0x01, 0x00, 0xdf, 0xe1, 0xfe, 0xcc, 0x28, 0x17, 0xf1, 0xcd, + 0xcd, 0x2a, 0xf5, 0x50, 0x59, 0xe5, 0xdf, 0xe1, 0xfe, 0xcc, 0x28, 0x09, + 0xfe, 0x29, 0xc2, 0x8a, 0x1c, 0x62, 0x6b, 0x18, 0x13, 0xe5, 0xe7, 0xe1, + 0xfe, 0x29, 0x28, 0x0c, 0xf1, 0xcd, 0xcd, 0x2a, 0xf5, 0xdf, 0x60, 0x69, + 0xfe, 0x29, 0x20, 0xe6, 0xf1, 0xe3, 0x19, 0x2b, 0xe3, 0xa7, 0xed, 0x52, + 0x01, 0x00, 0x00, 0x38, 0x07, 0x23, 0xa7, 0xfa, 0x20, 0x2a, 0x44, 0x4d, + 0xd1, 0xfd, 0xcb, 0x01, 0xb6, 0xcd, 0x30, 0x25, 0xc8, 0xaf, 0xfd, 0xcb, + 0x01, 0xb6, 0xc5, 0xcd, 0xa9, 0x33, 0xc1, 0x2a, 0x65, 0x5c, 0x77, 0x23, + 0x73, 0x23, 0x72, 0x23, 0x71, 0x23, 0x70, 0x23, 0x22, 0x65, 0x5c, 0xc9, + 0xaf, 0xd5, 0xe5, 0xf5, 0xcd, 0x82, 0x1c, 0xf1, 0xcd, 0x30, 0x25, 0x28, + 0x12, 0xf5, 0xcd, 0x99, 0x1e, 0xd1, 0x78, 0xb1, 0x37, 0x28, 0x05, 0xe1, + 0xe5, 0xa7, 0xed, 0x42, 0x7a, 0xde, 0x00, 0xe1, 0xd1, 0xc9, 0xeb, 0x23, + 0x5e, 0x23, 0x56, 0xc9, 0xcd, 0x30, 0x25, 0xc8, 0xcd, 0xa9, 0x30, 0xda, + 0x15, 0x1f, 0xc9, 0x2a, 0x4d, 0x5c, 0xfd, 0xcb, 0x37, 0x4e, 0x28, 0x5e, + 0x01, 0x05, 0x00, 0x03, 0x23, 0x7e, 0xfe, 0x20, 0x28, 0xfa, 0x30, 0x0b, + 0xfe, 0x10, 0x38, 0x11, 0xfe, 0x16, 0x30, 0x0d, 0x23, 0x18, 0xed, 0xcd, + 0x88, 0x2c, 0x38, 0xe7, 0xfe, 0x24, 0xca, 0xc0, 0x2b, 0x79, 0x2a, 0x59, + 0x5c, 0x2b, 0xcd, 0x55, 0x16, 0x23, 0x23, 0xeb, 0xd5, 0x2a, 0x4d, 0x5c, + 0x1b, 0xd6, 0x06, 0x47, 0x28, 0x11, 0x23, 0x7e, 0xfe, 0x21, 0x38, 0xfa, + 0xf6, 0x20, 0x13, 0x12, 0x10, 0xf4, 0xf6, 0x80, 0x12, 0x3e, 0xc0, 0x2a, + 0x4d, 0x5c, 0xae, 0xf6, 0x20, 0xe1, 0xcd, 0xea, 0x2b, 0xe5, 0xef, 0x02, + 0x38, 0xe1, 0x01, 0x05, 0x00, 0xa7, 0xed, 0x42, 0x18, 0x40, 0xfd, 0xcb, + 0x01, 0x76, 0x28, 0x06, 0x11, 0x06, 0x00, 0x19, 0x18, 0xe7, 0x2a, 0x4d, + 0x5c, 0xed, 0x4b, 0x72, 0x5c, 0xfd, 0xcb, 0x37, 0x46, 0x20, 0x30, 0x78, + 0xb1, 0xc8, 0xe5, 0xf7, 0xd5, 0xc5, 0x54, 0x5d, 0x23, 0x36, 0x20, 0xed, + 0xb8, 0xe5, 0xcd, 0xf1, 0x2b, 0xe1, 0xe3, 0xa7, 0xed, 0x42, 0x09, 0x30, + 0x02, 0x44, 0x4d, 0xe3, 0xeb, 0x78, 0xb1, 0x28, 0x02, 0xed, 0xb0, 0xc1, + 0xd1, 0xe1, 0xeb, 0x78, 0xb1, 0xc8, 0xd5, 0xed, 0xb0, 0xe1, 0xc9, 0x2b, + 0x2b, 0x2b, 0x7e, 0xe5, 0xc5, 0xcd, 0xc6, 0x2b, 0xc1, 0xe1, 0x03, 0x03, + 0x03, 0xc3, 0xe8, 0x19, 0x3e, 0xdf, 0x2a, 0x4d, 0x5c, 0xa6, 0xf5, 0xcd, + 0xf1, 0x2b, 0xeb, 0x09, 0xc5, 0x2b, 0x22, 0x4d, 0x5c, 0x03, 0x03, 0x03, + 0x2a, 0x59, 0x5c, 0x2b, 0xcd, 0x55, 0x16, 0x2a, 0x4d, 0x5c, 0xc1, 0xc5, + 0x03, 0xed, 0xb8, 0xeb, 0x23, 0xc1, 0x70, 0x2b, 0x71, 0xf1, 0x2b, 0x77, + 0x2a, 0x59, 0x5c, 0x2b, 0xc9, 0x2a, 0x65, 0x5c, 0x2b, 0x46, 0x2b, 0x4e, + 0x2b, 0x56, 0x2b, 0x5e, 0x2b, 0x7e, 0x22, 0x65, 0x5c, 0xc9, 0xcd, 0xb2, + 0x28, 0xc2, 0x8a, 0x1c, 0xcd, 0x30, 0x25, 0x20, 0x08, 0xcb, 0xb1, 0xcd, + 0x96, 0x29, 0xcd, 0xee, 0x1b, 0x38, 0x08, 0xc5, 0xcd, 0xb8, 0x19, 0xcd, + 0xe8, 0x19, 0xc1, 0xcb, 0xf9, 0x06, 0x00, 0xc5, 0x21, 0x01, 0x00, 0xcb, + 0x71, 0x20, 0x02, 0x2e, 0x05, 0xeb, 0xe7, 0x26, 0xff, 0xcd, 0xcc, 0x2a, + 0xda, 0x20, 0x2a, 0xe1, 0xc5, 0x24, 0xe5, 0x60, 0x69, 0xcd, 0xf4, 0x2a, + 0xeb, 0xdf, 0xfe, 0x2c, 0x28, 0xe8, 0xfe, 0x29, 0x20, 0xbb, 0xe7, 0xc1, + 0x79, 0x68, 0x26, 0x00, 0x23, 0x23, 0x29, 0x19, 0xda, 0x15, 0x1f, 0xd5, + 0xc5, 0xe5, 0x44, 0x4d, 0x2a, 0x59, 0x5c, 0x2b, 0xcd, 0x55, 0x16, 0x23, + 0x77, 0xc1, 0x0b, 0x0b, 0x0b, 0x23, 0x71, 0x23, 0x70, 0xc1, 0x78, 0x23, + 0x77, 0x62, 0x6b, 0x1b, 0x36, 0x00, 0xcb, 0x71, 0x28, 0x02, 0x36, 0x20, + 0xc1, 0xed, 0xb8, 0xc1, 0x70, 0x2b, 0x71, 0x2b, 0x3d, 0x20, 0xf8, 0xc9, + 0xcd, 0x1b, 0x2d, 0x3f, 0xd8, 0xfe, 0x41, 0x3f, 0xd0, 0xfe, 0x5b, 0xd8, + 0xfe, 0x61, 0x3f, 0xd0, 0xfe, 0x7b, 0xc9, 0xfe, 0xc4, 0x20, 0x19, 0x11, + 0x00, 0x00, 0xe7, 0xd6, 0x31, 0xce, 0x00, 0x20, 0x0a, 0xeb, 0x3f, 0xed, + 0x6a, 0xda, 0xad, 0x31, 0xeb, 0x18, 0xef, 0x42, 0x4b, 0xc3, 0x2b, 0x2d, + 0xfe, 0x2e, 0x28, 0x0f, 0xcd, 0x3b, 0x2d, 0xfe, 0x2e, 0x20, 0x28, 0xe7, + 0xcd, 0x1b, 0x2d, 0x38, 0x22, 0x18, 0x0a, 0xe7, 0xcd, 0x1b, 0x2d, 0xda, + 0x8a, 0x1c, 0xef, 0xa0, 0x38, 0xef, 0xa1, 0xc0, 0x02, 0x38, 0xdf, 0xcd, + 0x22, 0x2d, 0x38, 0x0b, 0xef, 0xe0, 0xa4, 0x05, 0xc0, 0x04, 0x0f, 0x38, + 0xe7, 0x18, 0xef, 0xfe, 0x45, 0x28, 0x03, 0xfe, 0x65, 0xc0, 0x06, 0xff, + 0xe7, 0xfe, 0x2b, 0x28, 0x05, 0xfe, 0x2d, 0x20, 0x02, 0x04, 0xe7, 0xcd, + 0x1b, 0x2d, 0x38, 0xcb, 0xc5, 0xcd, 0x3b, 0x2d, 0xcd, 0xd5, 0x2d, 0xc1, + 0xda, 0xad, 0x31, 0xa7, 0xfa, 0xad, 0x31, 0x04, 0x28, 0x02, 0xed, 0x44, + 0xc3, 0x4f, 0x2d, 0xfe, 0x30, 0xd8, 0xfe, 0x3a, 0x3f, 0xc9, 0xcd, 0x1b, + 0x2d, 0xd8, 0xd6, 0x30, 0x4f, 0x06, 0x00, 0xfd, 0x21, 0x3a, 0x5c, 0xaf, + 0x5f, 0x51, 0x48, 0x47, 0xcd, 0xb6, 0x2a, 0xef, 0x38, 0xa7, 0xc9, 0xf5, + 0xef, 0xa0, 0x38, 0xf1, 0xcd, 0x22, 0x2d, 0xd8, 0xef, 0x01, 0xa4, 0x04, + 0x0f, 0x38, 0xcd, 0x74, 0x00, 0x18, 0xf1, 0x07, 0x0f, 0x30, 0x02, 0x2f, + 0x3c, 0xf5, 0x21, 0x92, 0x5c, 0xcd, 0x0b, 0x35, 0xef, 0xa4, 0x38, 0xf1, + 0xcb, 0x3f, 0x30, 0x0d, 0xf5, 0xef, 0xc1, 0xe0, 0x00, 0x04, 0x04, 0x33, + 0x02, 0x05, 0xe1, 0x38, 0xf1, 0x28, 0x08, 0xf5, 0xef, 0x31, 0x04, 0x38, + 0xf1, 0x18, 0xe5, 0xef, 0x02, 0x38, 0xc9, 0x23, 0x4e, 0x23, 0x7e, 0xa9, + 0x91, 0x5f, 0x23, 0x7e, 0x89, 0xa9, 0x57, 0xc9, 0x0e, 0x00, 0xe5, 0x36, + 0x00, 0x23, 0x71, 0x23, 0x7b, 0xa9, 0x91, 0x77, 0x23, 0x7a, 0x89, 0xa9, + 0x77, 0x23, 0x36, 0x00, 0xe1, 0xc9, 0xef, 0x38, 0x7e, 0xa7, 0x28, 0x05, + 0xef, 0xa2, 0x0f, 0x27, 0x38, 0xef, 0x02, 0x38, 0xe5, 0xd5, 0xeb, 0x46, + 0xcd, 0x7f, 0x2d, 0xaf, 0x90, 0xcb, 0x79, 0x42, 0x4b, 0x7b, 0xd1, 0xe1, + 0xc9, 0x57, 0x17, 0x9f, 0x5f, 0x4f, 0xaf, 0x47, 0xcd, 0xb6, 0x2a, 0xef, + 0x34, 0xef, 0x1a, 0x20, 0x9a, 0x85, 0x04, 0x27, 0x38, 0xcd, 0xa2, 0x2d, + 0xd8, 0xf5, 0x05, 0x04, 0x28, 0x03, 0xf1, 0x37, 0xc9, 0xf1, 0xc9, 0xef, + 0x31, 0x36, 0x00, 0x0b, 0x31, 0x37, 0x00, 0x0d, 0x02, 0x38, 0x3e, 0x30, + 0xd7, 0xc9, 0x2a, 0x38, 0x3e, 0x2d, 0xd7, 0xef, 0xa0, 0xc3, 0xc4, 0xc5, + 0x02, 0x38, 0xd9, 0xe5, 0xd9, 0xef, 0x31, 0x27, 0xc2, 0x03, 0xe2, 0x01, + 0xc2, 0x02, 0x38, 0x7e, 0xa7, 0x20, 0x47, 0xcd, 0x7f, 0x2d, 0x06, 0x10, + 0x7a, 0xa7, 0x20, 0x06, 0xb3, 0x28, 0x09, 0x53, 0x06, 0x08, 0xd5, 0xd9, + 0xd1, 0xd9, 0x18, 0x57, 0xef, 0xe2, 0x38, 0x7e, 0xd6, 0x7e, 0xcd, 0xc1, + 0x2d, 0x57, 0x3a, 0xac, 0x5c, 0x92, 0x32, 0xac, 0x5c, 0x7a, 0xcd, 0x4f, + 0x2d, 0xef, 0x31, 0x27, 0xc1, 0x03, 0xe1, 0x38, 0xcd, 0xd5, 0x2d, 0xe5, + 0x32, 0xa1, 0x5c, 0x3d, 0x17, 0x9f, 0x3c, 0x21, 0xab, 0x5c, 0x77, 0x23, + 0x86, 0x77, 0xe1, 0xc3, 0xcf, 0x2e, 0xd6, 0x80, 0xfe, 0x1c, 0x38, 0x13, + 0xcd, 0xc1, 0x2d, 0xd6, 0x07, 0x47, 0x21, 0xac, 0x5c, 0x86, 0x77, 0x78, + 0xed, 0x44, 0xcd, 0x4f, 0x2d, 0x18, 0x92, 0xeb, 0xcd, 0xba, 0x2f, 0xd9, + 0xcb, 0xfa, 0x7d, 0xd9, 0xd6, 0x80, 0x47, 0xcb, 0x23, 0xcb, 0x12, 0xd9, + 0xcb, 0x13, 0xcb, 0x12, 0xd9, 0x21, 0xaa, 0x5c, 0x0e, 0x05, 0x7e, 0x8f, + 0x27, 0x77, 0x2b, 0x0d, 0x20, 0xf8, 0x10, 0xe7, 0xaf, 0x21, 0xa6, 0x5c, + 0x11, 0xa1, 0x5c, 0x06, 0x09, 0xed, 0x6f, 0x0e, 0xff, 0xed, 0x6f, 0x20, + 0x04, 0x0d, 0x0c, 0x20, 0x0a, 0x12, 0x13, 0xfd, 0x34, 0x71, 0xfd, 0x34, + 0x72, 0x0e, 0x00, 0xcb, 0x40, 0x28, 0x01, 0x23, 0x10, 0xe7, 0x3a, 0xab, + 0x5c, 0xd6, 0x09, 0x38, 0x0a, 0xfd, 0x35, 0x71, 0x3e, 0x04, 0xfd, 0xbe, + 0x6f, 0x18, 0x41, 0xef, 0x02, 0xe2, 0x38, 0xeb, 0xcd, 0xba, 0x2f, 0xd9, + 0x3e, 0x80, 0x95, 0x2e, 0x00, 0xcb, 0xfa, 0xd9, 0xcd, 0xdd, 0x2f, 0xfd, + 0x7e, 0x71, 0xfe, 0x08, 0x38, 0x06, 0xd9, 0xcb, 0x12, 0xd9, 0x18, 0x20, + 0x01, 0x00, 0x02, 0x7b, 0xcd, 0x8b, 0x2f, 0x5f, 0x7a, 0xcd, 0x8b, 0x2f, + 0x57, 0xc5, 0xd9, 0xc1, 0x10, 0xf1, 0x21, 0xa1, 0x5c, 0x79, 0xfd, 0x4e, + 0x71, 0x09, 0x77, 0xfd, 0x34, 0x71, 0x18, 0xd3, 0xf5, 0x21, 0xa1, 0x5c, + 0xfd, 0x4e, 0x71, 0x06, 0x00, 0x09, 0x41, 0xf1, 0x2b, 0x7e, 0xce, 0x00, + 0x77, 0xa7, 0x28, 0x05, 0xfe, 0x0a, 0x3f, 0x30, 0x08, 0x10, 0xf1, 0x36, + 0x01, 0x04, 0xfd, 0x34, 0x72, 0xfd, 0x70, 0x71, 0xef, 0x02, 0x38, 0xd9, + 0xe1, 0xd9, 0xed, 0x4b, 0xab, 0x5c, 0x21, 0xa1, 0x5c, 0x78, 0xfe, 0x09, + 0x38, 0x04, 0xfe, 0xfc, 0x38, 0x26, 0xa7, 0xcc, 0xef, 0x15, 0xaf, 0x90, + 0xfa, 0x52, 0x2f, 0x47, 0x18, 0x0c, 0x79, 0xa7, 0x28, 0x03, 0x7e, 0x23, + 0x0d, 0xcd, 0xef, 0x15, 0x10, 0xf4, 0x79, 0xa7, 0xc8, 0x04, 0x3e, 0x2e, + 0xd7, 0x3e, 0x30, 0x10, 0xfb, 0x41, 0x18, 0xe6, 0x50, 0x15, 0x06, 0x01, + 0xcd, 0x4a, 0x2f, 0x3e, 0x45, 0xd7, 0x4a, 0x79, 0xa7, 0xf2, 0x83, 0x2f, + 0xed, 0x44, 0x4f, 0x3e, 0x2d, 0x18, 0x02, 0x3e, 0x2b, 0xd7, 0x06, 0x00, + 0xc3, 0x1b, 0x1a, 0xd5, 0x6f, 0x26, 0x00, 0x5d, 0x54, 0x29, 0x29, 0x19, + 0x29, 0x59, 0x19, 0x4c, 0x7d, 0xd1, 0xc9, 0x7e, 0x36, 0x00, 0xa7, 0xc8, + 0x23, 0xcb, 0x7e, 0xcb, 0xfe, 0x2b, 0xc8, 0xc5, 0x01, 0x05, 0x00, 0x09, + 0x41, 0x4f, 0x37, 0x2b, 0x7e, 0x2f, 0xce, 0x00, 0x77, 0x10, 0xf8, 0x79, + 0xc1, 0xc9, 0xe5, 0xf5, 0x4e, 0x23, 0x46, 0x77, 0x23, 0x79, 0x4e, 0xc5, + 0x23, 0x4e, 0x23, 0x46, 0xeb, 0x57, 0x5e, 0xd5, 0x23, 0x56, 0x23, 0x5e, + 0xd5, 0xd9, 0xd1, 0xe1, 0xc1, 0xd9, 0x23, 0x56, 0x23, 0x5e, 0xf1, 0xe1, + 0xc9, 0xa7, 0xc8, 0xfe, 0x21, 0x30, 0x16, 0xc5, 0x47, 0xd9, 0xcb, 0x2d, + 0xcb, 0x1a, 0xcb, 0x1b, 0xd9, 0xcb, 0x1a, 0xcb, 0x1b, 0x10, 0xf2, 0xc1, + 0xd0, 0xcd, 0x04, 0x30, 0xc0, 0xd9, 0xaf, 0x2e, 0x00, 0x57, 0x5d, 0xd9, + 0x11, 0x00, 0x00, 0xc9, 0x1c, 0xc0, 0x14, 0xc0, 0xd9, 0x1c, 0x20, 0x01, + 0x14, 0xd9, 0xc9, 0xeb, 0xcd, 0x6e, 0x34, 0xeb, 0x1a, 0xb6, 0x20, 0x26, + 0xd5, 0x23, 0xe5, 0x23, 0x5e, 0x23, 0x56, 0x23, 0x23, 0x23, 0x7e, 0x23, + 0x4e, 0x23, 0x46, 0xe1, 0xeb, 0x09, 0xeb, 0x8e, 0x0f, 0xce, 0x00, 0x20, + 0x0b, 0x9f, 0x77, 0x23, 0x73, 0x23, 0x72, 0x2b, 0x2b, 0x2b, 0xd1, 0xc9, + 0x2b, 0xd1, 0xcd, 0x93, 0x32, 0xd9, 0xe5, 0xd9, 0xd5, 0xe5, 0xcd, 0x9b, + 0x2f, 0x47, 0xeb, 0xcd, 0x9b, 0x2f, 0x4f, 0xb8, 0x30, 0x03, 0x78, 0x41, + 0xeb, 0xf5, 0x90, 0xcd, 0xba, 0x2f, 0xcd, 0xdd, 0x2f, 0xf1, 0xe1, 0x77, + 0xe5, 0x68, 0x61, 0x19, 0xd9, 0xeb, 0xed, 0x4a, 0xeb, 0x7c, 0x8d, 0x6f, + 0x1f, 0xad, 0xd9, 0xeb, 0xe1, 0x1f, 0x30, 0x08, 0x3e, 0x01, 0xcd, 0xdd, + 0x2f, 0x34, 0x28, 0x23, 0xd9, 0x7d, 0xe6, 0x80, 0xd9, 0x23, 0x77, 0x2b, + 0x28, 0x1f, 0x7b, 0xed, 0x44, 0x3f, 0x5f, 0x7a, 0x2f, 0xce, 0x00, 0x57, + 0xd9, 0x7b, 0x2f, 0xce, 0x00, 0x5f, 0x7a, 0x2f, 0xce, 0x00, 0x30, 0x07, + 0x1f, 0xd9, 0x34, 0xca, 0xad, 0x31, 0xd9, 0x57, 0xd9, 0xaf, 0xc3, 0x55, + 0x31, 0xc5, 0x06, 0x10, 0x7c, 0x4d, 0x21, 0x00, 0x00, 0x29, 0x38, 0x0a, + 0xcb, 0x11, 0x17, 0x30, 0x03, 0x19, 0x38, 0x02, 0x10, 0xf3, 0xc1, 0xc9, + 0xcd, 0xe9, 0x34, 0xd8, 0x23, 0xae, 0xcb, 0xfe, 0x2b, 0xc9, 0x1a, 0xb6, + 0x20, 0x22, 0xd5, 0xe5, 0xd5, 0xcd, 0x7f, 0x2d, 0xeb, 0xe3, 0x41, 0xcd, + 0x7f, 0x2d, 0x78, 0xa9, 0x4f, 0xe1, 0xcd, 0xa9, 0x30, 0xeb, 0xe1, 0x38, + 0x0a, 0x7a, 0xb3, 0x20, 0x01, 0x4f, 0xcd, 0x8e, 0x2d, 0xd1, 0xc9, 0xd1, + 0xcd, 0x93, 0x32, 0xaf, 0xcd, 0xc0, 0x30, 0xd8, 0xd9, 0xe5, 0xd9, 0xd5, + 0xeb, 0xcd, 0xc0, 0x30, 0xeb, 0x38, 0x5a, 0xe5, 0xcd, 0xba, 0x2f, 0x78, + 0xa7, 0xed, 0x62, 0xd9, 0xe5, 0xed, 0x62, 0xd9, 0x06, 0x21, 0x18, 0x11, + 0x30, 0x05, 0x19, 0xd9, 0xed, 0x5a, 0xd9, 0xd9, 0xcb, 0x1c, 0xcb, 0x1d, + 0xd9, 0xcb, 0x1c, 0xcb, 0x1d, 0xd9, 0xcb, 0x18, 0xcb, 0x19, 0xd9, 0xcb, + 0x19, 0x1f, 0x10, 0xe4, 0xeb, 0xd9, 0xeb, 0xd9, 0xc1, 0xe1, 0x78, 0x81, + 0x20, 0x01, 0xa7, 0x3d, 0x3f, 0x17, 0x3f, 0x1f, 0xf2, 0x46, 0x31, 0x30, + 0x68, 0xa7, 0x3c, 0x20, 0x08, 0x38, 0x06, 0xd9, 0xcb, 0x7a, 0xd9, 0x20, + 0x5c, 0x77, 0xd9, 0x78, 0xd9, 0x30, 0x15, 0x7e, 0xa7, 0x3e, 0x80, 0x28, + 0x01, 0xaf, 0xd9, 0xa2, 0xcd, 0xfb, 0x2f, 0x07, 0x77, 0x38, 0x2e, 0x23, + 0x77, 0x2b, 0x18, 0x29, 0x06, 0x20, 0xd9, 0xcb, 0x7a, 0xd9, 0x20, 0x12, + 0x07, 0xcb, 0x13, 0xcb, 0x12, 0xd9, 0xcb, 0x13, 0xcb, 0x12, 0xd9, 0x35, + 0x28, 0xd7, 0x10, 0xea, 0x18, 0xd7, 0x17, 0x30, 0x0c, 0xcd, 0x04, 0x30, + 0x20, 0x07, 0xd9, 0x16, 0x80, 0xd9, 0x34, 0x28, 0x18, 0xe5, 0x23, 0xd9, + 0xd5, 0xd9, 0xc1, 0x78, 0x17, 0xcb, 0x16, 0x1f, 0x77, 0x23, 0x71, 0x23, + 0x72, 0x23, 0x73, 0xe1, 0xd1, 0xd9, 0xe1, 0xd9, 0xc9, 0xcf, 0x05, 0xcd, + 0x93, 0x32, 0xeb, 0xaf, 0xcd, 0xc0, 0x30, 0x38, 0xf4, 0xeb, 0xcd, 0xc0, + 0x30, 0xd8, 0xd9, 0xe5, 0xd9, 0xd5, 0xe5, 0xcd, 0xba, 0x2f, 0xd9, 0xe5, + 0x60, 0x69, 0xd9, 0x61, 0x68, 0xaf, 0x06, 0xdf, 0x18, 0x10, 0x17, 0xcb, + 0x11, 0xd9, 0xcb, 0x11, 0xcb, 0x10, 0xd9, 0x29, 0xd9, 0xed, 0x6a, 0xd9, + 0x38, 0x10, 0xed, 0x52, 0xd9, 0xed, 0x52, 0xd9, 0x30, 0x0f, 0x19, 0xd9, + 0xed, 0x5a, 0xd9, 0xa7, 0x18, 0x08, 0xa7, 0xed, 0x52, 0xd9, 0xed, 0x52, + 0xd9, 0x37, 0x04, 0xfa, 0xd2, 0x31, 0xf5, 0x28, 0xe1, 0x5f, 0x51, 0xd9, + 0x59, 0x50, 0xf1, 0xcb, 0x18, 0xf1, 0xcb, 0x18, 0xd9, 0xc1, 0xe1, 0x78, + 0x91, 0xc3, 0x3d, 0x31, 0x7e, 0xa7, 0xc8, 0xfe, 0x81, 0x30, 0x06, 0x36, + 0x00, 0x3e, 0x20, 0x18, 0x51, 0xfe, 0x91, 0x20, 0x1a, 0x23, 0x23, 0x23, + 0x3e, 0x80, 0xa6, 0x2b, 0xb6, 0x2b, 0x20, 0x03, 0x3e, 0x80, 0xae, 0x2b, + 0x20, 0x36, 0x77, 0x23, 0x36, 0xff, 0x2b, 0x3e, 0x18, 0x18, 0x33, 0x30, + 0x2c, 0xd5, 0x2f, 0xc6, 0x91, 0x23, 0x56, 0x23, 0x5e, 0x2b, 0x2b, 0x0e, + 0x00, 0xcb, 0x7a, 0x28, 0x01, 0x0d, 0xcb, 0xfa, 0x06, 0x08, 0x90, 0x80, + 0x38, 0x04, 0x5a, 0x16, 0x00, 0x90, 0x28, 0x07, 0x47, 0xcb, 0x3a, 0xcb, + 0x1b, 0x10, 0xfa, 0xcd, 0x8e, 0x2d, 0xd1, 0xc9, 0x7e, 0xd6, 0xa0, 0xf0, + 0xed, 0x44, 0xd5, 0xeb, 0x2b, 0x47, 0xcb, 0x38, 0xcb, 0x38, 0xcb, 0x38, + 0x28, 0x05, 0x36, 0x00, 0x2b, 0x10, 0xfb, 0xe6, 0x07, 0x28, 0x09, 0x47, + 0x3e, 0xff, 0xcb, 0x27, 0x10, 0xfc, 0xa6, 0x77, 0xeb, 0xd1, 0xc9, 0xcd, + 0x96, 0x32, 0xeb, 0x7e, 0xa7, 0xc0, 0xd5, 0xcd, 0x7f, 0x2d, 0xaf, 0x23, + 0x77, 0x2b, 0x77, 0x06, 0x91, 0x7a, 0xa7, 0x20, 0x08, 0xb3, 0x42, 0x28, + 0x10, 0x53, 0x58, 0x06, 0x89, 0xeb, 0x05, 0x29, 0x30, 0xfc, 0xcb, 0x09, + 0xcb, 0x1c, 0xcb, 0x1d, 0xeb, 0x2b, 0x73, 0x2b, 0x72, 0x2b, 0x70, 0xd1, + 0xc9, 0x00, 0xb0, 0x00, 0x40, 0xb0, 0x00, 0x01, 0x30, 0x00, 0xf1, 0x49, + 0x0f, 0xda, 0xa2, 0x40, 0xb0, 0x00, 0x0a, 0x8f, 0x36, 0x3c, 0x34, 0xa1, + 0x33, 0x0f, 0x30, 0xca, 0x30, 0xaf, 0x31, 0x51, 0x38, 0x1b, 0x35, 0x24, + 0x35, 0x3b, 0x35, 0x3b, 0x35, 0x3b, 0x35, 0x3b, 0x35, 0x3b, 0x35, 0x3b, + 0x35, 0x14, 0x30, 0x2d, 0x35, 0x3b, 0x35, 0x3b, 0x35, 0x3b, 0x35, 0x3b, + 0x35, 0x3b, 0x35, 0x3b, 0x35, 0x9c, 0x35, 0xde, 0x35, 0xbc, 0x34, 0x45, + 0x36, 0x6e, 0x34, 0x69, 0x36, 0xde, 0x35, 0x74, 0x36, 0xb5, 0x37, 0xaa, + 0x37, 0xda, 0x37, 0x33, 0x38, 0x43, 0x38, 0xe2, 0x37, 0x13, 0x37, 0xc4, + 0x36, 0xaf, 0x36, 0x4a, 0x38, 0x92, 0x34, 0x6a, 0x34, 0xac, 0x34, 0xa5, + 0x34, 0xb3, 0x34, 0x1f, 0x36, 0xc9, 0x35, 0x01, 0x35, 0xc0, 0x33, 0xa0, + 0x36, 0x86, 0x36, 0xc6, 0x33, 0x7a, 0x36, 0x06, 0x35, 0xf9, 0x34, 0x9b, + 0x36, 0x83, 0x37, 0x14, 0x32, 0xa2, 0x33, 0x4f, 0x2d, 0x97, 0x32, 0x49, + 0x34, 0x1b, 0x34, 0x2d, 0x34, 0x0f, 0x34, 0xcd, 0xbf, 0x35, 0x78, 0x32, + 0x67, 0x5c, 0xd9, 0xe3, 0xd9, 0xed, 0x53, 0x65, 0x5c, 0xd9, 0x7e, 0x23, + 0xe5, 0xa7, 0xf2, 0x80, 0x33, 0x57, 0xe6, 0x60, 0x0f, 0x0f, 0x0f, 0x0f, + 0xc6, 0x7c, 0x6f, 0x7a, 0xe6, 0x1f, 0x18, 0x0e, 0xfe, 0x18, 0x30, 0x08, + 0xd9, 0x01, 0xfb, 0xff, 0x54, 0x5d, 0x09, 0xd9, 0x07, 0x6f, 0x11, 0xd7, + 0x32, 0x26, 0x00, 0x19, 0x5e, 0x23, 0x56, 0x21, 0x65, 0x33, 0xe3, 0xd5, + 0xd9, 0xed, 0x4b, 0x66, 0x5c, 0xc9, 0xf1, 0x3a, 0x67, 0x5c, 0xd9, 0x18, + 0xc3, 0xd5, 0xe5, 0x01, 0x05, 0x00, 0xcd, 0x05, 0x1f, 0xe1, 0xd1, 0xc9, + 0xed, 0x5b, 0x65, 0x5c, 0xcd, 0xc0, 0x33, 0xed, 0x53, 0x65, 0x5c, 0xc9, + 0xcd, 0xa9, 0x33, 0xed, 0xb0, 0xc9, 0x62, 0x6b, 0xcd, 0xa9, 0x33, 0xd9, + 0xe5, 0xd9, 0xe3, 0xc5, 0x7e, 0xe6, 0xc0, 0x07, 0x07, 0x4f, 0x0c, 0x7e, + 0xe6, 0x3f, 0x20, 0x02, 0x23, 0x7e, 0xc6, 0x50, 0x12, 0x3e, 0x05, 0x91, + 0x23, 0x13, 0x06, 0x00, 0xed, 0xb0, 0xc1, 0xe3, 0xd9, 0xe1, 0xd9, 0x47, + 0xaf, 0x05, 0xc8, 0x12, 0x13, 0x18, 0xfa, 0xa7, 0xc8, 0xf5, 0xd5, 0x11, + 0x00, 0x00, 0xcd, 0xc8, 0x33, 0xd1, 0xf1, 0x3d, 0x18, 0xf2, 0x4f, 0x07, + 0x07, 0x81, 0x4f, 0x06, 0x00, 0x09, 0xc9, 0xd5, 0x2a, 0x68, 0x5c, 0xcd, + 0x06, 0x34, 0xcd, 0xc0, 0x33, 0xe1, 0xc9, 0x62, 0x6b, 0xd9, 0xe5, 0x21, + 0xc5, 0x32, 0xd9, 0xcd, 0xf7, 0x33, 0xcd, 0xc8, 0x33, 0xd9, 0xe1, 0xd9, + 0xc9, 0xe5, 0xeb, 0x2a, 0x68, 0x5c, 0xcd, 0x06, 0x34, 0xeb, 0xcd, 0xc0, + 0x33, 0xeb, 0xe1, 0xc9, 0x06, 0x05, 0x1a, 0x4e, 0xeb, 0x12, 0x71, 0x23, + 0x13, 0x10, 0xf7, 0xeb, 0xc9, 0x47, 0xcd, 0x5e, 0x33, 0x31, 0x0f, 0xc0, + 0x02, 0xa0, 0xc2, 0x31, 0xe0, 0x04, 0xe2, 0xc1, 0x03, 0x38, 0xcd, 0xc6, + 0x33, 0xcd, 0x62, 0x33, 0x0f, 0x01, 0xc2, 0x02, 0x35, 0xee, 0xe1, 0x03, + 0x38, 0xc9, 0x06, 0xff, 0x18, 0x06, 0xcd, 0xe9, 0x34, 0xd8, 0x06, 0x00, + 0x7e, 0xa7, 0x28, 0x0b, 0x23, 0x78, 0xe6, 0x80, 0xb6, 0x17, 0x3f, 0x1f, + 0x77, 0x2b, 0xc9, 0xd5, 0xe5, 0xcd, 0x7f, 0x2d, 0xe1, 0x78, 0xb1, 0x2f, + 0x4f, 0xcd, 0x8e, 0x2d, 0xd1, 0xc9, 0xcd, 0xe9, 0x34, 0xd8, 0xd5, 0x11, + 0x01, 0x00, 0x23, 0xcb, 0x16, 0x2b, 0x9f, 0x4f, 0xcd, 0x8e, 0x2d, 0xd1, + 0xc9, 0xcd, 0x99, 0x1e, 0xed, 0x78, 0x18, 0x04, 0xcd, 0x99, 0x1e, 0x0a, + 0xc3, 0x28, 0x2d, 0xcd, 0x99, 0x1e, 0x21, 0x2b, 0x2d, 0xe5, 0xc5, 0xc9, + 0xcd, 0xf1, 0x2b, 0x0b, 0x78, 0xb1, 0x20, 0x23, 0x1a, 0xcd, 0x8d, 0x2c, + 0x38, 0x09, 0xd6, 0x90, 0x38, 0x19, 0xfe, 0x15, 0x30, 0x15, 0x3c, 0x3d, + 0x87, 0x87, 0x87, 0xfe, 0xa8, 0x30, 0x0c, 0xed, 0x4b, 0x7b, 0x5c, 0x81, + 0x4f, 0x30, 0x01, 0x04, 0xc3, 0x2b, 0x2d, 0xcf, 0x09, 0xe5, 0xc5, 0x47, + 0x7e, 0x23, 0xb6, 0x23, 0xb6, 0x23, 0xb6, 0x78, 0xc1, 0xe1, 0xc0, 0x37, + 0xc9, 0xcd, 0xe9, 0x34, 0xd8, 0x3e, 0xff, 0x18, 0x06, 0xcd, 0xe9, 0x34, + 0x18, 0x05, 0xaf, 0x23, 0xae, 0x2b, 0x07, 0xe5, 0x3e, 0x00, 0x77, 0x23, + 0x77, 0x23, 0x17, 0x77, 0x1f, 0x23, 0x77, 0x23, 0x77, 0xe1, 0xc9, 0xeb, + 0xcd, 0xe9, 0x34, 0xeb, 0xd8, 0x37, 0x18, 0xe7, 0xeb, 0xcd, 0xe9, 0x34, + 0xeb, 0xd0, 0xa7, 0x18, 0xde, 0xeb, 0xcd, 0xe9, 0x34, 0xeb, 0xd0, 0xd5, + 0x1b, 0xaf, 0x12, 0x1b, 0x12, 0xd1, 0xc9, 0x78, 0xd6, 0x08, 0xcb, 0x57, + 0x20, 0x01, 0x3d, 0x0f, 0x30, 0x08, 0xf5, 0xe5, 0xcd, 0x3c, 0x34, 0xd1, + 0xeb, 0xf1, 0xcb, 0x57, 0x20, 0x07, 0x0f, 0xf5, 0xcd, 0x0f, 0x30, 0x18, + 0x33, 0x0f, 0xf5, 0xcd, 0xf1, 0x2b, 0xd5, 0xc5, 0xcd, 0xf1, 0x2b, 0xe1, + 0x7c, 0xb5, 0xe3, 0x78, 0x20, 0x0b, 0xb1, 0xc1, 0x28, 0x04, 0xf1, 0x3f, + 0x18, 0x16, 0xf1, 0x18, 0x13, 0xb1, 0x28, 0x0d, 0x1a, 0x96, 0x38, 0x09, + 0x20, 0xed, 0x0b, 0x13, 0x23, 0xe3, 0x2b, 0x18, 0xdf, 0xc1, 0xf1, 0xa7, + 0xf5, 0xef, 0xa0, 0x38, 0xf1, 0xf5, 0xdc, 0x01, 0x35, 0xf1, 0xf5, 0xd4, + 0xf9, 0x34, 0xf1, 0x0f, 0xd4, 0x01, 0x35, 0xc9, 0xcd, 0xf1, 0x2b, 0xd5, + 0xc5, 0xcd, 0xf1, 0x2b, 0xe1, 0xe5, 0xd5, 0xc5, 0x09, 0x44, 0x4d, 0xf7, + 0xcd, 0xb2, 0x2a, 0xc1, 0xe1, 0x78, 0xb1, 0x28, 0x02, 0xed, 0xb0, 0xc1, + 0xe1, 0x78, 0xb1, 0x28, 0x02, 0xed, 0xb0, 0x2a, 0x65, 0x5c, 0x11, 0xfb, + 0xff, 0xe5, 0x19, 0xd1, 0xc9, 0xcd, 0xd5, 0x2d, 0x38, 0x0e, 0x20, 0x0c, + 0xf5, 0x01, 0x01, 0x00, 0xf7, 0xf1, 0x12, 0xcd, 0xb2, 0x2a, 0xeb, 0xc9, + 0xcf, 0x0a, 0x2a, 0x5d, 0x5c, 0xe5, 0x78, 0xc6, 0xe3, 0x9f, 0xf5, 0xcd, + 0xf1, 0x2b, 0xd5, 0x03, 0xf7, 0xe1, 0xed, 0x53, 0x5d, 0x5c, 0xd5, 0xed, + 0xb0, 0xeb, 0x2b, 0x36, 0x0d, 0xfd, 0xcb, 0x01, 0xbe, 0xcd, 0xfb, 0x24, + 0xdf, 0xfe, 0x0d, 0x20, 0x07, 0xe1, 0xf1, 0xfd, 0xae, 0x01, 0xe6, 0x40, + 0xc2, 0x8a, 0x1c, 0x22, 0x5d, 0x5c, 0xfd, 0xcb, 0x01, 0xfe, 0xcd, 0xfb, + 0x24, 0xe1, 0x22, 0x5d, 0x5c, 0x18, 0xa0, 0x01, 0x01, 0x00, 0xf7, 0x22, + 0x5b, 0x5c, 0xe5, 0x2a, 0x51, 0x5c, 0xe5, 0x3e, 0xff, 0xcd, 0x01, 0x16, + 0xcd, 0xe3, 0x2d, 0xe1, 0xcd, 0x15, 0x16, 0xd1, 0x2a, 0x5b, 0x5c, 0xa7, + 0xed, 0x52, 0x44, 0x4d, 0xcd, 0xb2, 0x2a, 0xeb, 0xc9, 0xcd, 0x94, 0x1e, + 0xfe, 0x10, 0xd2, 0x9f, 0x1e, 0x2a, 0x51, 0x5c, 0xe5, 0xcd, 0x01, 0x16, + 0xcd, 0xe6, 0x15, 0x01, 0x00, 0x00, 0x30, 0x03, 0x0c, 0xf7, 0x12, 0xcd, + 0xb2, 0x2a, 0xe1, 0xcd, 0x15, 0x16, 0xc3, 0xbf, 0x35, 0xcd, 0xf1, 0x2b, + 0x78, 0xb1, 0x28, 0x01, 0x1a, 0xc3, 0x28, 0x2d, 0xcd, 0xf1, 0x2b, 0xc3, + 0x2b, 0x2d, 0xd9, 0xe5, 0x21, 0x67, 0x5c, 0x35, 0xe1, 0x20, 0x04, 0x23, + 0xd9, 0xc9, 0xd9, 0x5e, 0x7b, 0x17, 0x9f, 0x57, 0x19, 0xd9, 0xc9, 0x13, + 0x13, 0x1a, 0x1b, 0x1b, 0xa7, 0x20, 0xef, 0xd9, 0x23, 0xd9, 0xc9, 0xf1, + 0xd9, 0xe3, 0xd9, 0xc9, 0xef, 0xc0, 0x02, 0x31, 0xe0, 0x05, 0x27, 0xe0, + 0x01, 0xc0, 0x04, 0x03, 0xe0, 0x38, 0xc9, 0xef, 0x31, 0x36, 0x00, 0x04, + 0x3a, 0x38, 0xc9, 0x31, 0x3a, 0xc0, 0x03, 0xe0, 0x01, 0x30, 0x00, 0x03, + 0xa1, 0x03, 0x38, 0xc9, 0xef, 0x3d, 0x34, 0xf1, 0x38, 0xaa, 0x3b, 0x29, + 0x04, 0x31, 0x27, 0xc3, 0x03, 0x31, 0x0f, 0xa1, 0x03, 0x88, 0x13, 0x36, + 0x58, 0x65, 0x66, 0x9d, 0x78, 0x65, 0x40, 0xa2, 0x60, 0x32, 0xc9, 0xe7, + 0x21, 0xf7, 0xaf, 0x24, 0xeb, 0x2f, 0xb0, 0xb0, 0x14, 0xee, 0x7e, 0xbb, + 0x94, 0x58, 0xf1, 0x3a, 0x7e, 0xf8, 0xcf, 0xe3, 0x38, 0xcd, 0xd5, 0x2d, + 0x20, 0x07, 0x38, 0x03, 0x86, 0x30, 0x09, 0xcf, 0x05, 0x38, 0x07, 0x96, + 0x30, 0x04, 0xed, 0x44, 0x77, 0xc9, 0xef, 0x02, 0xa0, 0x38, 0xc9, 0xef, + 0x3d, 0x31, 0x37, 0x00, 0x04, 0x38, 0xcf, 0x09, 0xa0, 0x02, 0x38, 0x7e, + 0x36, 0x80, 0xcd, 0x28, 0x2d, 0xef, 0x34, 0x38, 0x00, 0x03, 0x01, 0x31, + 0x34, 0xf0, 0x4c, 0xcc, 0xcc, 0xcd, 0x03, 0x37, 0x00, 0x08, 0x01, 0xa1, + 0x03, 0x01, 0x38, 0x34, 0xef, 0x01, 0x34, 0xf0, 0x31, 0x72, 0x17, 0xf8, + 0x04, 0x01, 0xa2, 0x03, 0xa2, 0x03, 0x31, 0x34, 0x32, 0x20, 0x04, 0xa2, + 0x03, 0x8c, 0x11, 0xac, 0x14, 0x09, 0x56, 0xda, 0xa5, 0x59, 0x30, 0xc5, + 0x5c, 0x90, 0xaa, 0x9e, 0x70, 0x6f, 0x61, 0xa1, 0xcb, 0xda, 0x96, 0xa4, + 0x31, 0x9f, 0xb4, 0xe7, 0xa0, 0xfe, 0x5c, 0xfc, 0xea, 0x1b, 0x43, 0xca, + 0x36, 0xed, 0xa7, 0x9c, 0x7e, 0x5e, 0xf0, 0x6e, 0x23, 0x80, 0x93, 0x04, + 0x0f, 0x38, 0xc9, 0xef, 0x3d, 0x34, 0xee, 0x22, 0xf9, 0x83, 0x6e, 0x04, + 0x31, 0xa2, 0x0f, 0x27, 0x03, 0x31, 0x0f, 0x31, 0x0f, 0x31, 0x2a, 0xa1, + 0x03, 0x31, 0x37, 0xc0, 0x00, 0x04, 0x02, 0x38, 0xc9, 0xa1, 0x03, 0x01, + 0x36, 0x00, 0x02, 0x1b, 0x38, 0xc9, 0xef, 0x39, 0x2a, 0xa1, 0x03, 0xe0, + 0x00, 0x06, 0x1b, 0x33, 0x03, 0xef, 0x39, 0x31, 0x31, 0x04, 0x31, 0x0f, + 0xa1, 0x03, 0x86, 0x14, 0xe6, 0x5c, 0x1f, 0x0b, 0xa3, 0x8f, 0x38, 0xee, + 0xe9, 0x15, 0x63, 0xbb, 0x23, 0xee, 0x92, 0x0d, 0xcd, 0xed, 0xf1, 0x23, + 0x5d, 0x1b, 0xea, 0x04, 0x38, 0xc9, 0xef, 0x31, 0x1f, 0x01, 0x20, 0x05, + 0x38, 0xc9, 0xcd, 0x97, 0x32, 0x7e, 0xfe, 0x81, 0x38, 0x0e, 0xef, 0xa1, + 0x1b, 0x01, 0x05, 0x31, 0x36, 0xa3, 0x01, 0x00, 0x06, 0x1b, 0x33, 0x03, + 0xef, 0xa0, 0x01, 0x31, 0x31, 0x04, 0x31, 0x0f, 0xa1, 0x03, 0x8c, 0x10, + 0xb2, 0x13, 0x0e, 0x55, 0xe4, 0x8d, 0x58, 0x39, 0xbc, 0x5b, 0x98, 0xfd, + 0x9e, 0x00, 0x36, 0x75, 0xa0, 0xdb, 0xe8, 0xb4, 0x63, 0x42, 0xc4, 0xe6, + 0xb5, 0x09, 0x36, 0xbe, 0xe9, 0x36, 0x73, 0x1b, 0x5d, 0xec, 0xd8, 0xde, + 0x63, 0xbe, 0xf0, 0x61, 0xa1, 0xb3, 0x0c, 0x04, 0x0f, 0x38, 0xc9, 0xef, + 0x31, 0x31, 0x04, 0xa1, 0x03, 0x1b, 0x28, 0xa1, 0x0f, 0x05, 0x24, 0x31, + 0x0f, 0x38, 0xc9, 0xef, 0x22, 0xa3, 0x03, 0x1b, 0x38, 0xc9, 0xef, 0x31, + 0x30, 0x00, 0x1e, 0xa2, 0x38, 0xef, 0x01, 0x31, 0x30, 0x00, 0x07, 0x25, + 0x04, 0x38, 0xc3, 0xc4, 0x36, 0x02, 0x31, 0x30, 0x00, 0x09, 0xa0, 0x01, + 0x37, 0x00, 0x06, 0xa1, 0x01, 0x05, 0x02, 0xa1, 0x38, 0xc9, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x10, 0x00, 0x00, 0x24, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x7e, 0x24, 0x24, 0x7e, 0x24, 0x00, + 0x00, 0x08, 0x3e, 0x28, 0x3e, 0x0a, 0x3e, 0x08, 0x00, 0x62, 0x64, 0x08, + 0x10, 0x26, 0x46, 0x00, 0x00, 0x10, 0x28, 0x10, 0x2a, 0x44, 0x3a, 0x00, + 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x08, + 0x08, 0x08, 0x04, 0x00, 0x00, 0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0x00, + 0x00, 0x00, 0x14, 0x08, 0x3e, 0x08, 0x14, 0x00, 0x00, 0x00, 0x08, 0x08, + 0x3e, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, + 0x00, 0x3c, 0x46, 0x4a, 0x52, 0x62, 0x3c, 0x00, 0x00, 0x18, 0x28, 0x08, + 0x08, 0x08, 0x3e, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x3c, 0x40, 0x7e, 0x00, + 0x00, 0x3c, 0x42, 0x0c, 0x02, 0x42, 0x3c, 0x00, 0x00, 0x08, 0x18, 0x28, + 0x48, 0x7e, 0x08, 0x00, 0x00, 0x7e, 0x40, 0x7c, 0x02, 0x42, 0x3c, 0x00, + 0x00, 0x3c, 0x40, 0x7c, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x7e, 0x02, 0x04, + 0x08, 0x10, 0x10, 0x00, 0x00, 0x3c, 0x42, 0x3c, 0x42, 0x42, 0x3c, 0x00, + 0x00, 0x3c, 0x42, 0x42, 0x3e, 0x02, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x20, + 0x00, 0x00, 0x04, 0x08, 0x10, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x04, 0x08, 0x10, 0x00, + 0x00, 0x3c, 0x42, 0x04, 0x08, 0x00, 0x08, 0x00, 0x00, 0x3c, 0x4a, 0x56, + 0x5e, 0x40, 0x3c, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x00, + 0x00, 0x7c, 0x42, 0x7c, 0x42, 0x42, 0x7c, 0x00, 0x00, 0x3c, 0x42, 0x40, + 0x40, 0x42, 0x3c, 0x00, 0x00, 0x78, 0x44, 0x42, 0x42, 0x44, 0x78, 0x00, + 0x00, 0x7e, 0x40, 0x7c, 0x40, 0x40, 0x7e, 0x00, 0x00, 0x7e, 0x40, 0x7c, + 0x40, 0x40, 0x40, 0x00, 0x00, 0x3c, 0x42, 0x40, 0x4e, 0x42, 0x3c, 0x00, + 0x00, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x00, 0x00, 0x3e, 0x08, 0x08, + 0x08, 0x08, 0x3e, 0x00, 0x00, 0x02, 0x02, 0x02, 0x42, 0x42, 0x3c, 0x00, + 0x00, 0x44, 0x48, 0x70, 0x48, 0x44, 0x42, 0x00, 0x00, 0x40, 0x40, 0x40, + 0x40, 0x40, 0x7e, 0x00, 0x00, 0x42, 0x66, 0x5a, 0x42, 0x42, 0x42, 0x00, + 0x00, 0x42, 0x62, 0x52, 0x4a, 0x46, 0x42, 0x00, 0x00, 0x3c, 0x42, 0x42, + 0x42, 0x42, 0x3c, 0x00, 0x00, 0x7c, 0x42, 0x42, 0x7c, 0x40, 0x40, 0x00, + 0x00, 0x3c, 0x42, 0x42, 0x52, 0x4a, 0x3c, 0x00, 0x00, 0x7c, 0x42, 0x42, + 0x7c, 0x44, 0x42, 0x00, 0x00, 0x3c, 0x40, 0x3c, 0x02, 0x42, 0x3c, 0x00, + 0x00, 0xfe, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x42, 0x42, 0x42, + 0x42, 0x42, 0x3c, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, + 0x00, 0x42, 0x42, 0x42, 0x42, 0x5a, 0x24, 0x00, 0x00, 0x42, 0x24, 0x18, + 0x18, 0x24, 0x42, 0x00, 0x00, 0x82, 0x44, 0x28, 0x10, 0x10, 0x10, 0x00, + 0x00, 0x7e, 0x04, 0x08, 0x10, 0x20, 0x7e, 0x00, 0x00, 0x0e, 0x08, 0x08, + 0x08, 0x08, 0x0e, 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, + 0x00, 0x70, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, 0x00, 0x10, 0x38, 0x54, + 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x1c, 0x22, 0x78, 0x20, 0x20, 0x7e, 0x00, 0x00, 0x00, 0x38, 0x04, + 0x3c, 0x44, 0x3c, 0x00, 0x00, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x3c, 0x00, + 0x00, 0x00, 0x1c, 0x20, 0x20, 0x20, 0x1c, 0x00, 0x00, 0x04, 0x04, 0x3c, + 0x44, 0x44, 0x3c, 0x00, 0x00, 0x00, 0x38, 0x44, 0x78, 0x40, 0x3c, 0x00, + 0x00, 0x0c, 0x10, 0x18, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x3c, 0x44, + 0x44, 0x3c, 0x04, 0x38, 0x00, 0x40, 0x40, 0x78, 0x44, 0x44, 0x44, 0x00, + 0x00, 0x10, 0x00, 0x30, 0x10, 0x10, 0x38, 0x00, 0x00, 0x04, 0x00, 0x04, + 0x04, 0x04, 0x24, 0x18, 0x00, 0x20, 0x28, 0x30, 0x30, 0x28, 0x24, 0x00, + 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x68, 0x54, + 0x54, 0x54, 0x54, 0x00, 0x00, 0x00, 0x78, 0x44, 0x44, 0x44, 0x44, 0x00, + 0x00, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x78, 0x44, + 0x44, 0x78, 0x40, 0x40, 0x00, 0x00, 0x3c, 0x44, 0x44, 0x3c, 0x04, 0x06, + 0x00, 0x00, 0x1c, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x38, 0x40, + 0x38, 0x04, 0x78, 0x00, 0x00, 0x10, 0x38, 0x10, 0x10, 0x10, 0x0c, 0x00, + 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x38, 0x00, 0x00, 0x00, 0x44, 0x44, + 0x28, 0x28, 0x10, 0x00, 0x00, 0x00, 0x44, 0x54, 0x54, 0x54, 0x28, 0x00, + 0x00, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x44, 0x44, + 0x44, 0x3c, 0x04, 0x38, 0x00, 0x00, 0x7c, 0x08, 0x10, 0x20, 0x7c, 0x00, + 0x00, 0x0e, 0x08, 0x30, 0x08, 0x08, 0x0e, 0x00, 0x00, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x00, 0x00, 0x70, 0x10, 0x0c, 0x10, 0x10, 0x70, 0x00, + 0x00, 0x14, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x99, 0xa1, + 0xa1, 0x99, 0x42, 0x3c +}; \ No newline at end of file diff --git a/khan/roms/khan128_roms.txt b/khan/roms/khan128_roms.txt new file mode 100644 index 0000000..c0c7719 --- /dev/null +++ b/khan/roms/khan128_roms.txt @@ -0,0 +1,4 @@ +ROM_48 = sos48.rom +ROM_128_0 = sos128_0.rom +ROM_128_1 = sos128_1.rom + diff --git a/khan/roms/khan_roms.txt b/khan/roms/khan_roms.txt new file mode 100644 index 0000000..be85b46 --- /dev/null +++ b/khan/roms/khan_roms.txt @@ -0,0 +1 @@ +ROM_48 = sos48.rom diff --git a/khan/roms/roms.h b/khan/roms/roms.h new file mode 100644 index 0000000..2ec772d --- /dev/null +++ b/khan/roms/roms.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef ROMS_H +#define ROMS_H + +#include +#include "../../std.h" + +typedef struct { + const char *name; + const uint8_t *data_z; + const uint data_z_size; + const uint data_size; +} embedded_rom_t; + +extern embedded_rom_t embedded_roms[]; +extern int embedded_rom_count; +#endif diff --git a/khan/sdl_keys.h b/khan/sdl_keys.h new file mode 100644 index 0000000..bcc3bb8 --- /dev/null +++ b/khan/sdl_keys.h @@ -0,0 +1,429 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _SDL_KEYS_H +#define _SDL_KEYS_H + +#include "pico/types.h" +/** + * \brief The SDL keyboard scancode representation. + * + * Values of this type are used to represent keyboard keys, among other places + * in the \link SDL_Keysym::scancode key.keysym.scancode \endlink field of the + * SDL_Event structure. + * + * The values in this enumeration are based on the USB usage page standard: + * https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf + */ +typedef enum +{ + SDL_SCANCODE_UNKNOWN = 0, + + /** + * \name Usage page 0x07 + * + * These values are from usage page 0x07 (USB keyboard page). + */ + /* @{ */ + + SDL_SCANCODE_A = 4, + SDL_SCANCODE_B = 5, + SDL_SCANCODE_C = 6, + SDL_SCANCODE_D = 7, + SDL_SCANCODE_E = 8, + SDL_SCANCODE_F = 9, + SDL_SCANCODE_G = 10, + SDL_SCANCODE_H = 11, + SDL_SCANCODE_I = 12, + SDL_SCANCODE_J = 13, + SDL_SCANCODE_K = 14, + SDL_SCANCODE_L = 15, + SDL_SCANCODE_M = 16, + SDL_SCANCODE_N = 17, + SDL_SCANCODE_O = 18, + SDL_SCANCODE_P = 19, + SDL_SCANCODE_Q = 20, + SDL_SCANCODE_R = 21, + SDL_SCANCODE_S = 22, + SDL_SCANCODE_T = 23, + SDL_SCANCODE_U = 24, + SDL_SCANCODE_V = 25, + SDL_SCANCODE_W = 26, + SDL_SCANCODE_X = 27, + SDL_SCANCODE_Y = 28, + SDL_SCANCODE_Z = 29, + + SDL_SCANCODE_1 = 30, + SDL_SCANCODE_2 = 31, + SDL_SCANCODE_3 = 32, + SDL_SCANCODE_4 = 33, + SDL_SCANCODE_5 = 34, + SDL_SCANCODE_6 = 35, + SDL_SCANCODE_7 = 36, + SDL_SCANCODE_8 = 37, + SDL_SCANCODE_9 = 38, + SDL_SCANCODE_0 = 39, + + SDL_SCANCODE_RETURN = 40, + SDL_SCANCODE_ESCAPE = 41, + SDL_SCANCODE_BACKSPACE = 42, + SDL_SCANCODE_TAB = 43, + SDL_SCANCODE_SPACE = 44, + + SDL_SCANCODE_MINUS = 45, + SDL_SCANCODE_EQUALS = 46, + SDL_SCANCODE_LEFTBRACKET = 47, + SDL_SCANCODE_RIGHTBRACKET = 48, + SDL_SCANCODE_BACKSLASH = 49, /**< Located at the lower left of the return + * key on ISO keyboards and at the right end + * of the QWERTY row on ANSI keyboards. + * Produces REVERSE SOLIDUS (backslash) and + * VERTICAL LINE in a US layout, REVERSE + * SOLIDUS and VERTICAL LINE in a UK Mac + * layout, NUMBER SIGN and TILDE in a UK + * Windows layout, DOLLAR SIGN and POUND SIGN + * in a Swiss German layout, NUMBER SIGN and + * APOSTROPHE in a German layout, GRAVE + * ACCENT and POUND SIGN in a French Mac + * layout, and ASTERISK and MICRO SIGN in a + * French Windows layout. + */ + SDL_SCANCODE_NONUSHASH = 50, /**< ISO USB keyboards actually use this code + * instead of 49 for the same key, but all + * OSes I've seen treat the two codes + * identically. So, as an implementor, unless + * your keyboard generates both of those + * codes and your OS treats them differently, + * you should generate SDL_SCANCODE_BACKSLASH + * instead of this code. As a user, you + * should not rely on this code because SDL + * will never generate it with most (all?) + * keyboards. + */ + SDL_SCANCODE_SEMICOLON = 51, + SDL_SCANCODE_APOSTROPHE = 52, + SDL_SCANCODE_GRAVE = 53, /**< Located in the top left corner (on both ANSI + * and ISO keyboards). Produces GRAVE ACCENT and + * TILDE in a US Windows layout and in US and UK + * Mac layouts on ANSI keyboards, GRAVE ACCENT + * and NOT SIGN in a UK Windows layout, SECTION + * SIGN and PLUS-MINUS SIGN in US and UK Mac + * layouts on ISO keyboards, SECTION SIGN and + * DEGREE SIGN in a Swiss German layout (Mac: + * only on ISO keyboards), CIRCUMFLEX ACCENT and + * DEGREE SIGN in a German layout (Mac: only on + * ISO keyboards), SUPERSCRIPT TWO and TILDE in a + * French Windows layout, COMMERCIAL AT and + * NUMBER SIGN in a French Mac layout on ISO + * keyboards, and LESS-THAN SIGN and GREATER-THAN + * SIGN in a Swiss German, German, or French Mac + * layout on ANSI keyboards. + */ + SDL_SCANCODE_COMMA = 54, + SDL_SCANCODE_PERIOD = 55, + SDL_SCANCODE_SLASH = 56, + + SDL_SCANCODE_CAPSLOCK = 57, + + SDL_SCANCODE_F1 = 58, + SDL_SCANCODE_F2 = 59, + SDL_SCANCODE_F3 = 60, + SDL_SCANCODE_F4 = 61, + SDL_SCANCODE_F5 = 62, + SDL_SCANCODE_F6 = 63, + SDL_SCANCODE_F7 = 64, + SDL_SCANCODE_F8 = 65, + SDL_SCANCODE_F9 = 66, + SDL_SCANCODE_F10 = 67, + SDL_SCANCODE_F11 = 68, + SDL_SCANCODE_F12 = 69, + + SDL_SCANCODE_PRINTSCREEN = 70, + SDL_SCANCODE_SCROLLLOCK = 71, + SDL_SCANCODE_PAUSE = 72, + SDL_SCANCODE_INSERT = 73, /**< insert on PC, help on some Mac keyboards (but + does send code 73, not 117) */ + SDL_SCANCODE_HOME = 74, + SDL_SCANCODE_PAGEUP = 75, + SDL_SCANCODE_DELETE = 76, + SDL_SCANCODE_END = 77, + SDL_SCANCODE_PAGEDOWN = 78, + SDL_SCANCODE_RIGHT = 79, + SDL_SCANCODE_LEFT = 80, + SDL_SCANCODE_DOWN = 81, + SDL_SCANCODE_UP = 82, + + SDL_SCANCODE_NUMLOCKCLEAR = 83, /**< num lock on PC, clear on Mac keyboards + */ + SDL_SCANCODE_KP_DIVIDE = 84, + SDL_SCANCODE_KP_MULTIPLY = 85, + SDL_SCANCODE_KP_MINUS = 86, + SDL_SCANCODE_KP_PLUS = 87, + SDL_SCANCODE_KP_ENTER = 88, + SDL_SCANCODE_KP_1 = 89, + SDL_SCANCODE_KP_2 = 90, + SDL_SCANCODE_KP_3 = 91, + SDL_SCANCODE_KP_4 = 92, + SDL_SCANCODE_KP_5 = 93, + SDL_SCANCODE_KP_6 = 94, + SDL_SCANCODE_KP_7 = 95, + SDL_SCANCODE_KP_8 = 96, + SDL_SCANCODE_KP_9 = 97, + SDL_SCANCODE_KP_0 = 98, + SDL_SCANCODE_KP_PERIOD = 99, + + SDL_SCANCODE_NONUSBACKSLASH = 100, /**< This is the additional key that ISO + * keyboards have over ANSI ones, + * located between left shift and Y. + * Produces GRAVE ACCENT and TILDE in a + * US or UK Mac layout, REVERSE SOLIDUS + * (backslash) and VERTICAL LINE in a + * US or UK Windows layout, and + * LESS-THAN SIGN and GREATER-THAN SIGN + * in a Swiss German, German, or French + * layout. */ + SDL_SCANCODE_APPLICATION = 101, /**< windows contextual menu, compose */ + SDL_SCANCODE_POWER = 102, /**< The USB document says this is a status flag, + * not a physical key - but some Mac keyboards + * do have a power key. */ + SDL_SCANCODE_KP_EQUALS = 103, + SDL_SCANCODE_F13 = 104, + SDL_SCANCODE_F14 = 105, + SDL_SCANCODE_F15 = 106, + SDL_SCANCODE_F16 = 107, + SDL_SCANCODE_F17 = 108, + SDL_SCANCODE_F18 = 109, + SDL_SCANCODE_F19 = 110, + SDL_SCANCODE_F20 = 111, + SDL_SCANCODE_F21 = 112, + SDL_SCANCODE_F22 = 113, + SDL_SCANCODE_F23 = 114, + SDL_SCANCODE_F24 = 115, + SDL_SCANCODE_EXECUTE = 116, + SDL_SCANCODE_HELP = 117, + SDL_SCANCODE_MENU = 118, + SDL_SCANCODE_SELECT = 119, + SDL_SCANCODE_STOP = 120, + SDL_SCANCODE_AGAIN = 121, /**< redo */ + SDL_SCANCODE_UNDO = 122, + SDL_SCANCODE_CUT = 123, + SDL_SCANCODE_COPY = 124, + SDL_SCANCODE_PASTE = 125, + SDL_SCANCODE_FIND = 126, + SDL_SCANCODE_MUTE = 127, + SDL_SCANCODE_VOLUMEUP = 128, + SDL_SCANCODE_VOLUMEDOWN = 129, +/* not sure whether there's a reason to enable these */ +/* SDL_SCANCODE_LOCKINGCAPSLOCK = 130, */ +/* SDL_SCANCODE_LOCKINGNUMLOCK = 131, */ +/* SDL_SCANCODE_LOCKINGSCROLLLOCK = 132, */ + SDL_SCANCODE_KP_COMMA = 133, + SDL_SCANCODE_KP_EQUALSAS400 = 134, + + SDL_SCANCODE_INTERNATIONAL1 = 135, /**< used on Asian keyboards, see + footnotes in USB doc */ + SDL_SCANCODE_INTERNATIONAL2 = 136, + SDL_SCANCODE_INTERNATIONAL3 = 137, /**< Yen */ + SDL_SCANCODE_INTERNATIONAL4 = 138, + SDL_SCANCODE_INTERNATIONAL5 = 139, + SDL_SCANCODE_INTERNATIONAL6 = 140, + SDL_SCANCODE_INTERNATIONAL7 = 141, + SDL_SCANCODE_INTERNATIONAL8 = 142, + SDL_SCANCODE_INTERNATIONAL9 = 143, + SDL_SCANCODE_LANG1 = 144, /**< Hangul/English toggle */ + SDL_SCANCODE_LANG2 = 145, /**< Hanja conversion */ + SDL_SCANCODE_LANG3 = 146, /**< Katakana */ + SDL_SCANCODE_LANG4 = 147, /**< Hiragana */ + SDL_SCANCODE_LANG5 = 148, /**< Zenkaku/Hankaku */ + SDL_SCANCODE_LANG6 = 149, /**< reserved */ + SDL_SCANCODE_LANG7 = 150, /**< reserved */ + SDL_SCANCODE_LANG8 = 151, /**< reserved */ + SDL_SCANCODE_LANG9 = 152, /**< reserved */ + + SDL_SCANCODE_ALTERASE = 153, /**< Erase-Eaze */ + SDL_SCANCODE_SYSREQ = 154, + SDL_SCANCODE_CANCEL = 155, + SDL_SCANCODE_CLEAR = 156, + SDL_SCANCODE_PRIOR = 157, + SDL_SCANCODE_RETURN2 = 158, + SDL_SCANCODE_SEPARATOR = 159, + SDL_SCANCODE_OUT = 160, + SDL_SCANCODE_OPER = 161, + SDL_SCANCODE_CLEARAGAIN = 162, + SDL_SCANCODE_CRSEL = 163, + SDL_SCANCODE_EXSEL = 164, + + SDL_SCANCODE_KP_00 = 176, + SDL_SCANCODE_KP_000 = 177, + SDL_SCANCODE_THOUSANDSSEPARATOR = 178, + SDL_SCANCODE_DECIMALSEPARATOR = 179, + SDL_SCANCODE_CURRENCYUNIT = 180, + SDL_SCANCODE_CURRENCYSUBUNIT = 181, + SDL_SCANCODE_KP_LEFTPAREN = 182, + SDL_SCANCODE_KP_RIGHTPAREN = 183, + SDL_SCANCODE_KP_LEFTBRACE = 184, + SDL_SCANCODE_KP_RIGHTBRACE = 185, + SDL_SCANCODE_KP_TAB = 186, + SDL_SCANCODE_KP_BACKSPACE = 187, + SDL_SCANCODE_KP_A = 188, + SDL_SCANCODE_KP_B = 189, + SDL_SCANCODE_KP_C = 190, + SDL_SCANCODE_KP_D = 191, + SDL_SCANCODE_KP_E = 192, + SDL_SCANCODE_KP_F = 193, + SDL_SCANCODE_KP_XOR = 194, + SDL_SCANCODE_KP_POWER = 195, + SDL_SCANCODE_KP_PERCENT = 196, + SDL_SCANCODE_KP_LESS = 197, + SDL_SCANCODE_KP_GREATER = 198, + SDL_SCANCODE_KP_AMPERSAND = 199, + SDL_SCANCODE_KP_DBLAMPERSAND = 200, + SDL_SCANCODE_KP_VERTICALBAR = 201, + SDL_SCANCODE_KP_DBLVERTICALBAR = 202, + SDL_SCANCODE_KP_COLON = 203, + SDL_SCANCODE_KP_HASH = 204, + SDL_SCANCODE_KP_SPACE = 205, + SDL_SCANCODE_KP_AT = 206, + SDL_SCANCODE_KP_EXCLAM = 207, + SDL_SCANCODE_KP_MEMSTORE = 208, + SDL_SCANCODE_KP_MEMRECALL = 209, + SDL_SCANCODE_KP_MEMCLEAR = 210, + SDL_SCANCODE_KP_MEMADD = 211, + SDL_SCANCODE_KP_MEMSUBTRACT = 212, + SDL_SCANCODE_KP_MEMMULTIPLY = 213, + SDL_SCANCODE_KP_MEMDIVIDE = 214, + SDL_SCANCODE_KP_PLUSMINUS = 215, + SDL_SCANCODE_KP_CLEAR = 216, + SDL_SCANCODE_KP_CLEARENTRY = 217, + SDL_SCANCODE_KP_BINARY = 218, + SDL_SCANCODE_KP_OCTAL = 219, + SDL_SCANCODE_KP_DECIMAL = 220, + SDL_SCANCODE_KP_HEXADECIMAL = 221, + + SDL_SCANCODE_LCTRL = 224, + SDL_SCANCODE_LSHIFT = 225, + SDL_SCANCODE_LALT = 226, /**< alt, option */ + SDL_SCANCODE_LGUI = 227, /**< windows, command (apple), meta */ + SDL_SCANCODE_RCTRL = 228, + SDL_SCANCODE_RSHIFT = 229, + SDL_SCANCODE_RALT = 230, /**< alt gr, option */ + SDL_SCANCODE_RGUI = 231, /**< windows, command (apple), meta */ + + SDL_SCANCODE_MODE = 257, /**< I'm not sure if this is really not covered + * by any of the above, but since there's a + * special KMOD_MODE for it I'm adding it here + */ + + /* @} *//* Usage page 0x07 */ + + /** + * \name Usage page 0x0C + * + * These values are mapped from usage page 0x0C (USB consumer page). + */ + /* @{ */ + + SDL_SCANCODE_AUDIONEXT = 258, + SDL_SCANCODE_AUDIOPREV = 259, + SDL_SCANCODE_AUDIOSTOP = 260, + SDL_SCANCODE_AUDIOPLAY = 261, + SDL_SCANCODE_AUDIOMUTE = 262, + SDL_SCANCODE_MEDIASELECT = 263, + SDL_SCANCODE_WWW = 264, + SDL_SCANCODE_MAIL = 265, + SDL_SCANCODE_CALCULATOR = 266, + SDL_SCANCODE_COMPUTER = 267, + SDL_SCANCODE_AC_SEARCH = 268, + SDL_SCANCODE_AC_HOME = 269, + SDL_SCANCODE_AC_BACK = 270, + SDL_SCANCODE_AC_FORWARD = 271, + SDL_SCANCODE_AC_STOP = 272, + SDL_SCANCODE_AC_REFRESH = 273, + SDL_SCANCODE_AC_BOOKMARKS = 274, + + /* @} *//* Usage page 0x0C */ + + /** + * \name Walther keys + * + * These are values that Christian Walther added (for mac keyboard?). + */ + /* @{ */ + + SDL_SCANCODE_BRIGHTNESSDOWN = 275, + SDL_SCANCODE_BRIGHTNESSUP = 276, + SDL_SCANCODE_DISPLAYSWITCH = 277, /**< display mirroring/dual display + switch, video mode switch */ + SDL_SCANCODE_KBDILLUMTOGGLE = 278, + SDL_SCANCODE_KBDILLUMDOWN = 279, + SDL_SCANCODE_KBDILLUMUP = 280, + SDL_SCANCODE_EJECT = 281, + SDL_SCANCODE_SLEEP = 282, + + SDL_SCANCODE_APP1 = 283, + SDL_SCANCODE_APP2 = 284, + + /* @} *//* Walther keys */ + + /** + * \name Usage page 0x0C (additional media keys) + * + * These values are mapped from usage page 0x0C (USB consumer page). + */ + /* @{ */ + + SDL_SCANCODE_AUDIOREWIND = 285, + SDL_SCANCODE_AUDIOFASTFORWARD = 286, + + /* @} *//* Usage page 0x0C (additional media keys) */ + + /* Add any other keys here. */ + + SDL_NUM_SCANCODES = 512 /**< not a key, just marks the number of scancodes + for array bounds */ +} SDL_Scancode; + +/** + * \brief Enumeration of valid key mods (possibly OR'd together). + */ +typedef enum +{ + KMOD_NONE = 0x0000, + KMOD_LSHIFT = 0x0001, + KMOD_RSHIFT = 0x0002, + KMOD_LCTRL = 0x0040, + KMOD_RCTRL = 0x0080, + KMOD_LALT = 0x0100, + KMOD_RALT = 0x0200, + KMOD_LGUI = 0x0400, + KMOD_RGUI = 0x0800, + KMOD_NUM = 0x1000, + KMOD_CAPS = 0x2000, + KMOD_MODE = 0x4000, + KMOD_RESERVED = 0x8000 +} SDL_Keymod; + +#define KMOD_CTRL (KMOD_LCTRL|KMOD_RCTRL) +#define KMOD_SHIFT (KMOD_LSHIFT|KMOD_RSHIFT) +#define KMOD_ALT (KMOD_LALT|KMOD_RALT) +#define KMOD_GUI (KMOD_LGUI|KMOD_RGUI) + +#endif diff --git a/khan/spoon.cpp b/khan/spoon.cpp new file mode 100644 index 0000000..3647690 --- /dev/null +++ b/khan/spoon.cpp @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include "z80t.h" + +void spoono() { + Z80t::eZ80t cpu; + cpu.generate_arm(); +} diff --git a/khan/tusb_config.h b/khan/tusb_config.h new file mode 100644 index 0000000..3579384 --- /dev/null +++ b/khan/tusb_config.h @@ -0,0 +1,98 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by compiler flags for flexibility +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +#if CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) +#else + #define CFG_TUSB_RHPORT0_MODE OPT_MODE_HOST +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +#define USB_MAX_ENDPOINTS 4 +//-------------------------------------------------------------------- +// CONFIGURATION +//-------------------------------------------------------------------- + +// Size of buffer to hold descriptors and other data used for enumeration +#define CFG_TUH_ENUMERATION_BUFSIZE 128 + +#define CFG_TUH_HUB 1 +#define CFG_TUH_CDC 0 +#define CFG_TUH_MSC 0 +#define CFG_TUH_VENDOR 0 + +// max device support (excluding hub device) +//#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports +// note tinyusb is very wasteful on space +#define CFG_TUH_DEVICE_MAX 1 +#define CFG_TUH_HID 4 // typical keyboard + mouse device can have 3-4 HID interfaces +//------------- HID -------------// +#define CFG_TUH_HID_EPIN_BUFSIZE 64 +//#define CFG_TUH_HID_EPOUT_BUFSIZE 64 +// not sure we send much +#define CFG_TUH_HID_EPOUT_BUFSIZE 16 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/khan/ui_font.cpp b/khan/ui_font.cpp new file mode 100644 index 0000000..11d8778 --- /dev/null +++ b/khan/ui_font.cpp @@ -0,0 +1,1275 @@ +#include "khan_lib.h" + +/*********************************************************************************** + * AtlantisInternational.ttf 12 px Font in U+0020 ( ) .. U+007e (~) range with 1 bpp +***********************************************************************************/ + +/*Store the image of the letters (glyph)*/ +const uint8_t atlantis_glyph_bitmap[] = + { + /*Unicode: U+0020 ( ) , Width: 4 */ + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x00, //.... + +/*Unicode: U+0021 (!) , Width: 1 */ + 0x00, //. + 0x80, //% + 0x80, //% + 0x80, //% + 0x80, //% + 0x80, //% + 0x00, //. + 0x80, //% + 0x00, //. + +/*Unicode: U+0022 (") , Width: 3 */ + 0xa0, //%.% + 0xa0, //%.% + 0xa0, //%.% + 0x00, //... + 0x00, //... + 0x00, //... + 0x00, //... + 0x00, //... + 0x00, //... + +/*Unicode: U+0023 (#) , Width: 6 */ + 0x28, //..%.%. + 0x28, //..%.%. + 0xfc, //%%%%%% + 0x50, //.%.%.. + 0x50, //.%.%.. + 0xfc, //%%%%%% + 0x50, //.%.%.. + 0x50, //.%.%.. + 0x00, //...... + +/*Unicode: U+0024 ($) , Width: 5 */ + 0x20, //..%.. + 0x70, //.%%%. + 0xa8, //%.%.% + 0xa0, //%.%.. + 0x70, //.%%%. + 0x28, //..%.% + 0xa8, //%.%.% + 0x70, //.%%%. + 0x20, //..%.. + +/*Unicode: U+0025 (%) , Width: 8 */ + 0x00, //........ + 0xe4, //%%%..%.. + 0xa8, //%.%.%... + 0xe8, //%%%.%... + 0x17, //...%.%%% + 0x15, //...%.%.% + 0x27, //..%..%%% + 0x20, //..%..... + 0x00, //........ + +/*Unicode: U+0026 (&) , Width: 7 */ + 0x30, //..%%... + 0x48, //.%..%.. + 0x48, //.%..%.. + 0x30, //..%%... + 0x52, //.%.%..% + 0x8c, //%...%%. + 0x8c, //%...%%. + 0x72, //.%%%..% + 0x00, //....... + +/*Unicode: U+0027 (') , Width: 1 */ + 0x00, //. + 0x80, //% + 0x80, //% + 0x00, //. + 0x00, //. + 0x00, //. + 0x00, //. + 0x00, //. + 0x00, //. + +/*Unicode: U+0028 (() , Width: 3 */ + 0x00, //... + 0x20, //..% + 0x40, //.%. + 0x80, //%.. + 0x80, //%.. + 0x80, //%.. + 0x80, //%.. + 0x40, //.%. + 0x20, //..% + +/*Unicode: U+0029 ()) , Width: 3 */ + 0x00, //... + 0x80, //%.. + 0x40, //.%. + 0x20, //..% + 0x20, //..% + 0x20, //..% + 0x20, //..% + 0x40, //.%. + 0x80, //%.. + +/*Unicode: U+002a (*) , Width: 5 */ + 0x00, //..... + 0xa8, //%.%.% + 0x70, //.%%%. + 0xa8, //%.%.% + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x00, //..... + +/*Unicode: U+002b (+) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x20, //..%.. + 0x20, //..%.. + 0xf8, //%%%%% + 0x20, //..%.. + 0x20, //..%.. + 0x00, //..... + 0x00, //..... + +/*Unicode: U+002c (,) , Width: 2 */ + 0x00, //.. + 0x00, //.. + 0x00, //.. + 0x00, //.. + 0x00, //.. + 0x00, //.. + 0x40, //.% + 0x40, //.% + 0x80, //%. + +/*Unicode: U+002d (-) , Width: 3 */ + 0x00, //... + 0x00, //... + 0x00, //... + 0x00, //... + 0x00, //... + 0xe0, //%%% + 0x00, //... + 0x00, //... + 0x00, //... + +/*Unicode: U+002e (.) , Width: 1 */ + 0x00, //. + 0x00, //. + 0x00, //. + 0x00, //. + 0x00, //. + 0x00, //. + 0x80, //% + 0x80, //% + 0x00, //. + +/*Unicode: U+002f (/) , Width: 3 */ + 0x00, //... + 0x20, //..% + 0x20, //..% + 0x40, //.%. + 0x40, //.%. + 0x40, //.%. + 0x80, //%.. + 0x80, //%.. + 0x00, //... + +/*Unicode: U+0030 (0) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0031 (1) , Width: 5 */ + 0x00, //..... + 0x20, //..%.. + 0x60, //.%%.. + 0x20, //..%.. + 0x20, //..%.. + 0x20, //..%.. + 0x20, //..%.. + 0xf8, //%%%%% + 0x00, //..... + +/*Unicode: U+0032 (2) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x08, //....% + 0x10, //...%. + 0x20, //..%.. + 0x40, //.%... + 0xf8, //%%%%% + 0x00, //..... + +/*Unicode: U+0033 (3) , Width: 5 */ + 0x00, //..... + 0xf0, //%%%%. + 0x08, //....% + 0x08, //....% + 0x30, //..%%. + 0x08, //....% + 0x08, //....% + 0xf0, //%%%%. + 0x00, //..... + +/*Unicode: U+0034 (4) , Width: 5 */ + 0x00, //..... + 0x10, //...%. + 0x30, //..%%. + 0x50, //.%.%. + 0x90, //%..%. + 0xf8, //%%%%% + 0x10, //...%. + 0x10, //...%. + 0x00, //..... + +/*Unicode: U+0035 (5) , Width: 5 */ + 0x00, //..... + 0xf8, //%%%%% + 0x80, //%.... + 0x80, //%.... + 0xf0, //%%%%. + 0x08, //....% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0036 (6) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x80, //%.... + 0xf0, //%%%%. + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0037 (7) , Width: 5 */ + 0x00, //..... + 0xf8, //%%%%% + 0x08, //....% + 0x10, //...%. + 0x20, //..%.. + 0x20, //..%.. + 0x40, //.%... + 0x40, //.%... + 0x00, //..... + +/*Unicode: U+0038 (8) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0039 (9) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0x78, //.%%%% + 0x08, //....% + 0x08, //....% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+003a (:) , Width: 1 */ + 0x00, //. + 0x00, //. + 0x00, //. + 0x80, //% + 0x00, //. + 0x00, //. + 0x00, //. + 0x80, //% + 0x00, //. + +/*Unicode: U+003b (;) , Width: 2 */ + 0x00, //.. + 0x00, //.. + 0x00, //.. + 0x40, //.% + 0x00, //.. + 0x00, //.. + 0x40, //.% + 0x40, //.% + 0x80, //%. + +/*Unicode: U+003c (<) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x18, //...%% + 0x60, //.%%.. + 0x80, //%.... + 0x60, //.%%.. + 0x18, //...%% + 0x00, //..... + +/*Unicode: U+003d (=) , Width: 4 */ + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0xf0, //%%%% + 0x00, //.... + 0xf0, //%%%% + 0x00, //.... + 0x00, //.... + +/*Unicode: U+003e (>) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0xc0, //%%... + 0x30, //..%%. + 0x08, //....% + 0x30, //..%%. + 0xc0, //%%... + 0x00, //..... + +/*Unicode: U+003f (?) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x08, //....% + 0x10, //...%. + 0x20, //..%.. + 0x00, //..... + 0x20, //..%.. + 0x00, //..... + +/*Unicode: U+0040 (@) , Width: 8 */ + 0x00, //........ + 0x3c, //..%%%%.. + 0x42, //.%....%. + 0x9d, //%..%%%.% + 0xa5, //%.%..%.% + 0xa5, //%.%..%.% + 0x9e, //%..%%%%. + 0x41, //.%.....% + 0x3e, //..%%%%%. + +/*Unicode: U+0041 (A) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0xf8, //%%%%% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x00, //..... + +/*Unicode: U+0042 (B) , Width: 5 */ + 0x00, //..... + 0xf0, //%%%%. + 0x88, //%...% + 0x88, //%...% + 0xf0, //%%%%. + 0x88, //%...% + 0x88, //%...% + 0xf0, //%%%%. + 0x00, //..... + +/*Unicode: U+0043 (C) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x80, //%.... + 0x80, //%.... + 0x80, //%.... + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0044 (D) , Width: 5 */ + 0x00, //..... + 0xf0, //%%%%. + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0xf0, //%%%%. + 0x00, //..... + +/*Unicode: U+0045 (E) , Width: 6 */ + 0x00, //...... + 0xf8, //%%%%%. + 0x80, //%..... + 0x80, //%..... + 0xf0, //%%%%.. + 0x80, //%..... + 0x80, //%..... + 0xf8, //%%%%%. + 0x00, //...... + +/*Unicode: U+0046 (F) , Width: 6 */ + 0x00, //...... + 0xf8, //%%%%%. + 0x80, //%..... + 0x80, //%..... + 0xf0, //%%%%.. + 0x80, //%..... + 0x80, //%..... + 0x80, //%..... + 0x00, //...... + +/*Unicode: U+0047 (G) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x80, //%.... + 0xb8, //%.%%% + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0048 (H) , Width: 5 */ + 0x00, //..... + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0xf8, //%%%%% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x00, //..... + +/*Unicode: U+0049 (I) , Width: 3 */ + 0x00, //... + 0xe0, //%%% + 0x40, //.%. + 0x40, //.%. + 0x40, //.%. + 0x40, //.%. + 0x40, //.%. + 0xe0, //%%% + 0x00, //... + +/*Unicode: U+004a (J) , Width: 5 */ + 0x00, //..... + 0x08, //....% + 0x08, //....% + 0x08, //....% + 0x08, //....% + 0x08, //....% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+004b (K) , Width: 5 */ + 0x00, //..... + 0x88, //%...% + 0x90, //%..%. + 0xa0, //%.%.. + 0xc0, //%%... + 0xa0, //%.%.. + 0x90, //%..%. + 0x88, //%...% + 0x00, //..... + +/*Unicode: U+004c (L) , Width: 6 */ + 0x00, //...... + 0x80, //%..... + 0x80, //%..... + 0x80, //%..... + 0x80, //%..... + 0x80, //%..... + 0x80, //%..... + 0xf8, //%%%%%. + 0x00, //...... + +/*Unicode: U+004d (M) , Width: 7 */ + 0x00, //....... + 0x82, //%.....% + 0xc6, //%%...%% + 0xaa, //%.%.%.% + 0x92, //%..%..% + 0x82, //%.....% + 0x82, //%.....% + 0x82, //%.....% + 0x00, //....... + +/*Unicode: U+004e (N) , Width: 6 */ + 0x00, //...... + 0x84, //%....% + 0xc4, //%%...% + 0xa4, //%.%..% + 0x94, //%..%.% + 0x8c, //%...%% + 0x84, //%....% + 0x84, //%....% + 0x00, //...... + +/*Unicode: U+004f (O) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0050 (P) , Width: 5 */ + 0x00, //..... + 0xf0, //%%%%. + 0x88, //%...% + 0x88, //%...% + 0xf0, //%%%%. + 0x80, //%.... + 0x80, //%.... + 0x80, //%.... + 0x00, //..... + +/*Unicode: U+0051 (Q) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x08, //....% + +/*Unicode: U+0052 (R) , Width: 5 */ + 0x00, //..... + 0xf0, //%%%%. + 0x88, //%...% + 0x88, //%...% + 0xf0, //%%%%. + 0xa0, //%.%.. + 0x90, //%..%. + 0x88, //%...% + 0x00, //..... + +/*Unicode: U+0053 (S) , Width: 5 */ + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x80, //%.... + 0x70, //.%%%. + 0x08, //....% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0054 (T) , Width: 5 */ + 0x00, //..... + 0xf8, //%%%%% + 0x20, //..%.. + 0x20, //..%.. + 0x20, //..%.. + 0x20, //..%.. + 0x20, //..%.. + 0x20, //..%.. + 0x00, //..... + +/*Unicode: U+0055 (U) , Width: 5 */ + 0x00, //..... + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0056 (V) , Width: 7 */ + 0x00, //....... + 0x82, //%.....% + 0x44, //.%...%. + 0x44, //.%...%. + 0x28, //..%.%.. + 0x28, //..%.%.. + 0x10, //...%... + 0x10, //...%... + 0x00, //....... + +/*Unicode: U+0057 (W) , Width: 7 */ + 0x00, //....... + 0x82, //%.....% + 0x82, //%.....% + 0x82, //%.....% + 0x92, //%..%..% + 0xaa, //%.%.%.% + 0xc6, //%%...%% + 0x82, //%.....% + 0x00, //....... + +/*Unicode: U+0058 (X) , Width: 5 */ + 0x00, //..... + 0x88, //%...% + 0x88, //%...% + 0x50, //.%.%. + 0x20, //..%.. + 0x50, //.%.%. + 0x88, //%...% + 0x88, //%...% + 0x00, //..... + +/*Unicode: U+0059 (Y) , Width: 5 */ + 0x00, //..... + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x50, //.%.%. + 0x20, //..%.. + 0x20, //..%.. + 0x20, //..%.. + 0x00, //..... + +/*Unicode: U+005a (Z) , Width: 5 */ + 0x00, //..... + 0xf8, //%%%%% + 0x08, //....% + 0x10, //...%. + 0x20, //..%.. + 0x40, //.%... + 0x80, //%.... + 0xf8, //%%%%% + 0x00, //..... + +/*Unicode: U+005b ([) , Width: 2 */ + 0x00, //.. + 0xc0, //%% + 0x80, //%. + 0x80, //%. + 0x80, //%. + 0x80, //%. + 0x80, //%. + 0x80, //%. + 0xc0, //%% + +/*Unicode: U+005c (\) , Width: 3 */ + 0x00, //... + 0x80, //%.. + 0x80, //%.. + 0x40, //.%. + 0x40, //.%. + 0x40, //.%. + 0x20, //..% + 0x20, //..% + 0x00, //... + +/*Unicode: U+005d (]) , Width: 2 */ + 0x00, //.. + 0xc0, //%% + 0x40, //.% + 0x40, //.% + 0x40, //.% + 0x40, //.% + 0x40, //.% + 0x40, //.% + 0xc0, //%% + +/*Unicode: U+005e (^) , Width: 5 */ + 0x00, //..... + 0x20, //..%.. + 0x50, //.%.%. + 0x88, //%...% + 0x88, //%...% + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x00, //..... + +/*Unicode: U+005f (_) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0xf8, //%%%%% + +/*Unicode: U+0060 (`) , Width: 2 */ + 0x00, //.. + 0x80, //%. + 0x40, //.% + 0x00, //.. + 0x00, //.. + 0x00, //.. + 0x00, //.. + 0x00, //.. + 0x00, //.. + +/*Unicode: U+0061 (a) , Width: 6 */ + 0x00, //...... + 0x00, //...... + 0x00, //...... + 0x70, //.%%%.. + 0x08, //....%. + 0x78, //.%%%%. + 0x88, //%...%. + 0x74, //.%%%.% + 0x00, //...... + +/*Unicode: U+0062 (b) , Width: 5 */ + 0x00, //..... + 0x80, //%.... + 0x80, //%.... + 0xf0, //%%%%. + 0x88, //%...% + 0x88, //%...% + 0xc8, //%%..% + 0xb0, //%.%%. + 0x00, //..... + +/*Unicode: U+0063 (c) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x80, //%.... + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0064 (d) , Width: 5 */ + 0x00, //..... + 0x08, //....% + 0x08, //....% + 0x78, //.%%%% + 0x88, //%...% + 0x88, //%...% + 0x98, //%..%% + 0x68, //.%%.% + 0x00, //..... + +/*Unicode: U+0065 (e) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0xf8, //%%%%% + 0x80, //%.... + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0066 (f) , Width: 5 */ + 0x00, //..... + 0x30, //..%%. + 0x40, //.%... + 0xe0, //%%%.. + 0x40, //.%... + 0x40, //.%... + 0x40, //.%... + 0x40, //.%... + 0x00, //..... + +/*Unicode: U+0067 (g) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0x78, //.%%%% + 0x08, //....% + 0x70, //.%%%. + +/*Unicode: U+0068 (h) , Width: 5 */ + 0x00, //..... + 0x80, //%.... + 0x80, //%.... + 0xb0, //%.%%. + 0xc8, //%%..% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x00, //..... + +/*Unicode: U+0069 (i) , Width: 3 */ + 0x00, //... + 0x40, //.%. + 0x00, //... + 0xc0, //%%. + 0x40, //.%. + 0x40, //.%. + 0x40, //.%. + 0xe0, //%%% + 0x00, //... + +/*Unicode: U+006a (j) , Width: 3 */ + 0x00, //... + 0x20, //..% + 0x00, //... + 0x20, //..% + 0x20, //..% + 0x20, //..% + 0x20, //..% + 0x20, //..% + 0xc0, //%%. + +/*Unicode: U+006b (k) , Width: 4 */ + 0x00, //.... + 0x80, //%... + 0x80, //%... + 0x90, //%..% + 0xa0, //%.%. + 0xc0, //%%.. + 0xa0, //%.%. + 0x90, //%..% + 0x00, //.... + +/*Unicode: U+006c (l) , Width: 2 */ + 0x00, //.. + 0xc0, //%% + 0x40, //.% + 0x40, //.% + 0x40, //.% + 0x40, //.% + 0x40, //.% + 0x40, //.% + 0x00, //.. + +/*Unicode: U+006d (m) , Width: 7 */ + 0x00, //....... + 0x00, //....... + 0x00, //....... + 0xec, //%%%.%%. + 0x92, //%..%..% + 0x92, //%..%..% + 0x92, //%..%..% + 0x92, //%..%..% + 0x00, //....... + +/*Unicode: U+006e (n) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0xb0, //%.%%. + 0xc8, //%%..% + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x00, //..... + +/*Unicode: U+006f (o) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x70, //.%%%. + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x70, //.%%%. + 0x00, //..... + +/*Unicode: U+0070 (p) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0xf0, //%%%%. + 0x88, //%...% + 0x88, //%...% + 0xf0, //%%%%. + 0x80, //%.... + 0x80, //%.... + +/*Unicode: U+0071 (q) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x78, //.%%%% + 0x88, //%...% + 0x88, //%...% + 0x78, //.%%%% + 0x08, //....% + 0x08, //....% + +/*Unicode: U+0072 (r) , Width: 4 */ + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0xb0, //%.%% + 0xc0, //%%.. + 0x80, //%... + 0x80, //%... + 0x80, //%... + 0x00, //.... + +/*Unicode: U+0073 (s) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x78, //.%%%% + 0x80, //%.... + 0x70, //.%%%. + 0x08, //....% + 0xf0, //%%%%. + 0x00, //..... + +/*Unicode: U+0074 (t) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x20, //..%.. + 0xf8, //%%%%% + 0x20, //..%.. + 0x20, //..%.. + 0x20, //..%.. + 0x10, //...%. + 0x00, //..... + +/*Unicode: U+0075 (u) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x98, //%..%% + 0x68, //.%%.% + 0x00, //..... + +/*Unicode: U+0076 (v) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x50, //.%.%. + 0x20, //..%.. + 0x00, //..... + +/*Unicode: U+0077 (w) , Width: 7 */ + 0x00, //....... + 0x00, //....... + 0x00, //....... + 0x92, //%..%..% + 0x92, //%..%..% + 0x92, //%..%..% + 0x92, //%..%..% + 0x6c, //.%%.%%. + 0x00, //....... + +/*Unicode: U+0078 (x) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x88, //%...% + 0x50, //.%.%. + 0x20, //..%.. + 0x50, //.%.%. + 0x88, //%...% + 0x00, //..... + +/*Unicode: U+0079 (y) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0x88, //%...% + 0x88, //%...% + 0x88, //%...% + 0x78, //.%%%% + 0x08, //....% + 0x70, //.%%%. + +/*Unicode: U+007a (z) , Width: 5 */ + 0x00, //..... + 0x00, //..... + 0x00, //..... + 0xf8, //%%%%% + 0x10, //...%. + 0x20, //..%.. + 0x40, //.%... + 0xf8, //%%%%% + 0x00, //..... + +/*Unicode: U+007b ({) , Width: 3 */ + 0x00, //... + 0x00, //... + 0x20, //..% + 0x40, //.%. + 0x40, //.%. + 0x80, //%.. + 0x40, //.%. + 0x40, //.%. + 0x20, //..% + +/*Unicode: U+007c (|) , Width: 1 */ + 0x00, //. + 0x80, //% + 0x80, //% + 0x80, //% + 0x80, //% + 0x80, //% + 0x80, //% + 0x80, //% + 0x80, //% + +/*Unicode: U+007d (}) , Width: 3 */ + 0x00, //... + 0x00, //... + 0x80, //%.. + 0x40, //.%. + 0x40, //.%. + 0x20, //..% + 0x40, //.%. + 0x40, //.%. + 0x80, //%.. + +/*Unicode: U+007e (~) , Width: 4 */ + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x50, //.%.% + 0xa0, //%.%. + 0x00, //.... + 0x00, //.... + 0x00, //.... + 0x00, //.... + + }; + +const uint8_t atlantis_glyph_widths[] = + { + 2, //4, /*Unicode: U+0020 ( )*/ + 1, /*Unicode: U+0021 (!)*/ + 3, /*Unicode: U+0022 (")*/ + 6, /*Unicode: U+0023 (#)*/ + 5, /*Unicode: U+0024 ($)*/ + 8, /*Unicode: U+0025 (%)*/ + 7, /*Unicode: U+0026 (&)*/ + 1, /*Unicode: U+0027 (')*/ + 3, /*Unicode: U+0028 (()*/ + 3, /*Unicode: U+0029 ())*/ + 5, /*Unicode: U+002a (*)*/ + 5, /*Unicode: U+002b (+)*/ + 2, /*Unicode: U+002c (,)*/ + 3, /*Unicode: U+002d (-)*/ + 1, /*Unicode: U+002e (.)*/ + 3, /*Unicode: U+002f (/)*/ + 5, /*Unicode: U+0030 (0)*/ + 5, /*Unicode: U+0031 (1)*/ + 5, /*Unicode: U+0032 (2)*/ + 5, /*Unicode: U+0033 (3)*/ + 5, /*Unicode: U+0034 (4)*/ + 5, /*Unicode: U+0035 (5)*/ + 5, /*Unicode: U+0036 (6)*/ + 5, /*Unicode: U+0037 (7)*/ + 5, /*Unicode: U+0038 (8)*/ + 5, /*Unicode: U+0039 (9)*/ + 1, /*Unicode: U+003a (:)*/ + 2, /*Unicode: U+003b (;)*/ + 5, /*Unicode: U+003c (<)*/ + 4, /*Unicode: U+003d (=)*/ + 5, /*Unicode: U+003e (>)*/ + 5, /*Unicode: U+003f (?)*/ + 8, /*Unicode: U+0040 (@)*/ + 5, /*Unicode: U+0041 (A)*/ + 5, /*Unicode: U+0042 (B)*/ + 5, /*Unicode: U+0043 (C)*/ + 5, /*Unicode: U+0044 (D)*/ + 6, /*Unicode: U+0045 (E)*/ + 5, //6 /*Unicode: U+0046 (F)*/ + 5, /*Unicode: U+0047 (G)*/ + 5, /*Unicode: U+0048 (H)*/ + 3, /*Unicode: U+0049 (I)*/ + 5, /*Unicode: U+004a (J)*/ + 5, /*Unicode: U+004b (K)*/ + 6, /*Unicode: U+004c (L)*/ + 7, /*Unicode: U+004d (M)*/ + 6, /*Unicode: U+004e (N)*/ + 5, /*Unicode: U+004f (O)*/ + 5, /*Unicode: U+0050 (P)*/ + 5, /*Unicode: U+0051 (Q)*/ + 5, /*Unicode: U+0052 (R)*/ + 5, /*Unicode: U+0053 (S)*/ + 4, //5, /*Unicode: U+0054 (T)*/ + 5, /*Unicode: U+0055 (U)*/ + 7, /*Unicode: U+0056 (V)*/ + 7, /*Unicode: U+0057 (W)*/ + 5, /*Unicode: U+0058 (X)*/ + 5, /*Unicode: U+0059 (Y)*/ + 5, /*Unicode: U+005a (Z)*/ + 2, /*Unicode: U+005b ([)*/ + 3, /*Unicode: U+005c (\)*/ + 2, /*Unicode: U+005d (])*/ + 5, /*Unicode: U+005e (^)*/ + 5, /*Unicode: U+005f (_)*/ + 2, /*Unicode: U+0060 (`)*/ + 5, //6, /*Unicode: U+0061 (a)*/ + 5, /*Unicode: U+0062 (b)*/ + 5, /*Unicode: U+0063 (c)*/ + 5, /*Unicode: U+0064 (d)*/ + 5, /*Unicode: U+0065 (e)*/ + 4, //5, /*Unicode: U+0066 (f)*/ + 5, /*Unicode: U+0067 (g)*/ + 5, /*Unicode: U+0068 (h)*/ + 3, /*Unicode: U+0069 (i)*/ + 3, /*Unicode: U+006a (j)*/ + 4, /*Unicode: U+006b (k)*/ + 2, /*Unicode: U+006c (l)*/ + 7, /*Unicode: U+006d (m)*/ + 5, /*Unicode: U+006e (n)*/ + 5, /*Unicode: U+006f (o)*/ + 5, /*Unicode: U+0070 (p)*/ + 5, /*Unicode: U+0071 (q)*/ + 4, /*Unicode: U+0072 (r)*/ + 5, /*Unicode: U+0073 (s)*/ + 5, /*Unicode: U+0074 (t)*/ + 5, /*Unicode: U+0075 (u)*/ + 5, /*Unicode: U+0076 (v)*/ + 7, /*Unicode: U+0077 (w)*/ + 5, /*Unicode: U+0078 (x)*/ + 5, /*Unicode: U+0079 (y)*/ + 5, /*Unicode: U+007a (z)*/ + 3, /*Unicode: U+007b ({)*/ + 1, /*Unicode: U+007c (|)*/ + 3, /*Unicode: U+007d (})*/ + 4, /*Unicode: U+007e (~)*/ + }; + + +// note the above have 2 pixels off the top and 1 off the bottom +#if 0 +/*Store the glyph descriptions*/ +static const lv_font_glyph_dsc_t atlantis_glyph_dsc[] = + { + {.w_px = 4, .glyph_index = 0}, /*Unicode: U+0020 ( )*/ + {.w_px = 1, .glyph_index = 12}, /*Unicode: U+0021 (!)*/ + {.w_px = 3, .glyph_index = 24}, /*Unicode: U+0022 (")*/ + {.w_px = 6, .glyph_index = 36}, /*Unicode: U+0023 (#)*/ + {.w_px = 5, .glyph_index = 48}, /*Unicode: U+0024 ($)*/ + {.w_px = 8, .glyph_index = 60}, /*Unicode: U+0025 (%)*/ + {.w_px = 7, .glyph_index = 72}, /*Unicode: U+0026 (&)*/ + {.w_px = 1, .glyph_index = 84}, /*Unicode: U+0027 (')*/ + {.w_px = 3, .glyph_index = 96}, /*Unicode: U+0028 (()*/ + {.w_px = 3, .glyph_index = 108}, /*Unicode: U+0029 ())*/ + {.w_px = 5, .glyph_index = 120}, /*Unicode: U+002a (*)*/ + {.w_px = 5, .glyph_index = 132}, /*Unicode: U+002b (+)*/ + {.w_px = 2, .glyph_index = 144}, /*Unicode: U+002c (,)*/ + {.w_px = 3, .glyph_index = 156}, /*Unicode: U+002d (-)*/ + {.w_px = 1, .glyph_index = 168}, /*Unicode: U+002e (.)*/ + {.w_px = 3, .glyph_index = 180}, /*Unicode: U+002f (/)*/ + {.w_px = 5, .glyph_index = 192}, /*Unicode: U+0030 (0)*/ + {.w_px = 5, .glyph_index = 204}, /*Unicode: U+0031 (1)*/ + {.w_px = 5, .glyph_index = 216}, /*Unicode: U+0032 (2)*/ + {.w_px = 5, .glyph_index = 228}, /*Unicode: U+0033 (3)*/ + {.w_px = 5, .glyph_index = 240}, /*Unicode: U+0034 (4)*/ + {.w_px = 5, .glyph_index = 252}, /*Unicode: U+0035 (5)*/ + {.w_px = 5, .glyph_index = 264}, /*Unicode: U+0036 (6)*/ + {.w_px = 5, .glyph_index = 276}, /*Unicode: U+0037 (7)*/ + {.w_px = 5, .glyph_index = 288}, /*Unicode: U+0038 (8)*/ + {.w_px = 5, .glyph_index = 300}, /*Unicode: U+0039 (9)*/ + {.w_px = 1, .glyph_index = 312}, /*Unicode: U+003a (:)*/ + {.w_px = 2, .glyph_index = 324}, /*Unicode: U+003b (;)*/ + {.w_px = 5, .glyph_index = 336}, /*Unicode: U+003c (<)*/ + {.w_px = 4, .glyph_index = 348}, /*Unicode: U+003d (=)*/ + {.w_px = 5, .glyph_index = 360}, /*Unicode: U+003e (>)*/ + {.w_px = 5, .glyph_index = 372}, /*Unicode: U+003f (?)*/ + {.w_px = 8, .glyph_index = 384}, /*Unicode: U+0040 (@)*/ + {.w_px = 5, .glyph_index = 396}, /*Unicode: U+0041 (A)*/ + {.w_px = 5, .glyph_index = 408}, /*Unicode: U+0042 (B)*/ + {.w_px = 5, .glyph_index = 420}, /*Unicode: U+0043 (C)*/ + {.w_px = 5, .glyph_index = 432}, /*Unicode: U+0044 (D)*/ + {.w_px = 6, .glyph_index = 444}, /*Unicode: U+0045 (E)*/ + {.w_px = 6, .glyph_index = 456}, /*Unicode: U+0046 (F)*/ + {.w_px = 5, .glyph_index = 468}, /*Unicode: U+0047 (G)*/ + {.w_px = 5, .glyph_index = 480}, /*Unicode: U+0048 (H)*/ + {.w_px = 3, .glyph_index = 492}, /*Unicode: U+0049 (I)*/ + {.w_px = 5, .glyph_index = 504}, /*Unicode: U+004a (J)*/ + {.w_px = 5, .glyph_index = 516}, /*Unicode: U+004b (K)*/ + {.w_px = 6, .glyph_index = 528}, /*Unicode: U+004c (L)*/ + {.w_px = 7, .glyph_index = 540}, /*Unicode: U+004d (M)*/ + {.w_px = 6, .glyph_index = 552}, /*Unicode: U+004e (N)*/ + {.w_px = 5, .glyph_index = 564}, /*Unicode: U+004f (O)*/ + {.w_px = 5, .glyph_index = 576}, /*Unicode: U+0050 (P)*/ + {.w_px = 5, .glyph_index = 588}, /*Unicode: U+0051 (Q)*/ + {.w_px = 5, .glyph_index = 600}, /*Unicode: U+0052 (R)*/ + {.w_px = 5, .glyph_index = 612}, /*Unicode: U+0053 (S)*/ + {.w_px = 5, .glyph_index = 624}, /*Unicode: U+0054 (T)*/ + {.w_px = 5, .glyph_index = 636}, /*Unicode: U+0055 (U)*/ + {.w_px = 7, .glyph_index = 648}, /*Unicode: U+0056 (V)*/ + {.w_px = 7, .glyph_index = 660}, /*Unicode: U+0057 (W)*/ + {.w_px = 5, .glyph_index = 672}, /*Unicode: U+0058 (X)*/ + {.w_px = 5, .glyph_index = 684}, /*Unicode: U+0059 (Y)*/ + {.w_px = 5, .glyph_index = 696}, /*Unicode: U+005a (Z)*/ + {.w_px = 2, .glyph_index = 708}, /*Unicode: U+005b ([)*/ + {.w_px = 3, .glyph_index = 720}, /*Unicode: U+005c (\)*/ + {.w_px = 2, .glyph_index = 732}, /*Unicode: U+005d (])*/ + {.w_px = 5, .glyph_index = 744}, /*Unicode: U+005e (^)*/ + {.w_px = 5, .glyph_index = 756}, /*Unicode: U+005f (_)*/ + {.w_px = 2, .glyph_index = 768}, /*Unicode: U+0060 (`)*/ + {.w_px = 6, .glyph_index = 780}, /*Unicode: U+0061 (a)*/ + {.w_px = 5, .glyph_index = 792}, /*Unicode: U+0062 (b)*/ + {.w_px = 5, .glyph_index = 804}, /*Unicode: U+0063 (c)*/ + {.w_px = 5, .glyph_index = 816}, /*Unicode: U+0064 (d)*/ + {.w_px = 5, .glyph_index = 828}, /*Unicode: U+0065 (e)*/ + {.w_px = 5, .glyph_index = 840}, /*Unicode: U+0066 (f)*/ + {.w_px = 5, .glyph_index = 852}, /*Unicode: U+0067 (g)*/ + {.w_px = 5, .glyph_index = 864}, /*Unicode: U+0068 (h)*/ + {.w_px = 3, .glyph_index = 876}, /*Unicode: U+0069 (i)*/ + {.w_px = 3, .glyph_index = 888}, /*Unicode: U+006a (j)*/ + {.w_px = 4, .glyph_index = 900}, /*Unicode: U+006b (k)*/ + {.w_px = 2, .glyph_index = 912}, /*Unicode: U+006c (l)*/ + {.w_px = 7, .glyph_index = 924}, /*Unicode: U+006d (m)*/ + {.w_px = 5, .glyph_index = 936}, /*Unicode: U+006e (n)*/ + {.w_px = 5, .glyph_index = 948}, /*Unicode: U+006f (o)*/ + {.w_px = 5, .glyph_index = 960}, /*Unicode: U+0070 (p)*/ + {.w_px = 5, .glyph_index = 972}, /*Unicode: U+0071 (q)*/ + {.w_px = 4, .glyph_index = 984}, /*Unicode: U+0072 (r)*/ + {.w_px = 5, .glyph_index = 996}, /*Unicode: U+0073 (s)*/ + {.w_px = 5, .glyph_index = 1008}, /*Unicode: U+0074 (t)*/ + {.w_px = 5, .glyph_index = 1020}, /*Unicode: U+0075 (u)*/ + {.w_px = 5, .glyph_index = 1032}, /*Unicode: U+0076 (v)*/ + {.w_px = 7, .glyph_index = 1044}, /*Unicode: U+0077 (w)*/ + {.w_px = 5, .glyph_index = 1056}, /*Unicode: U+0078 (x)*/ + {.w_px = 5, .glyph_index = 1068}, /*Unicode: U+0079 (y)*/ + {.w_px = 5, .glyph_index = 1080}, /*Unicode: U+007a (z)*/ + {.w_px = 3, .glyph_index = 1092}, /*Unicode: U+007b ({)*/ + {.w_px = 1, .glyph_index = 1104}, /*Unicode: U+007c (|)*/ + {.w_px = 3, .glyph_index = 1116}, /*Unicode: U+007d (})*/ + {.w_px = 4, .glyph_index = 1128}, /*Unicode: U+007e (~)*/ + }; + + +lv_font_t atlantis = + { + .unicode_first = 32, /*First Unicode letter in this font*/ + .unicode_last = 126, /*Last Unicode letter in this font*/ + .h_px = 12, /*Font height in pixels*/ + .glyph_bitmap = atlantis_glyph_bitmap, /*Bitmap of glyphs*/ + .glyph_dsc = atlantis_glyph_dsc, /*Description of glyphs*/ + .glyph_cnt = 95, /*Number of glyphs in the font*/ + .unicode_list = NULL, /*Every character in the font from 'unicode_first' to 'unicode_last'*/ + .get_bitmap = lv_font_get_bitmap_continuous, /*Function pointer to get glyph's bitmap*/ + .get_width = lv_font_get_width_continuous, /*Function pointer to get glyph's width*/ + .bpp = 1, /*Bit per pixel*/ + .monospace = 0, /*Fix width (0: if not used)*/ + .next_page = NULL, /*Pointer to a font extension*/ + }; +#endif diff --git a/khan/z80arm.cpp b/khan/z80arm.cpp new file mode 100644 index 0000000..3d2b3aa --- /dev/null +++ b/khan/z80arm.cpp @@ -0,0 +1,175 @@ +/* +Portable ZX-Spectrum emulator. +Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson + +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 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifdef USE_Z80_ARM + +#ifdef USE_BANKED_MEMORY_ACCESS +// we want somewhere we can dump writes to ROM +#include "hardware/structs/xip_ctrl.h" +#endif +#include "../std.h" +#include "../devices/memory.h" +#include "../devices/ula.h" +#include "../devices/device.h" + +#include "z80arm.h" +#include "../platform/platform.h" +#include "../speccy.h" + +#include "z80khan.h" +#include "hardware/interp.h" + +namespace xZ80 +{ + +//============================================================================= +// eZ80::eZ80 +//----------------------------------------------------------------------------- + eZ80::eZ80(eMemory* _m, eDevices* _d, dword _frame_tacts) + : memory(_m), rom(_d->Get()), ula(_d->Get()), devices(_d) + , frame_tacts(_frame_tacts) +#ifndef NO_USE_REPLAY + fetches(0) +#endif + { + // todo eipos + __builtin_memset(&z80a_resting_state, 0, sizeof(z80a_resting_state)); +#ifndef USE_BANKED_MEMORY_ACCESS + z80a_resting_state.memory_64k = memory->Get(eRom::ROM_48); +#endif +#ifndef NDEBUG +#ifdef ENABLE_BREAKPOINT_IN_DEBUG + z80a_resting_state.bp_addr = -1; +#endif +#endif + } + +#ifdef USE_BANKED_MEMORY_ACCESS + void eZ80::InitMemoryBanks() + { + uint8_t **bank_writes = memory->GetBankWrites(); +#if !PICO_ON_DEVICE + bank_writes[0] = 0; +#else + z80a_resting_state.interp = interp0; + hw_clear_bits(&xip_ctrl_hw->ctrl, XIP_CTRL_ERR_BADWRITE_BITS); + auto bit_dumpster = (uint8_t *)XIP_NOCACHE_NOALLOC_BASE; + bank_writes[0] = bit_dumpster; + + interp0->base[0] = (uintptr_t)memory->GetBankReads(); + interp0->base[1] = 0; + interp1->base[0] = (uintptr_t)bank_writes; + interp1->base[1] = 0; + +#ifndef NDEBUG + printf("%p %p\n", memory->GetBankReads(), bank_writes); + printf("%p %p\n", memory->GetBankReads()[0], bank_writes[0]); + printf("%p %p\n", memory->GetBankReads()[1], bank_writes[1]); + printf("%p %p\n", memory->GetBankReads()[2], bank_writes[2]); + printf("%p %p\n", memory->GetBankReads()[3], bank_writes[3]); +#endif + interp_config c = interp_default_config(); + interp_config_set_shift(&c, 12); + interp_config_set_mask(&c, 2, 3); + interp_set_config(interp0, 0, &c); + interp_set_config(interp1, 0, &c); +#endif + } +#endif +//============================================================================= +// eZ80::Reset +//----------------------------------------------------------------------------- + void eZ80::Reset() + { + assert(!z80a_resting_state.active); +#ifndef NO_USE_FAST_TAPE + HandlerStep(NULL); +#endif +#ifndef USE_HACKED_DEVICE_ABSTRACTION + handler.io = NULL; +#endif + z80a_reset(); + } + void eZ80::Update(int int_len, int* nmi_pending) + { + assert(!z80a_resting_state.active); + //assert(!nmi_pending); + z80a_update(int_len); + } +#ifndef NO_USE_REPLAY + //============================================================================= +// eZ80::Replay +//----------------------------------------------------------------------------- +void eZ80::Replay(int _fetches) +{ + fetches = _fetches; + t = 0; + eipos = -1; + while(fetches > 0) + { + Step(); + } + if(iff1) + Int(); + fetches = 0; +} +#endif +////============================================================================= +//// eZ80::Nmi +////----------------------------------------------------------------------------- +// void eZ80::Nmi() +// { +//#if 1 +// assert(false); +//#else +// push(pc); +// pc = 0x66; +// iff1 = halted = 0; +//#endif +// } + +void z80a_step_hook_func() { +#ifndef NO_USE_FAST_TAPE + static eZ80 *z80; + if (!z80) { + z80 = xPlatform::Handler()->Speccy()->CPU(); + } + assert(z80->HandlerStep()); + z80->HandlerStep()->Z80_Step(z80); +#endif +} + +#ifndef NDEBUG +#ifdef ENABLE_BREAKPOINT_IN_DEBUG + void eZ80::set_breakpoint(int pc_addr) { + assert(!z80a_resting_state.active); + z80a_resting_state.bp_addr = pc_addr; + } +#endif +#endif +}//namespace xZ80 + +#ifndef NDEBUG +void z80a_breakpoint_hit() { + struct _z80a_resting_state *s = &z80a_resting_state; + static int i=0; + printf("%d %d %04x %04x\n", i++, (int)s->t, s->hl, s->af); +} +#endif +#endif diff --git a/khan/z80arm.h b/khan/z80arm.h new file mode 100644 index 0000000..474dc88 --- /dev/null +++ b/khan/z80arm.h @@ -0,0 +1,186 @@ +/* +Portable ZX-Spectrum emulator. +Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson + +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 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef __Z80ARM_H__ +#define __Z80ARM_H__ + +#include "../std.h" +#include "z80khan.h" + +#pragma once + +class eMemory; +class eRom; +class eUla; +class eDevices; + +namespace xZ80 +{ + enum eFlags + { + CF = 0x01, + NF = 0x02, + PV = 0x04, + F3 = 0x08, + HF = 0x10, + F5 = 0x20, + ZF = 0x40, + SF = 0x80 + }; + +//***************************************************************************** +// eZ80 +//----------------------------------------------------------------------------- + void z80a_step_hook_func(); + + class eZ80 + { + public: + eZ80(eMemory* m, eDevices* d, dword frame_tacts); +#ifdef USE_BANKED_MEMORY_ACCESS + void InitMemoryBanks(); // must be called on correct core +#endif + void Reset(); + void Update(int int_len, int* nmi_pending); +#ifndef NO_USE_REPLAY + void Replay(int fetches); +#endif + + dword FrameTacts() const { return frame_tacts; } + dword T() const { + assert(!z80a_resting_state.active); // until we find we need it + return z80a_resting_state.t; + } + +#ifndef USE_HACKED_DEVICE_ABSTRACTION + class eHandlerIo + { + public: + virtual byte Z80_IoRead(word port, int tact) = 0; + }; + void HandlerIo(eHandlerIo* h) { handler.io = h; } + eHandlerIo* HandlerIo() const { return handler.io; } +#endif + + class eHandlerStep + { + public: + virtual void Z80_Step(eZ80* z80) = 0; + }; +#ifndef NO_USE_FAST_TAPE + void HandlerStep(eHandlerStep* h) { + if (h == NULL) { + z80a_resting_state.step_hook_func = 0; + } else { + assert(!z80a_resting_state.active); + z80a_resting_state.step_hook_func = &z80a_step_hook_func; + } + handler.step = h; + } + eHandlerStep* HandlerStep() const { + return handler.step; + } +#endif + + //protected: +// void Int(); + void Nmi(); +// void Step(); +// void StepF(); + +// byte IoRead(word port) const; +// void IoWrite(word port, byte v); +// byte Read(word addr) const; +// byte ReadInc(int& addr) const; +// int Read2(word addr) const; +// int Read2Inc(int& addr) const; +// void Write(word addr, byte v); +// void Write2(word addr, int v); + +// typedef void (eZ80::*CALLFUNC)(); +// typedef byte (eZ80::*CALLFUNCI)(byte); + +#ifndef NDEBUG + void set_breakpoint(int bp_addr); +#endif + protected: + eMemory* memory; + eRom* rom; + eUla* ula; + eDevices* devices; + + struct eHandler { + eHandler() { +#ifndef USE_HACKED_DEVICE_ABSTRACTION + io = NULL; +#endif +#ifndef NO_USE_FAST_TAPE + step = NULL; +#endif + } +#ifndef USE_HACKED_DEVICE_ABSTRACTION + eHandlerIo* io; +#endif +#ifndef NO_USE_FAST_TAPE + eHandlerStep* step; +#endif + } handler; + + int eipos; + const int frame_tacts; // t-states per frame +#ifndef NO_USE_REPLAY + int fetches; // .rzx replay fetches +#endif + + inline struct _z80a_resting_state *get_caller_regs() const { + assert(!z80a_resting_state.active); + return &z80a_resting_state; + } + inline word get_caller_pc() const { + return get_caller_regs()->pc; + } + inline void set_caller_pc(word v) const { + get_caller_regs()->pc = v; + } + inline byte get_caller_a() const { return get_caller_regs()->a; } + inline void set_caller_a(byte v) { get_caller_regs()->a = v; } + inline void set_caller_flag(byte flags) { get_caller_regs()->f |= flags; } + inline byte get_caller_b() const { return get_caller_regs()->b; } + inline void set_caller_b(byte v) { get_caller_regs()->b = v; } + inline byte get_caller_c() const { return get_caller_regs()->c; } + inline void set_caller_bc(word v) { + get_caller_regs()->bc = v; + } + inline word get_caller_de() const { return get_caller_regs()->de; } + inline void set_caller_de(word v) { + get_caller_regs()->de = v; + } + inline word get_caller_ix() const { return get_caller_regs()->ix; } + inline void set_caller_ix(word v) { + get_caller_regs()->ix = v; + } + inline byte get_caller_l() const { return get_caller_regs()->l; } + inline void set_caller_l(byte v) { get_caller_regs()->l = v; } + inline void set_caller_h(byte v) { get_caller_regs()->h = v; } + inline void delta_caller_t(int delta) { get_caller_regs()->t += delta; } + }; + +}//namespace xZ80 + +#endif//__Z80_H__ diff --git a/khan/z80khan.S b/khan/z80khan.S new file mode 100644 index 0000000..985c50c --- /dev/null +++ b/khan/z80khan.S @@ -0,0 +1,1704 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "hardware/regs/sio.h" +.syntax unified +.cpu cortex-m0plus +.thumb + +#define _u(x) x +#define INTERP_OFFSET0(x) ((x) - SIO_INTERP0_ACCUM0_OFFSET) +#define INTERP_OFFSET1(x) (INTERP_OFFSET0(x) + SIO_INTERP1_ACCUM0_OFFSET - SIO_INTERP0_ACCUM0_OFFSET) + +#undef ENABLE_BREAKPOINT_IN_DEBUG + +#ifndef NDEBUG +#ifdef ENABLE_BREAKPOINT_IN_DEBUG +#define ENABLE_BREAKPOINT +#endif +//#define ENABLE_ACTIVE_ASSERTION +//#define ENABLE_64K_ADDRESS_CHECK +#endif + +// moved everything to data for now (ROM 32K limit), but we need it to be fast anyway +.section .data_z80a,"x" +#define CF 0x01 +#define NF 0x02 +#define PV 0x04 +#define F3 0x08 +#define HF 0x10 +#define F5 0x20 +#define ZF 0x40 +#define SF 0x80 + +#define CF_INDEX 0 +#define NF_INDEX 1 +#define PV_INDEX 2 +#define F3_INDEX 3 +#define HF_INDEX 4 +#define F5_INDEX 5 +#define ZF_INDEX 6 +#define SF_INDEX 7 + + /** + * r0 - + * r1 + * r2 - temp_restricted (a scratch register) + * r3 - temp (either a temporary 16 bit register for intra instruction work, or the current ix/iy for xy functions (none of which need a temp register)) + * r4 - pc + * r5 - memory (either a single 64k block, or an array of 8 pointers to 16K blocks; 4 for read, 4 for write) dependeing on mode + * r6 - t + * r7 - af + * r8 - bc + * r9 - de + * r10 - hl + * r11 - memptr + * r12 - sp + */ + +#define r_temp_restricted r2 +#define r_temp r3 +#define r_ixy r3 + +#define r_pc r4 +#ifndef USE_BANKED_MEMORY_ACCESS +#define memory r5 +#else +#define interp_base r5 +#endif +#define r_t r6 +#define r_af r7 +#define r_bc r8 +#define r_de r9 +#define r_hl r10 +#define r_memptr r11 +#define r_sp r12 + +.macro preserve_only_flags reg flags:vararg + movs \reg, #255 ^ (\flags) + bics r_af, \reg +.endm + +.macro read8_internal addr_reg +#ifdef ENABLE_64K_ADDRESS_CHECK +.ifc r0,\addr_reg + push {r0} +.endif + lsrs r0, \addr_reg, #16 +.ifc r0,\addr_reg + pop {r0} +.endif + beq 8f + bkpt #0 +8: +#endif +#ifndef USE_BANKED_MEMORY_ACCESS + ldrb r0, [memory, \addr_reg] +#else + str \addr_reg, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r1, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r1, [r1] + ldrb r0, [r1, \addr_reg] +#endif +.endm + +.macro write8_internal_r1 +#ifdef ENABLE_64K_ADDRESS_CHECK + lsrs r2, r1, #16 + beq 8f + bkpt #0 +8: +#endif +#ifndef USE_BANKED_MEMORY_ACCESS + lsrs r2, r1, #14 + beq 1f + strb r0, [memory, r1] +1: +#else + // if we can spare 16k we can use it as a dumpground for writes to ROM + str r1, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r2, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r2, [r2] + strb r0, [r2, r1] +#endif +.endm + +// addr_reg may not be r0 +// seems like r2 is spare +.macro read8inc_internal addr_reg +#ifdef ENABLE_64K_ADDRESS_CHECK + lsrs r0, \addr_reg, #16 + beq 8f + bkpt #0 +8: +#endif +#ifndef USE_BANKED_MEMORY_ACCESS + ldrb r0, [memory, \addr_reg] +#else + // todo one offset for read, one for write... we can do a dump bank + str \addr_reg, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r0, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r0, [r0] + ldrb r0, [r0, \addr_reg] +#endif + adds \addr_reg, #1 + uxth \addr_reg, \addr_reg +.endm + +.macro read16_internal_r0 +#ifdef ENABLE_64K_ADDRESS_CHECK + lsrs r1, r0, #16 + beq 8f + bkpt #0 +8: +#endif + lsrs r1, r0, #1 + bcs 1f // unaligned +#ifndef USE_BANKED_MEMORY_ACCESS + ldrh r0, [memory, r0] + b 2f +1: + ldrb r1, [memory, r0] + adds r0, #1 + uxth r0, r0 + ldrb r0, [memory, r0] + lsls r0, #8 + orrs r0, r1 +#else + str r0, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r1, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r1, [r1] + ldrh r0, [r1, r0] + b 2f +1: + str r0, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r1, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r1, [r1] + ldrb r1, [r1, r0] + adds r0, #1 + str r0, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r2, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_PEEK_LANE0_OFFSET)] + uxth r0, r0 ; // we need to wrap the address since we use it as an offset + ldr r2, [r2] + ldrb r0, [r2, r0] + lsls r0, #8 + orrs r0, r1 +#endif +2: +.endm + +.macro read16inc_internal_r1 +#ifdef ENABLE_64K_ADDRESS_CHECK + lsrs r0, r1, #16 + beq 8f + bkpt #0 +8: +#endif + lsrs r0, r1, #1 + bcs 1f // unaligned +#ifndef USE_BANKED_MEMORY_ACCESS + ldrh r0, [memory, r1] + adds r1, #2 + b 2f +1: + ldrb r0, [memory, r1] + adds r1, #1 + uxth r1, r1 + ldrb r2, [memory, r1] + lsls r2, #8 + orrs r0, r2 + adds r1, #1 +#else + str r1, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r0, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r0, [r0] + ldrh r0, [r0, r1] + adds r1, #2 + b 2f +1: + str r1, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r2, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r2, [r2] + ldrb r0, [r2, r1] + adds r1, #1 + str r1, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_ACCUM0_OFFSET)] + uxth r1, r1 + ldr r2, [interp_base, #INTERP_OFFSET0(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r2, [r2] + ldrb r2, [r2, r1] + lsls r2, #8 + orrs r0, r2 + adds r1, #1 +#endif +2: + uxth r1, r1 +.endm + +.macro write16_internal_r1 +#ifdef ENABLE_64K_ADDRESS_CHECK + lsrs r2, r1, #16 + beq 8f + bkpt #0 +8: +#endif + lsrs r2, r1, #1 + bcs 1f // unaligned +#ifndef USE_BANKED_MEMORY_ACCESS + lsrs r2, r1, #14 + beq 3f + strh r0, [memory, r1] + b 3f +1: + lsrs r2, r1, #14 + beq 2f // skip first write + strb r0, [memory, r1] +2: + adds r1, #1 + uxth r1, r1 + lsrs r2, r1, #14 + beq 3f // skip second write + lsrs r0, #8 + strb r0, [memory, r1] +3: +#else + str r1, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r2, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r2, [r2] + strh r0, [r2, r1] + b 3f +1: + str r1, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r2, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r2, [r2] + strb r0, [r2, r1] +2: + adds r1, #1 + str r1, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_ACCUM0_OFFSET)] + uxth r1, r1 + ldr r2, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r2, [r2] + lsrs r0, #8 + strb r0, [r2, r1] +3: +#endif +.endm + +.macro fetch +#ifndef NO_USE_REPLAY +#error Need: --fetches; +#endif +#ifndef NO_UPDATE_RLOW_IN_FETCH + ldr r2, =z80a_resting_state + ldrb r0, [r2, #(r_low - z80a_resting_state)] + adds r0, #1 + strb r0, [r2, #(r_low - z80a_resting_state)] +#endif + adds r_t, #4 + read8inc_internal r_pc +.endm + +.global z80a_reset +.type z80a_reset,%function +.thumb_func +z80a_reset: + movs r0, #0 + adr r1, z80a_resting_state + str r0, [r1, #(_int_flags - z80a_resting_state)] + str r0, [r1, #(ir_register - z80a_resting_state)] + str r0, [r1, #(_im - z80a_resting_state)] + str r0, [r1, #(saved_r_pc - z80a_resting_state)] + bx lr + +z80a_load_state: + adr r0, saved_register_file_hi +#ifdef ENABLE_ACTIVE_ASSERTION + ldr r1, [r0, #(is_active - saved_register_file_hi)] + cmp r1, #0 + beq 1f + bkpt #0 // assertion fail +1: +#endif + movs r1, #1 + str r1, [r0, #(is_active - saved_register_file_hi)] + ldm r0!, {r1, r2, r3, r4, r5} + mov r_bc, r1 + mov r_de, r2 + mov r_hl, r3 + mov r_memptr, r4 + mov r_sp, r5 + adr r0, saved_register_file_lo + ldm r0!, { r_pc, r5, r_t, r_af} +#ifdef USE_Z80_ARM_OFFSET_T + ldr r2, frame_tacts + subs r_t, r2 +#endif + bx lr + +z80a_unload_state: + adr r0, saved_register_file_lo +#ifdef ENABLE_ACTIVE_ASSERTION + ldr r1, [r0, #(is_active - saved_register_file_lo)] + cmp r1, #1 + beq 1f + bkpt #0 // assertion fail +1: +#endif + movs r1, #0 + str r1, [r0, #(is_active - saved_register_file_lo)] +#ifdef USE_Z80_ARM_OFFSET_T + ldr r2, frame_tacts + adds r_t, r2 +#endif + stm r0!, { r_pc, r5, r_t, r_af} + mov r1, r_bc + mov r2, r_de + mov r3, r_hl + mov r4, r_memptr + mov r5, r_sp + // already at saved_register_file_hi + stm r0!, {r1, r2, r3, r4, r5} + bx lr + +.align 2 +//9: .word z80a_resting_state +.global z80a_resting_state +z80a_resting_state: +// note this layout should match struct in z80a.h +_im: .word 0 +_eipos: .word 0 +_int_flags: + .byte 0 // r_hi; +_iff1: + .byte 0 // iff1; + .byte 0 // iff2; +_halted: + .byte 0 // halted; +// these Z80 registers don't have their own ARM reg +ir_register: +r_low: .byte 0 +i: .byte 0 +.byte 0 +.byte 0 +ix_register: .word 0 +iy_register: .word 0 +// location to save the other registers +saved_register_file_lo: +saved_r_pc: +.word 0 // r4 r_pc +.word 0 // r5 memory +.word 0 // r6 r_t +.word 0 // r7 r_af +// ---- +saved_register_file_hi: +.word 0 // r8 r_bc +.word 0 // r9 r_de +.word 0 // r10 r_hl +.word 0 // r11 r_sp +.word 0 // r12 r_memptr + +.word 0 // alt_af +.word 0 // alt_bc +.word 0 // alt_de +.word 0 // alt_hl +scratch: .word 0 +step_hook_func: .word 0 +is_active: .word 0 // active +#ifdef ENABLE_BREAKPOINT +bp_addr: .word 0 +last_pc: .word 0 +#endif + +.macro dbg_check_breakpoint +#ifdef ENABLE_BREAKPOINT + ldr r0, =z80a_resting_state + ldr r1, [r0, #(bp_addr - z80a_resting_state)] + cmp r_pc, r1 + bne 7f + #if 0 + push {lr} + // want to have data available + bl z80a_unload_state + bl z80a_breakpoint_hit + bl z80a_load_state + pop {r0} + mov lr, r0 + #else + bkpt #0 + #endif +7: + ldr r0, =z80a_resting_state + str r_pc, [r0, #(last_pc - z80a_resting_state)] +#endif +.endm + +.macro step_op_table_in_r_temp +#ifndef NO_USE_DOS +#error rom->Read(pc); +#endif + #(this->*normal_opcodes[Fetch()])(); + fetch + lsls r0, #1 + ldrh r0, [r_temp, r0] + adds r0, r_temp + blx r0 +.endm + +.macro step_op_table_in_r_temp_maybe_neg +#ifndef NO_USE_DOS +#error rom->Read(pc); +#endif + #(this->*normal_opcodes[Fetch()])(); + fetch + lsls r0, #1 + ldrh r0, [r_temp, r0] + sxth r0, r0 + adds r0, r_temp + blx r0 +.endm + +.align 1 + +.global z80a_step +.type z80a_step,%function +.thumb_func +z80a_step: + push {lr} + bl z80a_load_state + dbg_check_breakpoint + ldr r_temp, =op_table + step_op_table_in_r_temp + b z80a_unload_state + pop {pc} + +.section .data_z80a,"x" + +// NOTE: general convention is to preserve anything other than r0-r1 in a function unless it is an explicit side effect + +// r0 = address +// return in r0 +// this is a one instruction thing with single 64K RAM, so inline it everywhere +//#ifndef USE_SINGLE_64K_MEMORY +//read8: +// read8_internal r0 +// bx lr +//#endif + +// r0 = address +// return in r0 +ioread8: + mov r1, r_t + push {r3, lr} + mov r3, r12 + push {r3} +#ifdef USE_Z80_ARM_OFFSET_T + ldr r3, frame_tacts + adds r1, r3 +#endif + // todo caused by code being in data section + ldr r2, =static_device_io_read + blx r2 + pop {r3} + mov r12, r3 + pop {r3, pc} + +// r0 = address +// return value r0 address+1 in r1 +read8inc: + mov r1, r0 + read8inc_internal r1 + bx lr + +// r0 = address +// return value in r0 +read16: + read16_internal_r0 + bx lr + +// r0 = address +// return value r0 address+2 in r1 +read16inc: + mov r1, r0 + read16inc_internal_r1 + bx lr + +// r0 = value +// r1 = address +.thumb_func +write8: + write8_internal_r1 + bx lr + +// r0 = value +// r1 = address +.thumb_func +iowrite8: + mov r2, r_t + push {r3, lr} + mov r3, r12 + push {r3} +#ifdef USE_Z80_ARM_OFFSET_T + ldr r3, frame_tacts + adds r2, r3 +#endif +// todo caused by code being in data section + ldr r3, =static_device_io_write + blx r3 + pop {r3} + mov r12, r3 + pop {r3, pc} + +.ltorg + +// r0 = value +// r1 = address +.thumb_func +write16: + write16_internal_r1 + bx lr + +.thumb_func +_push: + mov r2, r_sp + subs r2, #2 + uxth r2, r2 + mov r_sp, r2 + + lsrs r1, r2, #1 + bcs 1f // misaligned + +#ifndef USE_BANKED_MEMORY_ACCESS + lsrs r1, r2, #14 + beq 3f + strh r0, [memory, r2] + bx lr + +1: + lsrs r1, r2, #14 + beq 2f + strb r0, [memory, r2] +2: + adds r2, #1 + uxth r2, r2 + lsrs r1, r2, #14 + beq 3f + + lsrs r0, #8 + strb r0, [memory, r2] +#else + str r2, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r1, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r1, [r1] + strh r0, [r1, r2] + bx lr +1: + str r2, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_ACCUM0_OFFSET)] + ldr r1, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r1, [r1] + strb r0, [r1, r2] +2: + adds r2, #1 + str r2, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_ACCUM0_OFFSET)] + uxth r2, r2 + ldr r1, [interp_base, #INTERP_OFFSET1(SIO_INTERP0_PEEK_LANE0_OFFSET)] + ldr r1, [r1] + lsrs r0, #8 + strb r0, [r1, r2] +#endif +3: + bx lr + +.section .data_z80a_arith,"x" +.thumb_func +// Combined subs ands sbcs8 for size at small speed cost for sub +.thumb_func +sub8: + // clear the z80 carry + movs r1, #CF + bics r_af, r1 + +.thumb_func +sbc8: + // toggle the carry (ARM is reversed for subtract) + movs r1, #CF + eors r_af, r1 + + lsrs r2, r_af, #8 // a + lsls r2, #24 // (a << 24) + lsls r0, #24 // (val << 24) + + push {r3} + + // perform subtraction + + // argggh on thumb this updares flags (it is adds r3, r2, #0) + // todo this should no longer be the case with .unified + mov r3, r2 + // set arm carry to !z80 carry + lsrs r1, r_af, #1 + sbcs r3, r0 // (a << 24) - ((val << 24) + c) + + // save away 4 arm flags in low nibble of r1 + mrs r1, apsr + lsrs r1, #28 + + // low nibble of a in high nibble of of r2 + lsls r2, r2, #4 + + // low nibble of val in high nibble of r0 + lsls r0, #4 + + // set arm carry to !z80 carry + lsrs r_af, #1 + + // set flags based on half carry + sbcs r2, r0 + + // set z80 flags based on saved arm flags + // note arm carry flag is preserved across the next two instructions + adr r2, flag_translate_sub_no_ZF + ldrb r_af, [r2, r1] // a = 0; f = corrsect scv (note z is not corrsect because of ffffff in low 24 bits + + // set the half carry (note we set when ARM carry is clear) + bcs 1f + adds r_af, #HF +1: + // set N flag + adds r_af, #NF + // r1 = result + lsrs r1, r3, #24 + + // need to set zero flag according to result + bne 1f + adds r_af, #ZF +1: + + // set F3, F5 from result + movs r3, #F3|F5 + ands r3, r1 + orrs r_af, r3 + + // ... ands store the result in a + lsls r1, r1, #8 + orrs r_af, r1 + + pop {r3} + bx lr + +.thumb_func +neg8: + lsrs r0, r_af, #8 // a + lsls r0, #24 + + // perform subtraction to set the flags + negs r2, r0 // -a + + mrs r_af, apsr + adr r1, flag_translate_sub + lsrs r_af, #28 + ldrb r_af, [r1, r_af] // a = 0; f = corrsect szcv + + lsrs r1, r2, #16 + orrs r_af, r1 // store result + + // set F3, F5 from result + movs r2, #F3|F5 + lsrs r1, #8 + ands r1, r2 + orrs r_af, r1 + + // ands low nibble of val in high nibble of r0 + lsls r0, #4 + + // set flags based on half carry + negs r1, r0 + + // ARM c flag on subtract is opposite of Z80 one + bcc 1f + + // H is now clear, N is set + adds r_af, #NF + bx lr +1: + // H is now set, N is set + adds r_af, #HF|NF + bx lr + +.thumb_func +cp8: + lsrs r2, r_af, #8 // a + lsls r2, #24 + lsls r0, #24 + + // perform subtraction to set the flags + subs r1, r2, r0 // a - val + + mrs r_af, apsr + adr r1, flag_translate_sub + lsrs r_af, #28 + ldrb r_af, [r1, r_af] // a = 0; f = corrsect szcv + + lsrs r1, r2, #16 + orrs r_af, r1 // restore a + + // set F3, F5 from val + movs r1, #F3|F5 + rev r0, r0 + ands r1, r0 + orrs r_af, r1 + + // restore low nibble of a in high nibble of of r1 + lsls r1, r2, #4 + // ands low nibble of val in high nibble of r0 + lsls r0, #28 + + // set flags based on half carry + subs r1, r0 + + // ARM c flag on subtract is opposite of Z80 one + bcc 1f + + // H is now clear, N is set + adds r_af, #NF + bx lr +1: + // H is now set, N is set + adds r_af, #HF|NF + bx lr + +.thumb_func +adc16: + mov r2, r_hl + mvns r2, r2 + lsls r2, #16 + mvns r2, r2 // (a << 16) + 0x0000ffff + + lsls r0, #16 // (val << 16) + + push {r3} + + // perform addition + + // argggh on thumb this updares flags (it is adds r3, r2, #0) + mov r3, r2 + // set arm carry to z80 carry + lsrs r1, r_af, #1 + adcs r3, r0 // (hl << 16) + (val << 16) + c + 0x0000ffff + + // save away 4 arm flags in low nibble of r1 + mrs r1, apsr + lsrs r1, #28 + + // low 3 nibbles of hl in high nibbles of of r2 ands 1s in bits 0 to 19 + lsls r2, r2, #4 + adds r2, #0xf + + // low 3 nibbles of val in high nibbles of r0 + lsls r0, #4 + + // set arm carry to z80 carry + lsrs r_af, #1 + + // set flags based on half carry + adcs r2, r0 + + // set z80 flags based on saved arm flags + // note arm carry flag is preserved across the next two instructions + adr r2, flag_translate_add_no_ZF + ldrb r2, [r2, r1] // r2 = corrsect scv (note z is not corrsect because of ffffff in low 24 bits + + // set the half carry + bcc 1f + adds r2, #HF +1: + + lsrs r_af, #7 + lsls r_af, #8 // af now a:0 + orrs r_af, r2 + + // r1 = result + lsrs r1, r3, #16 + + // need to set zero flag according to result + bne 1f + adds r_af, #ZF +1: + + // set F3, F5 from result + lsrs r0, r1, #8 + movs r2, #F3|F5 + ands r2, r0 + + orrs r_af, r2 + + mov r_hl, r1 + + pop {r3} + bx lr + +.thumb_func +sbc16: + // toggle the carry (ARM is reversed for subtract) + movs r1, #CF + eors r_af, r1 + + mov r2, r_hl + lsls r2, #16 // (hl << 16) + lsls r0, #16 // (val << 16) + + push {r3} + + // perform subtraction + + // argggh on thumb this updares flags (it is adds r3, r2, #0) + mov r3, r2 + // set arm carry to !z80 carry + lsrs r1, r_af, #1 + sbcs r3, r0 // (hl << 16) - ((val << 16) + c) + + // save away 4 arm flags in low nibble of r1 + mrs r1, apsr + lsrs r1, #28 + + // low 3 nibbles of a in high 3 nibbles of of r2 + lsls r2, r2, #4 + + // low nibble of val in high nibble of r0 + lsls r0, #4 + + // set arm carry to !z80 carry + lsrs r_af, #1 + + // set flags based on half carry + sbcs r2, r0 + + // set z80 flags based on saved arm flags + // note arm carry flag is preserved across the next two instructions + adr r2, flag_translate_sub_no_ZF + ldrb r2, [r2, r1] // r2 = corrsect scv (note z is not corrsect because of ffffff in low 24 bits + + // set the half carry (note we set when ARM carry is clear) + bcs 1f + adds r2, #HF +1: + + lsrs r_af, #7 + lsls r_af, #8 // af now a:0 + orrs r_af, r2 + + // set N flag + adds r_af, #NF + + // r1 = result + lsrs r1, r3, #16 + + // need to set zero flag according to result + bne 1f + adds r_af, #ZF +1: + // set F3, F5 from result hi byte + lsrs r0, r1, #8 + movs r2, #F3|F5 + ands r2, r0 + orrs r_af, r2 + + mov r_hl, r1 + + pop {r3} + bx lr + +.thumb_func +add16: + lsls r2, r1, #16 + lsls r0, #16 + + push {r3} + preserve_only_flags r3, (SF|ZF|PV) + + adds r3, r2, r0 // v0 + v1 + + bcc 1f + adds r_af, #CF +1: + + // restore low nibble of a in high nibble of of r1 + lsls r1, r2, #4 + // ands low nibble of val in high nibble of r0 + lsls r3, r0, #4 + + // set flags based on half carry + adds r1, r3 + + bcc 2f + + movs r1, #HF + orrs r_af, r1 +2: + + pop {r3} + + // redo addition (cheaper than push/pop) ands we now have result in r0 + adds r0, r2 + lsrs r0, #16 + + // set F3, F5 from result + movs r1, #F3|F5 + lsrs r2, r0, #8 + ands r1, r2 + orrs r_af, r1 + + bx lr + +.align 2 +// arm NZCV to z80 ZVC +flag_translate_sub: + .byte 0 | CF + .byte 0 | CF | PV + .byte 0 + .byte 0 | PV + .byte 0 | ZF | CF + .byte 0 | ZF | CF | PV + .byte 0 | ZF + .byte 0 | ZF | | PV + + .byte SF | CF + .byte SF | CF | PV + .byte SF + .byte SF | | PV + .byte SF | ZF | CF + .byte SF | ZF | CF | PV + .byte SF | ZF + .byte SF | ZF | | PV + +.align 2 +// arm NZCV to z80 SVC +flag_translate_sub_no_ZF: + .byte 0 | CF + .byte 0 | CF | PV + .byte 0 + .byte 0 | PV + .byte 0 | CF + .byte 0 | CF | PV + .byte 0 + .byte 0 | PV + + .byte SF | CF + .byte SF | CF | PV + .byte SF + .byte SF | PV + .byte SF | CF + .byte SF | CF | PV + .byte SF + .byte SF | PV + +// Combined adds ands adcs8 for size at small speed cost for add +.thumb_func +add8: + // clear the z80 carry + movs r1, #CF + bics r_af, r1 + +.thumb_func +adc8: + lsrs r2, r_af, #8 // a + mvns r2, r2 + lsls r2, #24 + mvns r2, r2 // (a << 24) + 0x00ffffff + + lsls r0, #24 // (val << 24) + + push {r3} + + // perform addition + + // argggh on thumb this updares flags (it is adds r3, r2, #0) + mov r3, r2 + // set arm carry to z80 carry + lsrs r1, r_af, #1 + adcs r3, r0 // (a << 24) + (val << 24) + c + 0x00ffffff + + // save away 4 arm flags in low nibble of r1 + mrs r1, apsr + lsrs r1, #28 + + // low nibble of a in high nibble of of r2 ands 1s in bits 0 to 27 + lsls r2, r2, #4 + adds r2, #0xf + + // low nibble of val in high nibble of r0 + lsls r0, #4 + + // set arm carry to z80 carry + lsrs r_af, #1 + + // set flags based on half carry + adcs r2, r0 + + // set z80 flags based on saved arm flags + // note arm carry flag is preserved across the next two instructions + adr r2, flag_translate_add_no_ZF + ldrb r_af, [r2, r1] // a = 0; f = corrsect scv (note z is not corrsect because of ffffff in low 24 bits + + // set the half carry + bcc 1f + adds r_af, #HF +1: + + // r1 = result + lsrs r1, r3, #24 + + // need to set zero flag according to result + bne 1f + adds r_af, #ZF +1: + + // set F3, F5 from result + movs r3, #F3|F5 + ands r3, r1 + orrs r_af, r3 + + // ... ands store the result in a + lsls r1, r1, #8 + orrs r_af, r1 + + pop {r3} + bx lr + +.align 2 +flag_translate_add_no_ZF: + .byte 0 + .byte 0 | PV + .byte 0 | CF + .byte 0 | CF | PV + .byte 0 + .byte 0 | PV + .byte 0 | CF + .byte 0 | CF | PV + + .byte SF + .byte SF | PV + .byte SF | CF + .byte SF | CF | PV + .byte SF + .byte SF | PV + .byte SF | CF + .byte SF | CF | PV + +.thumb_func +set_af35_special_r_temp: + // (tempbyte & F3) + ((tempbyte << 4) & F5); + movs r0, #F3 + ands r0, r_temp + orrs r_af, r0 + lsrs r0, r_temp, #2 + bcc 1f + movs r0, #F5 + orrs r_af, r0 +1: + bx lr + +.section .data_z70a_op,"x" +.align 1 +.thumb_func +// instruction prefix hell!! +opDDFD: +/** + byte op1; // last DD/FD prefix + do + { + op1 = opcode; + opcode = Fetch(); + } while((opcode | 0x20) == 0xFD); // opcode == DD/FD +*/ +1: + mov r1, r0 + fetch + cmp r0, #0xed + bne 4f + // ED + ldr r0, =oped + bx r0 +4: + cmp r0, #0xdd + beq 1b + cmp r0, #0xfd + beq 1b + ldr r2, =z80a_resting_state + ldr r_temp, =opxy_table + cmp r1, #0xfd + beq 3f + // DD + cmp r0, #0xcb + bne 4f + ldr r1, [r2, #(ix_register - z80a_resting_state)] + read8inc_internal r_pc + sxtb r0, r0 + adds r0, r1 + mov r_memptr, r0 + b 2f +4: + lsls r0, #1 + ldrh r0, [r_temp, r0] + sxth r0, r0 + adds r0, r_temp + // note r_ixy == r_temp so we can't do this above + ldr r_ixy, [r2, #(ix_register - z80a_resting_state)] + push {lr} + blx r0 + ldr r2, =z80a_resting_state + str r_ixy, [r2, #(ix_register - z80a_resting_state)] + pop {pc} +3: + // FD + cmp r0, #0xcb + bne 4f + ldr r1, [r2, #(iy_register - z80a_resting_state)] + read8inc_internal r_pc + sxtb r0, r0 + adds r0, r1 + mov r_memptr, r0 + b 2f +4: + lsls r0, #1 + ldrh r0, [r_temp, r0] + sxth r0, r0 + adds r0, r_temp + // note r_ixy == r_temp so we can't do this above + ldr r_ixy, [r2, #(iy_register - z80a_resting_state)] + push {lr} + blx r0 + ldr r2, =z80a_resting_state + str r_ixy, [r2, #(iy_register - z80a_resting_state)] + pop {pc} +2: + // (FD/DD)CB + ldr r_temp, =opli_m35_table + // not doing fetch here because of something about not incrementing r (although we don't do that anyway atm) + read8inc_internal r_pc + // this is rolled into the ddcb/ddcbit functions + // adds r_t, #4 + + lsrs r2, r0, #3 + lsls r2, #1 + ldrh r2, [r_temp, r2] + sxth r2, r2 + adds r2, r_temp + + ldr r_temp, =opddcb_X_table + lsrs r1, r0, #7 + // arg no single test for this flag combo; basically this (with the shift above) is 0x40 == (r0 & 0xc0) i.e. is this bitmem() + bcc 1f + bne 1f + // note that bitmem has different wrapper functions that don't update memory (ands increment t differently) + adds r_temp, #(opddcb_bitX_table - opddcb_X_table) +1: + movs r1, #7 + ands r0, r1 + lsls r0, #1 + ldrh r1, [r_temp, r0] + sxth r1, r1 + adds r1, r_temp + bx r1 +.ltorg + +/** + + if(opcode == 0xCB) + { + dword ptr; // pointer to DDCB operands + ptr = ((op1 == 0xDD) ? ix : iy) + (signed char)ReadInc(pc); + memptr = ptr; + // DDCBnnXX,FDCBnnXX increment R by 2, not 3! + opcode = ReadInc(pc); + t += 4; + byte v = (this->*logic_ix_opcodes[opcode])(Read(ptr)); + if((opcode & 0xC0) == 0x40)// bit n,rm + { + t += 8; + return; + } + // select destination register for shift/res/set + (this->*reg_offset[opcode & 7]) = v; // ??? + Write(ptr, v); + t += 11; + return; + } + if(opcode == 0xED) + { + OpED(); + return; + } + // one prefix: DD/FD + op1 == 0xDD ? (this->*ix_opcodes[opcode])() : (this->*iy_opcodes[opcode])(); + */ + +.section .data_z80a_inc,"x" +.thumb_func +inc8: + preserve_only_flags r1, CF + adr r1, _incf + ldrb r1, [r1, r0] + orrs r_af, r1 + adds r0, #1 + uxtb r0, r0 + bx lr + +.section .data_z80a_dec,"x" +.thumb_func +dec8: + preserve_only_flags r1, CF + adr r1, _decf + ldrb r1, [r1, r0] + orrs r_af, r1 + subs r0, #1 + uxtb r0, r0 + bx lr + +.section .data_z80a_logic,"x" +.thumb_func +ands8: + // r0 is value to ands in low 8 + lsrs r_af, #8 + ands r_af, r0 + adr r1, _log_f + ldrb r0, [r1, r_af] + lsls r_af, #8 + movs r1, #HF + orrs r_af, r0 + orrs r_af, r1 + bx lr + +.thumb_func +or8: + // r0 is value to ands in low 8 + lsrs r_af, #8 + orrs r_af, r0 + adr r1, _log_f + ldrb r0, [r1, r_af] + lsls r_af, #8 + orrs r_af, r0 + bx lr + +.thumb_func +xor8: + // r0 is value to ands in low 8 + lsrs r_af, #8 + eors r_af, r0 + adr r1, _log_f + ldrb r0, [r1, r_af] + lsls r_af, #8 + orrs r_af, r0 + bx lr + +.thumb_func +rlc8: + lsrs r_af, #8 + lsls r_af, #8 + + movs r1, #0 + lsls r0, #25 + bcc 1f + movs r1, #1 + orrs r_af, r1 +1: + lsrs r0, #24 + orrs r0, r1 + + adr r1, _log_f + ldrb r1, [r1, r0] + orrs r_af, r1 + + bx lr + +.thumb_func +rl8: + movs r1, #CF + ands r1, r_af + lsrs r_af, #8 + lsls r_af, #8 + + lsls r0, #25 + bcc 1f + movs r2, #CF + orrs r_af, r2 +1: + lsrs r0, #24 + orrs r0, r1 + + adr r1, _log_f + ldrb r1, [r1, r0] + orrs r_af, r1 + + bx lr + +.thumb_func +rr8: + movs r1, #CF + ands r1, r_af + lsrs r_af, #8 + lsls r_af, #8 + + lsrs r0, #1 + bcc 1f + movs r2, #CF + orrs r_af, r2 +1: + lsls r1, #7 + orrs r0, r1 + + adr r1, _log_f + ldrb r1, [r1, r0] + orrs r_af, r1 + + bx lr + +.thumb_func +sla8: + lsrs r_af, #8 + lsls r_af, #8 + + lsls r0, #25 + bcc 1f + movs r1, #CF + orrs r_af, r1 +1: + lsrs r0, #24 + + adr r1, _log_f + ldrb r1, [r1, r0] + orrs r_af, r1 + + bx lr + +.thumb_func +sli8: + movs r1, #CF + lsrs r_af, #8 + lsls r_af, #8 + + lsls r0, #25 + bcc 1f + orrs r_af, r1 +1: + lsrs r0, #24 + orrs r0, r1 + + adr r1, _log_f + ldrb r1, [r1, r0] + orrs r_af, r1 + + bx lr + + +.thumb_func +sra8: + lsrs r_af, #8 + lsls r_af, #8 + + lsls r0, #24 + asrs r0, #25 + + bcc 1f + movs r1, #CF + orrs r_af, r1 +1: + uxtb r0, r0 + + adr r1, _log_f + ldrb r1, [r1, r0] + orrs r_af, r1 + + bx lr + +.thumb_func +srl8: + lsrs r_af, #8 + lsls r_af, #8 + + lsrs r0, #1 + bcc 1f + movs r1, #CF + orrs r_af, r1 +1: + adr r1, _log_f + ldrb r1, [r1, r0] + orrs r_af, r1 + + bx lr + +.thumb_func +rrc8: + lsrs r_af, #8 + lsls r_af, #8 + + lsrs r0, #1 + bcc 1f + movs r1, #CF + orrs r_af, r1 + movs r1, #128 + orrs r0, r1 +1: + + adr r1, _log_f + ldrb r1, [r1, r0] + orrs r_af, r1 + + bx lr + +.section .data_z80a_inc +.align 2 +_incf: + .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08 + .byte 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10 + .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08 + .byte 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x30 + .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28 + .byte 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x30 + .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28 + .byte 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x10 + .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08 + .byte 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10 + .byte 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08 + .byte 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x30 + .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28 + .byte 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x30 + .byte 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x28 + .byte 0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x94 + .byte 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88 + .byte 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x90 + .byte 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88 + .byte 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0xb0 + .byte 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8 + .byte 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xb0 + .byte 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8 + .byte 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0x90 + .byte 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88 + .byte 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x90 + .byte 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x88 + .byte 0x88,0x88,0x88,0x88,0x88,0x88,0x88,0xb0 + .byte 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8 + .byte 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xb0 + .byte 0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa0,0xa8 + .byte 0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0xa8,0x50 + +.section .data_z80a_dec +.align 2 +_decf: + .byte 0xba,0x42,0x02,0x02,0x02,0x02,0x02,0x02 + .byte 0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a + .byte 0x1a,0x02,0x02,0x02,0x02,0x02,0x02,0x02 + .byte 0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a + .byte 0x1a,0x22,0x22,0x22,0x22,0x22,0x22,0x22 + .byte 0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a + .byte 0x3a,0x22,0x22,0x22,0x22,0x22,0x22,0x22 + .byte 0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a + .byte 0x3a,0x02,0x02,0x02,0x02,0x02,0x02,0x02 + .byte 0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a + .byte 0x1a,0x02,0x02,0x02,0x02,0x02,0x02,0x02 + .byte 0x02,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a,0x0a + .byte 0x1a,0x22,0x22,0x22,0x22,0x22,0x22,0x22 + .byte 0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a + .byte 0x3a,0x22,0x22,0x22,0x22,0x22,0x22,0x22 + .byte 0x22,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a,0x2a + .byte 0x3e,0x82,0x82,0x82,0x82,0x82,0x82,0x82 + .byte 0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a + .byte 0x9a,0x82,0x82,0x82,0x82,0x82,0x82,0x82 + .byte 0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a + .byte 0x9a,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2 + .byte 0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa + .byte 0xba,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2 + .byte 0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa + .byte 0xba,0x82,0x82,0x82,0x82,0x82,0x82,0x82 + .byte 0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a + .byte 0x9a,0x82,0x82,0x82,0x82,0x82,0x82,0x82 + .byte 0x82,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a + .byte 0x9a,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2 + .byte 0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa + .byte 0xba,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2,0xa2 + .byte 0xa2,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa + +.global _log_f + +.section .data_z80a_logic +.align 2 +_log_f: + .byte 0x44,0x00,0x00,0x04,0x00,0x04,0x04,0x00 + .byte 0x08,0x0c,0x0c,0x08,0x0c,0x08,0x08,0x0c + .byte 0x00,0x04,0x04,0x00,0x04,0x00,0x00,0x04 + .byte 0x0c,0x08,0x08,0x0c,0x08,0x0c,0x0c,0x08 + .byte 0x20,0x24,0x24,0x20,0x24,0x20,0x20,0x24 + .byte 0x2c,0x28,0x28,0x2c,0x28,0x2c,0x2c,0x28 + .byte 0x24,0x20,0x20,0x24,0x20,0x24,0x24,0x20 + .byte 0x28,0x2c,0x2c,0x28,0x2c,0x28,0x28,0x2c + .byte 0x00,0x04,0x04,0x00,0x04,0x00,0x00,0x04 + .byte 0x0c,0x08,0x08,0x0c,0x08,0x0c,0x0c,0x08 + .byte 0x04,0x00,0x00,0x04,0x00,0x04,0x04,0x00 + .byte 0x08,0x0c,0x0c,0x08,0x0c,0x08,0x08,0x0c + .byte 0x24,0x20,0x20,0x24,0x20,0x24,0x24,0x20 + .byte 0x28,0x2c,0x2c,0x28,0x2c,0x28,0x28,0x2c + .byte 0x20,0x24,0x24,0x20,0x24,0x20,0x20,0x24 + .byte 0x2c,0x28,0x28,0x2c,0x28,0x2c,0x2c,0x28 + .byte 0x80,0x84,0x84,0x80,0x84,0x80,0x80,0x84 + .byte 0x8c,0x88,0x88,0x8c,0x88,0x8c,0x8c,0x88 + .byte 0x84,0x80,0x80,0x84,0x80,0x84,0x84,0x80 + .byte 0x88,0x8c,0x8c,0x88,0x8c,0x88,0x88,0x8c + .byte 0xa4,0xa0,0xa0,0xa4,0xa0,0xa4,0xa4,0xa0 + .byte 0xa8,0xac,0xac,0xa8,0xac,0xa8,0xa8,0xac + .byte 0xa0,0xa4,0xa4,0xa0,0xa4,0xa0,0xa0,0xa4 + .byte 0xac,0xa8,0xa8,0xac,0xa8,0xac,0xac,0xa8 + .byte 0x84,0x80,0x80,0x84,0x80,0x84,0x84,0x80 + .byte 0x88,0x8c,0x8c,0x88,0x8c,0x88,0x88,0x8c + .byte 0x80,0x84,0x84,0x80,0x84,0x80,0x80,0x84 + .byte 0x8c,0x88,0x88,0x8c,0x88,0x8c,0x8c,0x88 + .byte 0xa0,0xa4,0xa4,0xa0,0xa4,0xa0,0xa0,0xa4 + .byte 0xac,0xa8,0xa8,0xac,0xa8,0xac,0xac,0xa8 + .byte 0xa4,0xa0,0xa0,0xa4,0xa0,0xa4,0xa4,0xa0 + .byte 0xa8,0xac,0xac,0xa8,0xac,0xa8,0xa8,0xac + +.section .data_z80a + +// note this is down here to be close to op_table +.global z80a_update +.type z80a_update,%function +.thumb_func +// run until end of frame (t <= frame_tacts) or (t <= 0 if USE_Z80_ARM_OFFSET_T - saves a reg) +z80a_update: + push {r4-r7,lr} + mov r0, r8 + mov r1, r9 + mov r2, r10 + mov r3, r11 + push {r0-r3} + + bl z80a_load_state + + ldr r1, =z80a_resting_state + ldrb r0, [r1, #(_halted - z80a_resting_state)] + + //if(!iff1 && halted) + cmp r0, #0 + bne irqcheck_instruction_loop_test + ldrb r0, [r1, #(_iff1 - z80a_resting_state)] + beq irqcheck_instruction_loop_test + // return + b 9f + // INT check separated from main Z80 loop to improve emulation speed + //while(t < int_len) + //{ +irqcheck_instruction_loop: + // if(iff1 && t != eipos) // int enabled in CPU not issued after EI + // { + // Int(); + // break; + // } + ldrb r0, [r1, #(_iff1 - z80a_resting_state)] + cmp r0, #0 + beq 3f + + ldr r0, [r1, #(_eipos - z80a_resting_state)] +#ifdef USE_Z80_ARM_OFFSET_T + ldr r2, frame_tacts + subs r0, r2 +#endif + cmp r_t, r0 + beq 3f + + bl z80a_interrupt + b 2f +3: + + // Step(); + dbg_check_breakpoint + adr r_temp, op_table + step_op_table_in_r_temp + + ldr r1, =z80a_resting_state + + //if(halted) + // break; + ldrb r0, [r1, #(_halted - z80a_resting_state)] + cmp r0, #0 + bne 2f + +irqcheck_instruction_loop_test: + // note r1 should have z80a_resting_state at this point (even for first iteration) + // } +#ifdef USE_Z80_ARM_OFFSET_T + ldr r2, frame_tacts + adds r2, r_t + cmp r2, #32 +#else + cmp r_t, #32 +#endif + blt irqcheck_instruction_loop + +2: + // eipos = -1 + movs r0, #0 + mvns r0, r0 + str r0, [r1, #(_eipos - z80a_resting_state)] + +hook_instruction_loop: + ldr r0, [r1, #(step_hook_func - z80a_resting_state)] + cmp r0, #0 + beq main_instruction_loop_test + bl z80a_unload_state + ldr r1, =z80a_resting_state + ldr r0, [r1, #(step_hook_func - z80a_resting_state)] + + // call hook func + blx r0 + bl z80a_load_state + + dbg_check_breakpoint + adr r_temp, op_table + step_op_table_in_r_temp + ldr r1, =z80a_resting_state +#ifdef USE_Z80_ARM_OFFSET_T + cmp r_t, #0 +#else + ldr r0, frame_tacts + cmp r_t, r0 +#endif + blt hook_instruction_loop + b 8f + +main_instruction_loop: + dbg_check_breakpoint + adr r_temp, op_table + step_op_table_in_r_temp +main_instruction_loop_test: +#ifdef USE_Z80_ARM_OFFSET_T + cmp r_t, #0 +#else + ldr r0, frame_tacts + cmp r_t, r0 +#endif + blt main_instruction_loop + +8: // finished; restore state + ldr r0, frame_tacts + subs r_t, r0 + ldr r1, =z80a_resting_state + ldr r2, [r1, #(_eipos - z80a_resting_state)] + subs r2, r0 + str r2, [r1, #(_eipos - z80a_resting_state)] + + bl z80a_unload_state + +9: // return + pop {r0-r3} + mov r8, r0 + mov r9, r1 + mov r10, r2 + mov r11, r3 + pop {r4-r7, pc} + +.align 2 +.ltorg +// todo externalize +frame_tacts: .word 71680 + +#include "z80khan_gen.S" \ No newline at end of file diff --git a/khan/z80khan.h b/khan/z80khan.h new file mode 100644 index 0000000..1e1b192 --- /dev/null +++ b/khan/z80khan.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SOFTWARE_Z80KHAN_H +#define SOFTWARE_Z80KHAN_H + +#ifdef __cplusplus +extern "C" { +#endif +// reset the z80 +extern void z80a_reset(); +//// prepare for execution by loading execution state into registers +//extern void z80a_load_state(); +//// save execution state back to memory +//extern void z80a_unload_state(); + +// this is very inefficient as it full restores then saves the processor state +extern void z80a_step(); +// run until t >= FRAME_TACTS (or t >= 0 if USE_Z80_ARM_OFFSET_T) +extern void z80a_update(int int_len); +extern void z80a_breakpoint_hit(); + +#pragma pack(push, 1) + +#ifdef USE_BIG_ENDIAN +#define DECLARE_ZREG16(reg, low, high)\ +union\ +{\ + struct\ + {\ + uint8_t reg##xx;\ + uint8_t reg##x;\ + uint8_t high;\ + uint8_t low;\ + };\ + int reg;\ +}; +#else//USE_BIG_ENDIAN +#define DECLARE_ZREG16(reg, low, high)\ +union\ +{\ + struct\ + {\ + uint8_t low;\ + uint8_t high;\ + uint8_t reg##x;\ + uint8_t reg##xx;\ + };\ + int reg;\ +}; +#endif//USE_BIG_ENDIAN + +extern struct _z80a_caller_regs *z80a_caller_regs; +struct _z80a_resting_state { +// argghhhh had fancy __builtin_offsetof stuff here, but was bitten by the fact that i'm compiling for generation on 64 bit :-( +#define rs_offsetof_im 0 + uint32_t im; +#define rs_offsetof_eipos (rs_offsetof_im + 4) + int32_t eipos; + union { + struct { +#define rs_offsetof_r_hi (rs_offsetof_eipos + 4) + uint8_t r_hi; +#define rs_offsetof_iff1 (rs_offsetof_r_hi + 1) + uint8_t iff1; +#define rs_offsetof_iff2 (rs_offsetof_iff1 + 1) + uint8_t iff2; +#define rs_offsetof_halted (rs_offsetof_iff2 + 1) + uint8_t halted; + }; + uint32_t int_flags; + }; + union { + struct { +#define rs_offsetof_r_low (rs_offsetof_halted + 1) + uint8_t r_low; +#define rs_offsetof_i (rs_offsetof_r_low + 1) + uint8_t i; + }; + uint32_t ir; + }; + uint32_t ix; + uint32_t iy; + + uint32_t pc; +#ifndef USE_BANKED_MEMORY_ACCESS +#ifdef USE_SINGLE_64K_MEMORY + uint8_t *memory_64k; +#else +#if PICO_ON_DEVICE +#error require USE_BANKED_MEMORY_ACCESS for non 64K +#endif +#endif +#else + void *interp; +#endif + int32_t t; + DECLARE_ZREG16(af, f, a) + + DECLARE_ZREG16(bc, c, b) + DECLARE_ZREG16(de, e, d) + DECLARE_ZREG16(hl, l, h) + DECLARE_ZREG16(memptr, mem_l, mem_h) + DECLARE_ZREG16(sp, sp_l, sp_h) +#define rs_offsetof_alt_af 60 + uint32_t alt_af; +#define rs_offsetof_alt_bc (rs_offsetof_alt_af + 4) + uint32_t alt_bc; +#define rs_offsetof_alt_de (rs_offsetof_alt_bc + 4) + uint32_t alt_de; +#define rs_offsetof_alt_hl (rs_offsetof_alt_de + 4) + uint32_t alt_hl; +#define rs_offsetof_scratch (rs_offsetof_alt_hl + 4) + uint32_t scratch; + + void (*step_hook_func)(); // called for each instruction + uint32_t active; // not byte for assembly offset distance reasons. +#ifndef NDEBUG +#ifdef ENABLE_BREAKPOINT_IN_DEBUG + uint32_t bp_addr; + uint32_t last_pc; +#endif +#endif +}; +#pragma pack(pop) + +extern _z80a_resting_state z80a_resting_state; +#ifdef __cplusplus +} +#endif + +#endif //SOFTWARE_Z80KHAN_H diff --git a/khan/z80khan_gen.S b/khan/z80khan_gen.S new file mode 100644 index 0000000..49ca55a --- /dev/null +++ b/khan/z80khan_gen.S @@ -0,0 +1,9265 @@ +// ================================ +// == AUTOGENERATED: DO NOT EDIT == +// ================================ + +// ALWAYS DIFF THIS CODE AFTER GENERATION - SENSITVE TO COMPILER VERSION CHANGES! + +// === BEGIN top level opcodes +op_table: +.short op00 + 1 - op_table +.short op01 + 1 - op_table +.short op02 + 1 - op_table +.short op03 + 1 - op_table +.short op04 + 1 - op_table +.short op05 + 1 - op_table +.short op06 + 1 - op_table +.short op07 + 1 - op_table +.short op08 + 1 - op_table +.short op09 + 1 - op_table +.short op0a + 1 - op_table +.short op0b + 1 - op_table +.short op0c + 1 - op_table +.short op0d + 1 - op_table +.short op0e + 1 - op_table +.short op0f + 1 - op_table +.short op10 + 1 - op_table +.short op11 + 1 - op_table +.short op12 + 1 - op_table +.short op13 + 1 - op_table +.short op14 + 1 - op_table +.short op15 + 1 - op_table +.short op16 + 1 - op_table +.short op17 + 1 - op_table +.short op18 + 1 - op_table +.short op19 + 1 - op_table +.short op1a + 1 - op_table +.short op1b + 1 - op_table +.short op1c + 1 - op_table +.short op1d + 1 - op_table +.short op1e + 1 - op_table +.short op1f + 1 - op_table +.short op20 + 1 - op_table +.short op21 + 1 - op_table +.short op22 + 1 - op_table +.short op23 + 1 - op_table +.short op24 + 1 - op_table +.short op25 + 1 - op_table +.short op26 + 1 - op_table +.short op27 + 1 - op_table +.short op28 + 1 - op_table +.short op29 + 1 - op_table +.short op2a + 1 - op_table +.short op2b + 1 - op_table +.short op2c + 1 - op_table +.short op2d + 1 - op_table +.short op2e + 1 - op_table +.short op2f + 1 - op_table +.short op30 + 1 - op_table +.short op31 + 1 - op_table +.short op32 + 1 - op_table +.short op33 + 1 - op_table +.short op34 + 1 - op_table +.short op35 + 1 - op_table +.short op36 + 1 - op_table +.short op37 + 1 - op_table +.short op38 + 1 - op_table +.short op39 + 1 - op_table +.short op3a + 1 - op_table +.short op3b + 1 - op_table +.short op3c + 1 - op_table +.short op3d + 1 - op_table +.short op3e + 1 - op_table +.short op3f + 1 - op_table +.short op40 + 1 - op_table +.short op41 + 1 - op_table +.short op42 + 1 - op_table +.short op43 + 1 - op_table +.short op44 + 1 - op_table +.short op45 + 1 - op_table +.short op46 + 1 - op_table +.short op47 + 1 - op_table +.short op48 + 1 - op_table +.short op49 + 1 - op_table +.short op4a + 1 - op_table +.short op4b + 1 - op_table +.short op4c + 1 - op_table +.short op4d + 1 - op_table +.short op4e + 1 - op_table +.short op4f + 1 - op_table +.short op50 + 1 - op_table +.short op51 + 1 - op_table +.short op52 + 1 - op_table +.short op53 + 1 - op_table +.short op54 + 1 - op_table +.short op55 + 1 - op_table +.short op56 + 1 - op_table +.short op57 + 1 - op_table +.short op58 + 1 - op_table +.short op59 + 1 - op_table +.short op5a + 1 - op_table +.short op5b + 1 - op_table +.short op5c + 1 - op_table +.short op5d + 1 - op_table +.short op5e + 1 - op_table +.short op5f + 1 - op_table +.short op60 + 1 - op_table +.short op61 + 1 - op_table +.short op62 + 1 - op_table +.short op63 + 1 - op_table +.short op64 + 1 - op_table +.short op65 + 1 - op_table +.short op66 + 1 - op_table +.short op67 + 1 - op_table +.short op68 + 1 - op_table +.short op69 + 1 - op_table +.short op6a + 1 - op_table +.short op6b + 1 - op_table +.short op6c + 1 - op_table +.short op6d + 1 - op_table +.short op6e + 1 - op_table +.short op6f + 1 - op_table +.short op70 + 1 - op_table +.short op71 + 1 - op_table +.short op72 + 1 - op_table +.short op73 + 1 - op_table +.short op74 + 1 - op_table +.short op75 + 1 - op_table +.short op76 + 1 - op_table +.short op77 + 1 - op_table +.short op78 + 1 - op_table +.short op79 + 1 - op_table +.short op7a + 1 - op_table +.short op7b + 1 - op_table +.short op7c + 1 - op_table +.short op7d + 1 - op_table +.short op7e + 1 - op_table +.short op7f + 1 - op_table +.short op80 + 1 - op_table +.short op81 + 1 - op_table +.short op82 + 1 - op_table +.short op83 + 1 - op_table +.short op84 + 1 - op_table +.short op85 + 1 - op_table +.short op86 + 1 - op_table +.short op87 + 1 - op_table +.short op88 + 1 - op_table +.short op89 + 1 - op_table +.short op8a + 1 - op_table +.short op8b + 1 - op_table +.short op8c + 1 - op_table +.short op8d + 1 - op_table +.short op8e + 1 - op_table +.short op8f + 1 - op_table +.short op90 + 1 - op_table +.short op91 + 1 - op_table +.short op92 + 1 - op_table +.short op93 + 1 - op_table +.short op94 + 1 - op_table +.short op95 + 1 - op_table +.short op96 + 1 - op_table +.short op97 + 1 - op_table +.short op98 + 1 - op_table +.short op99 + 1 - op_table +.short op9a + 1 - op_table +.short op9b + 1 - op_table +.short op9c + 1 - op_table +.short op9d + 1 - op_table +.short op9e + 1 - op_table +.short op9f + 1 - op_table +.short opa0 + 1 - op_table +.short opa1 + 1 - op_table +.short opa2 + 1 - op_table +.short opa3 + 1 - op_table +.short opa4 + 1 - op_table +.short opa5 + 1 - op_table +.short opa6 + 1 - op_table +.short opa7 + 1 - op_table +.short opa8 + 1 - op_table +.short opa9 + 1 - op_table +.short opaa + 1 - op_table +.short opab + 1 - op_table +.short opac + 1 - op_table +.short opad + 1 - op_table +.short opae + 1 - op_table +.short opaf + 1 - op_table +.short opb0 + 1 - op_table +.short opb1 + 1 - op_table +.short opb2 + 1 - op_table +.short opb3 + 1 - op_table +.short opb4 + 1 - op_table +.short opb5 + 1 - op_table +.short opb6 + 1 - op_table +.short opb7 + 1 - op_table +.short opb8 + 1 - op_table +.short opb9 + 1 - op_table +.short opba + 1 - op_table +.short opbb + 1 - op_table +.short opbc + 1 - op_table +.short opbd + 1 - op_table +.short opbe + 1 - op_table +.short opbf + 1 - op_table +.short opc0 + 1 - op_table +.short opc1 + 1 - op_table +.short opc2 + 1 - op_table +.short opc3 + 1 - op_table +.short opc4 + 1 - op_table +.short opc5 + 1 - op_table +.short opc6 + 1 - op_table +.short opc7 + 1 - op_table +.short opc8 + 1 - op_table +.short opc9 + 1 - op_table +.short opca + 1 - op_table +.short opcb + 1 - op_table +.short opcc + 1 - op_table +.short opcd + 1 - op_table +.short opce + 1 - op_table +.short opcf + 1 - op_table +.short opd0 + 1 - op_table +.short opd1 + 1 - op_table +.short opd2 + 1 - op_table +.short opd3 + 1 - op_table +.short opd4 + 1 - op_table +.short opd5 + 1 - op_table +.short opd6 + 1 - op_table +.short opd7 + 1 - op_table +.short opd8 + 1 - op_table +.short opd9 + 1 - op_table +.short opda + 1 - op_table +.short opdb + 1 - op_table +.short opdc + 1 - op_table +.short opdd + 1 - op_table +.short opde + 1 - op_table +.short opdf + 1 - op_table +.short ope0 + 1 - op_table +.short ope1 + 1 - op_table +.short ope2 + 1 - op_table +.short ope3 + 1 - op_table +.short ope4 + 1 - op_table +.short ope5 + 1 - op_table +.short ope6 + 1 - op_table +.short ope7 + 1 - op_table +.short ope8 + 1 - op_table +.short ope9 + 1 - op_table +.short opea + 1 - op_table +.short opeb + 1 - op_table +.short opec + 1 - op_table +.short oped + 1 - op_table +.short opee + 1 - op_table +.short opef + 1 - op_table +.short opf0 + 1 - op_table +.short opf1 + 1 - op_table +.short opf2 + 1 - op_table +.short opf3 + 1 - op_table +.short opf4 + 1 - op_table +.short opf5 + 1 - op_table +.short opf6 + 1 - op_table +.short opf7 + 1 - op_table +.short opf8 + 1 - op_table +.short opf9 + 1 - op_table +.short opfa + 1 - op_table +.short opfb + 1 - op_table +.short opfc + 1 - op_table +.short opfd + 1 - op_table +.short opfe + 1 - op_table +.short opff + 1 - op_table +.thumb_func +z80a_interrupt: + movs r_temp, #56 + push {lr} + ldr r0, =z80a_resting_state + ldr r0, [r0, #0] // im + cmp r0, #2 + blt 1f + ldr r0, =z80a_resting_state + ldrb r0, [r0, #13] // i + lsls r0, #8 + movs r1, #0xff + orrs r0, r1 + bl read16 + mov r_temp, r0 + adds r_t, #6 +1: + adds r_t, #13 + mov r0, r_pc + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + movs r0, #0 + ldr r1, =z80a_resting_state + strb r0, [r1, #11] // halted + movs r0, #0 + ldr r1, =z80a_resting_state + strb r0, [r1, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + ldr r0, =z80a_resting_state + ldrb r0, [r0, #12] // r_low + adds r0, #1 + uxth r0, r0 + ldr r1, =z80a_resting_state + strb r0, [r1, #12] // r_low + pop {pc} + +op00: + bx lr + +op01: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_bc, r0 + adds r_t, #6 + pop {pc} + +op02: + lsrs r0, r_af, #8 + lsls r0, #8 + mov r1, r_memptr + uxtb r1, r1 + orrs r0, r1 + mov r_memptr, r0 + mov r0, r_bc + adds r0, #1 + uxtb r0, r0 + mov r1, r_memptr + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_memptr, r1 + adds r_t, #3 + mov r0, r_af + lsrs r0, #8 + mov r1, r_bc + ldr r2, =write8 + bx r2 + +op03: + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_bc, r0 + adds r_t, #2 + bx lr + +op04: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl inc8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +op05: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl dec8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +op06: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + adds r_t, #3 + pop {pc} + +op07: + movs r0, #(SF|ZF|PV) + ands r0, r_af // r0 = f & (SF|ZF|PV) + movs r1, #0 + lsrs r_af, #8 + lsls r_af, #25 + bcc 1f + movs r1, #CF + orrs r0, r1 // r0 = (f & (SF|ZF|PV)) | (a7 ? CF: 0) +1: + lsrs r_af, #24 + orrs r_af, r1 + movs r1, #(F3|F5) // r_af = (a << 1) | (a7 ? 1 : 0) + ands r1, r_af + lsls r_af, #8 // r_af = a' : 0 + orrs r_af, r0 // r_af = a' | f' + orrs r_af, r1 // r_af |= 35 from a' + bx lr + +op08: + mov r_temp, r_af + ldr r0, =z80a_resting_state + ldr r0, [r0, #60] // alt.af + mov r_af, r0 + mov r0, r_temp + ldr r1, =z80a_resting_state + str r0, [r1, #60] // alt.af + bx lr + +op09: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + mov r1, r_bc + push {lr} + bl add16 + mov r_hl, r0 + adds r_t, #7 + pop {pc} + +op0a: + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + read8_internal r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + adds r_t, #3 + bx lr + +op0b: + mov r0, r_bc + subs r0, #1 + uxth r0, r0 + mov r_bc, r0 + adds r_t, #2 + bx lr + +op0c: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl inc8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +op0d: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl dec8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +op0e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + adds r_t, #3 + pop {pc} + +op0f: + movs r0, #(SF|ZF|PV) + ands r0, r_af // r0 = f & (SF|ZF|PV) + lsrs r_af, r_af, #9 + bcc 1f + adds r0, #CF // r0 = (f & (SF|ZF|PV)) | (a0 ? CF: 0) + adds r_af, #128 // r_af = (a0 ? 0x80 : 0) | (a >> 1) +1: + movs r1, #(F3|F5) + ands r1, r_af + lsls r_af, #8 // r_af = a' : 0 + orrs r_af, r0 // r_af = a' | f' + orrs r_af, r1 // r_af |= 35 from a' + bx lr + +op10: + movs r1, #1 + lsls r1, #8 + mov r0, r_bc + subs r0, r1 + uxth r0, r0 + mov r_bc, r0 + mov r0, r_bc + lsrs r0, #8 + cmp r0, #0 + beq 1f + mov r0, r_pc + read8_internal r0 + sxtb r0, r0 + adds r0, #1 + add r_pc, r0 + uxth r_pc, r_pc + mov r_memptr, r_pc + adds r_t, #9 + bx lr + +1: + adds r_pc, #1 + uxth r_pc, r_pc + adds r_t, #4 + bx lr + +op11: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_de, r0 + adds r_t, #6 + pop {pc} + +op12: + lsrs r0, r_af, #8 + lsls r0, #8 + mov r1, r_memptr + uxtb r1, r1 + orrs r0, r1 + mov r_memptr, r0 + mov r0, r_de + adds r0, #1 + uxtb r0, r0 + mov r1, r_memptr + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_memptr, r1 + adds r_t, #3 + mov r0, r_af + lsrs r0, #8 + mov r1, r_de + ldr r2, =write8 + bx r2 + +op13: + mov r0, r_de + adds r0, #1 + uxth r0, r0 + mov r_de, r0 + adds r_t, #2 + bx lr + +op14: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl inc8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +op15: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl dec8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +op16: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + adds r_t, #3 + pop {pc} + +op17: + movs r0, #(SF|ZF|PV) + ands r0, r_af // r0 = f & (SF|ZF|PV) + movs r1, #CF + ands r1, r_af + lsrs r_af, #8 + lsls r_af, #25 + bcc 1f + adds r0, #CF // r0 = (f & (SF|ZF|PV)) | (a7 ? CF: 0) +1: + lsrs r_af, #24 + orrs r_af, r1 + movs r1, #(F3|F5) // r_af = (a << 1) | (c ? 1 : 0) + ands r1, r_af + lsls r_af, #8 // r_af = a' : 0 + orrs r_af, r0 // r_af = a' | f' + orrs r_af, r1 // r_af |= 35 from a' + bx lr + +op18: + mov r0, r_pc + read8_internal r0 + sxtb r0, r0 + adds r0, #1 + add r_pc, r0 + uxth r_pc, r_pc + mov r_memptr, r_pc + adds r_t, #8 + bx lr + +op19: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + mov r1, r_de + push {lr} + bl add16 + mov r_hl, r0 + adds r_t, #7 + pop {pc} + +op1a: + mov r0, r_de + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_de + read8_internal r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + adds r_t, #3 + bx lr + +op1b: + mov r0, r_de + subs r0, #1 + uxth r0, r0 + mov r_de, r0 + adds r_t, #2 + bx lr + +op1c: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl inc8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +op1d: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl dec8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +op1e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + adds r_t, #3 + pop {pc} + +op1f: + movs r0, #(SF|ZF|PV) + ands r0, r_af // r0 = f & (SF|ZF|PV) + lsls r2, r_af, #15 // r2 = (garbage : oldCF) << 15 + lsrs r1, r_af, #9 + bcc 1f + adds r0, #CF // r0 = (f & (SF|ZF|PV)) | (a0 ? CF: 0) +1: + lsls r_af, r1, #8 + orrs r_af, r2 + orrs r_af, r0 + movs r0, #(F3|F5) + uxth r_af, r_af + ands r0, r1 + orrs r_af, r0 + bx lr + +op20: + lsrs r0, r_af, #7 + bcs 3f + mov r0, r_pc + read8_internal r0 + sxtb r0, r0 + adds r0, #1 + add r_pc, r0 + uxth r_pc, r_pc + mov r_memptr, r_pc + adds r_t, #8 + bx lr + +3: + adds r_pc, #1 + uxth r_pc, r_pc + adds r_t, #3 + bx lr + +.ltorg +op21: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_hl, r0 + adds r_t, #6 + pop {pc} + +op22: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + mov r1, r_temp + bl write16 + adds r_t, #12 + pop {pc} + +op23: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_hl, r0 + adds r_t, #2 + bx lr + +op24: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl inc8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +op25: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl dec8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +op26: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + adds r_t, #3 + pop {pc} + +op27: + movs r0, #0 // delta + movs r_temp, #(CF|NF) + ands r_temp, r_af // new CHN flags - CH may be set later + // lo nibble + lsls r1, r_af, #20 + lsrs r1, #28 + lsrs r2, r_af, #HF_INDEX+1 + bcs 1f + cmp r1, #10 + blt 3f +1: + adds r0, #0x06 + cmp r_temp, #NF + blt 1f + subs r1, r0 + b 2f +1: + adds r1, r0 +2: + lsls r1, #28 + bcc 3f + movs r2, #HF + orrs r_temp, r2 +3: + // hi nibble + movs r2, #CF + lsrs r1, r_af, #8 + cmp r1, #0x9a + blt 1f + orrs r_temp, r2 + b 2f +1: + ands r2, r_af + beq 3f +2: + adds r0, #0x60 +3: + lsrs r2, r_af, #NF_INDEX+1 + bcc 1f + subs r1, r0 + b 2f +1: + adds r1, r0 +2: + uxtb r_af, r1 + ldr r2, =_log_f + ldrb r0, [r2, r_af] + lsls r_af, #8 + orrs r_af, r0 + orrs r_af, r_temp + bx lr + +op28: + lsrs r0, r_af, #7 + bcc 2f + mov r0, r_pc + read8_internal r0 + sxtb r0, r0 + adds r0, #1 + add r_pc, r0 + uxth r_pc, r_pc + mov r_memptr, r_pc + adds r_t, #8 + bx lr + +2: + adds r_pc, #1 + uxth r_pc, r_pc + adds r_t, #3 + bx lr + +op29: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + mov r1, r_hl + push {lr} + bl add16 + mov r_hl, r0 + adds r_t, #7 + pop {pc} + +op2a: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_temp + bl read16 + mov r_hl, r0 + adds r_t, #12 + pop {pc} + +op2b: + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + adds r_t, #2 + bx lr + +op2c: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl inc8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +op2d: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl dec8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +op2e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + adds r_t, #3 + pop {pc} + +op2f: + movs r0, #255 + lsls r0, #8 + eors r_af, r0 + preserve_only_flags r0, PV|ZF|SF|CF + adds r_af, #NF|HF + lsrs r0, r_af, #8 + movs r1, #(F3|F5) + ands r0, r1 + orrs r_af, r0 + bx lr + +op30: + lsrs r0, r_af, #1 + bcs 3f + mov r0, r_pc + read8_internal r0 + sxtb r0, r0 + adds r0, #1 + add r_pc, r0 + uxth r_pc, r_pc + mov r_memptr, r_pc + adds r_t, #8 + bx lr + +3: + adds r_pc, #1 + uxth r_pc, r_pc + adds r_t, #3 + bx lr + +op31: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_sp, r0 + adds r_t, #6 + pop {pc} + +op32: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxtb r0, r0 + mov r1, r_memptr + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_memptr, r1 + lsrs r0, r_af, #8 + lsls r0, #8 + mov r1, r_memptr + uxtb r1, r1 + orrs r0, r1 + mov r_memptr, r0 + adds r_t, #9 + mov r0, r_af + lsrs r0, #8 + mov r1, r_temp + bl write8 + pop {pc} + +op33: + mov r0, r_sp + adds r0, #1 + uxth r0, r0 + mov r_sp, r0 + adds r_t, #2 + bx lr + +op34: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl inc8 + mov r_temp, r0 // fine to overwrite hi in r_temp + adds r_t, #7 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + pop {pc} + +op35: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl dec8 + mov r_temp, r0 // fine to overwrite hi in r_temp + adds r_t, #7 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + pop {pc} + +op36: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + mov r1, r_hl + bl write8 + pop {pc} + +op37: + preserve_only_flags r0, PV|ZF|SF + adds r_af, #CF + lsrs r0, r_af, #8 + movs r1, #(F3|F5) + ands r0, r1 + orrs r_af, r0 + bx lr + +op38: + lsrs r0, r_af, #1 + bcc 2f + mov r0, r_pc + read8_internal r0 + sxtb r0, r0 + adds r0, #1 + add r_pc, r0 + uxth r_pc, r_pc + mov r_memptr, r_pc + adds r_t, #8 + bx lr + +2: + adds r_pc, #1 + uxth r_pc, r_pc + adds r_t, #3 + bx lr + +op39: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + mov r1, r_sp + push {lr} + bl add16 + mov r_hl, r0 + adds r_t, #7 + pop {pc} + +op3a: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_temp + read8_internal r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + adds r_t, #9 + pop {pc} + +op3b: + mov r0, r_sp + subs r0, #1 + uxth r0, r0 + mov r_sp, r0 + adds r_t, #2 + bx lr + +op3c: + lsrs r0, r_af, #8 + push {lr} + bl inc8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +op3d: + lsrs r0, r_af, #8 + push {lr} + bl dec8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +op3e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + adds r_t, #3 + pop {pc} + +op3f: + movs r1, #CF + lsrs r0, r_af, #1 + bcc 1f + movs r1, #HF +1: + preserve_only_flags r0, PV|ZF|SF + orrs r_af, r1 + lsrs r0, r_af, #8 + movs r1, #(F3|F5) + ands r0, r1 + orrs r_af, r0 + bx lr + +op40: + bx lr + +.ltorg +op41: + mov r0, r_bc + uxtb r0, r0 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + bx lr + +op42: + mov r0, r_de + lsrs r0, #8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + bx lr + +op43: + mov r0, r_de + uxtb r0, r0 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + bx lr + +op44: + mov r0, r_hl + lsrs r0, #8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + bx lr + +op45: + mov r0, r_hl + uxtb r0, r0 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + bx lr + +op46: + mov r0, r_hl + read8_internal r0 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + adds r_t, #3 + bx lr + +op47: + lsrs r0, r_af, #8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + bx lr + +op48: + mov r0, r_bc + lsrs r0, #8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + bx lr + +op49: + bx lr + +op4a: + mov r0, r_de + lsrs r0, #8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + bx lr + +op4b: + mov r0, r_de + uxtb r0, r0 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + bx lr + +op4c: + mov r0, r_hl + lsrs r0, #8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + bx lr + +op4d: + mov r0, r_hl + uxtb r0, r0 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + bx lr + +op4e: + mov r0, r_hl + read8_internal r0 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + adds r_t, #3 + bx lr + +op4f: + lsrs r0, r_af, #8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + bx lr + +op50: + mov r0, r_bc + lsrs r0, #8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + bx lr + +op51: + mov r0, r_bc + uxtb r0, r0 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + bx lr + +op52: + bx lr + +op53: + mov r0, r_de + uxtb r0, r0 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + bx lr + +op54: + mov r0, r_hl + lsrs r0, #8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + bx lr + +op55: + mov r0, r_hl + uxtb r0, r0 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + bx lr + +op56: + mov r0, r_hl + read8_internal r0 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + adds r_t, #3 + bx lr + +op57: + lsrs r0, r_af, #8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + bx lr + +op58: + mov r0, r_bc + lsrs r0, #8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + bx lr + +op59: + mov r0, r_bc + uxtb r0, r0 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + bx lr + +op5a: + mov r0, r_de + lsrs r0, #8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + bx lr + +op5b: + bx lr + +op5c: + mov r0, r_hl + lsrs r0, #8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + bx lr + +op5d: + mov r0, r_hl + uxtb r0, r0 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + bx lr + +op5e: + mov r0, r_hl + read8_internal r0 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + adds r_t, #3 + bx lr + +op5f: + lsrs r0, r_af, #8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + bx lr + +op60: + mov r0, r_bc + lsrs r0, #8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + bx lr + +.ltorg +op61: + mov r0, r_bc + uxtb r0, r0 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + bx lr + +op62: + mov r0, r_de + lsrs r0, #8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + bx lr + +op63: + mov r0, r_de + uxtb r0, r0 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + bx lr + +op64: + bx lr + +op65: + mov r0, r_hl + uxtb r0, r0 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + bx lr + +op66: + mov r0, r_hl + read8_internal r0 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + adds r_t, #3 + bx lr + +op67: + lsrs r0, r_af, #8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + bx lr + +op68: + mov r0, r_bc + lsrs r0, #8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + bx lr + +op69: + mov r0, r_bc + uxtb r0, r0 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + bx lr + +op6a: + mov r0, r_de + lsrs r0, #8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + bx lr + +op6b: + mov r0, r_de + uxtb r0, r0 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + bx lr + +op6c: + mov r0, r_hl + lsrs r0, #8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + bx lr + +op6d: + bx lr + +op6e: + mov r0, r_hl + read8_internal r0 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + adds r_t, #3 + bx lr + +op6f: + lsrs r0, r_af, #8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + bx lr + +op70: + adds r_t, #3 + mov r0, r_bc + lsrs r0, #8 + mov r1, r_hl + ldr r2, =write8 + bx r2 + +op71: + adds r_t, #3 + mov r0, r_bc // high half of word is ignored later + mov r1, r_hl + ldr r2, =write8 + bx r2 + +op72: + adds r_t, #3 + mov r0, r_de + lsrs r0, #8 + mov r1, r_hl + ldr r2, =write8 + bx r2 + +op73: + adds r_t, #3 + mov r0, r_de // high half of word is ignored later + mov r1, r_hl + ldr r2, =write8 + bx r2 + +op74: + adds r_t, #3 + mov r0, r_hl + lsrs r0, #8 + mov r1, r_hl + ldr r2, =write8 + bx r2 + +op75: + adds r_t, #3 + mov r0, r_hl // high half of word is ignored later + mov r1, r_hl + ldr r2, =write8 + bx r2 + +op76: + movs r0, #1 + ldr r1, =z80a_resting_state + strb r0, [r1, #11] // halted +#ifndef USE_Z80_ARM_OFFSET_T + ldr r0, frame_tacts +#else + movs r0, #0 +#endif + subs r0, r_t + movs r1, #3 + adds r0, r1 + bics r0, r1 + add r_t, r0 +#ifndef NO_UPDATE_RLOW_IN_FETCH + ldr r1, =z80a_resting_state + ldrb r1, [r1, #12] // r_low + lsrs r0, #2 + add r0, r1 + ldr r1, =z80a_resting_state + strb r0, [r1, #12] // r_low +#endif + bx lr + +op77: + adds r_t, #3 + mov r0, r_af + lsrs r0, #8 + mov r1, r_hl + ldr r2, =write8 + bx r2 + +op78: + mov r0, r_bc + lsrs r0, #8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + bx lr + +op79: + mov r0, r_bc + uxtb r0, r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + bx lr + +op7a: + mov r0, r_de + lsrs r0, #8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + bx lr + +op7b: + mov r0, r_de + uxtb r0, r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + bx lr + +op7c: + mov r0, r_hl + lsrs r0, #8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + bx lr + +op7d: + mov r0, r_hl + uxtb r0, r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + bx lr + +op7e: + mov r0, r_hl + read8_internal r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + adds r_t, #3 + bx lr + +op7f: + bx lr + +op80: + mov r0, r_bc + lsrs r0, #8 + ldr r2, =add8 + bx r2 + +.ltorg +op81: + mov r0, r_bc + uxtb r0, r0 + ldr r2, =add8 + bx r2 + +op82: + mov r0, r_de + lsrs r0, #8 + ldr r2, =add8 + bx r2 + +op83: + mov r0, r_de + uxtb r0, r0 + ldr r2, =add8 + bx r2 + +op84: + mov r0, r_hl + lsrs r0, #8 + ldr r2, =add8 + bx r2 + +op85: + mov r0, r_hl + uxtb r0, r0 + ldr r2, =add8 + bx r2 + +op86: + mov r0, r_hl + read8_internal r0 + push {lr} + bl add8 + adds r_t, #3 + pop {pc} + +op87: + lsrs r0, r_af, #8 + ldr r2, =add8 + bx r2 + +op88: + mov r0, r_bc + lsrs r0, #8 + ldr r2, =adc8 + bx r2 + +op89: + mov r0, r_bc + uxtb r0, r0 + ldr r2, =adc8 + bx r2 + +op8a: + mov r0, r_de + lsrs r0, #8 + ldr r2, =adc8 + bx r2 + +op8b: + mov r0, r_de + uxtb r0, r0 + ldr r2, =adc8 + bx r2 + +op8c: + mov r0, r_hl + lsrs r0, #8 + ldr r2, =adc8 + bx r2 + +op8d: + mov r0, r_hl + uxtb r0, r0 + ldr r2, =adc8 + bx r2 + +op8e: + mov r0, r_hl + read8_internal r0 + push {lr} + bl adc8 + adds r_t, #3 + pop {pc} + +op8f: + lsrs r0, r_af, #8 + ldr r2, =adc8 + bx r2 + +op90: + mov r0, r_bc + lsrs r0, #8 + ldr r2, =sub8 + bx r2 + +op91: + mov r0, r_bc + uxtb r0, r0 + ldr r2, =sub8 + bx r2 + +op92: + mov r0, r_de + lsrs r0, #8 + ldr r2, =sub8 + bx r2 + +op93: + mov r0, r_de + uxtb r0, r0 + ldr r2, =sub8 + bx r2 + +op94: + mov r0, r_hl + lsrs r0, #8 + ldr r2, =sub8 + bx r2 + +op95: + mov r0, r_hl + uxtb r0, r0 + ldr r2, =sub8 + bx r2 + +op96: + mov r0, r_hl + read8_internal r0 + push {lr} + bl sub8 + adds r_t, #3 + pop {pc} + +op97: + movs r_af, #66 + bx lr + +op98: + mov r0, r_bc + lsrs r0, #8 + ldr r2, =sbc8 + bx r2 + +op99: + mov r0, r_bc + uxtb r0, r0 + ldr r2, =sbc8 + bx r2 + +op9a: + mov r0, r_de + lsrs r0, #8 + ldr r2, =sbc8 + bx r2 + +op9b: + mov r0, r_de + uxtb r0, r0 + ldr r2, =sbc8 + bx r2 + +op9c: + mov r0, r_hl + lsrs r0, #8 + ldr r2, =sbc8 + bx r2 + +op9d: + mov r0, r_hl + uxtb r0, r0 + ldr r2, =sbc8 + bx r2 + +op9e: + mov r0, r_hl + read8_internal r0 + push {lr} + bl sbc8 + adds r_t, #3 + pop {pc} + +op9f: + lsrs r0, r_af, #8 + ldr r2, =sbc8 + bx r2 + +opa0: + mov r0, r_bc + lsrs r0, #8 + ldr r2, =ands8 + bx r2 + +.ltorg +opa1: + mov r0, r_bc + uxtb r0, r0 + ldr r2, =ands8 + bx r2 + +opa2: + mov r0, r_de + lsrs r0, #8 + ldr r2, =ands8 + bx r2 + +opa3: + mov r0, r_de + uxtb r0, r0 + ldr r2, =ands8 + bx r2 + +opa4: + mov r0, r_hl + lsrs r0, #8 + ldr r2, =ands8 + bx r2 + +opa5: + mov r0, r_hl + uxtb r0, r0 + ldr r2, =ands8 + bx r2 + +opa6: + mov r0, r_hl + read8_internal r0 + push {lr} + bl ands8 + adds r_t, #3 + pop {pc} + +opa7: + lsrs r0, r_af, #8 + ldr r2, =ands8 + bx r2 + +opa8: + mov r0, r_bc + lsrs r0, #8 + ldr r2, =xor8 + bx r2 + +opa9: + mov r0, r_bc + uxtb r0, r0 + ldr r2, =xor8 + bx r2 + +opaa: + mov r0, r_de + lsrs r0, #8 + ldr r2, =xor8 + bx r2 + +opab: + mov r0, r_de + uxtb r0, r0 + ldr r2, =xor8 + bx r2 + +opac: + mov r0, r_hl + lsrs r0, #8 + ldr r2, =xor8 + bx r2 + +opad: + mov r0, r_hl + uxtb r0, r0 + ldr r2, =xor8 + bx r2 + +opae: + mov r0, r_hl + read8_internal r0 + push {lr} + bl xor8 + adds r_t, #3 + pop {pc} + +opaf: + movs r_af, #68 + bx lr + +opb0: + mov r0, r_bc + lsrs r0, #8 + ldr r2, =or8 + bx r2 + +opb1: + mov r0, r_bc + uxtb r0, r0 + ldr r2, =or8 + bx r2 + +opb2: + mov r0, r_de + lsrs r0, #8 + ldr r2, =or8 + bx r2 + +opb3: + mov r0, r_de + uxtb r0, r0 + ldr r2, =or8 + bx r2 + +opb4: + mov r0, r_hl + lsrs r0, #8 + ldr r2, =or8 + bx r2 + +opb5: + mov r0, r_hl + uxtb r0, r0 + ldr r2, =or8 + bx r2 + +opb6: + mov r0, r_hl + read8_internal r0 + push {lr} + bl or8 + adds r_t, #3 + pop {pc} + +opb7: + lsrs r0, r_af, #8 + ldr r2, =or8 + bx r2 + +opb8: + mov r0, r_bc + lsrs r0, #8 + ldr r2, =cp8 + bx r2 + +opb9: + mov r0, r_bc + uxtb r0, r0 + ldr r2, =cp8 + bx r2 + +opba: + mov r0, r_de + lsrs r0, #8 + ldr r2, =cp8 + bx r2 + +opbb: + mov r0, r_de + uxtb r0, r0 + ldr r2, =cp8 + bx r2 + +opbc: + mov r0, r_hl + lsrs r0, #8 + ldr r2, =cp8 + bx r2 + +opbd: + mov r0, r_hl + uxtb r0, r0 + ldr r2, =cp8 + bx r2 + +opbe: + mov r0, r_hl + read8_internal r0 + push {lr} + bl cp8 + adds r_t, #3 + pop {pc} + +opbf: + lsrs r0, r_af, #8 + ldr r2, =cp8 + bx r2 + +opc0: + lsrs r0, r_af, #7 + bcs 3f + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #7 + pop {pc} + +3: + adds r_t, #1 + bx lr + +.ltorg +opc1: + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_bc, r0 + adds r_t, #6 + pop {pc} + +opc2: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read16 + mov r_memptr, r0 + lsrs r0, r_af, #7 + bcs 3f + mov r_pc, r_memptr + pop {pc} + +3: + adds r_pc, #2 + uxth r_pc, r_pc + pop {pc} + +opc3: + mov r0, r_pc + push {lr} + bl read16 + mov r_pc, r0 + mov r_memptr, r_pc + adds r_t, #6 + pop {pc} + +opc4: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_memptr, r0 + lsrs r0, r_af, #7 + bcs 3f + mov r0, r_pc + bl _push + mov r_pc, r_memptr + adds r_t, #13 + pop {pc} + +3: + adds r_t, #6 + pop {pc} + +opc5: + adds r_t, #7 + mov r0, r_bc + ldr r2, =_push + bx r2 + +opc6: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + bl add8 + adds r_t, #3 + pop {pc} + +opc7: + mov r0, r_pc + push {lr} + bl _push + movs r_pc, #0 + movs r0, #0 + mov r_memptr, r0 + adds r_t, #7 + pop {pc} + +opc8: + lsrs r0, r_af, #7 + bcc 2f + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #7 + pop {pc} + +2: + adds r_t, #1 + bx lr + +opc9: + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #6 + pop {pc} + +opca: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read16 + mov r_memptr, r0 + lsrs r0, r_af, #7 + bcc 2f + mov r_pc, r_memptr + pop {pc} + +2: + adds r_pc, #2 + uxth r_pc, r_pc + pop {pc} + +opcb: + ldr r_temp, =opl_table + fetch + lsls r0, #1 + ldrh r0, [r_temp, r0] + sxth r0, r0 + add r0, r_temp + bx r0 + bx lr + +opcc: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_memptr, r0 + lsrs r0, r_af, #7 + bcc 2f + mov r0, r_pc + bl _push + mov r_pc, r_memptr + adds r_t, #13 + pop {pc} + +2: + adds r_t, #6 + pop {pc} + +opcd: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + mov r0, r_pc + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #13 + pop {pc} + +opce: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + bl adc8 + adds r_t, #3 + pop {pc} + +opcf: + movs r_temp, #8 + mov r0, r_pc + push {lr} + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #7 + pop {pc} + +opd0: + lsrs r0, r_af, #1 + bcs 3f + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #7 + pop {pc} + +3: + adds r_t, #1 + bx lr + +opd1: + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_de, r0 + adds r_t, #6 + pop {pc} + +opd2: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read16 + mov r_memptr, r0 + lsrs r0, r_af, #1 + bcs 3f + mov r_pc, r_memptr + pop {pc} + +3: + adds r_pc, #2 + uxth r_pc, r_pc + pop {pc} + +opd3: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + mov r_temp, r0 + adds r_t, #7 + lsrs r1, r_af, #8 + lsls r1, #8 + adds r0, r_temp, #1 + uxtb r0, r0 + orrs r0, r1 + mov r_memptr, r0 + mov r0, r_af + lsrs r0, #8 + orrs r1, r_temp + bl iowrite8 + pop {pc} + +opd4: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_memptr, r0 + lsrs r0, r_af, #1 + bcs 3f + mov r0, r_pc + bl _push + mov r_pc, r_memptr + adds r_t, #13 + pop {pc} + +3: + adds r_t, #6 + pop {pc} + +opd5: + adds r_t, #7 + mov r0, r_de + ldr r2, =_push + bx r2 + +opd6: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + bl sub8 + adds r_t, #3 + pop {pc} + +opd7: + movs r_temp, #16 + mov r0, r_pc + push {lr} + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #7 + pop {pc} + +opd8: + lsrs r0, r_af, #1 + bcc 2f + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #7 + pop {pc} + +2: + adds r_t, #1 + bx lr + +opd9: + ldr r2, =z80a_resting_state + mov r0, r_bc + ldr r1, [r2, #64] // alt.bc + str r0, [r2, #64] // alt.bc + mov r_bc, r1 + mov r0, r_de + ldr r1, [r2, #68] // alt.de + str r0, [r2, #68] // alt.de + mov r_de, r1 + mov r0, r_hl + ldr r1, [r2, #72] // alt.hl + str r0, [r2, #72] // alt.hl + mov r_hl, r1 + bx lr + +opda: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read16 + mov r_memptr, r0 + lsrs r0, r_af, #1 + bcc 2f + mov r_pc, r_memptr + pop {pc} + +2: + adds r_pc, #2 + uxth r_pc, r_pc + pop {pc} + +opdb: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + mov r_temp, r0 + lsrs r1, r_af, #8 + lsls r1, #8 + orrs r_temp, r1 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + adds r_t, #7 + mov r0, r_temp + bl ioread8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opdc: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_memptr, r0 + lsrs r0, r_af, #1 + bcc 2f + mov r0, r_pc + bl _push + mov r_pc, r_memptr + adds r_t, #13 + pop {pc} + +2: + adds r_t, #6 + pop {pc} + +opdd: + movs r0, #0xdd + ldr r2, =opDDFD + bx r2 + +opde: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + bl sbc8 + adds r_t, #3 + pop {pc} + +opdf: + movs r_temp, #24 + mov r0, r_pc + push {lr} + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #7 + pop {pc} + +ope0: + lsrs r0, r_af, #3 + bcs 3f + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #7 + pop {pc} + +3: + adds r_t, #1 + bx lr + +.ltorg +ope1: + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_hl, r0 + adds r_t, #6 + pop {pc} + +ope2: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read16 + mov r_memptr, r0 + lsrs r0, r_af, #3 + bcs 3f + mov r_pc, r_memptr + pop {pc} + +3: + adds r_pc, #2 + uxth r_pc, r_pc + pop {pc} + +ope3: + mov r0, r_sp + push {lr} + bl read16 + mov r_temp, r0 + mov r0, r_hl + mov r1, r_sp + bl write16 + mov r_memptr, r_temp + mov r_hl, r_temp + adds r_t, #15 + pop {pc} + +ope4: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_memptr, r0 + lsrs r0, r_af, #3 + bcs 3f + mov r0, r_pc + bl _push + mov r_pc, r_memptr + adds r_t, #13 + pop {pc} + +3: + adds r_t, #6 + pop {pc} + +ope5: + adds r_t, #7 + mov r0, r_hl + ldr r2, =_push + bx r2 + +ope6: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + bl ands8 + adds r_t, #3 + pop {pc} + +ope7: + movs r_temp, #32 + mov r0, r_pc + push {lr} + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #7 + pop {pc} + +ope8: + lsrs r0, r_af, #3 + bcc 2f + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #7 + pop {pc} + +2: + adds r_t, #1 + bx lr + +ope9: + mov r_pc, r_hl + bx lr + +opea: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read16 + mov r_memptr, r0 + lsrs r0, r_af, #3 + bcc 2f + mov r_pc, r_memptr + pop {pc} + +2: + adds r_pc, #2 + uxth r_pc, r_pc + pop {pc} + +opeb: + mov r_temp, r_de + mov r_de, r_hl + mov r_hl, r_temp + bx lr + +opec: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_memptr, r0 + lsrs r0, r_af, #3 + bcc 2f + mov r0, r_pc + bl _push + mov r_pc, r_memptr + adds r_t, #13 + pop {pc} + +2: + adds r_t, #6 + pop {pc} + +oped: + push {lr} + ldr r_temp, =ope_table + step_op_table_in_r_temp_maybe_neg + pop {pc} + +opee: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + bl xor8 + adds r_t, #3 + pop {pc} + +opef: + movs r_temp, #40 + mov r0, r_pc + push {lr} + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #7 + pop {pc} + +opf0: + lsrs r0, r_af, #8 + bcs 3f + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #7 + pop {pc} + +3: + adds r_t, #1 + bx lr + +opf1: + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_af, r0 + adds r_t, #6 + pop {pc} + +opf2: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read16 + mov r_memptr, r0 + lsrs r0, r_af, #8 + bcs 3f + mov r_pc, r_memptr + pop {pc} + +3: + adds r_pc, #2 + uxth r_pc, r_pc + pop {pc} + +opf3: + movs r0, #0 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + movs r0, #0 + ldr r1, =z80a_resting_state + strb r0, [r1, #10] // iff2 + bx lr + +opf4: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_memptr, r0 + lsrs r0, r_af, #8 + bcs 3f + mov r0, r_pc + bl _push + mov r_pc, r_memptr + adds r_t, #13 + pop {pc} + +3: + adds r_t, #6 + pop {pc} + +opf5: + adds r_t, #7 + mov r0, r_af + ldr r2, =_push + bx r2 + +opf6: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + bl or8 + adds r_t, #3 + pop {pc} + +opf7: + movs r_temp, #48 + mov r0, r_pc + push {lr} + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #7 + pop {pc} + +opf8: + lsrs r0, r_af, #8 + bcc 2f + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_memptr, r0 + mov r_pc, r_memptr + adds r_t, #7 + pop {pc} + +2: + adds r_t, #1 + bx lr + +opf9: + mov r_sp, r_hl + adds r_t, #2 + bx lr + +opfa: + adds r_t, #6 + mov r0, r_pc + push {lr} + bl read16 + mov r_memptr, r0 + lsrs r0, r_af, #8 + bcc 2f + mov r_pc, r_memptr + pop {pc} + +2: + adds r_pc, #2 + uxth r_pc, r_pc + pop {pc} + +opfb: + movs r0, #1 + ldr r1, =z80a_resting_state + strb r0, [r1, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_t + ldr r1, =z80a_resting_state + str r0, [r1, #4] // eipos + bx lr + +opfc: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_memptr, r0 + lsrs r0, r_af, #8 + bcc 2f + mov r0, r_pc + bl _push + mov r_pc, r_memptr + adds r_t, #13 + pop {pc} + +2: + adds r_t, #6 + pop {pc} + +opfd: + movs r0, #0xfd + ldr r2, =opDDFD + bx r2 + +opfe: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + bl cp8 + adds r_t, #3 + pop {pc} + +opff: + movs r_temp, #56 + mov r0, r_pc + push {lr} + bl _push + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #7 + pop {pc} + +.ltorg +// === END top level opcodes + +// === BEGIN OPL inner logic functions which do core work on r_temp +opli_r35_table: +.short opli_r35_00 + 1 - opli_r35_table +.short opli_r35_08 + 1 - opli_r35_table +.short opli_r35_10 + 1 - opli_r35_table +.short opli_r35_18 + 1 - opli_r35_table +.short opli_r35_20 + 1 - opli_r35_table +.short opli_r35_28 + 1 - opli_r35_table +.short opli_r35_30 + 1 - opli_r35_table +.short opli_r35_38 + 1 - opli_r35_table +.short opli_r35_40 + 1 - opli_r35_table +.short opli_r35_48 + 1 - opli_r35_table +.short opli_r35_50 + 1 - opli_r35_table +.short opli_r35_58 + 1 - opli_r35_table +.short opli_r35_60 + 1 - opli_r35_table +.short opli_r35_68 + 1 - opli_r35_table +.short opli_r35_70 + 1 - opli_r35_table +.short opli_r35_78 + 1 - opli_r35_table +.short opli_r35_80 + 1 - opli_r35_table +.short opli_r35_88 + 1 - opli_r35_table +.short opli_r35_90 + 1 - opli_r35_table +.short opli_r35_98 + 1 - opli_r35_table +.short opli_r35_a0 + 1 - opli_r35_table +.short opli_r35_a8 + 1 - opli_r35_table +.short opli_r35_b0 + 1 - opli_r35_table +.short opli_r35_b8 + 1 - opli_r35_table +.short opli_r35_c0 + 1 - opli_r35_table +.short opli_r35_c8 + 1 - opli_r35_table +.short opli_r35_d0 + 1 - opli_r35_table +.short opli_r35_d8 + 1 - opli_r35_table +.short opli_r35_e0 + 1 - opli_r35_table +.short opli_r35_e8 + 1 - opli_r35_table +.short opli_r35_f0 + 1 - opli_r35_table +.short opli_r35_f8 + 1 - opli_r35_table +opddcb_bitX_table: +.short opddcb_bit + 1 - opddcb_bitX_table +.short opddcb_bit + 1 - opddcb_bitX_table +.short opddcb_bit + 1 - opddcb_bitX_table +.short opddcb_bit + 1 - opddcb_bitX_table +.short opddcb_bit + 1 - opddcb_bitX_table +.short opddcb_bit + 1 - opddcb_bitX_table +.short opddcb_bit + 1 - opddcb_bitX_table +.short opddcb_bit + 1 - opddcb_bitX_table + +opddcb_X_table: +.short opddcb_0 + 1 - opddcb_X_table +.short opddcb_1 + 1 - opddcb_X_table +.short opddcb_2 + 1 - opddcb_X_table +.short opddcb_3 + 1 - opddcb_X_table +.short opddcb_4 + 1 - opddcb_X_table +.short opddcb_5 + 1 - opddcb_X_table +.short opddcb_6 + 1 - opddcb_X_table +.short opddcb_7 + 1 - opddcb_X_table + +opddcb_0: + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + push {lr} + blx r2 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_memptr + bl write8 + adds r_t, #15 + pop {pc} + +opddcb_1: + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + push {lr} + blx r2 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + mov r0, r_temp // high half of word is ignored later + mov r1, r_memptr + bl write8 + adds r_t, #15 + pop {pc} + +opddcb_2: + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + push {lr} + blx r2 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_memptr + bl write8 + adds r_t, #15 + pop {pc} + +opddcb_3: + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + push {lr} + blx r2 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + mov r0, r_temp // high half of word is ignored later + mov r1, r_memptr + bl write8 + adds r_t, #15 + pop {pc} + +opddcb_4: + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + push {lr} + blx r2 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_memptr + bl write8 + adds r_t, #15 + pop {pc} + +opddcb_5: + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + push {lr} + blx r2 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + mov r0, r_temp // high half of word is ignored later + mov r1, r_memptr + bl write8 + adds r_t, #15 + pop {pc} + +opddcb_6: + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + push {lr} + blx r2 + mov r0, r_temp // high half of word is ignored later + mov r1, r_memptr + bl write8 + adds r_t, #15 + pop {pc} + +opddcb_7: + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + push {lr} + blx r2 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_memptr + bl write8 + adds r_t, #15 + pop {pc} + +opddcb_bit: + adds r_t, #12 + mov r0, r_memptr + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + bx r2 + +opli_r35_00: + uxtb r0, r_temp + push {lr} + bl rlc8 + mov r_temp, r0 // fine to overwrite hi in r_temp + pop {pc} + +opli_r35_08: + uxtb r0, r_temp + push {lr} + bl rrc8 + mov r_temp, r0 // fine to overwrite hi in r_temp + pop {pc} + +opli_r35_10: + uxtb r0, r_temp + push {lr} + bl rl8 + mov r_temp, r0 // fine to overwrite hi in r_temp + pop {pc} + +opli_r35_18: + uxtb r0, r_temp + push {lr} + bl rr8 + mov r_temp, r0 // fine to overwrite hi in r_temp + pop {pc} + +opli_r35_20: + uxtb r0, r_temp + push {lr} + bl sla8 + mov r_temp, r0 // fine to overwrite hi in r_temp + pop {pc} + +opli_r35_28: + uxtb r0, r_temp + push {lr} + bl sra8 + mov r_temp, r0 // fine to overwrite hi in r_temp + pop {pc} + +opli_r35_30: + uxtb r0, r_temp + push {lr} + bl sli8 + mov r_temp, r0 // fine to overwrite hi in r_temp + pop {pc} + +opli_r35_38: + uxtb r0, r_temp + push {lr} + bl srl8 + mov r_temp, r0 // fine to overwrite hi in r_temp + pop {pc} + +opli_r35_40: + // this is the regular version of the inner bit code - it uses r_temp for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x01 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r0, #(F3|F5) + ands r0, r_temp + orrs r_af, r0 // note this OR is safe because 35 from log_f[r_temp&mask] are <= + bx lr + +opli_r35_48: + // this is the regular version of the inner bit code - it uses r_temp for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x02 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r0, #(F3|F5) + ands r0, r_temp + orrs r_af, r0 // note this OR is safe because 35 from log_f[r_temp&mask] are <= + bx lr + +opli_r35_50: + // this is the regular version of the inner bit code - it uses r_temp for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x04 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r0, #(F3|F5) + ands r0, r_temp + orrs r_af, r0 // note this OR is safe because 35 from log_f[r_temp&mask] are <= + bx lr + +opli_r35_58: + // this is the regular version of the inner bit code - it uses r_temp for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x08 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r0, #(F3|F5) + ands r0, r_temp + orrs r_af, r0 // note this OR is safe because 35 from log_f[r_temp&mask] are <= + bx lr + +opli_r35_60: + // this is the regular version of the inner bit code - it uses r_temp for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x10 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r0, #(F3|F5) + ands r0, r_temp + orrs r_af, r0 // note this OR is safe because 35 from log_f[r_temp&mask] are <= + bx lr + +opli_r35_68: + // this is the regular version of the inner bit code - it uses r_temp for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x20 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r0, #(F3|F5) + ands r0, r_temp + orrs r_af, r0 // note this OR is safe because 35 from log_f[r_temp&mask] are <= + bx lr + +opli_r35_70: + // this is the regular version of the inner bit code - it uses r_temp for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x40 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r0, #(F3|F5) + ands r0, r_temp + orrs r_af, r0 // note this OR is safe because 35 from log_f[r_temp&mask] are <= + bx lr + +opli_r35_78: + // this is the regular version of the inner bit code - it uses r_temp for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x80 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r0, #(F3|F5) + ands r0, r_temp + orrs r_af, r0 // note this OR is safe because 35 from log_f[r_temp&mask] are <= + bx lr + +opli_r35_80: + movs r0, #0x01 + bics r_temp, r0 + bx lr + +opli_r35_88: + movs r0, #0x02 + bics r_temp, r0 + bx lr + +opli_r35_90: + movs r0, #0x04 + bics r_temp, r0 + bx lr + +opli_r35_98: + movs r0, #0x08 + bics r_temp, r0 + bx lr + +opli_r35_a0: + movs r0, #0x10 + bics r_temp, r0 + bx lr + +opli_r35_a8: + movs r0, #0x20 + bics r_temp, r0 + bx lr + +opli_r35_b0: + movs r0, #0x40 + bics r_temp, r0 + bx lr + +opli_r35_b8: + movs r0, #0x80 + bics r_temp, r0 + bx lr + +opli_r35_c0: + movs r0, #0x01 + orrs r_temp, r0 + bx lr + +opli_r35_c8: + movs r0, #0x02 + orrs r_temp, r0 + bx lr + +opli_r35_d0: + movs r0, #0x04 + orrs r_temp, r0 + bx lr + +opli_r35_d8: + movs r0, #0x08 + orrs r_temp, r0 + bx lr + +opli_r35_e0: + movs r0, #0x10 + orrs r_temp, r0 + bx lr + +opli_r35_e8: + movs r0, #0x20 + orrs r_temp, r0 + bx lr + +opli_r35_f0: + movs r0, #0x40 + orrs r_temp, r0 + bx lr + +opli_r35_f8: + movs r0, #0x80 + orrs r_temp, r0 + bx lr + +.ltorg +// === END OPL inner logic functions which do core work on r_temp + +// === BEGIN OPL inner logic functions which do core work on r_temp, but set F3&F5 based on mem_h +opli_m35_table: +.short opli_r35_00 + 1 - opli_m35_table +.short opli_r35_08 + 1 - opli_m35_table +.short opli_r35_10 + 1 - opli_m35_table +.short opli_r35_18 + 1 - opli_m35_table +.short opli_r35_20 + 1 - opli_m35_table +.short opli_r35_28 + 1 - opli_m35_table +.short opli_r35_30 + 1 - opli_m35_table +.short opli_r35_38 + 1 - opli_m35_table +.short opli_m35_40 + 1 - opli_m35_table +.short opli_m35_48 + 1 - opli_m35_table +.short opli_m35_50 + 1 - opli_m35_table +.short opli_m35_58 + 1 - opli_m35_table +.short opli_m35_60 + 1 - opli_m35_table +.short opli_m35_68 + 1 - opli_m35_table +.short opli_m35_70 + 1 - opli_m35_table +.short opli_m35_78 + 1 - opli_m35_table +.short opli_r35_80 + 1 - opli_m35_table +.short opli_r35_88 + 1 - opli_m35_table +.short opli_r35_90 + 1 - opli_m35_table +.short opli_r35_98 + 1 - opli_m35_table +.short opli_r35_a0 + 1 - opli_m35_table +.short opli_r35_a8 + 1 - opli_m35_table +.short opli_r35_b0 + 1 - opli_m35_table +.short opli_r35_b8 + 1 - opli_m35_table +.short opli_r35_c0 + 1 - opli_m35_table +.short opli_r35_c8 + 1 - opli_m35_table +.short opli_r35_d0 + 1 - opli_m35_table +.short opli_r35_d8 + 1 - opli_m35_table +.short opli_r35_e0 + 1 - opli_m35_table +.short opli_r35_e8 + 1 - opli_m35_table +.short opli_r35_f0 + 1 - opli_m35_table +.short opli_r35_f8 + 1 - opli_m35_table +opli_m35_40: + // Beware confusion; t may already be updated for the instruction + // this is the bitmem version of the inner bit code - it uses mem_h for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x01 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r1, #(F3|F5) + bics r_af, r1 + mov r0, r_memptr + lsrs r0, #8 + ands r0, r1 + orrs r_af, r0 + bx lr + +opli_m35_48: + // Beware confusion; t may already be updated for the instruction + // this is the bitmem version of the inner bit code - it uses mem_h for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x02 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r1, #(F3|F5) + bics r_af, r1 + mov r0, r_memptr + lsrs r0, #8 + ands r0, r1 + orrs r_af, r0 + bx lr + +opli_m35_50: + // Beware confusion; t may already be updated for the instruction + // this is the bitmem version of the inner bit code - it uses mem_h for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x04 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r1, #(F3|F5) + bics r_af, r1 + mov r0, r_memptr + lsrs r0, #8 + ands r0, r1 + orrs r_af, r0 + bx lr + +opli_m35_58: + // Beware confusion; t may already be updated for the instruction + // this is the bitmem version of the inner bit code - it uses mem_h for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x08 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r1, #(F3|F5) + bics r_af, r1 + mov r0, r_memptr + lsrs r0, #8 + ands r0, r1 + orrs r_af, r0 + bx lr + +opli_m35_60: + // Beware confusion; t may already be updated for the instruction + // this is the bitmem version of the inner bit code - it uses mem_h for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x10 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r1, #(F3|F5) + bics r_af, r1 + mov r0, r_memptr + lsrs r0, #8 + ands r0, r1 + orrs r_af, r0 + bx lr + +opli_m35_68: + // Beware confusion; t may already be updated for the instruction + // this is the bitmem version of the inner bit code - it uses mem_h for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x20 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r1, #(F3|F5) + bics r_af, r1 + mov r0, r_memptr + lsrs r0, #8 + ands r0, r1 + orrs r_af, r0 + bx lr + +opli_m35_70: + // Beware confusion; t may already be updated for the instruction + // this is the bitmem version of the inner bit code - it uses mem_h for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x40 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r1, #(F3|F5) + bics r_af, r1 + mov r0, r_memptr + lsrs r0, #8 + ands r0, r1 + orrs r_af, r0 + bx lr + +opli_m35_78: + // Beware confusion; t may already be updated for the instruction + // this is the bitmem version of the inner bit code - it uses mem_h for F3 & F5 + preserve_only_flags r0, CF + movs r0, #0x80 + ands r0, r_temp + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_af, #HF + movs r1, #(F3|F5) + bics r_af, r1 + mov r0, r_memptr + lsrs r0, #8 + ands r0, r1 + orrs r_af, r0 + bx lr + +.ltorg +// === END OPL inner logic functions which do core work on r_temp, but set F3&F5 based on mem_h + +// === BEGIN cb logic opcodes +opl_table: +.short opl00 + 1 - opl_table +.short opl01 + 1 - opl_table +.short opl02 + 1 - opl_table +.short opl03 + 1 - opl_table +.short opl04 + 1 - opl_table +.short opl05 + 1 - opl_table +.short opl06 + 1 - opl_table +.short opl07 + 1 - opl_table +.short opl08 + 1 - opl_table +.short opl09 + 1 - opl_table +.short opl0a + 1 - opl_table +.short opl0b + 1 - opl_table +.short opl0c + 1 - opl_table +.short opl0d + 1 - opl_table +.short opl0e + 1 - opl_table +.short opl0f + 1 - opl_table +.short opl10 + 1 - opl_table +.short opl11 + 1 - opl_table +.short opl12 + 1 - opl_table +.short opl13 + 1 - opl_table +.short opl14 + 1 - opl_table +.short opl15 + 1 - opl_table +.short opl16 + 1 - opl_table +.short opl17 + 1 - opl_table +.short opl18 + 1 - opl_table +.short opl19 + 1 - opl_table +.short opl1a + 1 - opl_table +.short opl1b + 1 - opl_table +.short opl1c + 1 - opl_table +.short opl1d + 1 - opl_table +.short opl1e + 1 - opl_table +.short opl1f + 1 - opl_table +.short opl20 + 1 - opl_table +.short opl21 + 1 - opl_table +.short opl22 + 1 - opl_table +.short opl23 + 1 - opl_table +.short opl24 + 1 - opl_table +.short opl25 + 1 - opl_table +.short opl26 + 1 - opl_table +.short opl27 + 1 - opl_table +.short opl28 + 1 - opl_table +.short opl29 + 1 - opl_table +.short opl2a + 1 - opl_table +.short opl2b + 1 - opl_table +.short opl2c + 1 - opl_table +.short opl2d + 1 - opl_table +.short opl2e + 1 - opl_table +.short opl2f + 1 - opl_table +.short opl30 + 1 - opl_table +.short opl31 + 1 - opl_table +.short opl32 + 1 - opl_table +.short opl33 + 1 - opl_table +.short opl34 + 1 - opl_table +.short opl35 + 1 - opl_table +.short opl36 + 1 - opl_table +.short opl37 + 1 - opl_table +.short opl38 + 1 - opl_table +.short opl39 + 1 - opl_table +.short opl3a + 1 - opl_table +.short opl3b + 1 - opl_table +.short opl3c + 1 - opl_table +.short opl3d + 1 - opl_table +.short opl3e + 1 - opl_table +.short opl3f + 1 - opl_table +.short opl40 + 1 - opl_table +.short opl41 + 1 - opl_table +.short opl42 + 1 - opl_table +.short opl43 + 1 - opl_table +.short opl44 + 1 - opl_table +.short opl45 + 1 - opl_table +.short opl46 + 1 - opl_table +.short opl47 + 1 - opl_table +.short opl48 + 1 - opl_table +.short opl49 + 1 - opl_table +.short opl4a + 1 - opl_table +.short opl4b + 1 - opl_table +.short opl4c + 1 - opl_table +.short opl4d + 1 - opl_table +.short opl4e + 1 - opl_table +.short opl4f + 1 - opl_table +.short opl50 + 1 - opl_table +.short opl51 + 1 - opl_table +.short opl52 + 1 - opl_table +.short opl53 + 1 - opl_table +.short opl54 + 1 - opl_table +.short opl55 + 1 - opl_table +.short opl56 + 1 - opl_table +.short opl57 + 1 - opl_table +.short opl58 + 1 - opl_table +.short opl59 + 1 - opl_table +.short opl5a + 1 - opl_table +.short opl5b + 1 - opl_table +.short opl5c + 1 - opl_table +.short opl5d + 1 - opl_table +.short opl5e + 1 - opl_table +.short opl5f + 1 - opl_table +.short opl60 + 1 - opl_table +.short opl61 + 1 - opl_table +.short opl62 + 1 - opl_table +.short opl63 + 1 - opl_table +.short opl64 + 1 - opl_table +.short opl65 + 1 - opl_table +.short opl66 + 1 - opl_table +.short opl67 + 1 - opl_table +.short opl68 + 1 - opl_table +.short opl69 + 1 - opl_table +.short opl6a + 1 - opl_table +.short opl6b + 1 - opl_table +.short opl6c + 1 - opl_table +.short opl6d + 1 - opl_table +.short opl6e + 1 - opl_table +.short opl6f + 1 - opl_table +.short opl70 + 1 - opl_table +.short opl71 + 1 - opl_table +.short opl72 + 1 - opl_table +.short opl73 + 1 - opl_table +.short opl74 + 1 - opl_table +.short opl75 + 1 - opl_table +.short opl76 + 1 - opl_table +.short opl77 + 1 - opl_table +.short opl78 + 1 - opl_table +.short opl79 + 1 - opl_table +.short opl7a + 1 - opl_table +.short opl7b + 1 - opl_table +.short opl7c + 1 - opl_table +.short opl7d + 1 - opl_table +.short opl7e + 1 - opl_table +.short opl7f + 1 - opl_table +.short opl80 + 1 - opl_table +.short opl81 + 1 - opl_table +.short opl82 + 1 - opl_table +.short opl83 + 1 - opl_table +.short opl84 + 1 - opl_table +.short opl85 + 1 - opl_table +.short opl86 + 1 - opl_table +.short opl87 + 1 - opl_table +.short opl88 + 1 - opl_table +.short opl89 + 1 - opl_table +.short opl8a + 1 - opl_table +.short opl8b + 1 - opl_table +.short opl8c + 1 - opl_table +.short opl8d + 1 - opl_table +.short opl8e + 1 - opl_table +.short opl8f + 1 - opl_table +.short opl90 + 1 - opl_table +.short opl91 + 1 - opl_table +.short opl92 + 1 - opl_table +.short opl93 + 1 - opl_table +.short opl94 + 1 - opl_table +.short opl95 + 1 - opl_table +.short opl96 + 1 - opl_table +.short opl97 + 1 - opl_table +.short opl98 + 1 - opl_table +.short opl99 + 1 - opl_table +.short opl9a + 1 - opl_table +.short opl9b + 1 - opl_table +.short opl9c + 1 - opl_table +.short opl9d + 1 - opl_table +.short opl9e + 1 - opl_table +.short opl9f + 1 - opl_table +.short opla0 + 1 - opl_table +.short opla1 + 1 - opl_table +.short opla2 + 1 - opl_table +.short opla3 + 1 - opl_table +.short opla4 + 1 - opl_table +.short opla5 + 1 - opl_table +.short opla6 + 1 - opl_table +.short opla7 + 1 - opl_table +.short opla8 + 1 - opl_table +.short opla9 + 1 - opl_table +.short oplaa + 1 - opl_table +.short oplab + 1 - opl_table +.short oplac + 1 - opl_table +.short oplad + 1 - opl_table +.short oplae + 1 - opl_table +.short oplaf + 1 - opl_table +.short oplb0 + 1 - opl_table +.short oplb1 + 1 - opl_table +.short oplb2 + 1 - opl_table +.short oplb3 + 1 - opl_table +.short oplb4 + 1 - opl_table +.short oplb5 + 1 - opl_table +.short oplb6 + 1 - opl_table +.short oplb7 + 1 - opl_table +.short oplb8 + 1 - opl_table +.short oplb9 + 1 - opl_table +.short oplba + 1 - opl_table +.short oplbb + 1 - opl_table +.short oplbc + 1 - opl_table +.short oplbd + 1 - opl_table +.short oplbe + 1 - opl_table +.short oplbf + 1 - opl_table +.short oplc0 + 1 - opl_table +.short oplc1 + 1 - opl_table +.short oplc2 + 1 - opl_table +.short oplc3 + 1 - opl_table +.short oplc4 + 1 - opl_table +.short oplc5 + 1 - opl_table +.short oplc6 + 1 - opl_table +.short oplc7 + 1 - opl_table +.short oplc8 + 1 - opl_table +.short oplc9 + 1 - opl_table +.short oplca + 1 - opl_table +.short oplcb + 1 - opl_table +.short oplcc + 1 - opl_table +.short oplcd + 1 - opl_table +.short oplce + 1 - opl_table +.short oplcf + 1 - opl_table +.short opld0 + 1 - opl_table +.short opld1 + 1 - opl_table +.short opld2 + 1 - opl_table +.short opld3 + 1 - opl_table +.short opld4 + 1 - opl_table +.short opld5 + 1 - opl_table +.short opld6 + 1 - opl_table +.short opld7 + 1 - opl_table +.short opld8 + 1 - opl_table +.short opld9 + 1 - opl_table +.short oplda + 1 - opl_table +.short opldb + 1 - opl_table +.short opldc + 1 - opl_table +.short opldd + 1 - opl_table +.short oplde + 1 - opl_table +.short opldf + 1 - opl_table +.short ople0 + 1 - opl_table +.short ople1 + 1 - opl_table +.short ople2 + 1 - opl_table +.short ople3 + 1 - opl_table +.short ople4 + 1 - opl_table +.short ople5 + 1 - opl_table +.short ople6 + 1 - opl_table +.short ople7 + 1 - opl_table +.short ople8 + 1 - opl_table +.short ople9 + 1 - opl_table +.short oplea + 1 - opl_table +.short opleb + 1 - opl_table +.short oplec + 1 - opl_table +.short opled + 1 - opl_table +.short oplee + 1 - opl_table +.short oplef + 1 - opl_table +.short oplf0 + 1 - opl_table +.short oplf1 + 1 - opl_table +.short oplf2 + 1 - opl_table +.short oplf3 + 1 - opl_table +.short oplf4 + 1 - opl_table +.short oplf5 + 1 - opl_table +.short oplf6 + 1 - opl_table +.short oplf7 + 1 - opl_table +.short oplf8 + 1 - opl_table +.short oplf9 + 1 - opl_table +.short oplfa + 1 - opl_table +.short oplfb + 1 - opl_table +.short oplfc + 1 - opl_table +.short oplfd + 1 - opl_table +.short oplfe + 1 - opl_table +.short oplff + 1 - opl_table +opl00: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl rlc8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl01: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl rlc8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +opl02: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl rlc8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl03: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl rlc8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +opl04: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl rlc8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl05: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl rlc8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +opl06: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl rlc8 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + adds r_t, #7 + pop {pc} + +opl07: + lsrs r0, r_af, #8 + push {lr} + bl rlc8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl08: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl rrc8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl09: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl rrc8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +opl0a: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl rrc8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl0b: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl rrc8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +opl0c: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl rrc8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl0d: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl rrc8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +opl0e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl rrc8 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + adds r_t, #7 + pop {pc} + +opl0f: + lsrs r0, r_af, #8 + push {lr} + bl rrc8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl10: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl rl8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl11: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl rl8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +opl12: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl rl8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl13: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl rl8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +opl14: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl rl8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl15: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl rl8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +opl16: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl rl8 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + adds r_t, #7 + pop {pc} + +opl17: + lsrs r0, r_af, #8 + push {lr} + bl rl8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl18: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl rr8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl19: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl rr8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +opl1a: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl rr8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl1b: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl rr8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +opl1c: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl rr8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl1d: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl rr8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +opl1e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl rr8 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + adds r_t, #7 + pop {pc} + +opl1f: + lsrs r0, r_af, #8 + push {lr} + bl rr8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl20: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl sla8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +.ltorg +opl21: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl sla8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +opl22: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl sla8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl23: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl sla8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +opl24: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl sla8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl25: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl sla8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +opl26: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl sla8 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + adds r_t, #7 + pop {pc} + +opl27: + lsrs r0, r_af, #8 + push {lr} + bl sla8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl28: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl sra8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl29: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl sra8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +opl2a: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl sra8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl2b: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl sra8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +opl2c: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl sra8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl2d: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl sra8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +opl2e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl sra8 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + adds r_t, #7 + pop {pc} + +opl2f: + lsrs r0, r_af, #8 + push {lr} + bl sra8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl30: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl sli8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl31: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl sli8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +opl32: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl sli8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl33: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl sli8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +opl34: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl sli8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl35: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl sli8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +opl36: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl sli8 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + adds r_t, #7 + pop {pc} + +opl37: + lsrs r0, r_af, #8 + push {lr} + bl sli8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl38: + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl srl8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl39: + mov r0, r_bc + uxtb r0, r0 + push {lr} + bl srl8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + pop {pc} + +opl3a: + mov r0, r_de + lsrs r0, #8 + push {lr} + bl srl8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl3b: + mov r0, r_de + uxtb r0, r0 + push {lr} + bl srl8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + pop {pc} + +opl3c: + mov r0, r_hl + lsrs r0, #8 + push {lr} + bl srl8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl3d: + mov r0, r_hl + uxtb r0, r0 + push {lr} + bl srl8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + pop {pc} + +opl3e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + uxtb r0, r_temp + push {lr} + bl srl8 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + adds r_t, #7 + pop {pc} + +opl3f: + lsrs r0, r_af, #8 + push {lr} + bl srl8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl40: + mov r_temp, r_bc + lsrs r_temp, #8 + push {lr} + bl opli_r35_40 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +.ltorg +opl41: + mov r_temp, r_bc + uxtb r3, r3 + push {lr} + bl opli_r35_40 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + pop {pc} + +opl42: + mov r_temp, r_de + lsrs r_temp, #8 + push {lr} + bl opli_r35_40 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl43: + mov r_temp, r_de + uxtb r3, r3 + push {lr} + bl opli_r35_40 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + pop {pc} + +opl44: + mov r_temp, r_hl + lsrs r_temp, #8 + push {lr} + bl opli_r35_40 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl45: + mov r_temp, r_hl + uxtb r3, r3 + push {lr} + bl opli_r35_40 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + pop {pc} + +opl46: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + push {lr} + bl opli_m35_40 + adds r_t, #4 + pop {pc} + +opl47: + lsrs r_temp, r_af, #8 + push {lr} + bl opli_r35_40 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl48: + mov r_temp, r_bc + lsrs r_temp, #8 + push {lr} + bl opli_r35_48 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl49: + mov r_temp, r_bc + uxtb r3, r3 + push {lr} + bl opli_r35_48 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + pop {pc} + +opl4a: + mov r_temp, r_de + lsrs r_temp, #8 + push {lr} + bl opli_r35_48 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl4b: + mov r_temp, r_de + uxtb r3, r3 + push {lr} + bl opli_r35_48 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + pop {pc} + +opl4c: + mov r_temp, r_hl + lsrs r_temp, #8 + push {lr} + bl opli_r35_48 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl4d: + mov r_temp, r_hl + uxtb r3, r3 + push {lr} + bl opli_r35_48 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + pop {pc} + +opl4e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + push {lr} + bl opli_m35_48 + adds r_t, #4 + pop {pc} + +opl4f: + lsrs r_temp, r_af, #8 + push {lr} + bl opli_r35_48 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl50: + mov r_temp, r_bc + lsrs r_temp, #8 + push {lr} + bl opli_r35_50 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl51: + mov r_temp, r_bc + uxtb r3, r3 + push {lr} + bl opli_r35_50 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + pop {pc} + +opl52: + mov r_temp, r_de + lsrs r_temp, #8 + push {lr} + bl opli_r35_50 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl53: + mov r_temp, r_de + uxtb r3, r3 + push {lr} + bl opli_r35_50 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + pop {pc} + +opl54: + mov r_temp, r_hl + lsrs r_temp, #8 + push {lr} + bl opli_r35_50 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl55: + mov r_temp, r_hl + uxtb r3, r3 + push {lr} + bl opli_r35_50 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + pop {pc} + +opl56: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + push {lr} + bl opli_m35_50 + adds r_t, #4 + pop {pc} + +opl57: + lsrs r_temp, r_af, #8 + push {lr} + bl opli_r35_50 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl58: + mov r_temp, r_bc + lsrs r_temp, #8 + push {lr} + bl opli_r35_58 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl59: + mov r_temp, r_bc + uxtb r3, r3 + push {lr} + bl opli_r35_58 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + pop {pc} + +opl5a: + mov r_temp, r_de + lsrs r_temp, #8 + push {lr} + bl opli_r35_58 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl5b: + mov r_temp, r_de + uxtb r3, r3 + push {lr} + bl opli_r35_58 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + pop {pc} + +opl5c: + mov r_temp, r_hl + lsrs r_temp, #8 + push {lr} + bl opli_r35_58 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl5d: + mov r_temp, r_hl + uxtb r3, r3 + push {lr} + bl opli_r35_58 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + pop {pc} + +opl5e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + push {lr} + bl opli_m35_58 + adds r_t, #4 + pop {pc} + +opl5f: + lsrs r_temp, r_af, #8 + push {lr} + bl opli_r35_58 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl60: + mov r_temp, r_bc + lsrs r_temp, #8 + push {lr} + bl opli_r35_60 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +.ltorg +opl61: + mov r_temp, r_bc + uxtb r3, r3 + push {lr} + bl opli_r35_60 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + pop {pc} + +opl62: + mov r_temp, r_de + lsrs r_temp, #8 + push {lr} + bl opli_r35_60 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl63: + mov r_temp, r_de + uxtb r3, r3 + push {lr} + bl opli_r35_60 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + pop {pc} + +opl64: + mov r_temp, r_hl + lsrs r_temp, #8 + push {lr} + bl opli_r35_60 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl65: + mov r_temp, r_hl + uxtb r3, r3 + push {lr} + bl opli_r35_60 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + pop {pc} + +opl66: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + push {lr} + bl opli_m35_60 + adds r_t, #4 + pop {pc} + +opl67: + lsrs r_temp, r_af, #8 + push {lr} + bl opli_r35_60 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl68: + mov r_temp, r_bc + lsrs r_temp, #8 + push {lr} + bl opli_r35_68 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl69: + mov r_temp, r_bc + uxtb r3, r3 + push {lr} + bl opli_r35_68 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + pop {pc} + +opl6a: + mov r_temp, r_de + lsrs r_temp, #8 + push {lr} + bl opli_r35_68 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl6b: + mov r_temp, r_de + uxtb r3, r3 + push {lr} + bl opli_r35_68 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + pop {pc} + +opl6c: + mov r_temp, r_hl + lsrs r_temp, #8 + push {lr} + bl opli_r35_68 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl6d: + mov r_temp, r_hl + uxtb r3, r3 + push {lr} + bl opli_r35_68 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + pop {pc} + +opl6e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + push {lr} + bl opli_m35_68 + adds r_t, #4 + pop {pc} + +opl6f: + lsrs r_temp, r_af, #8 + push {lr} + bl opli_r35_68 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl70: + mov r_temp, r_bc + lsrs r_temp, #8 + push {lr} + bl opli_r35_70 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl71: + mov r_temp, r_bc + uxtb r3, r3 + push {lr} + bl opli_r35_70 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + pop {pc} + +opl72: + mov r_temp, r_de + lsrs r_temp, #8 + push {lr} + bl opli_r35_70 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl73: + mov r_temp, r_de + uxtb r3, r3 + push {lr} + bl opli_r35_70 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + pop {pc} + +opl74: + mov r_temp, r_hl + lsrs r_temp, #8 + push {lr} + bl opli_r35_70 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl75: + mov r_temp, r_hl + uxtb r3, r3 + push {lr} + bl opli_r35_70 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + pop {pc} + +opl76: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + push {lr} + bl opli_m35_70 + adds r_t, #4 + pop {pc} + +opl77: + lsrs r_temp, r_af, #8 + push {lr} + bl opli_r35_70 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl78: + mov r_temp, r_bc + lsrs r_temp, #8 + push {lr} + bl opli_r35_78 + lsls r0, r_temp, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + pop {pc} + +opl79: + mov r_temp, r_bc + uxtb r3, r3 + push {lr} + bl opli_r35_78 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_bc, r1 + pop {pc} + +opl7a: + mov r_temp, r_de + lsrs r_temp, #8 + push {lr} + bl opli_r35_78 + lsls r0, r_temp, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + pop {pc} + +opl7b: + mov r_temp, r_de + uxtb r3, r3 + push {lr} + bl opli_r35_78 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_de, r1 + pop {pc} + +opl7c: + mov r_temp, r_hl + lsrs r_temp, #8 + push {lr} + bl opli_r35_78 + lsls r0, r_temp, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + pop {pc} + +opl7d: + mov r_temp, r_hl + uxtb r3, r3 + push {lr} + bl opli_r35_78 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r_temp + mov r_hl, r1 + pop {pc} + +opl7e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + push {lr} + bl opli_m35_78 + adds r_t, #4 + pop {pc} + +opl7f: + lsrs r_temp, r_af, #8 + push {lr} + bl opli_r35_78 + lsls r0, r_temp, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + pop {pc} + +opl80: + movs r0, #1 + lsls r0, #8 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +.ltorg +opl81: + movs r0, #0x01 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opl82: + movs r0, #1 + lsls r0, #8 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opl83: + movs r0, #0x01 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opl84: + movs r0, #1 + lsls r0, #8 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opl85: + movs r0, #0x01 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opl86: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x01 + bics r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +opl87: + movs r0, #1 + lsls r0, #8 + bics r_af, r0 + bx lr + +opl88: + movs r0, #1 + lsls r0, #9 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opl89: + movs r0, #0x02 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opl8a: + movs r0, #1 + lsls r0, #9 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opl8b: + movs r0, #0x02 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opl8c: + movs r0, #1 + lsls r0, #9 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opl8d: + movs r0, #0x02 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opl8e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x02 + bics r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +opl8f: + movs r0, #1 + lsls r0, #9 + bics r_af, r0 + bx lr + +opl90: + movs r0, #1 + lsls r0, #10 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opl91: + movs r0, #0x04 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opl92: + movs r0, #1 + lsls r0, #10 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opl93: + movs r0, #0x04 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opl94: + movs r0, #1 + lsls r0, #10 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opl95: + movs r0, #0x04 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opl96: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x04 + bics r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +opl97: + movs r0, #1 + lsls r0, #10 + bics r_af, r0 + bx lr + +opl98: + movs r0, #1 + lsls r0, #11 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opl99: + movs r0, #0x08 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opl9a: + movs r0, #1 + lsls r0, #11 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opl9b: + movs r0, #0x08 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opl9c: + movs r0, #1 + lsls r0, #11 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opl9d: + movs r0, #0x08 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opl9e: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x08 + bics r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +opl9f: + movs r0, #1 + lsls r0, #11 + bics r_af, r0 + bx lr + +opla0: + movs r0, #1 + lsls r0, #12 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +.ltorg +opla1: + movs r0, #0x10 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opla2: + movs r0, #1 + lsls r0, #12 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opla3: + movs r0, #0x10 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +opla4: + movs r0, #1 + lsls r0, #12 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opla5: + movs r0, #0x10 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +opla6: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x10 + bics r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +opla7: + movs r0, #1 + lsls r0, #12 + bics r_af, r0 + bx lr + +opla8: + movs r0, #1 + lsls r0, #13 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +opla9: + movs r0, #0x20 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +oplaa: + movs r0, #1 + lsls r0, #13 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +oplab: + movs r0, #0x20 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +oplac: + movs r0, #1 + lsls r0, #13 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +oplad: + movs r0, #0x20 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +oplae: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x20 + bics r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +oplaf: + movs r0, #1 + lsls r0, #13 + bics r_af, r0 + bx lr + +oplb0: + movs r0, #1 + lsls r0, #14 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +oplb1: + movs r0, #0x40 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +oplb2: + movs r0, #1 + lsls r0, #14 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +oplb3: + movs r0, #0x40 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +oplb4: + movs r0, #1 + lsls r0, #14 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +oplb5: + movs r0, #0x40 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +oplb6: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x40 + bics r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +oplb7: + movs r0, #1 + lsls r0, #14 + bics r_af, r0 + bx lr + +oplb8: + movs r0, #1 + lsls r0, #15 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +oplb9: + movs r0, #0x80 + mov r1, r_bc + bics r1, r0 + mov r_bc, r1 + bx lr + +oplba: + movs r0, #1 + lsls r0, #15 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +oplbb: + movs r0, #0x80 + mov r1, r_de + bics r1, r0 + mov r_de, r1 + bx lr + +oplbc: + movs r0, #1 + lsls r0, #15 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +oplbd: + movs r0, #0x80 + mov r1, r_hl + bics r1, r0 + mov r_hl, r1 + bx lr + +oplbe: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x80 + bics r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +oplbf: + movs r0, #1 + lsls r0, #15 + bics r_af, r0 + bx lr + +oplc0: + movs r0, #1 + lsls r0, #8 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +.ltorg +oplc1: + movs r0, #0x01 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplc2: + movs r0, #1 + lsls r0, #8 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplc3: + movs r0, #0x01 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplc4: + movs r0, #1 + lsls r0, #8 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplc5: + movs r0, #0x01 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplc6: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x01 + orrs r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +oplc7: + movs r0, #1 + lsls r0, #8 + orrs r_af, r0 + bx lr + +oplc8: + movs r0, #1 + lsls r0, #9 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplc9: + movs r0, #0x02 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplca: + movs r0, #1 + lsls r0, #9 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplcb: + movs r0, #0x02 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplcc: + movs r0, #1 + lsls r0, #9 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplcd: + movs r0, #0x02 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplce: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x02 + orrs r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +oplcf: + movs r0, #1 + lsls r0, #9 + orrs r_af, r0 + bx lr + +opld0: + movs r0, #1 + lsls r0, #10 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +opld1: + movs r0, #0x04 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +opld2: + movs r0, #1 + lsls r0, #10 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +opld3: + movs r0, #0x04 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +opld4: + movs r0, #1 + lsls r0, #10 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +opld5: + movs r0, #0x04 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +opld6: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x04 + orrs r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +opld7: + movs r0, #1 + lsls r0, #10 + orrs r_af, r0 + bx lr + +opld8: + movs r0, #1 + lsls r0, #11 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +opld9: + movs r0, #0x08 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplda: + movs r0, #1 + lsls r0, #11 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +opldb: + movs r0, #0x08 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +opldc: + movs r0, #1 + lsls r0, #11 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +opldd: + movs r0, #0x08 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplde: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x08 + orrs r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +opldf: + movs r0, #1 + lsls r0, #11 + orrs r_af, r0 + bx lr + +ople0: + movs r0, #1 + lsls r0, #12 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +.ltorg +ople1: + movs r0, #0x10 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +ople2: + movs r0, #1 + lsls r0, #12 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +ople3: + movs r0, #0x10 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +ople4: + movs r0, #1 + lsls r0, #12 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +ople5: + movs r0, #0x10 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +ople6: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x10 + orrs r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +ople7: + movs r0, #1 + lsls r0, #12 + orrs r_af, r0 + bx lr + +ople8: + movs r0, #1 + lsls r0, #13 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +ople9: + movs r0, #0x20 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplea: + movs r0, #1 + lsls r0, #13 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +opleb: + movs r0, #0x20 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplec: + movs r0, #1 + lsls r0, #13 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +opled: + movs r0, #0x20 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplee: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x20 + orrs r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +oplef: + movs r0, #1 + lsls r0, #13 + orrs r_af, r0 + bx lr + +oplf0: + movs r0, #1 + lsls r0, #14 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplf1: + movs r0, #0x40 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplf2: + movs r0, #1 + lsls r0, #14 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplf3: + movs r0, #0x40 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplf4: + movs r0, #1 + lsls r0, #14 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplf5: + movs r0, #0x40 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplf6: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x40 + orrs r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +oplf7: + movs r0, #1 + lsls r0, #14 + orrs r_af, r0 + bx lr + +oplf8: + movs r0, #1 + lsls r0, #15 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplf9: + movs r0, #0x80 + mov r1, r_bc + orrs r1, r0 + mov r_bc, r1 + bx lr + +oplfa: + movs r0, #1 + lsls r0, #15 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplfb: + movs r0, #0x80 + mov r1, r_de + orrs r1, r0 + mov r_de, r1 + bx lr + +oplfc: + movs r0, #1 + lsls r0, #15 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplfd: + movs r0, #0x80 + mov r1, r_hl + orrs r1, r0 + mov r_hl, r1 + bx lr + +oplfe: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r0, #0x80 + orrs r_temp, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + push {lr} + bl write8 + adds r_t, #7 + pop {pc} + +oplff: + movs r0, #1 + lsls r0, #15 + orrs r_af, r0 + bx lr + +.ltorg +// === END cb logic opcodes + +// === BEGIN dd/fd prefix xy opcodes r_temp holds ix or iy +opxy_table: +.short op00 + 1 - opxy_table +.short op01 + 1 - opxy_table +.short op02 + 1 - opxy_table +.short op03 + 1 - opxy_table +.short op04 + 1 - opxy_table +.short op05 + 1 - opxy_table +.short op06 + 1 - opxy_table +.short op07 + 1 - opxy_table +.short op08 + 1 - opxy_table +.short opxy09 + 1 - opxy_table +.short op0a + 1 - opxy_table +.short op0b + 1 - opxy_table +.short op0c + 1 - opxy_table +.short op0d + 1 - opxy_table +.short op0e + 1 - opxy_table +.short op0f + 1 - opxy_table +.short op10 + 1 - opxy_table +.short op11 + 1 - opxy_table +.short op12 + 1 - opxy_table +.short op13 + 1 - opxy_table +.short op14 + 1 - opxy_table +.short op15 + 1 - opxy_table +.short op16 + 1 - opxy_table +.short op17 + 1 - opxy_table +.short op18 + 1 - opxy_table +.short opxy19 + 1 - opxy_table +.short op1a + 1 - opxy_table +.short op1b + 1 - opxy_table +.short op1c + 1 - opxy_table +.short op1d + 1 - opxy_table +.short op1e + 1 - opxy_table +.short op1f + 1 - opxy_table +.short op20 + 1 - opxy_table +.short opxy21 + 1 - opxy_table +.short opxy22 + 1 - opxy_table +.short opxy23 + 1 - opxy_table +.short opxy24 + 1 - opxy_table +.short opxy25 + 1 - opxy_table +.short opxy26 + 1 - opxy_table +.short op27 + 1 - opxy_table +.short op28 + 1 - opxy_table +.short opxy29 + 1 - opxy_table +.short opxy2a + 1 - opxy_table +.short opxy2b + 1 - opxy_table +.short opxy2c + 1 - opxy_table +.short opxy2d + 1 - opxy_table +.short opxy2e + 1 - opxy_table +.short op2f + 1 - opxy_table +.short op30 + 1 - opxy_table +.short op31 + 1 - opxy_table +.short op32 + 1 - opxy_table +.short op33 + 1 - opxy_table +.short opxy34 + 1 - opxy_table +.short opxy35 + 1 - opxy_table +.short opxy36 + 1 - opxy_table +.short op37 + 1 - opxy_table +.short op38 + 1 - opxy_table +.short opxy39 + 1 - opxy_table +.short op3a + 1 - opxy_table +.short op3b + 1 - opxy_table +.short op3c + 1 - opxy_table +.short op3d + 1 - opxy_table +.short op3e + 1 - opxy_table +.short op3f + 1 - opxy_table +.short op40 + 1 - opxy_table +.short op41 + 1 - opxy_table +.short op42 + 1 - opxy_table +.short op43 + 1 - opxy_table +.short opxy44 + 1 - opxy_table +.short opxy45 + 1 - opxy_table +.short opxy46 + 1 - opxy_table +.short op47 + 1 - opxy_table +.short op48 + 1 - opxy_table +.short op49 + 1 - opxy_table +.short op4a + 1 - opxy_table +.short op4b + 1 - opxy_table +.short opxy4c + 1 - opxy_table +.short opxy4d + 1 - opxy_table +.short opxy4e + 1 - opxy_table +.short op4f + 1 - opxy_table +.short op50 + 1 - opxy_table +.short op51 + 1 - opxy_table +.short op52 + 1 - opxy_table +.short op53 + 1 - opxy_table +.short opxy54 + 1 - opxy_table +.short opxy55 + 1 - opxy_table +.short opxy56 + 1 - opxy_table +.short op57 + 1 - opxy_table +.short op58 + 1 - opxy_table +.short op59 + 1 - opxy_table +.short op5a + 1 - opxy_table +.short op5b + 1 - opxy_table +.short opxy5c + 1 - opxy_table +.short opxy5d + 1 - opxy_table +.short opxy5e + 1 - opxy_table +.short op5f + 1 - opxy_table +.short opxy60 + 1 - opxy_table +.short opxy61 + 1 - opxy_table +.short opxy62 + 1 - opxy_table +.short opxy63 + 1 - opxy_table +.short op64 + 1 - opxy_table +.short opxy65 + 1 - opxy_table +.short opxy66 + 1 - opxy_table +.short opxy67 + 1 - opxy_table +.short opxy68 + 1 - opxy_table +.short opxy69 + 1 - opxy_table +.short opxy6a + 1 - opxy_table +.short opxy6b + 1 - opxy_table +.short opxy6c + 1 - opxy_table +.short op6d + 1 - opxy_table +.short opxy6e + 1 - opxy_table +.short opxy6f + 1 - opxy_table +.short opxy70 + 1 - opxy_table +.short opxy71 + 1 - opxy_table +.short opxy72 + 1 - opxy_table +.short opxy73 + 1 - opxy_table +.short opxy74 + 1 - opxy_table +.short opxy75 + 1 - opxy_table +.short op76 + 1 - opxy_table +.short opxy77 + 1 - opxy_table +.short op78 + 1 - opxy_table +.short op79 + 1 - opxy_table +.short op7a + 1 - opxy_table +.short op7b + 1 - opxy_table +.short opxy7c + 1 - opxy_table +.short opxy7d + 1 - opxy_table +.short opxy7e + 1 - opxy_table +.short op7f + 1 - opxy_table +.short op80 + 1 - opxy_table +.short op81 + 1 - opxy_table +.short op82 + 1 - opxy_table +.short op83 + 1 - opxy_table +.short opxy84 + 1 - opxy_table +.short opxy85 + 1 - opxy_table +.short opxy86 + 1 - opxy_table +.short op87 + 1 - opxy_table +.short op88 + 1 - opxy_table +.short op89 + 1 - opxy_table +.short op8a + 1 - opxy_table +.short op8b + 1 - opxy_table +.short opxy8c + 1 - opxy_table +.short opxy8d + 1 - opxy_table +.short opxy8e + 1 - opxy_table +.short op8f + 1 - opxy_table +.short op90 + 1 - opxy_table +.short op91 + 1 - opxy_table +.short op92 + 1 - opxy_table +.short op93 + 1 - opxy_table +.short opxy94 + 1 - opxy_table +.short opxy95 + 1 - opxy_table +.short opxy96 + 1 - opxy_table +.short op97 + 1 - opxy_table +.short op98 + 1 - opxy_table +.short op99 + 1 - opxy_table +.short op9a + 1 - opxy_table +.short op9b + 1 - opxy_table +.short opxy9c + 1 - opxy_table +.short opxy9d + 1 - opxy_table +.short opxy9e + 1 - opxy_table +.short op9f + 1 - opxy_table +.short opa0 + 1 - opxy_table +.short opa1 + 1 - opxy_table +.short opa2 + 1 - opxy_table +.short opa3 + 1 - opxy_table +.short opxya4 + 1 - opxy_table +.short opxya5 + 1 - opxy_table +.short opxya6 + 1 - opxy_table +.short opa7 + 1 - opxy_table +.short opa8 + 1 - opxy_table +.short opa9 + 1 - opxy_table +.short opaa + 1 - opxy_table +.short opab + 1 - opxy_table +.short opxyac + 1 - opxy_table +.short opxyad + 1 - opxy_table +.short opxyae + 1 - opxy_table +.short opaf + 1 - opxy_table +.short opb0 + 1 - opxy_table +.short opb1 + 1 - opxy_table +.short opb2 + 1 - opxy_table +.short opb3 + 1 - opxy_table +.short opxyb4 + 1 - opxy_table +.short opxyb5 + 1 - opxy_table +.short opxyb6 + 1 - opxy_table +.short opb7 + 1 - opxy_table +.short opb8 + 1 - opxy_table +.short opb9 + 1 - opxy_table +.short opba + 1 - opxy_table +.short opbb + 1 - opxy_table +.short opxybc + 1 - opxy_table +.short opxybd + 1 - opxy_table +.short opxybe + 1 - opxy_table +.short opbf + 1 - opxy_table +.short opc0 + 1 - opxy_table +.short opc1 + 1 - opxy_table +.short opc2 + 1 - opxy_table +.short opc3 + 1 - opxy_table +.short opc4 + 1 - opxy_table +.short opc5 + 1 - opxy_table +.short opc6 + 1 - opxy_table +.short opc7 + 1 - opxy_table +.short opc8 + 1 - opxy_table +.short opc9 + 1 - opxy_table +.short opca + 1 - opxy_table +.short opcb + 1 - opxy_table +.short opcc + 1 - opxy_table +.short opcd + 1 - opxy_table +.short opce + 1 - opxy_table +.short opcf + 1 - opxy_table +.short opd0 + 1 - opxy_table +.short opd1 + 1 - opxy_table +.short opd2 + 1 - opxy_table +.short opd3 + 1 - opxy_table +.short opd4 + 1 - opxy_table +.short opd5 + 1 - opxy_table +.short opd6 + 1 - opxy_table +.short opd7 + 1 - opxy_table +.short opd8 + 1 - opxy_table +.short opd9 + 1 - opxy_table +.short opda + 1 - opxy_table +.short opdb + 1 - opxy_table +.short opdc + 1 - opxy_table +.short opdd + 1 - opxy_table +.short opde + 1 - opxy_table +.short opdf + 1 - opxy_table +.short ope0 + 1 - opxy_table +.short opxye1 + 1 - opxy_table +.short ope2 + 1 - opxy_table +.short opxye3 + 1 - opxy_table +.short ope4 + 1 - opxy_table +.short opxye5 + 1 - opxy_table +.short ope6 + 1 - opxy_table +.short ope7 + 1 - opxy_table +.short ope8 + 1 - opxy_table +.short opxye9 + 1 - opxy_table +.short opea + 1 - opxy_table +.short opeb + 1 - opxy_table +.short opec + 1 - opxy_table +.short oped + 1 - opxy_table +.short opee + 1 - opxy_table +.short opef + 1 - opxy_table +.short opf0 + 1 - opxy_table +.short opf1 + 1 - opxy_table +.short opf2 + 1 - opxy_table +.short opf3 + 1 - opxy_table +.short opf4 + 1 - opxy_table +.short opf5 + 1 - opxy_table +.short opf6 + 1 - opxy_table +.short opf7 + 1 - opxy_table +.short opf8 + 1 - opxy_table +.short opxyf9 + 1 - opxy_table +.short opfa + 1 - opxy_table +.short opfb + 1 - opxy_table +.short opfc + 1 - opxy_table +.short opfd + 1 - opxy_table +.short opfe + 1 - opxy_table +.short opff + 1 - opxy_table +opxy09: + mov r0, r_ixy + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_ixy + mov r1, r_bc + push {lr} + bl add16 + mov r_ixy, r0 + adds r_t, #7 + pop {pc} + +opxy19: + mov r0, r_ixy + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_ixy + mov r1, r_de + push {lr} + bl add16 + mov r_ixy, r0 + adds r_t, #7 + pop {pc} + +.ltorg +opxy21: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_ixy, r0 + adds r_t, #6 + pop {pc} + +opxy22: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp_restricted, r0 + adds r0, r_temp_restricted, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_ixy + mov r1, r_temp_restricted + bl write16 + adds r_t, #12 + pop {pc} + +opxy23: + mov r0, r_ixy + adds r0, #1 + uxth r0, r0 + mov r_ixy, r0 + adds r_t, #2 + bx lr + +opxy24: + mov r0, r_ixy + lsrs r0, #8 + push {lr} + bl inc8 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + pop {pc} + +opxy25: + mov r0, r_ixy + lsrs r0, #8 + push {lr} + bl dec8 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + pop {pc} + +opxy26: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + adds r_t, #3 + pop {pc} + +opxy29: + mov r0, r_ixy + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_ixy + mov r1, r_ixy + push {lr} + bl add16 + mov r_ixy, r0 + adds r_t, #7 + pop {pc} + +opxy2a: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp_restricted, r0 + mov r0, r_temp_restricted + bl read16 + mov r_ixy, r0 + adds r0, r_temp_restricted, #1 + uxth r0, r0 + mov r_memptr, r0 + adds r_t, #12 + pop {pc} + +opxy2b: + mov r0, r_ixy + subs r0, #1 + uxth r0, r0 + mov r_ixy, r0 + adds r_t, #2 + bx lr + +opxy2c: + mov r0, r_ixy + uxtb r0, r0 + push {lr} + bl inc8 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + pop {pc} + +opxy2d: + mov r0, r_ixy + uxtb r0, r0 + push {lr} + bl dec8 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + pop {pc} + +opxy2e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + adds r_t, #3 + pop {pc} + +opxy34: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + ldr r1, =z80a_resting_state + str r0, [r1, #76] // scratch + ldr r0, =z80a_resting_state + ldr r0, [r0, #76] // scratch + read8_internal r0 + mov r_temp_restricted, r0 // fine to overwrite hi in r_temp_restricted + uxtb r0, r_temp_restricted + bl inc8 + mov r_temp_restricted, r0 // fine to overwrite hi in r_temp_restricted + ldr r0, =z80a_resting_state + ldr r0, [r0, #76] // scratch + mov r1, r0 + uxtb r0, r_temp_restricted + bl write8 + adds r_t, #15 + pop {pc} + +opxy35: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + ldr r1, =z80a_resting_state + str r0, [r1, #76] // scratch + ldr r0, =z80a_resting_state + ldr r0, [r0, #76] // scratch + read8_internal r0 + mov r_temp_restricted, r0 // fine to overwrite hi in r_temp_restricted + uxtb r0, r_temp_restricted + bl dec8 + mov r_temp_restricted, r0 // fine to overwrite hi in r_temp_restricted + ldr r0, =z80a_resting_state + ldr r0, [r0, #76] // scratch + mov r1, r0 + uxtb r0, r_temp_restricted + bl write8 + adds r_t, #15 + pop {pc} + +opxy36: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + ldr r1, =z80a_resting_state + str r0, [r1, #76] // scratch + mov r0, r_pc + bl read8inc + mov r_pc, r1 + mov r_temp_restricted, r0 // fine to overwrite hi in r_temp_restricted + ldr r0, =z80a_resting_state + ldr r0, [r0, #76] // scratch + mov r1, r0 + uxtb r0, r_temp_restricted + bl write8 + adds r_t, #11 + pop {pc} + +opxy39: + mov r0, r_ixy + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_ixy + mov r1, r_sp + push {lr} + bl add16 + mov r_ixy, r0 + adds r_t, #7 + pop {pc} + +.ltorg +opxy44: + mov r0, r_ixy + lsrs r0, #8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + bx lr + +opxy45: + mov r0, r_ixy + uxtb r0, r0 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + bx lr + +opxy46: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + adds r_t, #11 + pop {pc} + +opxy4c: + mov r0, r_ixy + lsrs r0, #8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + bx lr + +opxy4d: + mov r0, r_ixy + uxtb r0, r0 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + bx lr + +opxy4e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + adds r_t, #11 + pop {pc} + +opxy54: + mov r0, r_ixy + lsrs r0, #8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + bx lr + +opxy55: + mov r0, r_ixy + uxtb r0, r0 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + bx lr + +opxy56: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + adds r_t, #11 + pop {pc} + +opxy5c: + mov r0, r_ixy + lsrs r0, #8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + bx lr + +opxy5d: + mov r0, r_ixy + uxtb r0, r0 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + bx lr + +opxy5e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + adds r_t, #11 + pop {pc} + +opxy60: + mov r0, r_bc + lsrs r0, #8 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + bx lr + +.ltorg +opxy61: + mov r0, r_bc + uxtb r0, r0 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + bx lr + +opxy62: + mov r0, r_de + lsrs r0, #8 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + bx lr + +opxy63: + mov r0, r_de + uxtb r0, r0 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + bx lr + +opxy65: + mov r0, r_ixy + uxtb r0, r0 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + bx lr + +opxy66: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + adds r_t, #11 + pop {pc} + +opxy67: + lsrs r0, r_af, #8 + lsls r0, #8 + mov r1, r_ixy + uxtb r1, r1 + orrs r0, r1 + mov r_ixy, r0 + bx lr + +opxy68: + mov r0, r_bc + lsrs r0, #8 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + bx lr + +opxy69: + mov r0, r_bc + uxtb r0, r0 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + bx lr + +opxy6a: + mov r0, r_de + lsrs r0, #8 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + bx lr + +opxy6b: + mov r0, r_de + uxtb r0, r0 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + bx lr + +opxy6c: + mov r0, r_ixy + lsrs r0, #8 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + bx lr + +opxy6e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + adds r_t, #11 + pop {pc} + +opxy6f: + lsrs r0, r_af, #8 + mov r1, r_ixy + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_ixy, r1 + bx lr + +opxy70: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + mov r1, r0 + mov r0, r_bc + lsrs r0, #8 + bl write8 + adds r_t, #11 + pop {pc} + +opxy71: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + mov r1, r0 + mov r0, r_bc + uxtb r0, r0 + bl write8 + adds r_t, #11 + pop {pc} + +opxy72: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + mov r1, r0 + mov r0, r_de + lsrs r0, #8 + bl write8 + adds r_t, #11 + pop {pc} + +opxy73: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + mov r1, r0 + mov r0, r_de + uxtb r0, r0 + bl write8 + adds r_t, #11 + pop {pc} + +opxy74: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + mov r1, r0 + mov r0, r_hl + lsrs r0, #8 + bl write8 + adds r_t, #11 + pop {pc} + +opxy75: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + mov r1, r0 + mov r0, r_hl + uxtb r0, r0 + bl write8 + adds r_t, #11 + pop {pc} + +opxy77: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + mov r1, r0 + lsrs r0, r_af, #8 + bl write8 + adds r_t, #11 + pop {pc} + +opxy7c: + mov r0, r_ixy + lsrs r0, #8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + bx lr + +opxy7d: + mov r0, r_ixy + uxtb r0, r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + bx lr + +opxy7e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + adds r_t, #11 + pop {pc} + +.ltorg +opxy84: + mov r0, r_ixy + lsrs r0, #8 + ldr r2, =add8 + bx r2 + +opxy85: + mov r0, r_ixy + uxtb r0, r0 + ldr r2, =add8 + bx r2 + +opxy86: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + bl add8 + adds r_t, #11 + pop {pc} + +opxy8c: + mov r0, r_ixy + lsrs r0, #8 + ldr r2, =adc8 + bx r2 + +opxy8d: + mov r0, r_ixy + uxtb r0, r0 + ldr r2, =adc8 + bx r2 + +opxy8e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + bl adc8 + adds r_t, #11 + pop {pc} + +opxy94: + mov r0, r_ixy + lsrs r0, #8 + ldr r2, =sub8 + bx r2 + +opxy95: + mov r0, r_ixy + uxtb r0, r0 + ldr r2, =sub8 + bx r2 + +opxy96: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + bl sub8 + adds r_t, #11 + pop {pc} + +opxy9c: + mov r0, r_ixy + lsrs r0, #8 + ldr r2, =sbc8 + bx r2 + +opxy9d: + mov r0, r_ixy + uxtb r0, r0 + ldr r2, =sbc8 + bx r2 + +opxy9e: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + bl sbc8 + adds r_t, #11 + pop {pc} + +.ltorg +opxya4: + mov r0, r_ixy + lsrs r0, #8 + ldr r2, =ands8 + bx r2 + +opxya5: + mov r0, r_ixy + uxtb r0, r0 + ldr r2, =ands8 + bx r2 + +opxya6: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + bl ands8 + adds r_t, #11 + pop {pc} + +opxyac: + mov r0, r_ixy + lsrs r0, #8 + ldr r2, =xor8 + bx r2 + +opxyad: + mov r0, r_ixy + uxtb r0, r0 + ldr r2, =xor8 + bx r2 + +opxyae: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + bl xor8 + adds r_t, #11 + pop {pc} + +opxyb4: + mov r0, r_ixy + lsrs r0, #8 + ldr r2, =or8 + bx r2 + +opxyb5: + mov r0, r_ixy + uxtb r0, r0 + ldr r2, =or8 + bx r2 + +opxyb6: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + bl or8 + adds r_t, #11 + pop {pc} + +opxybc: + mov r0, r_ixy + lsrs r0, #8 + ldr r2, =cp8 + bx r2 + +opxybd: + mov r0, r_ixy + uxtb r0, r0 + ldr r2, =cp8 + bx r2 + +opxybe: + mov r0, r_pc + push {lr} + bl read8inc + mov r_pc, r1 + sxtb r0, r0 + mov r1, r_ixy + add r0, r1 + uxth r0, r0 + read8_internal r0 + bl cp8 + adds r_t, #11 + pop {pc} + +.ltorg +.ltorg +opxye1: + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_ixy, r0 + adds r_t, #6 + pop {pc} + +opxye3: + mov r0, r_sp + push {lr} + bl read16 + mov r_memptr, r0 + mov r0, r_ixy + mov r1, r_sp + bl write16 + mov r_ixy, r_memptr + adds r_t, #15 + pop {pc} + +opxye5: + mov r0, r_ixy + push {lr} + bl _push + adds r_t, #7 + pop {pc} + +opxye9: + mov r_pc, r_ixy + bx lr + +opxyf9: + mov r_sp, r_ixy + adds r_t, #2 + bx lr + +.ltorg +// === END dd/fd prefix xy opcodes r_temp holds ix or iy + +// === BEGIN ed prefix +ope_table: +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short ope40 + 1 - ope_table +.short ope41 + 1 - ope_table +.short ope42 + 1 - ope_table +.short ope43 + 1 - ope_table +.short ope44 + 1 - ope_table +.short ope45 + 1 - ope_table +.short ope46 + 1 - ope_table +.short ope47 + 1 - ope_table +.short ope48 + 1 - ope_table +.short ope49 + 1 - ope_table +.short ope4a + 1 - ope_table +.short ope4b + 1 - ope_table +.short ope4c + 1 - ope_table +.short ope4d + 1 - ope_table +.short ope4e + 1 - ope_table +.short ope4f + 1 - ope_table +.short ope50 + 1 - ope_table +.short ope51 + 1 - ope_table +.short ope52 + 1 - ope_table +.short ope53 + 1 - ope_table +.short ope54 + 1 - ope_table +.short ope55 + 1 - ope_table +.short ope56 + 1 - ope_table +.short ope57 + 1 - ope_table +.short ope58 + 1 - ope_table +.short ope59 + 1 - ope_table +.short ope5a + 1 - ope_table +.short ope5b + 1 - ope_table +.short ope5c + 1 - ope_table +.short ope5d + 1 - ope_table +.short ope5e + 1 - ope_table +.short ope5f + 1 - ope_table +.short ope60 + 1 - ope_table +.short ope61 + 1 - ope_table +.short ope62 + 1 - ope_table +.short ope63 + 1 - ope_table +.short ope64 + 1 - ope_table +.short ope65 + 1 - ope_table +.short ope66 + 1 - ope_table +.short ope67 + 1 - ope_table +.short ope68 + 1 - ope_table +.short ope69 + 1 - ope_table +.short ope6a + 1 - ope_table +.short ope6b + 1 - ope_table +.short ope6c + 1 - ope_table +.short ope6d + 1 - ope_table +.short ope6e + 1 - ope_table +.short ope6f + 1 - ope_table +.short ope70 + 1 - ope_table +.short ope71 + 1 - ope_table +.short ope72 + 1 - ope_table +.short ope73 + 1 - ope_table +.short ope74 + 1 - ope_table +.short ope75 + 1 - ope_table +.short ope76 + 1 - ope_table +.short ope77 + 1 - ope_table +.short ope78 + 1 - ope_table +.short ope79 + 1 - ope_table +.short ope7a + 1 - ope_table +.short ope7b + 1 - ope_table +.short ope7c + 1 - ope_table +.short ope7d + 1 - ope_table +.short ope7e + 1 - ope_table +.short ope7f + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short opea0 + 1 - ope_table +.short opea1 + 1 - ope_table +.short opea2 + 1 - ope_table +.short opea3 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short opea8 + 1 - ope_table +.short opea9 + 1 - ope_table +.short opeaa + 1 - ope_table +.short opeab + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short opeb0 + 1 - ope_table +.short opeb1 + 1 - ope_table +.short opeb2 + 1 - ope_table +.short opeb3 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short opeb8 + 1 - ope_table +.short opeb9 + 1 - ope_table +.short opeba + 1 - ope_table +.short opebb + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.short op00 + 1 - ope_table +.ltorg +ope40: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + mov r0, r_bc + lsrs r0, #8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + pop {pc} + +.ltorg +ope41: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + lsrs r0, #8 + mov r1, r_bc + ldr r2, =iowrite8 + bx r2 + +ope42: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl sbc16 + adds r_t, #7 + pop {pc} + +ope43: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + mov r1, r_temp + bl write16 + adds r_t, #12 + pop {pc} + +ope44: + ldr r2, =neg8 + bx r2 + +ope45: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_temp, r0 + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #6 + pop {pc} + +ope46: + movs r0, #0 + ldr r1, =z80a_resting_state + str r0, [r1, #0] // im + bx lr + +ope47: + lsrs r0, r_af, #8 + ldr r1, =z80a_resting_state + strb r0, [r1, #13] // i + adds r_t, #1 + bx lr + +ope48: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + mov r1, r_bc + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_bc, r1 + mov r0, r_bc + uxtb r0, r0 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + pop {pc} + +ope49: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc // high half of word is ignored later + mov r1, r_bc + ldr r2, =iowrite8 + bx r2 + +ope4a: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl adc16 + adds r_t, #7 + pop {pc} + +ope4b: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_temp + bl read16 + mov r_bc, r0 + adds r_t, #12 + pop {pc} + +ope4c: + ldr r2, =neg8 + bx r2 + +ope4d: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_temp, r0 + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #6 + pop {pc} + +ope4e: + movs r0, #1 + ldr r1, =z80a_resting_state + str r0, [r1, #0] // im + bx lr + +ope4f: + lsrs r0, r_af, #8 + ldr r1, =z80a_resting_state + strb r0, [r1, #12] // r_low + lsrs r0, r_af, #8 + movs r1, #0x80 + ands r0, r1 + ldr r1, =z80a_resting_state + strb r0, [r1, #8] // r_hi + adds r_t, #1 + bx lr + +ope50: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + lsls r0, #8 + mov r1, r_de + uxtb r1, r1 + orrs r0, r1 + mov r_de, r0 + mov r0, r_de + lsrs r0, #8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + pop {pc} + +ope51: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_de + lsrs r0, #8 + mov r1, r_bc + ldr r2, =iowrite8 + bx r2 + +ope52: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_de + push {lr} + bl sbc16 + adds r_t, #7 + pop {pc} + +ope53: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_de + mov r1, r_temp + bl write16 + adds r_t, #12 + pop {pc} + +ope54: + ldr r2, =neg8 + bx r2 + +ope55: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_temp, r0 + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #6 + pop {pc} + +ope56: + movs r0, #1 + ldr r1, =z80a_resting_state + str r0, [r1, #0] // im + bx lr + +ope57: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #13] // i + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + lsrs r0, r_af, #8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + movs r1, #PV + bics r_af, r1 + adds r_t, #1 + ldr r0, =z80a_resting_state + ldrb r0, [r0, #9] // iff1 + cmp r0, #0 + beq 1f +#ifndef USE_Z80_ARM_OFFSET_T + ldr r0, frame_tacts +#else + movs r0, #0 +#endif + subs r0, #10 + cmp r_t, r0 + bge 1f + adds r_af, #PV +1: + bx lr + +ope58: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + mov r1, r_de + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_de, r1 + mov r0, r_de + uxtb r0, r0 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + pop {pc} + +ope59: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_de // high half of word is ignored later + mov r1, r_bc + ldr r2, =iowrite8 + bx r2 + +ope5a: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_de + push {lr} + bl adc16 + adds r_t, #7 + pop {pc} + +ope5b: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + mov r0, r_temp + bl read16 + mov r_de, r0 + adds r_t, #12 + pop {pc} + +ope5c: + ldr r2, =neg8 + bx r2 + +ope5d: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_temp, r0 + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #6 + pop {pc} + +ope5e: + movs r0, #2 + ldr r1, =z80a_resting_state + str r0, [r1, #0] // im + bx lr + +ope5f: +#ifdef NO_UPDATE_RLOW_IN_FETCH + lsrs r0, r_t, #2 +#else + ldr r0, =z80a_resting_state + ldrb r0, [r0, #12] // r_low +#endif + movs r1, #0x7f + ands r0, r1 + ldr r1, =z80a_resting_state + ldrb r1, [r1, #8] // r_hi + orrs r0, r1 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + lsrs r0, r_af, #8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + movs r1, #PV + bics r_af, r1 + adds r_t, #1 + ldr r0, =z80a_resting_state + ldrb r0, [r0, #9] // iff1 + cmp r0, #0 + beq 1f +#ifndef USE_Z80_ARM_OFFSET_T + ldr r0, frame_tacts +#else + movs r0, #0 +#endif + subs r0, #10 + cmp r_t, r0 + blt 2f + ldr r0, =z80a_resting_state + ldr r0, [r0, #4] // eipos + adds r0, #8 + cmp r0, r_t + bne 1f +2: + adds r_af, #PV +1: + bx lr + +ope60: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + lsls r0, #8 + mov r1, r_hl + uxtb r1, r1 + orrs r0, r1 + mov r_hl, r0 + mov r0, r_hl + lsrs r0, #8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + pop {pc} + +.ltorg +ope61: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + lsrs r0, #8 + mov r1, r_bc + ldr r2, =iowrite8 + bx r2 + +ope62: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + push {lr} + bl sbc16 + adds r_t, #7 + pop {pc} + +ope63: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + mov r1, r_temp + bl write16 + adds r_t, #12 + pop {pc} + +ope64: + ldr r2, =neg8 + bx r2 + +ope65: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_temp, r0 + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #6 + pop {pc} + +ope66: + movs r0, #0 + ldr r1, =z80a_resting_state + str r0, [r1, #0] // im + bx lr + +ope67: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + lsrs r0, r_af, #8 + lsls r0, #4 + uxtb r1, r_temp + lsrs r1, #4 + orrs r0, r1 + mov r1, r_hl + push {lr} + bl write8 + lsrs r0, r_af, #8 + movs r2, #0xf0 + ands r0, r2 + uxtb r1, r_temp + bics r1, r2 + orrs r0, r1 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + lsrs r0, r_af, #8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_t, #10 + pop {pc} + +ope68: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + mov r1, r_hl + lsrs r1, #8 + lsls r1, #8 + orrs r1, r0 + mov r_hl, r1 + mov r0, r_hl + uxtb r0, r0 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + pop {pc} + +ope69: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl // high half of word is ignored later + mov r1, r_bc + ldr r2, =iowrite8 + bx r2 + +ope6a: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_hl + push {lr} + bl adc16 + adds r_t, #7 + pop {pc} + +ope6b: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_temp + bl read16 + mov r_hl, r0 + adds r_t, #12 + pop {pc} + +ope6c: + ldr r2, =neg8 + bx r2 + +ope6d: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_temp, r0 + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #6 + pop {pc} + +ope6e: + movs r0, #1 + ldr r1, =z80a_resting_state + str r0, [r1, #0] // im + bx lr + +ope6f: + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + lsrs r0, r_af, #8 + movs r2, #0xf + ands r0, r2 + uxtb r1, r_temp + lsls r1, #4 + orrs r0, r1 + mov r1, r_hl + push {lr} + bl write8 + lsrs r0, r_af, #8 + movs r2, #0xf0 + ands r0, r2 + uxtb r1, r_temp + lsrs r1, #4 + orrs r0, r1 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + lsrs r0, r_af, #8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + adds r_t, #10 + pop {pc} + +ope70: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + pop {pc} + +ope71: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + movs r0, #0 + mov r1, r_bc + ldr r2, =iowrite8 + bx r2 + +ope72: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_sp + push {lr} + bl sbc16 + adds r_t, #7 + pop {pc} + +ope73: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_sp + mov r1, r_temp + bl write16 + adds r_t, #12 + pop {pc} + +ope74: + ldr r2, =neg8 + bx r2 + +ope75: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_temp, r0 + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #6 + pop {pc} + +ope76: + movs r0, #1 + ldr r1, =z80a_resting_state + str r0, [r1, #0] // im + bx lr + +ope77: + bx lr + +ope78: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + lsls r0, #8 + uxtb r1, r_af + orrs r0, r1 + mov r_af, r0 + lsrs r0, r_af, #8 + preserve_only_flags r1, CF + ldr r1, =_log_f + ldrb r0, [r1, r0] + orrs r_af, r0 + pop {pc} + +ope79: + adds r_t, #4 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_af + lsrs r0, #8 + mov r1, r_bc + ldr r2, =iowrite8 + bx r2 + +ope7a: + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_sp + push {lr} + bl adc16 + adds r_t, #7 + pop {pc} + +ope7b: + mov r0, r_pc + push {lr} + bl read16inc + mov r_pc, r1 + mov r_temp, r0 + adds r0, r_temp, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_temp + bl read16 + mov r_sp, r0 + adds r_t, #12 + pop {pc} + +ope7c: + ldr r2, =neg8 + bx r2 + +ope7d: + ldr r0, =z80a_resting_state + ldrb r0, [r0, #10] // iff2 + ldr r1, =z80a_resting_state + strb r0, [r1, #9] // iff1 + mov r0, r_sp + push {lr} + bl read16inc + mov r_sp, r1 + mov r_temp, r0 + mov r_pc, r_temp + mov r_memptr, r_temp + adds r_t, #6 + pop {pc} + +ope7e: + movs r0, #2 + ldr r1, =z80a_resting_state + str r0, [r1, #0] // im + bx lr + +ope7f: + bx lr + +.ltorg +opea0: + push {lr} + bl ldX_common + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_de + adds r0, #1 + uxth r0, r0 + mov r_de, r0 + pop {pc} + +.ltorg +opea1: + push {lr} + bl cpX_common + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_hl, r0 + pop {pc} + +opea2: + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + adds r_t, #8 + mov r0, r_bc + push {lr} + bl ioread8 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r1, #1 + lsls r1, #8 + mov r0, r_bc + subs r0, r1 + uxth r0, r0 + mov r_bc, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_bc // high half of word is ignored later + add r0, r_temp + adds r0, #1 + // a2a3aaabflags r_temp, r0 + lsrs r_af, #8 + lsls r_af, #8 + mov r2, r_bc + lsrs r2, #8 + ldr r1, =_log_f + ldrb r2, [r1, r2] + movs r1, #PV + bics r2, r1 + orrs r_af, r2 + uxtb r0, r0 + cmp r0, r_temp + bge 1f + adds r_af, #HF|CF +1: + lsrs r1, r_temp, #8 + bcc 1f + adds r_af, #NF +1: + lsls r1, r0, #29 + lsrs r1, #21 + mov r2, r_bc + eors r1, r2 + lsrs r1, #8 + ldr r0, =_log_f + ldrb r0, [r0, r1] + movs r1, #PV + ands r0, r1 + orrs r_af, r0 + pop {pc} + +opea3: + adds r_t, #8 + mov r0, r_hl + push {lr} + bl read8inc + mov r_hl, r1 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r1, #1 + lsls r1, #8 + mov r0, r_bc + subs r0, r1 + uxth r0, r0 + mov r_bc, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_bc + bl iowrite8 + mov r0, r_hl // high half of word is ignored later + add r0, r_temp + // a2a3aaabflags r_temp, r0 + lsrs r_af, #8 + lsls r_af, #8 + mov r2, r_bc + lsrs r2, #8 + ldr r1, =_log_f + ldrb r2, [r1, r2] + movs r1, #PV + bics r2, r1 + orrs r_af, r2 + uxtb r0, r0 + cmp r0, r_temp + bge 1f + adds r_af, #HF|CF +1: + lsrs r1, r_temp, #8 + bcc 1f + adds r_af, #NF +1: + lsls r1, r0, #29 + lsrs r1, #21 + mov r2, r_bc + eors r1, r2 + lsrs r1, #8 + ldr r0, =_log_f + ldrb r0, [r0, r1] + movs r1, #PV + ands r0, r1 + orrs r_af, r0 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + pop {pc} + +opea8: + push {lr} + bl ldX_common + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_de + subs r0, #1 + uxth r0, r0 + mov r_de, r0 + pop {pc} + +opea9: + push {lr} + bl cpX_common + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + pop {pc} + +opeaa: + mov r0, r_bc + subs r0, #1 + uxth r0, r0 + mov r_memptr, r0 + adds r_t, #8 + mov r0, r_bc + push {lr} + bl ioread8 + mov r_temp, r0 // fine to overwrite hi in r_temp + movs r1, #1 + lsls r1, #8 + mov r0, r_bc + subs r0, r1 + uxth r0, r0 + mov r_bc, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_hl + bl write8 + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_bc // high half of word is ignored later + add r0, r_temp + subs r0, #1 + // a2a3aaabflags r_temp, r0 + lsrs r_af, #8 + lsls r_af, #8 + mov r2, r_bc + lsrs r2, #8 + ldr r1, =_log_f + ldrb r2, [r1, r2] + movs r1, #PV + bics r2, r1 + orrs r_af, r2 + uxtb r0, r0 + cmp r0, r_temp + bge 1f + adds r_af, #HF|CF +1: + lsrs r1, r_temp, #8 + bcc 1f + adds r_af, #NF +1: + lsls r1, r0, #29 + lsrs r1, #21 + mov r2, r_bc + eors r1, r2 + lsrs r1, #8 + ldr r0, =_log_f + ldrb r0, [r0, r1] + movs r1, #PV + ands r0, r1 + orrs r_af, r0 + pop {pc} + +opeab: + adds r_t, #8 + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + movs r1, #1 + lsls r1, #8 + mov r0, r_bc + subs r0, r1 + uxth r0, r0 + mov r_bc, r0 + mov r0, r_temp // high half of word is ignored later + mov r1, r_bc + push {lr} + bl iowrite8 + mov r0, r_hl // high half of word is ignored later + add r0, r_temp + // a2a3aaabflags r_temp, r0 + lsrs r_af, #8 + lsls r_af, #8 + mov r2, r_bc + lsrs r2, #8 + ldr r1, =_log_f + ldrb r2, [r1, r2] + movs r1, #PV + bics r2, r1 + orrs r_af, r2 + uxtb r0, r0 + cmp r0, r_temp + bge 1f + adds r_af, #HF|CF +1: + lsrs r1, r_temp, #8 + bcc 1f + adds r_af, #NF +1: + lsls r1, r0, #29 + lsrs r1, #21 + mov r2, r_bc + eors r1, r2 + lsrs r1, #8 + ldr r0, =_log_f + ldrb r0, [r0, r1] + movs r1, #PV + ands r0, r1 + orrs r_af, r0 + mov r0, r_bc + subs r0, #1 + uxth r0, r0 + mov r_memptr, r0 + pop {pc} + +ldX_common: + adds r_t, #8 + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 // fine to overwrite hi in r_temp + mov r0, r_temp // high half of word is ignored later + mov r1, r_de + push {lr} + bl write8 + preserve_only_flags r1, (CF|SF|ZF) + lsrs r0, r_af, #8 + add r_temp, r0 + bl set_af35_special_r_temp + mov r0, r_bc + subs r0, #1 + uxth r0, r0 + mov r_bc, r0 + beq 6f + adds r_af, #PV +6: + pop {pc} + +cpX_common: + push {lr} + adds r_t, #8 + push {r_af} + mov r0, r_hl + read8_internal r0 + mov r_temp, r0 + bl cp8 + lsrs r0, r_af, #8 + subs r0, r_temp + lsrs r1, r_af, #HF_INDEX+1 +bcc 6f + subs r0, #1 +6: + mov r_temp, r0 + preserve_only_flags r1, (SF|HF|NF|ZF) + bl set_af35_special_r_temp + pop {r0} + movs r1, #CF + ands r0, r1 + orrs r_af, r0 + mov r0, r_bc + subs r0, #1 + uxth r0, r0 + mov r_bc, r0 + beq 6f + adds r_af, #PV +6: + mov r0, r_memptr + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + pop {pc} + +opeb0: + push {lr} + bl ldX_common + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_de + adds r0, #1 + uxth r0, r0 + mov r_de, r0 + lsrs r0, r_af, #3 + bcc 2f + subs r_pc, #2 + uxth r_pc, r_pc + adds r_t, #5 + adds r0, r_pc, #1 + uxth r0, r0 + mov r_memptr, r0 +2: + pop {pc} + +.ltorg +opeb1: + push {lr} + bl cpX_common + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_hl, r0 + lsrs r0, r_af, #3 + bcc 2f + lsrs r0, r_af, #7 + bcs 3f + subs r_pc, #2 + uxth r_pc, r_pc + adds r_t, #5 + adds r0, r_pc, #1 + uxth r0, r0 + mov r_memptr, r0 +3: +2: + pop {pc} + +opeb2: + adds r_t, #8 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + mov r1, r_hl + bl write8 + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_bc + lsrs r0, #8 + bl dec8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + mov r0, r_bc + lsrs r0, #8 + cmp r0, #0 + beq 1f + movs r0, #4 + orrs r_af, r0 + subs r_pc, #2 + uxth r_pc, r_pc + adds r_t, #5 + pop {pc} + +1: + movs r0, #4 + bics r_af, r0 + pop {pc} + +opeb3: + adds r_t, #8 + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl dec8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + mov r0, r_hl + read8_internal r0 + mov r1, r_bc + bl iowrite8 + mov r0, r_hl + adds r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_bc + adds r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + lsrs r0, #8 + cmp r0, #0 + beq 1f + movs r0, #4 + orrs r_af, r0 + subs r_pc, #2 + uxth r_pc, r_pc + adds r_t, #5 + movs r0, #1 + bics r_af, r0 + mov r0, r_hl + uxtb r0, r0 + cmp r0, #0 + bne 1f + movs r0, #1 + orrs r_af, r0 +1: + pop {pc} + +1: + movs r0, #4 + bics r_af, r0 + mov r0, r_hl + uxtb r0, r0 + cmp r0, #0 + bne 1f + movs r0, #1 + orrs r_af, r0 +1: + pop {pc} + +opeb8: + push {lr} + bl ldX_common + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_de + subs r0, #1 + uxth r0, r0 + mov r_de, r0 + lsrs r0, r_af, #3 + bcc 2f + subs r_pc, #2 + uxth r_pc, r_pc + adds r_t, #5 + adds r0, r_pc, #1 + uxth r0, r0 + mov r_memptr, r0 +2: + pop {pc} + +opeb9: + push {lr} + bl cpX_common + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + lsrs r0, r_af, #3 + bcc 2f + lsrs r0, r_af, #7 + bcs 3f + subs r_pc, #2 + uxth r_pc, r_pc + adds r_t, #5 + adds r0, r_pc, #1 + uxth r0, r0 + mov r_memptr, r0 +3: +2: + pop {pc} + +opeba: + adds r_t, #8 + mov r0, r_bc + subs r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + push {lr} + bl ioread8 + mov r1, r_hl + bl write8 + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_bc + lsrs r0, #8 + bl dec8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + mov r0, r_bc + lsrs r0, #8 + cmp r0, #0 + beq 1f + movs r0, #4 + orrs r_af, r0 + subs r_pc, #2 + uxth r_pc, r_pc + adds r_t, #5 + pop {pc} + +1: + movs r0, #4 + bics r_af, r0 + pop {pc} + +opebb: + adds r_t, #8 + mov r0, r_bc + lsrs r0, #8 + push {lr} + bl dec8 + lsls r0, #8 + mov r1, r_bc + uxtb r1, r1 + orrs r0, r1 + mov r_bc, r0 + mov r0, r_hl + read8_internal r0 + mov r1, r_bc + bl iowrite8 + mov r0, r_hl + subs r0, #1 + uxth r0, r0 + mov r_hl, r0 + mov r0, r_bc + subs r0, #1 + uxth r0, r0 + mov r_memptr, r0 + mov r0, r_bc + lsrs r0, #8 + cmp r0, #0 + beq 1f + movs r0, #4 + orrs r_af, r0 + subs r_pc, #2 + uxth r_pc, r_pc + adds r_t, #5 + movs r0, #1 + bics r_af, r0 + mov r0, r_hl + uxtb r0, r0 + cmp r0, #255 + bne 1f + movs r0, #1 + orrs r_af, r0 +1: + pop {pc} + +1: + movs r0, #4 + bics r_af, r0 + mov r0, r_hl + uxtb r0, r0 + cmp r0, #255 + bne 1f + movs r0, #1 + orrs r_af, r0 +1: + pop {pc} + +.ltorg +.ltorg +.ltorg +// === END ed prefix diff --git a/khan/z80t.cpp b/khan/z80t.cpp new file mode 100644 index 0000000..3dcec4c --- /dev/null +++ b/khan/z80t.cpp @@ -0,0 +1,576 @@ +/* +Portable ZX-Spectrum emulator. +Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson + +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 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "../std.h" +#include "../devices/memory.h" +#include "../devices/ula.h" +#include "../devices/device.h" + +#include "z80t.h" + +namespace Z80t +{ + const char *eZ80t::arm_regs[REG_COUNT]; + + static eZ80t::CALLFUNC seen_opcodes[2048]; + static const char *seen_prefix[2048]; + static int seen_num[2048]; + + int seen_opcode_count; + + bool eZ80t::lr_saved; + bool eZ80t::r_temp_used; + bool eZ80t::r_temp_restricted_used; + + rs_var_init(halted); + rs_var_init(iff1); + rs_var_init(iff2); + rs_var_init(r_hi); + rs_var_init(r_low); + rs_var_init(i); + rs_var_init(im); + rs_var_init(eipos); + + rs_var_struct_init(alt, bc); + rs_var_struct_init(alt, de); + rs_var_struct_init(alt, hl); + rs_var_struct_init(alt, af); + rs_var_init(scratch); + + const char *eZ80t::pending_call = NULL; + +#define IMPL_REG16(REGNAME, reg, low, high)\ + struct eZ80t::Reg16 eZ80t::reg; \ + struct eZ80t::RegLo eZ80t::low; \ + struct eZ80t::RegHi eZ80t::high; + + IMPL_REG16(PC, pc, pc_l, pc_h) + IMPL_REG16(SP, sp, sp_l, sp_h) + IMPL_REG16(MEMPTR, memptr, mem_l, mem_h) // undocumented register + IMPL_REG16(IX, ix, xl, xh) + IMPL_REG16(IY, iy, yl, yh) + + IMPL_REG16(BC, bc, c, b) + IMPL_REG16(DE, de, e, d) + IMPL_REG16(HL, hl, l, h) + IMPL_REG16(AF, af, f, a) + IMPL_REG16(TEMPORARY, temporary, temp_lo, temp_hi) + IMPL_REG16(TEMPORARY_RESTRICTED, restricted, restriced_lo, restricted_hi) + + //============================================================================= + // eZ80t::eZ80 + //----------------------------------------------------------------------------- + eZ80t::eZ80t() + { + + arm_regs[BC] = "r_bc"; + arm_regs[DE] = "r_de"; + arm_regs[AF] = "r_af"; + arm_regs[IX] = "r_ixy"; + arm_regs[IY] = "r_ixy"; + arm_regs[MEMPTR] = "r_memptr"; + arm_regs[PC] = "r_pc"; + arm_regs[HL] = "r_hl"; + arm_regs[SP] = "r_sp"; + arm_regs[TEMPORARY] = "r_temp"; + arm_regs[TEMPORARY_RESTRICTED] = "r_temp_restricted"; + } + //============================================================================= + eZ80t::ZeroExtendedByteInR0 eZ80t::IoRead(WordInR0 port) const { + // address already in r0 + emit_call_func("ioread8"); + return ZeroExtendedByteInR0(); + } + void eZ80t::IoWrite(WordInR1 port, LoByteAndGarbageInR0 v) { + emit_call_func("iowrite8"); + } + eZ80t::ZeroExtendedByteInR0 eZ80t::Read(WordInR0 addr) const { + // address already in r0 +#ifndef USE_SINGLE_64K_MEMORY + emit_call_func("read8"); +#else + emit("read8_internal", "r0"); +#endif + return ZeroExtendedByteInR0(); + } + eZ80t::ZeroExtendedByteInR0 eZ80t::ReadInc(Reg16Ref addr) const { + emit_r0_from_reg(addr); + emit_call_func("read8inc"); + emit_reg_from_r1(addr); + return ZeroExtendedByteInR0(); + } + eZ80t::WordInR0 eZ80t::Read2(WordInR0 addr) const { + // address already in r0 + emit_call_func("read16"); + return WordInR0(); + } // low then high + eZ80t::WordInR0 eZ80t::Read2Inc(Reg16Ref addr) const { + emit_r0_from_reg(addr); + emit_call_func("read16inc"); + emit_reg_from_r1(addr); + return WordInR0(); + } + void eZ80t::Write(Reg16Ref addr, LoByteAndGarbageInR0 v) { + WordInR1 __unused x = addr; + emit_call_func("write8"); + } + void eZ80t::WriteXY(WordInR0 addr, Reg8Ref v) { + emit("mov ", "r1, r0"); + if (v.lo) { + emit_r0_from_reg_lo(v); + } else { + emit_r0_from_reg_hi(v); + } + emit_call_func("write8"); + } + + void eZ80t::Write2(Reg16Ref addr, WordInR0 v) { + emit_r1_from_reg(addr); + emit_call_func("write16"); + } // low then high + + void eZ80t::generate_arm() { + emit("// ================================"); + emit("// == AUTOGENERATED: DO NOT EDIT =="); + emit("// ================================"); + emit(""); + emit("// ALWAYS DIFF THIS CODE AFTER GENERATION - SENSITVE TO COMPILER VERSION CHANGES!"); + emit(""); + + const CALLFUNC opcodes[] = + { + &eZ80t::Op00, &eZ80t::Op01, &eZ80t::Op02, &eZ80t::Op03, &eZ80t::Op04, &eZ80t::Op05, + &eZ80t::Op06, &eZ80t::Op07, + &eZ80t::Op08, &eZ80t::Op09, &eZ80t::Op0A, &eZ80t::Op0B, &eZ80t::Op0C, &eZ80t::Op0D, + &eZ80t::Op0E, &eZ80t::Op0F, + &eZ80t::Op10, &eZ80t::Op11, &eZ80t::Op12, &eZ80t::Op13, &eZ80t::Op14, &eZ80t::Op15, + &eZ80t::Op16, &eZ80t::Op17, + &eZ80t::Op18, &eZ80t::Op19, &eZ80t::Op1A, &eZ80t::Op1B, &eZ80t::Op1C, &eZ80t::Op1D, + &eZ80t::Op1E, &eZ80t::Op1F, + &eZ80t::Op20, &eZ80t::Op21, &eZ80t::Op22, &eZ80t::Op23, &eZ80t::Op24, &eZ80t::Op25, + &eZ80t::Op26, &eZ80t::Op27, + &eZ80t::Op28, &eZ80t::Op29, &eZ80t::Op2A, &eZ80t::Op2B, &eZ80t::Op2C, &eZ80t::Op2D, + &eZ80t::Op2E, &eZ80t::Op2F, + &eZ80t::Op30, &eZ80t::Op31, &eZ80t::Op32, &eZ80t::Op33, &eZ80t::Op34, &eZ80t::Op35, + &eZ80t::Op36, &eZ80t::Op37, + &eZ80t::Op38, &eZ80t::Op39, &eZ80t::Op3A, &eZ80t::Op3B, &eZ80t::Op3C, &eZ80t::Op3D, + &eZ80t::Op3E, &eZ80t::Op3F, + + &eZ80t::Op40, &eZ80t::Op41, &eZ80t::Op42, &eZ80t::Op43, &eZ80t::Op44, &eZ80t::Op45, + &eZ80t::Op46, &eZ80t::Op47, + &eZ80t::Op48, &eZ80t::Op49, &eZ80t::Op4A, &eZ80t::Op4B, &eZ80t::Op4C, &eZ80t::Op4D, + &eZ80t::Op4E, &eZ80t::Op4F, + &eZ80t::Op50, &eZ80t::Op51, &eZ80t::Op52, &eZ80t::Op53, &eZ80t::Op54, &eZ80t::Op55, + &eZ80t::Op56, &eZ80t::Op57, + &eZ80t::Op58, &eZ80t::Op59, &eZ80t::Op5A, &eZ80t::Op5B, &eZ80t::Op5C, &eZ80t::Op5D, + &eZ80t::Op5E, &eZ80t::Op5F, + &eZ80t::Op60, &eZ80t::Op61, &eZ80t::Op62, &eZ80t::Op63, &eZ80t::Op64, &eZ80t::Op65, + &eZ80t::Op66, &eZ80t::Op67, + &eZ80t::Op68, &eZ80t::Op69, &eZ80t::Op6A, &eZ80t::Op6B, &eZ80t::Op6C, &eZ80t::Op6D, + &eZ80t::Op6E, &eZ80t::Op6F, + &eZ80t::Op70, &eZ80t::Op71, &eZ80t::Op72, &eZ80t::Op73, &eZ80t::Op74, &eZ80t::Op75, + &eZ80t::Op76, &eZ80t::Op77, + &eZ80t::Op78, &eZ80t::Op79, &eZ80t::Op7A, &eZ80t::Op7B, &eZ80t::Op7C, &eZ80t::Op7D, + &eZ80t::Op7E, &eZ80t::Op7F, + + &eZ80t::Op80, &eZ80t::Op81, &eZ80t::Op82, &eZ80t::Op83, &eZ80t::Op84, &eZ80t::Op85, + &eZ80t::Op86, &eZ80t::Op87, + &eZ80t::Op88, &eZ80t::Op89, &eZ80t::Op8A, &eZ80t::Op8B, &eZ80t::Op8C, &eZ80t::Op8D, + &eZ80t::Op8E, &eZ80t::Op8F, + &eZ80t::Op90, &eZ80t::Op91, &eZ80t::Op92, &eZ80t::Op93, &eZ80t::Op94, &eZ80t::Op95, + &eZ80t::Op96, &eZ80t::Op97, + &eZ80t::Op98, &eZ80t::Op99, &eZ80t::Op9A, &eZ80t::Op9B, &eZ80t::Op9C, &eZ80t::Op9D, + &eZ80t::Op9E, &eZ80t::Op9F, + &eZ80t::OpA0, &eZ80t::OpA1, &eZ80t::OpA2, &eZ80t::OpA3, &eZ80t::OpA4, &eZ80t::OpA5, + &eZ80t::OpA6, &eZ80t::OpA7, + &eZ80t::OpA8, &eZ80t::OpA9, &eZ80t::OpAA, &eZ80t::OpAB, &eZ80t::OpAC, &eZ80t::OpAD, + &eZ80t::OpAE, &eZ80t::OpAF, + &eZ80t::OpB0, &eZ80t::OpB1, &eZ80t::OpB2, &eZ80t::OpB3, &eZ80t::OpB4, &eZ80t::OpB5, + &eZ80t::OpB6, &eZ80t::OpB7, + &eZ80t::OpB8, &eZ80t::OpB9, &eZ80t::OpBA, &eZ80t::OpBB, &eZ80t::OpBC, &eZ80t::OpBD, + &eZ80t::OpBE, &eZ80t::OpBF, + + &eZ80t::OpC0, &eZ80t::OpC1, &eZ80t::OpC2, &eZ80t::OpC3, &eZ80t::OpC4, &eZ80t::OpC5, + &eZ80t::OpC6, &eZ80t::OpC7, + &eZ80t::OpC8, &eZ80t::OpC9, &eZ80t::OpCA, &eZ80t::OpCB, &eZ80t::OpCC, &eZ80t::OpCD, + &eZ80t::OpCE, &eZ80t::OpCF, + &eZ80t::OpD0, &eZ80t::OpD1, &eZ80t::OpD2, &eZ80t::OpD3, &eZ80t::OpD4, &eZ80t::OpD5, + &eZ80t::OpD6, &eZ80t::OpD7, + &eZ80t::OpD8, &eZ80t::OpD9, &eZ80t::OpDA, &eZ80t::OpDB, &eZ80t::OpDC, &eZ80t::OpDD, + &eZ80t::OpDE, &eZ80t::OpDF, + &eZ80t::OpE0, &eZ80t::OpE1, &eZ80t::OpE2, &eZ80t::OpE3, &eZ80t::OpE4, &eZ80t::OpE5, + &eZ80t::OpE6, &eZ80t::OpE7, + &eZ80t::OpE8, &eZ80t::OpE9, &eZ80t::OpEA, &eZ80t::OpEB, &eZ80t::OpEC, &eZ80t::OpED, + &eZ80t::OpEE, &eZ80t::OpEF, + &eZ80t::OpF0, &eZ80t::OpF1, &eZ80t::OpF2, &eZ80t::OpF3, &eZ80t::OpF4, &eZ80t::OpF5, + &eZ80t::OpF6, &eZ80t::OpF7, + &eZ80t::OpF8, &eZ80t::OpF9, &eZ80t::OpFA, &eZ80t::OpFB, &eZ80t::OpFC, &eZ80t::OpFD, + &eZ80t::OpFE, &eZ80t::OpFF + }; + dump_instructions("op", opcodes, count_of(opcodes), "top level opcodes"); + + const CALLFUNC oplscodes[] = { + &eZ80t::Opl_rlc, + &eZ80t::Opl_rrc, + &eZ80t::Opl_rl, + &eZ80t::Opl_rr, + &eZ80t::Opl_sla, + &eZ80t::Opl_sra, + &eZ80t::Opl_sli, + &eZ80t::Opl_srl, + &eZ80t::Opl_bit0, + &eZ80t::Opl_bit1, + &eZ80t::Opl_bit2, + &eZ80t::Opl_bit3, + &eZ80t::Opl_bit4, + &eZ80t::Opl_bit5, + &eZ80t::Opl_bit6, + &eZ80t::Opl_bit7, + &eZ80t::Opl_res0, + &eZ80t::Opl_res1, + &eZ80t::Opl_res2, + &eZ80t::Opl_res3, + &eZ80t::Opl_res4, + &eZ80t::Opl_res5, + &eZ80t::Opl_res6, + &eZ80t::Opl_res7, + &eZ80t::Opl_set0, + &eZ80t::Opl_set1, + &eZ80t::Opl_set2, + &eZ80t::Opl_set3, + &eZ80t::Opl_set4, + &eZ80t::Opl_set5, + &eZ80t::Opl_set6, + &eZ80t::Opl_set7, + }; + + dump_instructions("opli_r35_", oplscodes, count_of(oplscodes), "OPL inner logic functions which do core work on r_temp", 8); + + const CALLFUNC oplsmcodes[] = { + &eZ80t::Opl_rlc, + &eZ80t::Opl_rrc, + &eZ80t::Opl_rl, + &eZ80t::Opl_rr, + &eZ80t::Opl_sla, + &eZ80t::Opl_sra, + &eZ80t::Opl_sli, + &eZ80t::Opl_srl, + &eZ80t::Opl_bit0m, + &eZ80t::Opl_bit1m, + &eZ80t::Opl_bit2m, + &eZ80t::Opl_bit3m, + &eZ80t::Opl_bit4m, + &eZ80t::Opl_bit5m, + &eZ80t::Opl_bit6m, + &eZ80t::Opl_bit7m, + &eZ80t::Opl_res0, + &eZ80t::Opl_res1, + &eZ80t::Opl_res2, + &eZ80t::Opl_res3, + &eZ80t::Opl_res4, + &eZ80t::Opl_res5, + &eZ80t::Opl_res6, + &eZ80t::Opl_res7, + &eZ80t::Opl_set0, + &eZ80t::Opl_set1, + &eZ80t::Opl_set2, + &eZ80t::Opl_set3, + &eZ80t::Opl_set4, + &eZ80t::Opl_set5, + &eZ80t::Opl_set6, + &eZ80t::Opl_set7, + }; + dump_instructions("opli_m35_", oplsmcodes, count_of(oplsmcodes), "OPL inner logic functions which do core work on r_temp, but set F3&F5 based on mem_h", 8); + +#ifdef USE_LARGER_FASTER_CB + const CALLFUNC oplcodes[] = + { + &eZ80t::Opl00, &eZ80t::Opl01, &eZ80t::Opl02, &eZ80t::Opl03, &eZ80t::Opl04, &eZ80t::Opl05, &eZ80t::Opl06, &eZ80t::Opl07, + &eZ80t::Opl08, &eZ80t::Opl09, &eZ80t::Opl0A, &eZ80t::Opl0B, &eZ80t::Opl0C, &eZ80t::Opl0D, &eZ80t::Opl0E, &eZ80t::Opl0F, + &eZ80t::Opl10, &eZ80t::Opl11, &eZ80t::Opl12, &eZ80t::Opl13, &eZ80t::Opl14, &eZ80t::Opl15, &eZ80t::Opl16, &eZ80t::Opl17, + &eZ80t::Opl18, &eZ80t::Opl19, &eZ80t::Opl1A, &eZ80t::Opl1B, &eZ80t::Opl1C, &eZ80t::Opl1D, &eZ80t::Opl1E, &eZ80t::Opl1F, + &eZ80t::Opl20, &eZ80t::Opl21, &eZ80t::Opl22, &eZ80t::Opl23, &eZ80t::Opl24, &eZ80t::Opl25, &eZ80t::Opl26, &eZ80t::Opl27, + &eZ80t::Opl28, &eZ80t::Opl29, &eZ80t::Opl2A, &eZ80t::Opl2B, &eZ80t::Opl2C, &eZ80t::Opl2D, &eZ80t::Opl2E, &eZ80t::Opl2F, + &eZ80t::Opl30, &eZ80t::Opl31, &eZ80t::Opl32, &eZ80t::Opl33, &eZ80t::Opl34, &eZ80t::Opl35, &eZ80t::Opl36, &eZ80t::Opl37, + &eZ80t::Opl38, &eZ80t::Opl39, &eZ80t::Opl3A, &eZ80t::Opl3B, &eZ80t::Opl3C, &eZ80t::Opl3D, &eZ80t::Opl3E, &eZ80t::Opl3F, + + &eZ80t::Opl40, &eZ80t::Opl41, &eZ80t::Opl42, &eZ80t::Opl43, &eZ80t::Opl44, &eZ80t::Opl45, &eZ80t::Opl46, &eZ80t::Opl47, + &eZ80t::Opl48, &eZ80t::Opl49, &eZ80t::Opl4A, &eZ80t::Opl4B, &eZ80t::Opl4C, &eZ80t::Opl4D, &eZ80t::Opl4E, &eZ80t::Opl4F, + &eZ80t::Opl50, &eZ80t::Opl51, &eZ80t::Opl52, &eZ80t::Opl53, &eZ80t::Opl54, &eZ80t::Opl55, &eZ80t::Opl56, &eZ80t::Opl57, + &eZ80t::Opl58, &eZ80t::Opl59, &eZ80t::Opl5A, &eZ80t::Opl5B, &eZ80t::Opl5C, &eZ80t::Opl5D, &eZ80t::Opl5E, &eZ80t::Opl5F, + &eZ80t::Opl60, &eZ80t::Opl61, &eZ80t::Opl62, &eZ80t::Opl63, &eZ80t::Opl64, &eZ80t::Opl65, &eZ80t::Opl66, &eZ80t::Opl67, + &eZ80t::Opl68, &eZ80t::Opl69, &eZ80t::Opl6A, &eZ80t::Opl6B, &eZ80t::Opl6C, &eZ80t::Opl6D, &eZ80t::Opl6E, &eZ80t::Opl6F, + &eZ80t::Opl70, &eZ80t::Opl71, &eZ80t::Opl72, &eZ80t::Opl73, &eZ80t::Opl74, &eZ80t::Opl75, &eZ80t::Opl76, &eZ80t::Opl77, + &eZ80t::Opl78, &eZ80t::Opl79, &eZ80t::Opl7A, &eZ80t::Opl7B, &eZ80t::Opl7C, &eZ80t::Opl7D, &eZ80t::Opl7E, &eZ80t::Opl7F, + + &eZ80t::Opl80, &eZ80t::Opl81, &eZ80t::Opl82, &eZ80t::Opl83, &eZ80t::Opl84, &eZ80t::Opl85, &eZ80t::Opl86, &eZ80t::Opl87, + &eZ80t::Opl88, &eZ80t::Opl89, &eZ80t::Opl8A, &eZ80t::Opl8B, &eZ80t::Opl8C, &eZ80t::Opl8D, &eZ80t::Opl8E, &eZ80t::Opl8F, + &eZ80t::Opl90, &eZ80t::Opl91, &eZ80t::Opl92, &eZ80t::Opl93, &eZ80t::Opl94, &eZ80t::Opl95, &eZ80t::Opl96, &eZ80t::Opl97, + &eZ80t::Opl98, &eZ80t::Opl99, &eZ80t::Opl9A, &eZ80t::Opl9B, &eZ80t::Opl9C, &eZ80t::Opl9D, &eZ80t::Opl9E, &eZ80t::Opl9F, + &eZ80t::OplA0, &eZ80t::OplA1, &eZ80t::OplA2, &eZ80t::OplA3, &eZ80t::OplA4, &eZ80t::OplA5, &eZ80t::OplA6, &eZ80t::OplA7, + &eZ80t::OplA8, &eZ80t::OplA9, &eZ80t::OplAA, &eZ80t::OplAB, &eZ80t::OplAC, &eZ80t::OplAD, &eZ80t::OplAE, &eZ80t::OplAF, + &eZ80t::OplB0, &eZ80t::OplB1, &eZ80t::OplB2, &eZ80t::OplB3, &eZ80t::OplB4, &eZ80t::OplB5, &eZ80t::OplB6, &eZ80t::OplB7, + &eZ80t::OplB8, &eZ80t::OplB9, &eZ80t::OplBA, &eZ80t::OplBB, &eZ80t::OplBC, &eZ80t::OplBD, &eZ80t::OplBE, &eZ80t::OplBF, + + &eZ80t::OplC0, &eZ80t::OplC1, &eZ80t::OplC2, &eZ80t::OplC3, &eZ80t::OplC4, &eZ80t::OplC5, &eZ80t::OplC6, &eZ80t::OplC7, + &eZ80t::OplC8, &eZ80t::OplC9, &eZ80t::OplCA, &eZ80t::OplCB, &eZ80t::OplCC, &eZ80t::OplCD, &eZ80t::OplCE, &eZ80t::OplCF, + &eZ80t::OplD0, &eZ80t::OplD1, &eZ80t::OplD2, &eZ80t::OplD3, &eZ80t::OplD4, &eZ80t::OplD5, &eZ80t::OplD6, &eZ80t::OplD7, + &eZ80t::OplD8, &eZ80t::OplD9, &eZ80t::OplDA, &eZ80t::OplDB, &eZ80t::OplDC, &eZ80t::OplDD, &eZ80t::OplDE, &eZ80t::OplDF, + &eZ80t::OplE0, &eZ80t::OplE1, &eZ80t::OplE2, &eZ80t::OplE3, &eZ80t::OplE4, &eZ80t::OplE5, &eZ80t::OplE6, &eZ80t::OplE7, + &eZ80t::OplE8, &eZ80t::OplE9, &eZ80t::OplEA, &eZ80t::OplEB, &eZ80t::OplEC, &eZ80t::OplED, &eZ80t::OplEE, &eZ80t::OplEF, + &eZ80t::OplF0, &eZ80t::OplF1, &eZ80t::OplF2, &eZ80t::OplF3, &eZ80t::OplF4, &eZ80t::OplF5, &eZ80t::OplF6, &eZ80t::OplF7, + &eZ80t::OplF8, &eZ80t::OplF9, &eZ80t::OplFA, &eZ80t::OplFB, &eZ80t::OplFC, &eZ80t::OplFD, &eZ80t::OplFE, &eZ80t::OplFF + }; + + dump_instructions("opl", oplcodes, count_of(oplcodes), "cb logic opcodes"); +#endif + + CALLFUNC const opddcodes[] = + { + &eZ80t::Op00, &eZ80t::Op01, &eZ80t::Op02, &eZ80t::Op03, &eZ80t::Op04, &eZ80t::Op05, + &eZ80t::Op06, &eZ80t::Op07, + &eZ80t::Op08, &eZ80t::Opx09, &eZ80t::Op0A, &eZ80t::Op0B, &eZ80t::Op0C, &eZ80t::Op0D, + &eZ80t::Op0E, &eZ80t::Op0F, + &eZ80t::Op10, &eZ80t::Op11, &eZ80t::Op12, &eZ80t::Op13, &eZ80t::Op14, &eZ80t::Op15, + &eZ80t::Op16, &eZ80t::Op17, + &eZ80t::Op18, &eZ80t::Opx19, &eZ80t::Op1A, &eZ80t::Op1B, &eZ80t::Op1C, &eZ80t::Op1D, + &eZ80t::Op1E, &eZ80t::Op1F, + &eZ80t::Op20, &eZ80t::Opx21, &eZ80t::Opx22, &eZ80t::Opx23, &eZ80t::Opx24, &eZ80t::Opx25, + &eZ80t::Opx26, &eZ80t::Op27, + &eZ80t::Op28, &eZ80t::Opx29, &eZ80t::Opx2A, &eZ80t::Opx2B, &eZ80t::Opx2C, &eZ80t::Opx2D, + &eZ80t::Opx2E, &eZ80t::Op2F, + &eZ80t::Op30, &eZ80t::Op31, &eZ80t::Op32, &eZ80t::Op33, &eZ80t::Opx34, &eZ80t::Opx35, + &eZ80t::Opx36, &eZ80t::Op37, + &eZ80t::Op38, &eZ80t::Opx39, &eZ80t::Op3A, &eZ80t::Op3B, &eZ80t::Op3C, &eZ80t::Op3D, + &eZ80t::Op3E, &eZ80t::Op3F, + + &eZ80t::Op40, &eZ80t::Op41, &eZ80t::Op42, &eZ80t::Op43, &eZ80t::Opx44, &eZ80t::Opx45, + &eZ80t::Opx46, &eZ80t::Op47, + &eZ80t::Op48, &eZ80t::Op49, &eZ80t::Op4A, &eZ80t::Op4B, &eZ80t::Opx4C, &eZ80t::Opx4D, + &eZ80t::Opx4E, &eZ80t::Op4F, + &eZ80t::Op50, &eZ80t::Op51, &eZ80t::Op52, &eZ80t::Op53, &eZ80t::Opx54, &eZ80t::Opx55, + &eZ80t::Opx56, &eZ80t::Op57, + &eZ80t::Op58, &eZ80t::Op59, &eZ80t::Op5A, &eZ80t::Op5B, &eZ80t::Opx5C, &eZ80t::Opx5D, + &eZ80t::Opx5E, &eZ80t::Op5F, + &eZ80t::Opx60, &eZ80t::Opx61, &eZ80t::Opx62, &eZ80t::Opx63, &eZ80t::Op64, &eZ80t::Opx65, + &eZ80t::Opx66, &eZ80t::Opx67, + &eZ80t::Opx68, &eZ80t::Opx69, &eZ80t::Opx6A, &eZ80t::Opx6B, &eZ80t::Opx6C, &eZ80t::Op6D, + &eZ80t::Opx6E, &eZ80t::Opx6F, + &eZ80t::Opx70, &eZ80t::Opx71, &eZ80t::Opx72, &eZ80t::Opx73, &eZ80t::Opx74, &eZ80t::Opx75, + &eZ80t::Op76, &eZ80t::Opx77, + &eZ80t::Op78, &eZ80t::Op79, &eZ80t::Op7A, &eZ80t::Op7B, &eZ80t::Opx7C, &eZ80t::Opx7D, + &eZ80t::Opx7E, &eZ80t::Op7F, + + &eZ80t::Op80, &eZ80t::Op81, &eZ80t::Op82, &eZ80t::Op83, &eZ80t::Opx84, &eZ80t::Opx85, + &eZ80t::Opx86, &eZ80t::Op87, + &eZ80t::Op88, &eZ80t::Op89, &eZ80t::Op8A, &eZ80t::Op8B, &eZ80t::Opx8C, &eZ80t::Opx8D, + &eZ80t::Opx8E, &eZ80t::Op8F, + &eZ80t::Op90, &eZ80t::Op91, &eZ80t::Op92, &eZ80t::Op93, &eZ80t::Opx94, &eZ80t::Opx95, + &eZ80t::Opx96, &eZ80t::Op97, + &eZ80t::Op98, &eZ80t::Op99, &eZ80t::Op9A, &eZ80t::Op9B, &eZ80t::Opx9C, &eZ80t::Opx9D, + &eZ80t::Opx9E, &eZ80t::Op9F, + &eZ80t::OpA0, &eZ80t::OpA1, &eZ80t::OpA2, &eZ80t::OpA3, &eZ80t::OpxA4, &eZ80t::OpxA5, + &eZ80t::OpxA6, &eZ80t::OpA7, + &eZ80t::OpA8, &eZ80t::OpA9, &eZ80t::OpAA, &eZ80t::OpAB, &eZ80t::OpxAC, &eZ80t::OpxAD, + &eZ80t::OpxAE, &eZ80t::OpAF, + &eZ80t::OpB0, &eZ80t::OpB1, &eZ80t::OpB2, &eZ80t::OpB3, &eZ80t::OpxB4, &eZ80t::OpxB5, + &eZ80t::OpxB6, &eZ80t::OpB7, + &eZ80t::OpB8, &eZ80t::OpB9, &eZ80t::OpBA, &eZ80t::OpBB, &eZ80t::OpxBC, &eZ80t::OpxBD, + &eZ80t::OpxBE, &eZ80t::OpBF, + + &eZ80t::OpC0, &eZ80t::OpC1, &eZ80t::OpC2, &eZ80t::OpC3, &eZ80t::OpC4, &eZ80t::OpC5, + &eZ80t::OpC6, &eZ80t::OpC7, + &eZ80t::OpC8, &eZ80t::OpC9, &eZ80t::OpCA, &eZ80t::OpCB, &eZ80t::OpCC, &eZ80t::OpCD, + &eZ80t::OpCE, &eZ80t::OpCF, + &eZ80t::OpD0, &eZ80t::OpD1, &eZ80t::OpD2, &eZ80t::OpD3, &eZ80t::OpD4, &eZ80t::OpD5, + &eZ80t::OpD6, &eZ80t::OpD7, + &eZ80t::OpD8, &eZ80t::OpD9, &eZ80t::OpDA, &eZ80t::OpDB, &eZ80t::OpDC, &eZ80t::OpDD, + &eZ80t::OpDE, &eZ80t::OpDF, + &eZ80t::OpE0, &eZ80t::OpxE1, &eZ80t::OpE2, &eZ80t::OpxE3, &eZ80t::OpE4, &eZ80t::OpxE5, + &eZ80t::OpE6, &eZ80t::OpE7, + &eZ80t::OpE8, &eZ80t::OpxE9, &eZ80t::OpEA, &eZ80t::OpEB, &eZ80t::OpEC, &eZ80t::OpED, + &eZ80t::OpEE, &eZ80t::OpEF, + &eZ80t::OpF0, &eZ80t::OpF1, &eZ80t::OpF2, &eZ80t::OpF3, &eZ80t::OpF4, &eZ80t::OpF5, + &eZ80t::OpF6, &eZ80t::OpF7, + &eZ80t::OpF8, &eZ80t::OpxF9, &eZ80t::OpFA, &eZ80t::OpFB, &eZ80t::OpFC, &eZ80t::OpFD, + &eZ80t::OpFE, &eZ80t::OpFF + }; + + dump_instructions("opxy", opddcodes, count_of(opddcodes), "dd/fd prefix xy opcodes r_temp holds ix or iy"); + + CALLFUNC const opedcodes[] = + { + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + + &eZ80t::Ope40, &eZ80t::Ope41, &eZ80t::Ope42, &eZ80t::Ope43, &eZ80t::Ope44, &eZ80t::Ope45, + &eZ80t::Ope46, &eZ80t::Ope47, + &eZ80t::Ope48, &eZ80t::Ope49, &eZ80t::Ope4A, &eZ80t::Ope4B, &eZ80t::Ope4C, &eZ80t::Ope4D, + &eZ80t::Ope4E, &eZ80t::Ope4F, + &eZ80t::Ope50, &eZ80t::Ope51, &eZ80t::Ope52, &eZ80t::Ope53, &eZ80t::Ope54, &eZ80t::Ope55, + &eZ80t::Ope56, &eZ80t::Ope57, + &eZ80t::Ope58, &eZ80t::Ope59, &eZ80t::Ope5A, &eZ80t::Ope5B, &eZ80t::Ope5C, &eZ80t::Ope5D, + &eZ80t::Ope5E, &eZ80t::Ope5F, + &eZ80t::Ope60, &eZ80t::Ope61, &eZ80t::Ope62, &eZ80t::Ope63, &eZ80t::Ope64, &eZ80t::Ope65, + &eZ80t::Ope66, &eZ80t::Ope67, + &eZ80t::Ope68, &eZ80t::Ope69, &eZ80t::Ope6A, &eZ80t::Ope6B, &eZ80t::Ope6C, &eZ80t::Ope6D, + &eZ80t::Ope6E, &eZ80t::Ope6F, + &eZ80t::Ope70, &eZ80t::Ope71, &eZ80t::Ope72, &eZ80t::Ope73, &eZ80t::Ope74, &eZ80t::Ope75, + &eZ80t::Ope76, &eZ80t::Ope77, + &eZ80t::Ope78, &eZ80t::Ope79, &eZ80t::Ope7A, &eZ80t::Ope7B, &eZ80t::Ope7C, &eZ80t::Ope7D, + &eZ80t::Ope7E, &eZ80t::Ope7F, + + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::OpeA0, &eZ80t::OpeA1, &eZ80t::OpeA2, &eZ80t::OpeA3, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::OpeA8, &eZ80t::OpeA9, &eZ80t::OpeAA, &eZ80t::OpeAB, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::OpeB0, &eZ80t::OpeB1, &eZ80t::OpeB2, &eZ80t::OpeB3, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::OpeB8, &eZ80t::OpeB9, &eZ80t::OpeBA, &eZ80t::OpeBB, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, &eZ80t::Op00, + &eZ80t::Op00, &eZ80t::Op00 + }; + + + dump_instructions("ope", opedcodes, count_of(opedcodes), "ed prefix"); + } + + void eZ80t::dump_instructions(const char *prefix, const CALLFUNC *instrs, int count, const char *description, int mult) { + char buf[256]; + if (description) { + snprintf(buf, sizeof(buf), "// === BEGIN %s", description); + emit(buf); + } + int seen[count]; + for(int i=0;i= 0) + { + snprintf(buf, sizeof(buf), ".short %s%02x + 1 - %s", seen_prefix[seen[i]], seen_num[seen[i]], table_name); + } else { + snprintf(buf, sizeof(buf), ".short %s%02x + 1 - %s", prefix, i * mult, table_name); + } + emit(buf); + } + emit_helper_functions(prefix, HELPER_START); + for(int i=0;i*instrs[i])(); + emit_function_return(); + if (!strcmp(prefix, "opxy")) { + // can't use r_temp and r_ixy since they are the same reg + assert(!r_temp_used); + } + } + if (i && !(i&31)) { + // code a bit too big to not have frequent references + emit(".ltorg"); + } + if (i == 0xb0 && !strcmp(prefix, "ope")) { + // extra one + emit(".ltorg"); + } + } + emit_helper_functions(prefix, HELPER_END); + emit(".ltorg"); + if (description) { + snprintf(buf, sizeof(buf), "// === END %s", description); + emit(buf); + } + printf("\n"); + } + +}//namespace xZ80 diff --git a/khan/z80t.h b/khan/z80t.h new file mode 100644 index 0000000..d36d7ec --- /dev/null +++ b/khan/z80t.h @@ -0,0 +1,2329 @@ +/* +Portable ZX-Spectrum emulator. +Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson + +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 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef __Z80X_H__ +#define __Z80X_H__ + +#include "pico.h" +#include "../std_types.h" +#include +#include +#include "z80khan.h" // needed for struct layout +#include + +#ifdef DECLARE_REG16 +#error z80 already included in same compilation unit +#endif + +#pragma once + +class eMemory; +class eRom; +class eUla; +class eDevices; + +extern bool always_true; +#define USE_Z80T +namespace Z80t +{ + +#define DECLARE_REG16(REGNAME, reg, low, high)\ + static struct Reg16 reg; \ + static struct RegLo low; \ + static struct RegHi high; + + enum eFlags + { + CF = 0x01, + NF = 0x02, + PV = 0x04, + F3 = 0x08, + HF = 0x10, + F5 = 0x20, + ZF = 0x40, + SF = 0x80 + }; + + enum Reg16Name { + BC, + DE, + HL, + AF, + IX, + IY, + MEMPTR, + PC, + SP, + TEMPORARY, + TEMPORARY_RESTRICTED, + REG_COUNT + }; + +#define R_TEMP_ARM 3 + +#define unimpl_assert(x) emit("bkpt", "#0", "// not implemented") + +//***************************************************************************** +// eZ80 +//----------------------------------------------------------------------------- +class eZ80t +{ +public: + + // is the register stored in an ARM low register (i.e. we can do direct arithmetic on it) + static bool is_arm_lo_reg(const enum Reg16Name x) { + return x == TEMPORARY || x == PC || x == AF || x == TEMPORARY_RESTRICTED; + } + + template struct RegLo; + template struct RegHi; + + // reference to an 8 bit or 16 bit reg + struct RegRef { + RegRef(const enum Reg16Name& reg) : reg(reg) { + if (reg == TEMPORARY) { + r_temp_used = true; + } + if (reg == TEMPORARY_RESTRICTED) { + r_temp_restricted_used = true; + } + } + enum Reg16Name reg; + }; + + struct Reg8Ref : public RegRef { + Reg8Ref(const enum Reg16Name& reg, bool lo) : RegRef(reg), lo(lo) {} + bool lo; + }; + + struct Reg16Ref : public RegRef + { + Reg16Ref(const enum Reg16Name ®) : RegRef(reg) {} + }; + + struct WordInR0HighWordUnknown; + struct WordInR1; + struct ZeroExtendedByteInR0; + + // this is a value in r0 that is explicitly constructed from either a value already in r0 or a Reg16Ref + struct WordInR0 { + WordInR0() { + // already in r0 + } + WordInR0(const WordInR0HighWordUnknown& x) { + emit("uxth", "r0, r0"); + } + WordInR0(const Reg16Ref& ref) { + char buf[128]; + // assign to r0 + snprintf(buf, sizeof(buf), "r0, %s", arm_regs[ref.reg]); + emit("mov ", buf); + } + WordInR0 operator|(const WordInR1& v) { + emit("orrs ", "r0, r1"); + return {}; + } + WordInR0HighWordUnknown operator+(int x) + { + char buf[32]; + snprintf(buf, sizeof(buf), "r0, #%d", x); + emit("adds ", buf); + return {}; + } + }; + + // this is a value in r1 that is explicitly constructed from either a value already in r1 or a Reg16Ref + struct WordInR1 { + WordInR1() { + // already in r1 + } + WordInR1(const Reg16Ref& ref) { + emit_r1_from_reg(ref); + } +// WordInR0 operator|(const ZeroExtendedByteInR0& v) { +// emit("orrs ", "r0, r1"); +// return {}; +// } + }; + + // this is a value in r0 that is explicitly constructed form a value in r0 which may need zeroing + struct WordInR0HighWordUnknown { + WordInR0 operator&(int v) { + assert(v == 255); + emit("uxtb", "r0, r0"); + return {}; + } + }; + + // this is a value in r0 that is explicitly constructed form either a value in r0, a Reg8Ref or a constant + struct ZeroExtendedByteInR0 { + ZeroExtendedByteInR0() { + // value already in r0 + } + ZeroExtendedByteInR0(int val) { + char buf[32]; + snprintf(buf, sizeof(buf), "r0, #%d", val); + emit("movs ", buf); + } + ZeroExtendedByteInR0(const Reg8Ref& ref) { + char buf[128]; + // assign to r0 + if (ref.lo) { + emit_zero_extend_z80_lo_to_arm(ref, 0); + } else { + if (is_arm_lo_reg(ref.reg)) { + snprintf(buf, sizeof(buf), "r0, %s, #8", arm_regs[ref.reg]); + emit("lsrs ", buf); + } else { + snprintf(buf, sizeof(buf), "r0, %s", arm_regs[ref.reg]); + emit("mov ", buf); + emit("lsrs ", "r0, #8"); + } + } + } + + operator WordInR0() { + return {}; + } + }; + + // similar to ZeroExtendedByteInR0 except the hi byte may not be zero. + struct LoByteAndGarbageInR0 { + // value already in r0 + LoByteAndGarbageInR0() {} + + LoByteAndGarbageInR0(const ZeroExtendedByteInR0& r) {} + + LoByteAndGarbageInR0(int val) { + char buf[32]; + snprintf(buf, sizeof(buf), "r0, #%d", val); + emit("movs ", buf); + } + LoByteAndGarbageInR0(const Reg8Ref& ref) { + char buf[128]; + // assign to r0 + if (ref.lo) { + snprintf(buf, sizeof(buf), "r0, %s", arm_regs[ref.reg]); + emit("mov ", buf, "// high half of word is ignored later"); + } else { + snprintf(buf, sizeof(buf), "r0, %s", arm_regs[ref.reg]); + emit("mov ", buf); + emit("lsrs ", "r0, #8"); + } + } + }; + + // note high word may not be corrsect, but we don't care + struct SignExtendedByteInR0HighWordUnknown { + SignExtendedByteInR0HighWordUnknown(ZeroExtendedByteInR0 v) { + emit("sxtb", "r0, r0"); + } + + WordInR0HighWordUnknown operator+(int val) { + assert(val == 1); + emit("adds ", "r0, #1"); + return {}; + } + + operator WordInR0HighWordUnknown() { + return {}; + } + }; + + // type of t variable + struct TState { + TState() {} + TState operator+=(int val) { + char buf[128]; + snprintf(buf, sizeof(buf), "r_t, #%d", val); + emit("adds ", buf); + return *this; + } + TState operator++(int) { + TState tmp = *this; + emit("adds ", "r_t, #1"); + return tmp; + } + }; + + // variable that doesn't have a dedicated register, so is load and stored to/from the resting state + struct RestingStateVariable { + private: + public: + RestingStateVariable(const char *name, int size, int offset) : name(name), offset(offset), size(size) {} + RestingStateVariable(const RestingStateVariable& v) = delete; + WordInR0 operator=(const RestingStateVariable&v) { + WordInR0 tmp = v; + return *this = tmp; + } + + void emit_read(int target, int state_ref) const { + char buf[32]; + snprintf(buf, sizeof(buf), "r%d, [r%d, #%d] // %s", target, state_ref, offset, name); + switch (size) { + case 1: + emit("ldrb", buf); + break; + case 2: + emit("ldrh", buf); + break; + case 4: + emit("ldr ", buf); + break; + default: + assert(false); + } + } + + void emit_read_to_r1() const { + emit("ldr ", "r1, =z80a_resting_state"); + emit_read(1, 1); + } + + void emit_read_to_r0() const { + emit("ldr ", "r0, =z80a_resting_state"); + emit_read(0, 0); + } + + void emit_write(int source, int state_ref) { + char buf[32]; + snprintf(buf, sizeof(buf), "r%d, [r%d, #%d] // %s", source, state_ref, offset, name); + switch (size) { + case 1: + emit("strb", buf); + break; + case 2: + emit("strh", buf); + break; + case 4: + emit("str ", buf); + break; + default: + assert(false); + } + } + + void emit_write_from_r0() { + emit("ldr ", "r1, =z80a_resting_state"); + emit_write(0, 1); + } + + WordInR0 operator=(int val) { + char buf[32]; + snprintf(buf, sizeof(buf), "r0, #%d", val); + emit("movs ", buf); + emit_write_from_r0(); + return {}; + } + + WordInR0 operator=(WordInR0 val) { + emit_write_from_r0(); + return {}; + } + + WordInR0 operator=(ZeroExtendedByteInR0 val) { + emit_write_from_r0(); + return {}; + } + + RestingStateVariable& operator=(const TState& t) { + emit("mov ", "r0, r_t"); + emit_write_from_r0(); + return *this; + } + + operator WordInR0() const { + emit_read_to_r0(); + return {}; + } + + const char *name; + int offset; + int size; + }; + + template struct Reg16 { + Reg16() : reg(R), thisref(R) { } + + // todo do we need this? or should we use Constorrseg16 + Reg16(const int& v) : reg(R), thisref(R) { + assert(R == TEMPORARY || R == TEMPORARY_RESTRICTED); + set(v); + } + Reg16(const WordInR0& v) : reg(R), thisref(R) { + emit_reg_from_r0(R); + } + Reg16(const ZeroExtendedByteInR0& v) : reg(R), thisref(R) { + assert(R == TEMPORARY || R == TEMPORARY_RESTRICTED); + char buf[32]; + snprintf(buf, sizeof(buf), "%s, r0", arm_regs[reg]); + emit("mov ", buf); + } + Reg16(const WordInR0HighWordUnknown& v) : reg(R), thisref(R) { + char buf[32]; + emit("uxth", "r0, r0"); + snprintf(buf, sizeof(buf), "%s, r0", arm_regs[reg]); + emit("mov ", buf); + } + + Reg16 operator|=(const WordInR0& v) { + assert(is_arm_lo_reg(R)); // no particular reason, just not used + char buf[32]; + snprintf(buf, sizeof(buf), "%s, r0", arm_regs[reg]); + emit("orrs ", buf); + return *this; + } + + Reg16 operator|=(const WordInR1& v) { + assert(is_arm_lo_reg(R)); // no particular reason, just not used + char buf[32]; + snprintf(buf, sizeof(buf), "%s, r1", arm_regs[reg]); + emit("orrs ", buf); + return *this; + } + + Reg16 operator ++() { + add(1); + return *this; + } + // return void since we don't return the old value (still allows a standalone foo++) + void operator ++(int) { + add(1); + } + Reg16 operator --() { + sub(1); + return *this; + } + void operator --(int) { + sub(1); + } + void add(int x) { + assert(x > 0 && x < 256); + char buf[128]; + if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "%s, #%d", arm_regs[reg], x); + emit("adds ", buf); + snprintf(buf, sizeof(buf), "%s, %s", arm_regs[reg], arm_regs[reg]); + emit("uxth", buf); + } else { + emit_r0_from_reg(thisref); + snprintf(buf, sizeof(buf), "r0, #%d", x); + emit("adds ", buf); + emit("uxth", "r0, r0"); + emit_reg_from_r0(thisref); + } + } + + void sub(int x) { + assert(x>=0 && x < 256); + char buf[128]; + if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "%s, #%d", arm_regs[reg], x); + emit("subs ", buf); + snprintf(buf, sizeof(buf), "%s, %s", arm_regs[reg], arm_regs[reg]); + emit("uxth", buf); + } else { + emit_r0_from_reg(thisref); + snprintf(buf, sizeof(buf), "r0, #%d", x); + emit("subs ", buf); + emit("uxth", "r0, r0"); + emit_reg_from_r0(thisref); + } + } + operator Reg16Ref() const { + return thisref; + } + void operator+=(int x) { + add(x); + } + void operator-=(int x) { + sub(x); + } + Reg16 operator+=(WordInR0HighWordUnknown x) { + char buf[128]; + if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "%s, r0", arm_regs[reg]); + emit("add ", buf); + snprintf(buf, sizeof(buf), "%s, %s", arm_regs[reg], arm_regs[reg]); + emit("uxth", buf); + } else { + emit_r1_from_reg(thisref); + snprintf(buf, sizeof(buf), "r0, r1"); + emit("add ", buf); + emit("uxth", "r0, r0"); + emit_reg_from_r0(thisref); + } + return *this; + } + + WordInR0 operator+(SignExtendedByteInR0HighWordUnknown x) const { + char buf[128]; + if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "r0, %s", arm_regs[reg]); + emit("add ", buf); + emit("uxth", "r0, r0"); + } else { + emit_r1_from_reg(thisref); + snprintf(buf, sizeof(buf), "r0, r1"); + emit("add ", buf); + emit("uxth", "r0, r0"); + } + return WordInR0(); + } + + WordInR0HighWordUnknown operator+(int x) const { + assert(x == 1); + char buf[128]; + if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "r0, %s, #%d", arm_regs[reg], x); + emit("adds ", buf); + } else { + emit_r0_from_reg(thisref); + snprintf(buf, sizeof(buf), "r0, #%d", x); + emit("adds ", buf); + } + return WordInR0HighWordUnknown(); + } + + WordInR0HighWordUnknown operator-(int x) const { + assert(x == 1); + char buf[128]; + if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "r0, %s, #%d", arm_regs[reg], x); + emit("subs ", buf); + } else { + emit_r0_from_reg(thisref); + snprintf(buf, sizeof(buf), "r0, #%d", x); + emit("subs ", buf); + } + return WordInR0HighWordUnknown(); + } + + Reg16& operator=(const Reg16Ref& s) { + if (s.reg != reg) + { + char buf[32]; + snprintf(buf, sizeof(buf), "%s, %s", arm_regs[reg], arm_regs[s.reg]); + emit("mov ", buf); + } + return *this; + } + + Reg16& operator=(const RestingStateVariable& s) { + *this = (WordInR0)s; + return *this; + } + + Reg16& operator=(int val) { + set(val); + return *this; + } + + WordInR1 operator|(const WordInR1& v) const { + char buf[32]; + if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "r1, %s", arm_regs[reg]); + emit("orrs ", buf); + } else { + assert(false); // for now + } + return WordInR1(); + } + + void set(int x) { + char buf[128]; + if (is_arm_lo_reg(reg)) { + snprintf(buf, sizeof(buf), "%s, #%d", arm_regs[reg], x); + emit("movs ", buf); + } else { + snprintf(buf, sizeof(buf), "r0, #%d", x); + emit("movs ", buf); + snprintf(buf, sizeof(buf), "%s, r0", arm_regs[reg]); + emit("mov ", buf); + } + } + operator WordInR0() const { + return WordInR0(thisref); + } + operator WordInR1() const { + return WordInR1(thisref); + } + enum Reg16Name reg; + Reg16Ref thisref; + word value; + }; + +#define rs_var(name) static RestingStateVariable name; +#define rs_var_init(name) eZ80t::RestingStateVariable eZ80t::name(#name, sizeof(z80a_resting_state.name), rs_offsetof_##name) +#define rs_var_struct_init(s, name) eZ80t::RestingStateVariable eZ80t::s::name(#s "." #name, sizeof(z80a_resting_state.name), rs_offsetof_##s##_##name) + + static void assign_hi_byte_from_lo_byte_clear_r0(Reg8Ref ref) { + assert(!ref.lo); + Reg16Name reg = ref.reg; + assert(reg != TEMPORARY && reg != TEMPORARY_RESTRICTED); // can't imagine anyone assigning to high byte of temporary + emit_zero_extend_z80_lo_to_arm(reg, 1); + emit("orrs ", "r0, r1"); + emit_reg_from_r0(ref); + } + + static void assign_lo_byte_from_hi_byte_clear_r(Reg8Ref ref, const char*arm_reg_name) { + assert(ref.lo); + char buf[128]; + Reg16Name reg = ref.reg; + if (reg == TEMPORARY) { + snprintf(buf, sizeof(buf), "r_temp, %s", arm_reg_name); + emit("mov ", buf, "// fine to overwrite hi in r_temp"); + } else if (reg == TEMPORARY_RESTRICTED) { + snprintf(buf, sizeof(buf), "r_temp_restricted, %s", arm_reg_name); + emit("mov ", buf, "// fine to overwrite hi in r_temp_restricted"); + } else if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "%s, #8", arm_regs[reg]); + emit("lsls ", buf); + emit("lsrs ", buf); + snprintf(buf, sizeof(buf), "%s, %s", arm_regs[reg], arm_reg_name); + emit("orrs ", buf); + } else { + emit_r1_from_reg(reg); + emit("lsrs ", "r1, #8"); + emit("lsls ", "r1, #8"); + snprintf(buf, sizeof(buf), "r1, %s", arm_reg_name); + emit("orrs ", buf); + emit_reg_from_r1(ref); + } + } + + friend RegLo& operator|=(RegLo& r, int val) { + char buf[32]; + assert(val>=0 && val<= 255); + snprintf(buf, sizeof(buf), "r0, #%d", val); + emit("movs ", buf); + emit("orrs ", "r_af, r0"); + return r; + } + + friend RegLo& operator&=(RegLo& r, int val) { + char buf[32]; + if (val < 0) { + val = ~val; + assert(val >= 0 && val <= 255); + snprintf(buf, sizeof(buf), "r0, #%d", val); + emit("movs ", buf); + emit("bics ", "r_af, r0"); + return r; + } else + { + assert(val >= 0 && val <= 255); + snprintf(buf, sizeof(buf), "r0, #%d", val); + emit("movs ", buf); + emit("ands ", "r_af, r0"); + return r; + } + } + + friend LoByteAndGarbageInR0 operator+(RegLo& r, LoByteAndGarbageInR0 v) { + emit("add ", "r0, r_temp"); + return v; + } + + friend LoByteAndGarbageInR0 operator+(LoByteAndGarbageInR0 l, int val) { + char buf[32]; + assert(val>=0 && val<= 255); + snprintf(buf, sizeof(buf), "r0, #%d", val); + emit("adds ", buf); + return l; + } + + friend LoByteAndGarbageInR0 operator-(LoByteAndGarbageInR0 l, int val) { + char buf[32]; + assert(val>=0 && val<= 255); + snprintf(buf, sizeof(buf), "r0, #%d", val); + emit("subs ", buf); + return l; + } + + template struct RegHi { + RegHi() : reg(R), thisref(R, false) { } + RegHi(ZeroExtendedByteInR0 v) : reg(R), thisref(R, false) { + emit("lsls", "r0, #8"); + assign_hi_byte_from_lo_byte_clear_r0(); + } + + operator Reg8Ref() { + return thisref; + } + + void assign_hi_byte_from_lo_byte_clear_r0() { + eZ80t::assign_hi_byte_from_lo_byte_clear_r0(thisref); + } + + template RegHi& operator=(const RegHi& s) { + emit_r0_from_reg_hi(s.thisref); + emit("lsls ", "r0, #8"); + assign_hi_byte_from_lo_byte_clear_r0(); + return *this; + } + + template RegHi& operator=(const RegLo& s) { + emit_zero_extend_z80_lo_to_arm(s.reg, 0); + emit("lsls", "r0, #8"); + assign_hi_byte_from_lo_byte_clear_r0(); + return *this; + } + + ZeroExtendedByteInR0 operator&(int v) { + ZeroExtendedByteInR0 rc = *this; + assert(v>=0 && v<=255); + char buf[32]; + snprintf(buf, sizeof(buf), "r1, #0x%02x", v); + emit("movs ", buf); + emit("ands ", "r0, r1"); + return rc; + } + + operator ZeroExtendedByteInR0() const { + return ZeroExtendedByteInR0(thisref); + } + + operator LoByteAndGarbageInR0() const { + return LoByteAndGarbageInR0(thisref); + } + + WordInR1 operator<<(int v) { + assert( v == 8); + if (is_arm_lo_reg(reg)) + { + char buf[32]; + snprintf(buf, sizeof(buf), "r1, %s, #8", arm_regs[reg]); + emit("lsrs ", buf); + } else { + emit_r1_from_reg_hi(thisref); + emit("lsrs ", "r1, #8"); + } + emit("lsls ", "r1, #8"); + return WordInR1(); + } + + RegHi operator --() { + sub(1); + return *this; + } + + RegHi operator^=(int v) { + char buf[32]; + assert(v >=0 && v <= 255); + assert(R == AF); + snprintf(buf, sizeof(buf), "r0, #%d", v); + emit("movs ", buf); + emit("lsls ", "r0, #8"); + emit("eors ", "r_af, r0"); + return *this; + } + + void sub(int x) { + assert(x>=0 && x < 256); + char buf[128]; + sprintf(buf, "r1, #%d", x); + emit("movs ", buf); + emit("lsls ", "r1, #8"); + if (is_arm_lo_reg(reg)) + { + snprintf(buf, sizeof(buf), "%s, r1", arm_regs[reg]); + emit("subs ", buf); + snprintf(buf, sizeof(buf), "%s, %s", arm_regs[reg], arm_regs[reg]); + emit("uxth", buf); + } else { + emit_r0_from_reg(thisref); + emit("subs ", "r0, r1"); + emit("uxth", "r0, r0"); + emit_reg_from_r0(thisref); + } + } + + enum Reg16Name reg; + Reg8Ref thisref; + }; + + template struct RegLo + { + RegLo() : reg(R), thisref(R, true) + { + } + + RegLo(ZeroExtendedByteInR0 v) : reg(R), thisref(R, true) + { + assign_lo_byte_from_hi_byte_clear_r0(); + } + + void assign_lo_byte_from_hi_byte_clear_r0() { + eZ80t::assign_lo_byte_from_hi_byte_clear_r(thisref, "r0"); + } + + operator Reg8Ref() { + return thisref; + } + + operator ZeroExtendedByteInR0() const { + return ZeroExtendedByteInR0(thisref); + } + + operator LoByteAndGarbageInR0() const { + return LoByteAndGarbageInR0(thisref); + } + + template RegLo& operator=(const RegLo& s) { + emit_zero_extend_z80_lo_to_arm(s.reg, 0); + assign_lo_byte_from_hi_byte_clear_r0(); + return *this; + } + + template RegLo& operator=(RegHi s) { + emit_r0_from_reg_hi(s); + assign_lo_byte_from_hi_byte_clear_r0(); + return *this; + } + + RegLo& operator=(const WordInR0HighWordUnknown& s) + { + if (reg == TEMPORARY) { + WordInR0 __unused x = s; + emit("mov ", "r_temp, r0","// fine to overwrite hi in r_temp"); + } else if (reg == TEMPORARY_RESTRICTED) { + __unused WordInR0 x = s; + emit("mov ", "r_temp_restricted, r0","// fine to overwrite hi in r_temp"); + } else + { + emit("uxtb", "r0, r0"); + assign_lo_byte_from_hi_byte_clear_r0(); + } + return *this; + } + + enum Reg16Name reg; + + Reg8Ref thisref; + }; + + typedef Reg16 temp16; + typedef RegLo temp8; + typedef WordInR1 temp16_2; + // the scratch must be global +#define scratch16 + typedef Reg16 temp16_xy; + typedef RegLo temp8_xy; + typedef SignExtendedByteInR0HighWordUnknown address_delta; + + TState t; + static const char *arm_regs[REG_COUNT]; + + eZ80t(); + + typedef void (eZ80t::*CALLFUNC)(); +// typedef temp8 (eZ80t::*CALLFUNCI)(temp8); + + void generate_arm(); + +protected: + ZeroExtendedByteInR0 IoRead(WordInR0 port) const; + void IoWrite(WordInR1 port, LoByteAndGarbageInR0 v); + ZeroExtendedByteInR0 Read(WordInR0 addr) const; + ZeroExtendedByteInR0 ReadInc(Reg16Ref addr) const; + WordInR0 Read2(WordInR0 addr) const; // low then high + WordInR0 Read2Inc(Reg16Ref addr) const; // low then high + void Write(Reg16Ref addr, LoByteAndGarbageInR0 v); + void WriteXY(WordInR0 addr, Reg8Ref v); + void Write2(Reg16Ref addr, WordInR0 v); // low then high + void dump_instructions(const char *prefix, const CALLFUNC *instrs, int count, const char *description=NULL, int mult = 1); + + static const char *pending_call; + static void emit_pending_call() { + if (pending_call) { + const char *funcname = pending_call; + pending_call = nullptr; + emit_save_lr_if_not_done_already(); + emit("bl ", funcname); + } + } + static void emit(const char *a) { + emit_pending_call(); + printf("%s\n", a); + } + static void emit(const char *a, const char *b, const char *c = "") { + char buf[128]; + snprintf(buf, sizeof(buf), "\t%s %s\t\t%s", a, b, c); + emit(buf); + } + static void emit_zero_extend_z80_lo_to_arm(RegRef lo, int arm_reg) { + char buf[32]; + if (is_arm_lo_reg(lo.reg)) + { + snprintf(buf, sizeof(buf), "r%d, %s", arm_reg, arm_regs[lo.reg]); + emit("uxtb", buf); + } else { + if (arm_reg == 0) { + emit_r0_from_reg(lo); + } else if (arm_reg == 1) { + emit_r1_from_reg(lo); + } else if (arm_reg == R_TEMP_ARM) { + emit_r_temp_from_reg(lo); + } else { + assert(false); + } + snprintf(buf, sizeof(buf), "r%d, r%d", arm_reg, arm_reg); + emit("uxtb", buf); + } + } + + static void emit_r0_from_reg(const RegRef &x) { + char buf[32]; + snprintf(buf, sizeof(buf), "r0, %s", arm_regs[x.reg]); + emit("mov ", buf); + } + + static void emit_r_temp_from_reg(const RegRef &x) { + char buf[32]; + snprintf(buf, sizeof(buf), "r_temp, %s", arm_regs[x.reg]); + emit("mov ", buf); + } + + static void emit_r_temp_from_reg8(const Reg8Ref &x) { + char buf[32]; + if (x.lo) { + emit_zero_extend_z80_lo_to_arm(x, R_TEMP_ARM); + } else { + if (is_arm_lo_reg(x.reg)) + { + snprintf(buf, sizeof(buf), "r_temp, %s, #8", arm_regs[x.reg]); + emit("lsrs ", buf); + } else { + emit_r_temp_from_reg((RegRef)x); + emit("lsrs ", "r_temp, #8"); + } + } + } + + static void emit_reg8_from_r_temp(const Reg8Ref &x) { + if (x.lo) { + assign_lo_byte_from_hi_byte_clear_r(x, "r_temp"); + } else { + emit("lsls ", "r0, r_temp, #8"); + assign_hi_byte_from_lo_byte_clear_r0(x); + } + } + + static void emit_r0_from_reg_lo(const Reg8Ref &x) { + assert(x.lo); + emit_zero_extend_z80_lo_to_arm(x, 0); + } + + static void emit_r1_from_reg_lo(const Reg8Ref &x) { + assert(x.lo); + emit_zero_extend_z80_lo_to_arm(x, 1); + } + + static void emit_r0_from_reg8(const Reg8Ref &x) { + if (x.lo) { + emit_r0_from_reg_lo(x); + } else { + emit_r0_from_reg_hi(x); + } + } + static void emit_reg8_from_r0(const Reg8Ref &x) { + if (x.lo) { + assign_lo_byte_from_hi_byte_clear_r(x, "r0"); + } else { + emit("lsls ", "r0, #8"); + assign_hi_byte_from_lo_byte_clear_r0(x); + } + } + + static void emit_r0_from_reg_hi(const Reg8Ref &x) { + char buf[32]; + assert(!x.lo); + if (is_arm_lo_reg(x.reg)) + { + snprintf(buf, sizeof(buf), "r0, %s, #8", arm_regs[x.reg]); + emit("lsrs ", buf); + } else { + emit_r0_from_reg(x); + emit("lsrs ", "r0, #8"); + } + } + static void emit_r1_from_reg_hi(const Reg8Ref &x) { + char buf[32]; + assert(!x.lo); + if (is_arm_lo_reg(x.reg)) + { + snprintf(buf, sizeof(buf), "r1, %s, #8", arm_regs[x.reg]); + emit("lsrs ", buf); + } else { + emit_r1_from_reg(x); + emit("lsrs ", "r1, #8"); + } + } + static void emit_reg_from_r0(const RegRef &x) { + char buf[32]; + snprintf(buf, sizeof(buf), "%s, r0", arm_regs[x.reg]); + emit("mov ", buf); + } + static void emit_reg_from_r1(const RegRef &x) { + char buf[32]; + snprintf(buf, sizeof(buf), "%s, r1", arm_regs[x.reg]); + emit("mov ", buf); + } + static void emit_r1_from_reg(const RegRef &x) { + char buf[32]; + snprintf(buf, sizeof(buf), "r1, %s", arm_regs[x.reg]); + emit("mov ", buf); + } + + template void if_nonzero(ZeroExtendedByteInR0 v, A&& a, B&& b) { + // can pass zero extended value + if_nonzero(WordInR0(), a, b); + } + + template void if_nonzero(WordInR0 v, A&& a, B&& b) { + emit("cmp ", "r0, #0"); + emit("beq ", "1f"); + int lr = lr_saved; + a(); + emit_function_return(); + lr_saved = lr; + emit("1:"); + b(); + } + + template void if_nonzero(WordInR0 v, A&& a) { + emit("cmp ", "r0, #0"); + emit("beq ", "1f"); + a(); + emit("1:"); + } + + template void if_zero(ZeroExtendedByteInR0 v, A&& a) { + if_zero((WordInR0)v, a); + } + + template void if_zero(WordInR0 v, A&& a) { + if_equal(v, 0, a); + } + + template void if_equal(ZeroExtendedByteInR0 v, int cmp, A&& a) { + if_equal((WordInR0)v, cmp, a); + } + + template void if_equal(WordInR0 v, int cmp, A&& a) { + char buf[128]; + assert(cmp >=0 && cmp <=255); + snprintf(buf, sizeof(buf), "r0, #%d", cmp); + emit("cmp ", buf); + emit("bne ", "1f"); + a(); + emit("1:"); + } + + template void if_flag_set(byte flag, A&& a, B&& b) { + assert(__builtin_popcount(flag) == 1); + char buf[32]; + snprintf(buf, sizeof(buf), "r0, r_af, #%d", 1 + __builtin_ctz(flag)); + emit("lsrs ", buf); + emit("bcc ", "2f"); + int lr = lr_saved; + a(); + emit_function_return(); + lr_saved = lr; + emit("2:"); + b(); + } + + template void if_flag_set(byte flag, A&& a) { + assert(__builtin_popcount(flag) == 1); + char buf[32]; + snprintf(buf, sizeof(buf), "r0, r_af, #%d", 1 + __builtin_ctz(flag)); + emit("lsrs ", buf); + emit("bcc ", "2f"); + a(); + emit("2:"); + } + + + template void if_flag_clear(byte flag, A&& a, B&& b) { + assert(__builtin_popcount(flag) == 1); + char buf[32]; + snprintf(buf, sizeof(buf), "r0, r_af, #%d", 1 + __builtin_ctz(flag)); + emit("lsrs ", buf); + emit("bcs ", "3f"); + int lr = lr_saved; + a(); + emit_function_return(); + lr_saved = lr; + emit("3:"); + b(); + } + + template void if_flag_clear(byte flag, A&& a) { + assert(__builtin_popcount(flag) == 1); + char buf[32]; + snprintf(buf, sizeof(buf), "r0, r_af, #%d", 1 + __builtin_ctz(flag)); + emit("lsrs ", buf); + emit("bcs ", "3f"); + a(); + emit("3:"); + } + +#define set_a35_flags_preserve_set(preserve, set) _set_a35_flags_preserve_set(#preserve, #set) + + void _set_a35_flags_preserve_set(const char *preserve, const char *set) { + char buf[128]; + snprintf(buf, sizeof(buf), "r0, %s", preserve); + emit("preserve_only_flags", buf); + snprintf(buf, sizeof(buf), "r_af, #%s", set); + emit("adds ", buf); + + ZeroExtendedByteInR0 __unused r0 = a; + emit("movs ", "r1, #(F3|F5)"); + emit("ands ", "r0, r1"); + emit("orrs ", "r_af, r0"); + } + +#define set_logic_flags_preserve(reg, preserve) _set_logic_flags_preserve_reset(reg, #preserve, NULL) +#define set_logic_flags_preserve_reset(reg, preserve, reset) _set_logic_flags_preserve_reset(reg, #preserve, #reset) + + void _set_logic_flags_preserve_reset(ZeroExtendedByteInR0 value, const char *preserve_flags, const char* reset_flags) { + char buf[128]; + snprintf(buf, sizeof(buf), "r1, %s", preserve_flags); + emit("preserve_only_flags", buf); + emit("ldr ", "r1, =_log_f"); + emit("ldrb", "r0, [r1, r0]"); + emit("orrs ", "r_af, r0"); + if (reset_flags) { + snprintf(buf, sizeof(buf), "r1, #%s", reset_flags); + emit("movs ", buf); + emit("bics ", "r_af, r1"); + } + } + + static bool r_temp_used; + static bool r_temp_restricted_used; + + void reset_function_state() { + r_temp_used = r_temp_restricted_used = false; + lr_saved = false; + } + void emit_function_return() { + if (pending_call && !lr_saved) { + // tail call (note we don't tail call when lr saved, since we can't pop lr + + // don't ever use r2 for arg (I hope) + char buf[32]; + // almost certainly too far for plain "b label" + snprintf(buf, sizeof(buf), "r2, =%s", pending_call); + pending_call = NULL; // reset so we don't recurse + emit("ldr ", buf); + emit("bx ", "r2"); + emit(""); + return; + } + emit_pending_call(); + if (lr_saved) { + emit("pop ", "{pc}"); + } else + { + emit("bx ", "lr"); + } + emit(""); + } + static void emit_save_lr_if_not_done_already() { + if (!lr_saved) + { + emit("push", "{lr}"); + lr_saved = true; + } + } + static bool lr_saved; + static void emit_call_func(const char *funcname) { + pending_call = funcname; + } + static void emit_call_func_with_reg(RegRef &x, const char *funcname) { + emit_r0_from_reg(x); + emit_call_func(funcname); + emit_reg_from_r0(x); + } + + void inc8(Reg8Ref x) + { + emit_r0_from_reg8(x); + emit_call_func("inc8"); + emit_reg8_from_r0(x); + } + void dec8(Reg8Ref x) + { + emit_r0_from_reg8(x); + emit_call_func("dec8"); + emit_reg8_from_r0(x); + } + void rlc8(Reg8Ref x) { + emit_r0_from_reg8(x); + emit_call_func("rlc8"); + emit_reg8_from_r0(x); + } + void rrc8(Reg8Ref x) { + emit_r0_from_reg8(x); + emit_call_func("rrc8"); + emit_reg8_from_r0(x); + } + void rl8(Reg8Ref x) { + emit_r0_from_reg8(x); + emit_call_func("rl8"); + emit_reg8_from_r0(x); + } + void rr8(Reg8Ref x) { + emit_r0_from_reg8(x); + emit_call_func("rr8"); + emit_reg8_from_r0(x); + } + void sla8(Reg8Ref x) { + emit_r0_from_reg8(x); + emit_call_func("sla8"); + emit_reg8_from_r0(x); + } + void sli8(Reg8Ref x) { + emit_r0_from_reg8(x); + emit_call_func("sli8"); + emit_reg8_from_r0(x); + } + void sra8(Reg8Ref x) { + emit_r0_from_reg8(x); + emit_call_func("sra8"); + emit_reg8_from_r0(x); + } + void srl8(Reg8Ref x) { + emit_r0_from_reg8(x); + emit_call_func("srl8"); + emit_reg8_from_r0(x); + } +#ifdef USE_LARGER_FASTER_CB + void bit(Reg8Ref x, byte bit) + { + // this function is large, so call the corrsesponding ls function + emit_r_temp_from_reg8(x); + char buf[32]; + assert(bit >= 0 && bit <= 7); + snprintf(buf, sizeof(buf), "opli_r35_%02x", 8*(8 + bit)); // note 8-15 are bit functions + emit_call_func(buf); + emit_reg8_from_r_temp(x); + } + void bitmem(ZeroExtendedByteInR0 src, byte bit) + { + // this function is large, so call the corrsespdnding ls function + emit("mov ", "r_temp, r0"); + char buf[32]; + assert(bit >= 0 && bit <= 7); + snprintf(buf, sizeof(buf), "opli_m35_%02x", 8*(8 + bit)); // note 8-15 are bit functions + emit_call_func(buf); + } + // These are also slightly faster than the 8x32 versions since they can work on values in place (e.g. high reg) + void res(Reg8Ref x, byte bit) const + { + if (!x.lo) + { + bit += 8; + } + char buf[32]; + if (bit < 8) + { + snprintf(buf, sizeof(buf), "r0, #0x%02x", 1 << bit); + emit("movs ", buf); + } else { + emit("movs ", "r0, #1"); + snprintf(buf, sizeof(buf), "r0, #%d", bit); + emit("lsls ", buf); + } + if (is_arm_lo_reg(x.reg)) { + snprintf(buf, sizeof(buf), "%s, r0", arm_regs[x.reg]); + emit("bics ", buf); + } else + { + emit_r1_from_reg(x); + emit("bics ", "r1, r0"); + emit_reg_from_r1(x); + } + } + void set(Reg8Ref x, byte bit) const + { + if (!x.lo) + { + bit += 8; + } + char buf[32]; + if (bit < 8) + { + snprintf(buf, sizeof(buf), "r0, #0x%02x", 1 << bit); + emit("movs ", buf); + } else { + emit("movs ", "r0, #1"); + snprintf(buf, sizeof(buf), "r0, #%d", bit); + emit("lsls ", buf); + } + if (is_arm_lo_reg(x.reg)) { + snprintf(buf, sizeof(buf), "%s, r0", arm_regs[x.reg]); + emit("orrs ", buf); + } else + { + emit_r1_from_reg(x); + emit("orrs ", "r1, r0"); + emit_reg_from_r1(x); + } + } +#endif + void add8(ZeroExtendedByteInR0 src) + { + emit_call_func("add8"); + } + void adc8(ZeroExtendedByteInR0 src) + { + emit_call_func("adc8"); + } + void sub8(ZeroExtendedByteInR0 src) + { + emit_call_func("sub8"); + } + void sbc8(ZeroExtendedByteInR0 src) + { + emit_call_func("sbc8"); + } + void and8(ZeroExtendedByteInR0 src) + { + emit_call_func("ands8"); + } + void or8(ZeroExtendedByteInR0 src) + { + emit_call_func("or8"); + } + void xor8(ZeroExtendedByteInR0 src) + { + emit_call_func("xor8"); + } + void cp8(ZeroExtendedByteInR0 src) + { + emit_call_func("cp8"); + } + + void OpeA2A3AAABFlags(const RegLo& val, LoByteAndGarbageInR0 tmp) + { + emit("// a2a3aaabflags r_temp, r0", ""); + + // f = 0; + emit("lsrs ", "r_af, #8"); + emit("lsls ", "r_af, #8"); + +// f = log_f[b] & ~PV; + emit("mov ", "r2, r_bc"); + emit("lsrs ", "r2, #8"); + emit("ldr ", "r1, =_log_f"); + emit("ldrb", "r2, [r1, r2]"); + emit("movs ", "r1, #PV"); + emit("bics ", "r2, r1"); + emit("orrs ", "r_af, r2"); + + // if(tmp < val) f |= (HF|CF); + emit("uxtb", "r0, r0"); + emit("cmp ", "r0, r_temp"); + emit("bge ", "1f"); + emit("adds ", "r_af, #HF|CF"); + emit("1:"); + + // if(val & 0x80) f |= NF; + emit("lsrs ", "r1, r_temp, #8"); + emit("bcc ", "1f"); + emit("adds ", "r_af, #NF"); + emit("1:"); + +// if(log_f[(tmp & 0x07) ^ b] & PV) f |= PV; + emit("lsls ", "r1, r0, #29"); + emit("lsrs ", "r1, #21"); + emit("mov ", "r2, r_bc"); + emit("eors ", "r1, r2"); + emit("lsrs ", "r1, #8"); + emit("ldr ", "r0, =_log_f"); + emit("ldrb", "r0, [r0, r1]"); + emit("movs ", "r1, #PV"); + emit("ands ", "r0, r1"); + emit("orrs ", "r_af, r0"); + } + + // --- logic (cb) ops -------------------------------------- + + // Note these are the versions that work on r_temp + + void Opl_rlc() { + rlc8(temp_lo); + } + + void Opl_rrc() { + rrc8(temp_lo); + } + + void Opl_rl() { + rl8(temp_lo); + } + + void Opl_rr() { + rr8(temp_lo); + } + + void Opl_sla() { + sla8(temp_lo); + } + + void Opl_sra() { + sra8(temp_lo); + } + + void Opl_sli() { + sli8(temp_lo); + } + + void Opl_srl() { + srl8(temp_lo); + } + + void Opl_bit0() { + bit_regular_35(0); + } + + void Opl_bit1() { + bit_regular_35(1); + } + + void Opl_bit2() { + bit_regular_35(2); + } + + void Opl_bit3() { + bit_regular_35(3); + } + + void Opl_bit4() { + bit_regular_35(4); + } + + void Opl_bit5() { + bit_regular_35(5); + } + + void Opl_bit6() { + bit_regular_35(6); + } + + void Opl_bit7() { + bit_regular_35(7); + } + + void Opl_bit0m() { + bit_mem_h_35(0); + } + + void Opl_bit1m() { + bit_mem_h_35(1); + } + + void Opl_bit2m() { + bit_mem_h_35(2); + } + + void Opl_bit3m() { + bit_mem_h_35(3); + } + + void Opl_bit4m() { + bit_mem_h_35(4); + } + + void Opl_bit5m() { + bit_mem_h_35(5); + } + + void Opl_bit6m() { + bit_mem_h_35(6); + } + + void Opl_bit7m() { + bit_mem_h_35(7); + } + + void Opl_res0() { + res_rtemp(0); + } + + void Opl_res1() { + res_rtemp(1); + } + + void Opl_res2() { + res_rtemp(2); + } + + void Opl_res3() { + res_rtemp(3); + } + + void Opl_res4() { + res_rtemp(4); + } + + void Opl_res5() { + res_rtemp(5); + } + + void Opl_res6() { + res_rtemp(6); + } + + void Opl_res7() { + res_rtemp(7); + } + + void Opl_set0() { + set_rtemp(0); + } + + void Opl_set1() { + set_rtemp(1); + } + + void Opl_set2() { + set_rtemp(2); + } + + void Opl_set3() { + set_rtemp(3); + } + + void Opl_set4() { + set_rtemp(4); + } + + void Opl_set5() { + set_rtemp(5); + } + + void Opl_set6() { + set_rtemp(6); + } + + void Opl_set7() { + set_rtemp(7); + } + + void res_rtemp(byte bit) const + { + char buf[32]; + snprintf(buf, sizeof(buf), "r0, #0x%02x", 1< void add16(Reg16 regM, Reg16Ref regN) { + memptr = regM+1; + emit_r0_from_reg(regM); + emit_r1_from_reg(regN); + emit_call_func("add16"); + emit_reg_from_r0(regM); + t += 7; + } + + void adc16(Reg16Ref reg) { + memptr = hl+1; + emit_r0_from_reg(reg); + emit_call_func("adc16"); + t += 7; + } + + // hl, reg + void sbc16(Reg16Ref reg) { + memptr = hl+1; + emit_r0_from_reg(reg); + emit_call_func("sbc16"); + t += 7; + } + + void push(Reg16Ref v) const { + emit_r0_from_reg(v); + emit_call_func("_push"); + } + +#include "../z80/z80_op_noprefix.h" + +#ifdef USE_LARGER_FASTER_CB + #include "../z80/z80_op_cb.h" +#endif + #include "../z80/z80_op_dd.h" + #include "../z80/z80_op_fd.h" + #include "../z80/z80_op_ed.h" + + // hand coded instructions + void Op3F() { // ccf + emit("movs ", "r1, #CF"); + emit("lsrs ", "r0, r_af, #1"); + emit("bcc ", "1f"); + emit("movs ", "r1, #HF"); + emit("1:"); + emit("preserve_only_flags", "r0, PV|ZF|SF"); + emit("orrs ", "r_af, r1"); + emit("lsrs ", "r0, r_af, #8"); + emit("movs ", "r1, #(F3|F5)"); + emit("ands ", "r0, r1"); + emit("orrs ", "r_af, r0"); + } + + void OpED() + { + emit_save_lr_if_not_done_already(); + emit("ldr ","r_temp, =ope_table"); + emit("step_op_table_in_r_temp_maybe_neg",""); + } + + void OpDD() + { + emit("movs ", "r0, #0xdd"); + emit_call_func("opDDFD"); + } + + void OpFD() + { + emit("movs ", "r0, #0xfd"); + emit_call_func("opDDFD"); + } + + void OpCB() + { +#ifndef USE_LARGER_FASTER_CB + emit("", "// note this is a slow implementation which is 4K smaller... define USE_LARGER_FASTER_CB for speed"); + emit("fetch", ""); + // r1 is inner instruction + emit("lsrs ", "r2, r0, #3"); + emit("lsls ", "r2, #1"); + + emit("movs ", "r1, #7"); + emit("ands ", "r1, r0", "// r1 is outer instruction"); + + emit("cmp ", "r1, #6", "// check for memory operand"); + emit("bne ", "2f"); + emit("ldr ", "r_temp, =opli_m35_table"); + emit("b ", "3f"); + emit("2:"); + emit("ldr ", "r_temp, =opli_r35_table"); + emit("3:"); + emit("ldrh", "r2, [r_temp, r2]"); + emit("sxth", "r2, r2"); + emit("add ", "r2, r_temp"); + + emit("ldr ", "r_temp, =opcbX_table"); + emit("lsrs ", "r0, #7"); + emit("bcc ","1f", "// arg no single test for this flag combo"); + emit("bne ","1f"); + emit("adds ", "r_temp, #(opcbbitX_table - opcbX_table)"); + emit("1:"); + emit("lsls ", "r0, r1, #1"); + emit("ldrh", "r1, [r_temp, r0]"); + emit("sxth", "r1, r1"); + emit("add ", "r1, r_temp"); + + emit("bx ", "r1"); +#else + emit("ldr ", "r_temp, =opl_table"); + emit("fetch", ""); + + emit("lsls ", "r0, #1"); + emit("ldrh", "r0, [r_temp, r0]"); + emit("sxth", "r0, r0"); + emit("add ", "r0, r_temp"); + emit("bx ", "r0"); +#endif + } + + void OpD9() { // exx + // todo this isn't strictly necessary to hard code, but is slightly better than generated code + emit("ldr ", "r2, =z80a_resting_state"); + emit("mov ", "r0, r_bc"); + alt.bc.emit_read(1, 2); + alt.bc.emit_write(0, 2); + emit("mov ", "r_bc, r1"); + emit("mov ", "r0, r_de"); + alt.de.emit_read(1, 2); + alt.de.emit_write(0, 2); + emit("mov ", "r_de, r1"); + emit("mov ", "r0, r_hl"); + alt.hl.emit_read(1, 2); + alt.hl.emit_write(0, 2); + emit("mov ", "r_hl, r1"); + } + + void Op07() { // rlca + emit("movs ", "r0, #(SF|ZF|PV)"); + emit("ands ", "r0, r_af", "// r0 = f & (SF|ZF|PV)"); + emit("movs ", "r1, #0"); + emit("lsrs ", "r_af, #8"); + emit("lsls ", "r_af, #25"); + emit("bcc ", "1f"); + emit("movs ", "r1, #CF"); + emit("orrs ", "r0, r1", "// r0 = (f & (SF|ZF|PV)) | (a7 ? CF: 0)"); + emit("1:"); + emit("lsrs ", "r_af, #24"); + emit("orrs ", "r_af, r1"); + emit("movs ", "r1, #(F3|F5)", "// r_af = (a << 1) | (a7 ? 1 : 0)"); + emit("ands ", "r1, r_af"); + emit("lsls ", "r_af, #8", "// r_af = a' : 0"); + emit("orrs ", "r_af, r0", "// r_af = a' | f'"); + emit("orrs ", "r_af, r1", "// r_af |= 35 from a'"); + } + + void Op0F() { // rrca + emit("movs ", "r0, #(SF|ZF|PV)"); + emit("ands ", "r0, r_af", "// r0 = f & (SF|ZF|PV)"); + emit("lsrs ", "r_af, r_af, #9"); + emit("bcc ", "1f"); + emit("adds ", "r0, #CF", "// r0 = (f & (SF|ZF|PV)) | (a0 ? CF: 0)"); + emit("adds ", "r_af, #128", "// r_af = (a0 ? 0x80 : 0) | (a >> 1)"); + emit("1:"); + emit("movs ", "r1, #(F3|F5)"); + emit("ands ", "r1, r_af"); + emit("lsls ", "r_af, #8", "// r_af = a' : 0"); + emit("orrs ", "r_af, r0", "// r_af = a' | f'"); + emit("orrs ", "r_af, r1", "// r_af |= 35 from a'"); + } + + void Op17() { // rla + emit("movs ", "r0, #(SF|ZF|PV)"); + emit("ands ", "r0, r_af", "// r0 = f & (SF|ZF|PV)"); + emit("movs ", "r1, #CF"); + emit("ands ", "r1, r_af"); + emit("lsrs ", "r_af, #8"); + emit("lsls ", "r_af, #25"); + emit("bcc ", "1f"); + emit("adds ", "r0, #CF", "// r0 = (f & (SF|ZF|PV)) | (a7 ? CF: 0)"); + emit("1:"); + emit("lsrs ", "r_af, #24"); + emit("orrs ", "r_af, r1"); + emit("movs ", "r1, #(F3|F5)", "// r_af = (a << 1) | (c ? 1 : 0)"); + emit("ands ", "r1, r_af"); + emit("lsls ", "r_af, #8", "// r_af = a' : 0"); + emit("orrs ", "r_af, r0", "// r_af = a' | f'"); + emit("orrs ", "r_af, r1", "// r_af |= 35 from a'"); + } + + void Op1F() { // rra + emit("movs ", "r0, #(SF|ZF|PV)"); + emit("ands ", "r0, r_af", "// r0 = f & (SF|ZF|PV)"); + emit("lsls ", "r2, r_af, #15", "// r2 = (garbage : oldCF) << 15"); + emit("lsrs ", "r1, r_af, #9"); + emit("bcc ", "1f"); + emit("adds ", "r0, #CF", "// r0 = (f & (SF|ZF|PV)) | (a0 ? CF: 0)"); + emit("1:"); + emit("lsls ", "r_af, r1, #8"); + emit("orrs ", "r_af, r2"); + emit("orrs ", "r_af, r0"); + emit("movs ", "r0, #(F3|F5)"); + emit("uxth", "r_af, r_af"); + emit("ands ", "r0, r1"); // note we are anding with only 7 bits of A, but they include those we care about + emit("orrs ", "r_af, r0"); + } + + void Op27() { // daa +// int delta = 0; +// int newflags = f&(CF|NF); + + emit("movs ", "r0, #0", "// delta"); + emit("movs ", "r_temp, #(CF|NF)"); + emit("ands ", "r_temp, r_af", "// new CHN flags - CH may be set later"); + +// if ((f & HF) || (a & 0xf) > 9) { +// delta = 0x06; + + emit("// lo nibble", ""); + emit("lsls ", "r1, r_af, #20"); + emit("lsrs ", "r1, #28"); + emit("lsrs ", "r2, r_af, #HF_INDEX+1"); + emit("bcs ", "1f"); + emit("cmp ", "r1, #10"); + emit("blt ", "3f"); + emit("1:"); + emit("adds ", "r0, #0x06"); + +// if ((f & HF) || (a & 0xf) > 9) { +// delta = 0x06; +// if (f&NF) { +// if (0x10 & ((a&0xf)-delta)) newflags |= HF; +// } else { +// if (0x10 & ((a&0xf)+delta)) newflags |= HF; +// } +// } + + emit("cmp ", "r_temp, #NF"); + emit("blt ", "1f"); + emit("subs ", "r1, r0"); + emit("b ", "2f"); + emit("1:"); + emit("adds ", "r1, r0"); + emit("2:"); + emit("lsls ", "r1, #28"); + emit("bcc ", "3f"); + emit("movs ", "r2, #HF"); + emit("orrs ", "r_temp, r2"); + emit("3:"); + +// if (a > 0x99) { +// newflags |= CF; +// delta |= 0x60; +// } else if (f&CF) { +// delta |= 0x60; +// } + + emit("// hi nibble", ""); + emit("movs ", "r2, #CF"); + emit("lsrs ", "r1, r_af, #8"); + emit("cmp ", "r1, #0x9a"); + emit("blt ", "1f"); + emit("orrs ", "r_temp, r2"); + emit("b ", "2f"); + emit("1:"); + emit("ands ", "r2, r_af"); + emit("beq ", "3f"); + emit("2:"); + emit("adds ", "r0, #0x60"); + emit("3:"); + +// if( f & NF) { +// a = (a-delta)&0xff; +// } else { +// a = (a+delta)&0xff; +// } +// f = log_f[a] | newflags; + + emit("lsrs ", "r2, r_af, #NF_INDEX+1"); + emit("bcc ", "1f"); + emit("subs ", "r1, r0"); + emit("b ", "2f"); + emit("1:"); + emit("adds ", "r1, r0"); + emit("2:"); + emit("uxtb", "r_af, r1"); + + emit("ldr ", "r2, =_log_f"); + emit("ldrb", "r0, [r2, r_af]"); + + emit("lsls ", "r_af, #8"); + emit("orrs ", "r_af, r0"); + emit("orrs ", "r_af, r_temp"); + } + + void Ope57() { // ld a,i + i.emit_read_to_r0(); + a = ZeroExtendedByteInR0(); + set_logic_flags_preserve_reset(a, CF, PV); + t++; +// if (iff1 && (t+10 < frame_tacts)) f |= PV; + iff1.emit_read_to_r0(); + emit("cmp ", "r0, #0"); + emit("beq ", "1f"); + emit("#ifndef USE_Z80_ARM_OFFSET_T"); + emit("ldr ", "r0, frame_tacts"); + emit("#else"); + emit("movs ", "r0, #0"); + emit("#endif"); + emit("subs ", "r0, #10"); + emit("cmp ", "r_t, r0"); + emit("bge ", "1f"); + emit("adds ", "r_af, #PV"); + emit("1:"); + } + + void Ope5F() { // ld a,r + emit("#ifdef NO_UPDATE_RLOW_IN_FETCH"); + // save on counting r_low + emit("lsrs ", "r0, r_t, #2"); + emit("#else"); + r_low.emit_read_to_r0(); + emit("#endif"); + emit("movs ", "r1, #0x7f"); + emit("ands ", "r0, r1"); + r_hi.emit_read_to_r1(); + emit("orrs ", "r0, r1"); + emit_reg8_from_r0(a); + +// f = (log_f[a] | (f & CF)) & ~PV; + set_logic_flags_preserve_reset(a, CF, PV); + t++; +// if (iff2 && ((t+10 < frame_tacts) || eipos+8==t)) f |= PV; + iff1.emit_read_to_r0(); + emit("cmp ", "r0, #0"); + emit("beq ", "1f"); + emit("#ifndef USE_Z80_ARM_OFFSET_T"); + emit("ldr ", "r0, frame_tacts"); + emit("#else"); + emit("movs ", "r0, #0"); + emit("#endif"); + emit("subs ", "r0, #10"); + emit("cmp ", "r_t, r0"); + emit("blt ", "2f"); + eipos.emit_read_to_r0(); + emit("adds ", "r0, #8"); + // don't need offset_t check for eipos, since it is also offset + emit("cmp ", "r0, r_t"); + emit("bne ", "1f"); + emit("2:"); + emit("adds ", "r_af, #PV"); + emit("1:"); + } + + void Ope6F() { // rld + temp8 tmp = Read(hl); + memptr = hl+1; +// Write(hl, (a & 0x0F) | (tmp << 4)); + emit_r0_from_reg8(a); + emit("movs ", "r2, #0xf"); + emit("ands ", "r0, r2"); + emit_r1_from_reg_lo(tmp); + emit("lsls ", "r1, #4"); + emit("orrs ", "r0, r1"); + Write(hl, LoByteAndGarbageInR0()); +// a = (a & 0xF0) | (tmp >> 4); + emit_r0_from_reg8(a); + emit("movs ", "r2, #0xf0"); + emit("ands ", "r0, r2"); + emit_r1_from_reg_lo(tmp); + emit("lsrs ", "r1, #4"); + emit("orrs ", "r0, r1"); + a = ZeroExtendedByteInR0(); + set_logic_flags_preserve(a, CF); + t += 10; + } + + void Ope67() { // rrd + temp8 tmp = Read(hl); + memptr = hl+1; +// Write(hl, (a << 4) | (tmp >> 4)); + emit_r0_from_reg8(a); + emit("lsls ", "r0, #4"); + emit_r1_from_reg_lo(tmp); + emit("lsrs ", "r1, #4"); + emit("orrs ", "r0, r1"); + Write(hl, LoByteAndGarbageInR0()); +// a = (a & 0xF0) | (tmp & 0x0F); + emit_r0_from_reg8(a); + emit("movs ", "r2, #0xf0"); + emit("ands ", "r0, r2"); + emit_r1_from_reg_lo(tmp); + emit("bics ", "r1, r2"); + emit("orrs ", "r0, r1"); + a = ZeroExtendedByteInR0(); + set_logic_flags_preserve(a, CF); + t += 10; + } + + void Op76() { // halt + halted = 1; +// unsigned int st = (frame_tacts - t - 1) / 4 + 1; +// t += 4 * st; + emit("#ifndef USE_Z80_ARM_OFFSET_T"); + emit("ldr ", "r0, frame_tacts"); + emit("#else"); + emit("movs ", "r0, #0"); + emit("#endif"); + emit("subs ", "r0, r_t"); + emit("movs ", "r1, #3"); + emit("adds ", "r0, r1"); + emit("bics ", "r0, r1"); + emit("add ", "r_t, r0"); +#ifndef NO_USE_REPLAY + if(handler.io) // replay is active + { + r_low += fetches; + fetches = 0; + } + else + r_low += st; +#else + emit("#ifndef NO_UPDATE_RLOW_IN_FETCH"); + r_low.emit_read_to_r1(); + emit("lsrs ", "r0, #2"); + emit("add ", "r0, r1"); + r_low.emit_write_from_r0(); + emit("#endif"); +#endif + } + + void Ope44() { // neg + // there are multiple neg op codes, so call (well bx) the function + emit_call_func("neg8"); + } + +#define HELPER_START -1 + #define HELPER_END -2 + void emit_helper_functions(const char *prefix, int num) { + char buf[128]; + reset_function_state(); + if (!strcmp(prefix, "ope")) { + if (num == 0xb0) { + emit("ldX_common:"); + t += 8; + temp8 tempbyte = Read(hl); + Write(de, tempbyte); +// tempbyte += a; tempbyte = (tempbyte & F3) + ((tempbyte << 4) & F5); +// f = (f & ~(NF|HF|PV|F3|F5)) + tempbyte; + emit("preserve_only_flags", "r1, (CF|SF|ZF)"); + emit("lsrs ", "r0, r_af, #8"); + emit("add ", "r_temp, r0"); + emit_call_func("set_af35_special_r_temp"); + + --bc; + // if (bc) { + emit("beq ", "6f"); + // f |= PV; + emit("adds ", "r_af, #PV"); + // } + emit("6:"); + emit_function_return(); + + reset_function_state(); + emit("cpX_common:"); + emit_save_lr_if_not_done_already(); // save this now so our stack doesn't get tangled + /** + t += 8; + byte cf = f & CF; + byte tempbyte = Read(hl++); + f = cpf8b[a*0x100 + tempbyte] + cf; + + NOTE + byte tempbyte = (i >> 8) - (i & 0xFF) - ((_sbcf[i] & HF) >> 4); + _cpf8b[i] = (_sbcf[i] & ~(F3|F5|PV|CF)) + (tempbyte & F3) + ((tempbyte << 4) & F5); + + if (--bc & 0xFFFF) f |= PV; //??? + memptr++; + */ + t += 8; + emit("push", "{r_af}"); + Read(hl); + emit("mov ", "r_temp, r0"); + emit_call_func("cp8"); + emit("lsrs ", "r0, r_af, #8"); + emit("subs ", "r0, r_temp"); + emit("lsrs ", "r1, r_af, #HF_INDEX+1"); + emit("bcc 6f"); + emit("subs ", "r0, #1"); + emit("6:"); + emit("mov ", "r_temp, r0"); + emit("preserve_only_flags", "r1, (SF|HF|NF|ZF)"); + emit_call_func("set_af35_special_r_temp"); + emit("pop ", "{r0}"); + emit("movs ", "r1, #CF"); + emit("ands ", "r0, r1"); + emit("orrs ", "r_af, r0"); + + --bc; + // if (bc) { + emit("beq ", "6f"); + // f |= PV; + emit("adds ", "r_af, #PV"); + // } + emit("6:"); + memptr++; + emit_function_return(); + } + } else if (!strcmp(prefix, "opli_r35_")) { + if (num == HELPER_START) { + // note that mem_l is a marker + Reg8Ref arg[] = { b, c, d, e, h, l, mem_l, a}; + +#ifndef USE_LARGER_FASTER_CB + // we start here as it is right at the end of opl_table and it has too be close + emit("opcbbitX_table:"); + for(int i=0;i*logic_ix_opcodes[opcode])(Read(ptr)); + // // select destination register for shift/res/set + // (this->*reg_offset[opcode & 7]) = v; // ??? + // Write(ptr, v); + // t += 11; + + emit_save_lr_if_not_done_already(); + emit("blx ", "r2"); + if (arg[i].reg != MEMPTR) { + emit_reg8_from_r_temp(arg[i]); + } + Write(memptr, v); + t += 11 + 4; // 4 from fetch earlier + emit_function_return(); + } + reset_function_state(); + emit("opddcb_bit:"); + // use a common bitm variant as it isn't dependent on which register + // e.g. + // t += 4; + // byte v = (this->*logic_ix_opcodes[opcode])(Read(ptr)); + // t += 8; + // select destination register for shift/res/set + // all the ddcb bit functions use memptr for r3,r5 and don't store the result + t += 8 + 4; // 4 from fetch earlier + temp8 __unused v = Read(memptr); // required because it is used in r_temp in the called function + emit("bx ", "r2"); + emit(""); + } + } else if (!strcmp(prefix, "op")) { + if (num == HELPER_START) { + // Define the interrupt function... simple enough to do here, so no need to hard code + emit(".thumb_func"); + reset_function_state(); + emit("z80a_interrupt:"); + temp16 __unused intad = 0x38; // this has side effects in generated code +// byte vector = 0xff; +// if(im >= 2) // im2 +// { +// word vec = vector + i*0x100; +// intad = Read(vec) + 0x100*Read(vec+1); +// t += 19; +// } else { +// t += 13; +// } + + // need lr push in both codepaths + emit_save_lr_if_not_done_already(); + im.emit_read_to_r0(); + emit("cmp ", "r0, #2"); + emit("blt ", "1f"); + i.emit_read_to_r0(); + emit("lsls ", "r0, #8"); + emit("movs ", "r1, #0xff"); + emit("orrs ", "r0, r1"); + intad = Read2(WordInR0()); + emit("adds ", "r_t, #6"); + emit("1:"); + emit("adds ", "r_t, #13"); + + push(pc); + pc = intad; + memptr = intad; + halted = 0; + iff1 = iff2 = 0; + r_low = ((WordInR0)r_low) + 1; + emit_function_return(); + } + } + } + + void OpeA0() { // ldi + emit_call_func("ldX_common"); + ++hl; + ++de; + } + + void OpeA1() { // cpi + emit_call_func("cpX_common"); + ++hl; + } + + void OpeA8() { // ldd + emit_call_func("ldX_common"); + --hl; + --de; + } + + void OpeA9() { // cpd + emit_call_func("cpX_common"); + --hl; + } + + DECLARE_REG16(PC, pc, pc_l, pc_h) + DECLARE_REG16(SP, sp, sp_l, sp_h) + rs_var(r_hi); + rs_var(iff1); + rs_var(iff2); + rs_var(halted); + rs_var(r_low); + rs_var(i); + rs_var(eipos); + rs_var(im); + /** + * r0 - + * r1 + * r2 - iy + * r3 - temp? + * r4 - pc + * r5 - sp + * r6 - t + * r7 - af + * r8 - bc + * r9 - de + * r10 - hl + * r11 - ix + * r12 - memptr + */ + DECLARE_REG16(MEMPTR, memptr, mem_l, mem_h) // undocumented register + DECLARE_REG16(IX, ix, xl, xh) + DECLARE_REG16(IY, iy, yl, yh) + + DECLARE_REG16(BC, bc, c, b) + DECLARE_REG16(DE, de, e, d) + DECLARE_REG16(HL, hl, l, h) + DECLARE_REG16(AF, af, f, a) + DECLARE_REG16(TEMPORARY, temporary, temp_lo, temp_hi) + DECLARE_REG16(TEMPORARY_RESTRICTED, restricted, restriced_lo, restricted_hi) + struct alt { + rs_var(bc); + rs_var(de); + rs_var(hl); + rs_var(af); + } alt; + rs_var(scratch); // another scratch register +}; + +}//namespace xZ80 + +#endif//__Z80_H__ diff --git a/options_common.cpp b/options_common.cpp index 6eaa9f8..baff71d 100755 --- a/options_common.cpp +++ b/options_common.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -19,13 +20,16 @@ along with this program. If not, see . #include "platform/platform.h" #include "platform/io.h" #include "tools/options.h" +#ifdef USE_UI #include "ui/ui.h" +#endif #include "options_common.h" #include "file_type.h" namespace xPlatform { +#ifndef NO_USE_SAVE static struct eOptionStoreSlot : public xOptions::eOptionInt { virtual const char* Name() const { return "save slot"; } @@ -113,7 +117,9 @@ static struct eOptionSaveFile : public eOptionSave } virtual int Order() const { return 8; } } op_save_file; +#endif +#ifndef NO_USE_SAVE static struct eOptionSaveState : public eOptionSave { virtual const char* Name() const { return "save state"; } @@ -141,33 +147,105 @@ static struct eOptionLoadState : public eOptionSave } virtual int Order() const { return 6; } } op_load_state; +#endif -static struct eOptionTape : public xOptions::eOptionInt +#ifndef NO_USE_TAPE +static struct eOptionTape : public xOptions::eOptionIntWithPending { eOptionTape() { storeable = false; } - virtual const char* Name() const { return "tape"; } - virtual const char** Values() const + virtual const char* Name() const override { +#ifndef USE_MU + return "tape"; +#else + return "Tape"; +#endif + } + enum { + V_NONE = 0, + V_STOPPED = 1, + V_PLAYING = 2, + V_STOP = 3, + V_PLAY = 4, + V_REWIND = 5 + }; + const char** Values() const override { - static const char* values[] = { "n/a", "stop", "start", NULL }; + static const char* values[] = { "none", "stopped", "playing", "stop", "play", "rewind", NULL }; return values; } - virtual void Change(bool next = true) - { - switch(Handler()->OnAction(A_TAPE_TOGGLE)) + static int translate(eActionResult result) { + switch(result) { - case AR_TAPE_NOT_INSERTED: Set(0); break; - case AR_TAPE_STOPPED: Set(1); break; - case AR_TAPE_STARTED: Set(2); break; - default: break; + case AR_TAPE_STOPPED: + return V_STOPPED; + case AR_TAPE_STARTED: + return V_PLAYING; + default: + return V_NONE; + } + } + + void Change(bool next = true) override { + int v = eOptionTape::translate(Handler()->OnAction(A_TAPE_QUERY)); + if (v == V_NONE) { + SetNow(V_NONE); + } else { + int cur; + if (*this == V_PLAY || *this == V_STOP) { + cur = 1; + } else if (*this == V_REWIND) { + cur = 2; + } else { + cur = 0; + } + cur += (next?1:2); + if (cur >= 3) cur -= 3; + if (cur == 0) { + Set(value); // set to the current value (i.e no longer pending) + } else if (cur == 1) { + Set(v == V_PLAYING ? V_STOP : V_PLAY); + } else { + Set(V_REWIND); + } + } + } + + void Complete(bool accept) override + { + if (accept && is_change_pending) { + SetNow(translate(Handler()->OnAction(A_TAPE_TOGGLE))); } + is_change_pending = false; + } + + int Order() const override { return 40; } + + bool Refresh() { + int v = eOptionTape::translate(Handler()->OnAction(A_TAPE_QUERY)); + if (v != value) { + // underlying state change compared to current (actual) value + SetNow(v); + return true; + } + return false; } - virtual int Order() const { return 40; } } op_tape; -static struct eOptionPause : public xOptions::eOptionBool +bool OpTapeRefresh() { + return op_tape.Refresh(); +} +#endif + +static struct eOptionPause : public xOptions::eOptionBoolYesNo { eOptionPause() { storeable = false; } - virtual const char* Name() const { return "pause"; } + virtual const char* Name() const { +#ifndef USE_MU + return "pause"; +#else + return "Paused"; +#endif + } virtual void Change(bool next = true) { eOptionBool::Change(); @@ -176,6 +254,28 @@ static struct eOptionPause : public xOptions::eOptionBool virtual int Order() const { return 70; } } op_pause; +#if 0 //ndef NO_USE_AY +static struct eOptionSound : public xOptions::eOptionInt +{ + eOptionSound() { + Set(S_AY); + } + virtual const char* Name() const { return "Sound"; } + virtual const char** Values() const + { + static const char* values[] = { "beeper", "ay", "tape", NULL }; + return values; + } + virtual void Change(bool next = true) + { + eOptionInt::Change(S_FIRST, S_LAST, next); + } + virtual int Order() const { return 20; } +} op_sound; +#endif + +#ifndef NO_USE_SOUND +#ifndef USE_MU static struct eOptionSound : public xOptions::eOptionInt { eOptionSound() { Set(S_AY); } @@ -191,11 +291,13 @@ static struct eOptionSound : public xOptions::eOptionInt } virtual int Order() const { return 20; } } op_sound; +eVolume OpVolume() { return (eVolume)(int)op_sound; } +void OpVolume(eVolume v) { op_sound.Set(v); } -static struct eOptionVolume : public xOptions::eOptionInt +#else +struct eOptionVolume : public xOptions::eOptionInt { - eOptionVolume() { Set(V_50); } - virtual const char* Name() const { return "volume"; } + eOptionVolume() { Set(V_70); } virtual const char** Values() const { static const char* values[] = { "mute", "10%", "20%", "30%", "40%", "50%", "60%", "70%", "80%", "90%", "100%", NULL }; @@ -205,15 +307,45 @@ static struct eOptionVolume : public xOptions::eOptionInt { eOptionInt::Change(V_FIRST, V_LAST, next); } +}; + +static struct eOptionSoundVolume : public eOptionVolume +{ + eOptionSoundVolume() { +#ifndef NO_USE_AY + Set(V_50); +#else + Set(V_30); +#endif + } + virtual const char* Name() const { + return "Speaker volume"; + } virtual int Order() const { return 30; } -} op_volume; +} op_sound_volume; + +static struct eOptionTapeVolume : public eOptionVolume +{ + eOptionTapeVolume() { Set(V_30); } + virtual const char* Name() const { return "Tape volume"; } + virtual int Order() const { return 32; } +} op_tape_volume; +#endif -eVolume OpVolume() { return (eVolume)(int)op_volume; } -void OpVolume(eVolume v) { op_volume.Set(v); } +eVolume OpSoundVolume() { return (eVolume)(int)op_sound_volume; } +void OpSoundVolume(eVolume v) { op_sound_volume.Set(v); } +eVolume OpTapeVolume() { return (eVolume)(int)op_tape_volume; } +void OpTapeVolume(eVolume v) { op_tape_volume.Set(v); } +#endif + +#if 0 // ifndef NO_USE_AY eSound OpSound() { return (eSound)(int)op_sound; } void OpSound(eSound s) { op_sound.Set(s); } +#endif +#ifndef NO_USE_KEMPSTON +#ifndef USE_MU static struct eOptionJoy : public xOptions::eOptionInt { eOptionJoy() { Set(J_KEMPSTON); } @@ -229,7 +361,9 @@ static struct eOptionJoy : public xOptions::eOptionInt } virtual int Order() const { return 10; } } op_joy; - +#endif +#endif +#ifndef NO_USE_FDD static struct eOptionDrive : public xOptions::eOptionInt { eOptionDrive() { storeable = false; Set(D_A); } @@ -245,15 +379,43 @@ static struct eOptionDrive : public xOptions::eOptionInt } virtual int Order() const { return 60; } } op_drive; +#endif + +#ifdef USE_MU + static struct eOptionVSyncRate : public xOptions::eOptionInt + { + eOptionVSyncRate() { storeable = false; } + virtual const char* Name() const { return "VSync rate"; } + virtual const char** Values() const + { + static const char* values[] = { "50/60", "50", "60", "free", "wrong", NULL }; + return values; + } + virtual int Order() const { return 67; } + } op_vsync; + void OpVsyncRate(eVsyncRate vr) { op_vsync.Set(vr); } +#endif static struct eOptionReset : public xOptions::eOptionB { - eOptionReset() { storeable = false; } - virtual const char* Name() const { return "reset"; } + eOptionReset() { +#ifdef USE_MU + is_action = true; +#endif + storeable = false; + } + virtual const char* Name() const { +#ifndef USE_MU + return "reset"; +#else + return "Reset"; +#endif + } virtual void Change(bool next = true) { Handler()->OnAction(A_RESET); } virtual int Order() const { return 80; } } op_reset; +#ifndef USE_MU_SIMPLIFICATIONS static struct eOptionQuit : public xOptions::eOptionBool { eOptionQuit() { storeable = false; } @@ -261,7 +423,9 @@ static struct eOptionQuit : public xOptions::eOptionBool virtual int Order() const { return 100; } virtual const char** Values() const { return NULL; } } op_quit; +#endif +#ifndef USE_MU_SIMPLIFICATIONS static struct eOptionLastFile : public xOptions::eOptionString { eOptionLastFile() { customizable = false; } @@ -283,13 +447,20 @@ const char* OpLastFolder() return lf; } void OpLastFile(const char* name) { op_last_file.Set(name); } +#endif +#ifndef USE_MU_SIMPLIFICATIONS bool OpQuit() { return op_quit; } void OpQuit(bool v) { op_quit.Set(v); } +#endif +#ifndef NO_USE_FDD eDrive OpDrive() { return (eDrive)(int)op_drive; } void OpDrive(eDrive d) { op_drive.Set(d); } +#endif +#ifndef NO_USE_KEMPSTON +#ifndef USE_MU eJoystick OpJoystick() { return (eJoystick)(int)op_joy; } void OpJoystick(eJoystick v) { op_joy.Set(v); } dword OpJoyKeyFlags() @@ -303,6 +474,7 @@ dword OpJoyKeyFlags() } return KF_QAOP; } - +#endif +#endif } //namespace xPlatform diff --git a/options_common.h b/options_common.h index bcf487b..1fb4aa0 100755 --- a/options_common.h +++ b/options_common.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -28,27 +29,47 @@ enum eJoystick { J_FIRST, J_KEMPSTON = J_FIRST, J_CURSOR, J_QAOP, J_SINCLAIR2, J enum eSound { S_FIRST, S_BEEPER = S_FIRST, S_AY, S_TAPE, S_LAST }; enum eVolume { V_FIRST, V_MUTE = V_FIRST, V_10, V_20, V_30, V_40, V_50, V_60, V_70, V_80, V_90, V_100, V_LAST }; enum eDrive { D_FIRST, D_A = D_FIRST, D_B, D_C, D_D, D_LAST }; +enum eVsyncRate { VR_FIRST, VR_5060 = VR_FIRST , VR_50, VR_60, VR_FREE, VR_WRONG, VR_LAST }; const char* OpLastFolder(); const char* OpLastFile(); void OpLastFile(const char* path); +#ifndef USE_MU_SIMPLIFICATIONS bool OpQuit(); void OpQuit(bool v); +#endif +#ifndef NO_USE_FDD eDrive OpDrive(); void OpDrive(eDrive d); +#endif +#ifndef NO_USE_KEMPSTON +#ifndef USE_MU eJoystick OpJoystick(); void OpJoystick(eJoystick v); dword OpJoyKeyFlags(); +#endif +#endif +#ifndef USE_MU eVolume OpVolume(); void OpVolume(eVolume v); +#else +eVolume OpSoundVolume(); +void OpSoundVolume(eVolume v); +eVolume OpTapeVolume(); +void OpTapeVolume(eVolume v); +#ifndef NO_USE_TAPE +bool OpTapeRefresh(); +#endif +#endif eSound OpSound(); void OpSound(eSound s); +void OpVsyncRate(eVsyncRate vr); } //namespace xPlatform diff --git a/pico_extras_import.cmake b/pico_extras_import.cmake new file mode 100644 index 0000000..1f07720 --- /dev/null +++ b/pico_extras_import.cmake @@ -0,0 +1,57 @@ +if (DEFINED ENV{PICO_EXTRAS_PATH} AND (NOT PICO_EXTRAS_PATH)) + set(PICO_EXTRAS_PATH $ENV{PICO_EXTRAS_PATH}) + message("Using PICO_EXTRAS_PATH from environment ('${PICO_EXTRAS_PATH}')") +endif () + +if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT)) + set(PICO_EXTRAS_FETCH_FROM_GIT $ENV{PICO_EXTRAS_FETCH_FROM_GIT}) + message("Using PICO_EXTRAS_FETCH_FROM_GIT from environment ('${PICO_EXTRAS_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH} AND (NOT PICO_EXTRAS_FETCH_FROM_GIT_PATH)) + set(PICO_EXTRAS_FETCH_FROM_GIT_PATH $ENV{PICO_EXTRAS_FETCH_FROM_GIT_PATH}) + message("Using PICO_EXTRAS_FETCH_FROM_GIT_PATH from environment ('${PICO_EXTRAS_FETCH_FROM_GIT_PATH}')") +endif () + +if (NOT PICO_EXTRAS_PATH) + if (PICO_EXTRAS_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_EXTRAS_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + PICO_EXTRAS + GIT_REPOSITORY git@asic-git.pitowers.org:projectmu/pico_extras.git + GIT_TAG master + ) + if (NOT PICO_EXTRAS) + message("Downloading PICO EXTRAS") + FetchContent_Populate(PICO_EXTRAS) + set(PICO_EXTRAS_PATH ${PICO_EXTRAS_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + if (PICO_SDK_PATH AND EXISTS "${PICO_SDK_PATH}/../pico-extras") + set(PICO_EXTRAS_PATH ${PICO_SDK_PATH}/../pico-extras) + message("Defaulting PICO_EXTRAS_PATH as sibling of PICO_SDK_PATH: ${PICO_EXTRAS_PATH}") + else() + message(FATAL_ERROR + "PICO EXTRAS location was not specified. Please set PICO_EXTRAS_PATH or set PICO_EXTRAS_FETCH_FROM_GIT to on to fetch from git." + ) + endif() + endif () +endif () + +set(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" CACHE PATH "Path to the PICO EXTRAS") +set(PICO_EXTRAS_FETCH_FROM_GIT "${PICO_EXTRAS_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO EXTRAS from git if not otherwise locatable") +set(PICO_EXTRAS_FETCH_FROM_GIT_PATH "${PICO_EXTRAS_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download EXTRAS") + +get_filename_component(PICO_EXTRAS_PATH "${PICO_EXTRAS_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_EXTRAS_PATH}) + message(FATAL_ERROR "Directory '${PICO_EXTRAS_PATH}' not found") +endif () + +set(PICO_EXTRAS_PATH ${PICO_EXTRAS_PATH} CACHE PATH "Path to the PICO EXTRAS" FORCE) + +add_subdirectory(${PICO_EXTRAS_PATH} pico_extras) \ No newline at end of file diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..2548382 --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,64 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# todo document + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the PICO SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of PICO SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY git@asic-git.pitowers.org:projectmu/pico_sdk.git + GIT_TAG master + ) + if (NOT pico_sdk) + message("Downloading PICO SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "PICO SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the PICO SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the PICO SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/platform/custom_ui/ui_file_open.cpp b/platform/custom_ui/ui_file_open.cpp index b4e2580..2d55cd0 100755 --- a/platform/custom_ui/ui_file_open.cpp +++ b/platform/custom_ui/ui_file_open.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -16,6 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#ifdef USE_UI #include "../../std.h" #include "ui_file_open.h" #include "../../ui/ui_list.h" @@ -27,7 +29,7 @@ along with this program. If not, see . #include "../../file_type.h" #include -#ifdef USE_UI +#ifdef USE_FILE_OPEN namespace xUi { @@ -188,4 +190,5 @@ void eFileOpenDialog::OnNotify(byte n, byte from) } //namespace xUi +#endif #endif//USE_UI diff --git a/platform/custom_ui/ui_keyboard.cpp b/platform/custom_ui/ui_keyboard.cpp index 9b718f7..e3bdae5 100755 --- a/platform/custom_ui/ui_keyboard.cpp +++ b/platform/custom_ui/ui_keyboard.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -16,12 +17,11 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#ifdef USE_UI #include "../platform.h" #include "ui_keyboard.h" #include "../../ui/ui_button.h" -#ifdef USE_UI - namespace xUi { diff --git a/platform/io.cpp b/platform/io.cpp index e431d72..dbf55c8 100644 --- a/platform/io.cpp +++ b/platform/io.cpp @@ -62,24 +62,24 @@ bool MkDir(const char* path); // platform-specific //============================================================================= // PathCreate //----------------------------------------------------------------------------- -bool PathCreate(const char* path) -{ - char buf[MAX_PATH_LEN]; - int p = 0; - int l = strlen(path); - for(int i = 0; i < l; ++i) - { - if(path[i] == '\\' || path[i] == '/') - { - strncpy(buf + p, path + p, i - p); - buf[i] = 0; - p = i; - if(i > 0 && !MkDir(buf)) - return false; - } - } - return MkDir(path); -} +bool PathCreate(const char* path) +{ + char buf[MAX_PATH_LEN]; + int p = 0; + int l = strlen(path); + for(int i = 0; i < l; ++i) + { + if(path[i] == '\\' || path[i] == '/') + { + strncpy(buf + p, path + p, i - p); + buf[i] = 0; + p = i; + if(i > 0 && !MkDir(buf)) + return false; + } + } + return MkDir(path); +} //============================================================================= // GetPathParent //----------------------------------------------------------------------------- diff --git a/platform/io.h b/platform/io.h index 398e448..eade702 100644 --- a/platform/io.h +++ b/platform/io.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -24,7 +25,13 @@ along with this program. If not, see . namespace xIo { -enum { MAX_PATH_LEN = 1024 }; +enum { +#ifndef USE_MU + MAX_PATH_LEN = 1024 +#else + MAX_PATH_LEN = 64 +#endif +}; void SetResourcePath(const char* resource_path); const char* ResourcePath(const char* path); @@ -32,7 +39,7 @@ const char* ResourcePath(const char* path); void SetProfilePath(const char* profile_path); const char* ProfilePath(const char* path); -bool PathCreate(const char* path); +bool PathCreate(const char* path); void GetPathParent(char* parent, const char* path); } diff --git a/platform/platform.cpp b/platform/platform.cpp index 2ef483a..ae765d2 100644 --- a/platform/platform.cpp +++ b/platform/platform.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -28,12 +29,15 @@ eHandler::eHandler() assert(!handler); handler = this; } +#ifndef NO_USE_DESTRUCTORS eHandler::~eHandler() { handler = NULL; } +#endif eHandler* Handler() { return handler; } +#ifndef USE_MU void GetScaleWithAspectRatio43(float* sx, float* sy, int _w, int _h) { *sx = 1.0f; @@ -45,6 +49,6 @@ void GetScaleWithAspectRatio43(float* sx, float* sy, int _w, int _h) else *sy = a/a43; } - +#endif } //namespace xPlatform diff --git a/platform/platform.h b/platform/platform.h index 4d421fb..71b9713 100644 --- a/platform/platform.h +++ b/platform/platform.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2015 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -73,7 +74,13 @@ enum eKeyFlags enum eMouseAction { MA_MOVE, MA_BUTTON, MA_WHEEL }; enum eAction { - A_RESET, A_TAPE_TOGGLE, A_TAPE_QUERY, A_DISK_QUERY + A_RESET, +#ifndef NO_USE_TAPE + A_TAPE_TOGGLE, A_TAPE_QUERY, A_TAPE_REWIND +#endif +#ifndef NO_USE_FDD + A_DISK_QUERY +#endif }; enum eActionResult { @@ -86,26 +93,46 @@ enum eActionResult struct eHandler { eHandler(); +#ifndef NO_USE_DESTRUCTORS ~eHandler(); +#endif virtual void OnInit() = 0; virtual void OnDone() = 0; virtual const char* OnLoop() = 0; // error desc if not NULL +#ifndef USE_MU_SIMPLIFICATIONS virtual const char* WindowCaption() const = 0; +#endif virtual void OnKey(char key, dword flags) = 0; +#ifndef NO_USE_KEMPSTON +#ifndef USE_MU virtual void OnMouse(eMouseAction action, byte a, byte b) = 0; - +#endif +#endif virtual bool OnOpenFile(const char* name, const void* data = NULL, size_t data_size = 0) = 0; +#ifdef USE_STREAM + virtual bool OnOpenFileCompressed(const char* name, const void* comressed_data, size_t compressed_data_size, size_t data_size) = 0; +#endif +#ifndef NO_USE_SAVE virtual bool OnSaveFile(const char* name) = 0; +#endif virtual bool FileTypeSupported(const char* name) const = 0; virtual eActionResult OnAction(eAction action) = 0; +#ifndef NO_USE_REPLAY virtual bool GetReplayProgress(dword* frame_current, dword* frames_total, dword* frames_cached) = 0; +#endif // data to draw +#ifndef NO_USE_SCREEN virtual void* VideoData() const = 0; +#endif +#ifndef NO_USE_UI virtual void* VideoDataUI() const = 0; +#endif // pause/resume function for sync video by audio virtual void VideoPaused(bool paused) = 0; + virtual bool IsVideoPaused() = 0; +#ifndef USE_MU_SIMPLIFICATIONS virtual int VideoFrame() const = 0; // audio virtual int AudioSources() const = 0; @@ -113,7 +140,7 @@ struct eHandler virtual dword AudioDataReady(int source) const = 0; virtual void AudioDataUse(int source, dword size) = 0; virtual void AudioSetSampleRate(dword sample_rate) = 0; - +#endif virtual bool FullSpeed() const = 0; virtual eSpeccy* Speccy() const = 0; diff --git a/res/3D_Pacman_1983_Freddy_Kristiansen.z80 b/res/3D_Pacman_1983_Freddy_Kristiansen.z80 new file mode 100644 index 0000000..3d60eea Binary files /dev/null and b/res/3D_Pacman_1983_Freddy_Kristiansen.z80 differ diff --git a/res/CHRON128.SCL b/res/CHRON128.SCL new file mode 100644 index 0000000..8f65c46 Binary files /dev/null and b/res/CHRON128.SCL differ diff --git a/res/CHRON128.sna b/res/CHRON128.sna new file mode 100644 index 0000000..4babf2c Binary files /dev/null and b/res/CHRON128.sna differ diff --git a/res/DOOMDE_1.TAP b/res/DOOMDE_1.TAP new file mode 100644 index 0000000..9e308bc Binary files /dev/null and b/res/DOOMDE_1.TAP differ diff --git a/res/DOOM_PD.Z80 b/res/DOOM_PD.Z80 new file mode 100644 index 0000000..1442b2e Binary files /dev/null and b/res/DOOM_PD.Z80 differ diff --git a/res/Donkey_Kong_1986_Ocean.z80 b/res/Donkey_Kong_1986_Ocean.z80 new file mode 100644 index 0000000..eb86d7b Binary files /dev/null and b/res/Donkey_Kong_1986_Ocean.z80 differ diff --git a/res/Hobbit_The_v1.2_1982_Melbourne_House_a.z80 b/res/Hobbit_The_v1.2_1982_Melbourne_House_a.z80 new file mode 100644 index 0000000..99549a5 Binary files /dev/null and b/res/Hobbit_The_v1.2_1982_Melbourne_House_a.z80 differ diff --git a/res/JETSET.TAP b/res/JETSET.TAP new file mode 100644 index 0000000..dbd99fb Binary files /dev/null and b/res/JETSET.TAP differ diff --git a/res/Jet Set Willy (R D Foord Software).tzx b/res/Jet Set Willy (R D Foord Software).tzx new file mode 100644 index 0000000..1210bdf Binary files /dev/null and b/res/Jet Set Willy (R D Foord Software).tzx differ diff --git a/res/Manic_Miner_1983_Software_Projects.sna b/res/Manic_Miner_1983_Software_Projects.sna new file mode 100644 index 0000000..307d78d Binary files /dev/null and b/res/Manic_Miner_1983_Software_Projects.sna differ diff --git a/res/Manic_Miner_1983_Software_Projects.z80 b/res/Manic_Miner_1983_Software_Projects.z80 new file mode 100644 index 0000000..13e4b6f Binary files /dev/null and b/res/Manic_Miner_1983_Software_Projects.z80 differ diff --git a/res/ROBOCOP3.TAP b/res/ROBOCOP3.TAP new file mode 100644 index 0000000..d9236be Binary files /dev/null and b/res/ROBOCOP3.TAP differ diff --git a/res/Star_Wars_1987_Domark.z80 b/res/Star_Wars_1987_Domark.z80 new file mode 100644 index 0000000..b1245e6 Binary files /dev/null and b/res/Star_Wars_1987_Domark.z80 differ diff --git a/res/The Sentinel.tzx b/res/The Sentinel.tzx new file mode 100644 index 0000000..10454d9 Binary files /dev/null and b/res/The Sentinel.tzx differ diff --git a/res/aticatac.tap b/res/aticatac.tap new file mode 100644 index 0000000..f94e2dd Binary files /dev/null and b/res/aticatac.tap differ diff --git a/res/aticatac.tzx b/res/aticatac.tzx new file mode 100644 index 0000000..7f88d9e Binary files /dev/null and b/res/aticatac.tzx differ diff --git a/res/ay2.z80 b/res/ay2.z80 new file mode 100644 index 0000000..fab6c9e Binary files /dev/null and b/res/ay2.z80 differ diff --git a/res/bitbang.tap b/res/bitbang.tap new file mode 100644 index 0000000..0a7ea71 Binary files /dev/null and b/res/bitbang.tap differ diff --git a/res/chase.tap b/res/chase.tap new file mode 100644 index 0000000..f5b4927 Binary files /dev/null and b/res/chase.tap differ diff --git a/res/dandare1.tap b/res/dandare1.tap new file mode 100644 index 0000000..b85a184 Binary files /dev/null and b/res/dandare1.tap differ diff --git a/res/down_abc.tap b/res/down_abc.tap new file mode 100644 index 0000000..b7c7f6c Binary files /dev/null and b/res/down_abc.tap differ diff --git a/res/foo.tap b/res/foo.tap new file mode 100644 index 0000000..e3df29e Binary files /dev/null and b/res/foo.tap differ diff --git a/res/g+g.sna b/res/g+g.sna new file mode 100644 index 0000000..580af8e Binary files /dev/null and b/res/g+g.sna differ diff --git a/res/g+g.tap b/res/g+g.tap new file mode 100644 index 0000000..7b2b026 Binary files /dev/null and b/res/g+g.tap differ diff --git a/res/g+g.tzx b/res/g+g.tzx new file mode 100644 index 0000000..a951257 Binary files /dev/null and b/res/g+g.tzx differ diff --git a/res/knightlore.sna b/res/knightlore.sna new file mode 100644 index 0000000..6e1914b Binary files /dev/null and b/res/knightlore.sna differ diff --git a/res/knightlore.tzx b/res/knightlore.tzx new file mode 100644 index 0000000..8f35df9 Binary files /dev/null and b/res/knightlore.tzx differ diff --git a/res/shock.tap b/res/shock.tap new file mode 100644 index 0000000..a30667f Binary files /dev/null and b/res/shock.tap differ diff --git a/res/steg.tap b/res/steg.tap new file mode 100644 index 0000000..a509ffa Binary files /dev/null and b/res/steg.tap differ diff --git a/res/thundercats.tap b/res/thundercats.tap new file mode 100644 index 0000000..f9a3fb1 Binary files /dev/null and b/res/thundercats.tap differ diff --git a/res/z80ccf.tap b/res/z80ccf.tap new file mode 100644 index 0000000..59d8d36 Binary files /dev/null and b/res/z80ccf.tap differ diff --git a/res/z80doc.tap b/res/z80doc.tap new file mode 100644 index 0000000..53d0dd8 Binary files /dev/null and b/res/z80doc.tap differ diff --git a/res/z80docflags.tap b/res/z80docflags.tap new file mode 100644 index 0000000..cb36636 Binary files /dev/null and b/res/z80docflags.tap differ diff --git a/res/z80flags.tap b/res/z80flags.tap new file mode 100644 index 0000000..ae2dac7 Binary files /dev/null and b/res/z80flags.tap differ diff --git a/res/z80full.tap b/res/z80full.tap new file mode 100644 index 0000000..76a4cfe Binary files /dev/null and b/res/z80full.tap differ diff --git a/res/z80memptr.tap b/res/z80memptr.tap new file mode 100644 index 0000000..2f5330b Binary files /dev/null and b/res/z80memptr.tap differ diff --git a/res/z80tests.tap b/res/z80tests.tap new file mode 100644 index 0000000..2cc68ae Binary files /dev/null and b/res/z80tests.tap differ diff --git a/res/zxpico.tap b/res/zxpico.tap new file mode 100644 index 0000000..ade6b94 Binary files /dev/null and b/res/zxpico.tap differ diff --git a/snapshot/snapshot.cpp b/snapshot/snapshot.cpp index 53f5f8e..174d6af 100644 --- a/snapshot/snapshot.cpp +++ b/snapshot/snapshot.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -20,7 +21,9 @@ along with this program. If not, see . #include "../z80/z80.h" #include "../devices/memory.h" #include "../devices/ula.h" +#ifndef NO_USE_AY #include "../devices/sound/ay.h" +#endif #include "../speccy.h" #include "../platform/endian.h" #include "../platform/platform.h" @@ -31,7 +34,8 @@ namespace xSnapshot { #pragma pack(push, 1) -struct eSnapshot_SNA +#ifndef NO_USE_SNA +struct eSnapshot_SNA_header { byte i; word alt_hl, alt_de, alt_bc, alt_af; @@ -40,18 +44,35 @@ struct eSnapshot_SNA byte r; word af, sp; byte im, pFE; - byte page5[eMemory::PAGE_SIZE]; // 4000-7FFF - byte page2[eMemory::PAGE_SIZE]; // 8000-BFFF - byte page[eMemory::PAGE_SIZE]; // C000-FFFF +}; + +struct eSnaphsot_SNA128_header { // 128k extension word pc; +#ifndef NO_USE_128K byte p7FFD; byte trdos; +#endif +}; + +struct eSnapshot_SNA { + struct eSnapshot_SNA_header header; + byte page5[eMemory::PAGE_SIZE]; // 4000-7FFF + byte page2[eMemory::PAGE_SIZE]; // 8000-BFFF + byte page[eMemory::PAGE_SIZE]; // C000-FFFF + struct eSnaphsot_SNA128_header header_128; +#ifndef NO_USE_128K byte pages[6 * eMemory::PAGE_SIZE]; // all other pages (can contain duplicated page 5 or 2) - enum { S_48 = 49179, S_128_5 = 131103, S_128_6 = 147487 }; +#endif + enum { S_48 = 49179, +#ifndef NO_USE_128K + S_128_5 = 131103, S_128_6 = 147487 +#endif + }; }; +#endif -struct eSnapshot_Z80 +struct eSnapshot_Z80_v1 { byte a,f; word bc,hl,pc,sp; @@ -60,37 +81,81 @@ struct eSnapshot_Z80 byte a1,f1; word iy,ix; byte iff1, iff2, im; - /* 2.01 extension */ +}; + +struct eSnapshot_Z80_v2 +{ word len, newpc; byte model, p7FFD; byte r1,r2, p7FFD_1; byte AY[16]; +}; + +struct eSnapshot_Z80 +{ + eSnapshot_Z80_v1 v1; + /* 2.01 extension */ + eSnapshot_Z80_v2 v2; /* 3.0 extension */ }; #pragma pack(pop) struct eZ80Accessor : public xZ80::eZ80 { +#ifndef USE_STREAM +#ifndef NO_USE_SNA bool SetState(const eSnapshot_SNA* s, size_t buf_size); size_t StoreState(eSnapshot_SNA* s); +#endif bool SetState(const eSnapshot_Z80* s, size_t buf_size); - void UnpackPage(byte* dst, int dstlen, byte* src, int srclen); +#else +#ifndef NO_USE_SNA + bool SetStateSNA(struct stream *stream); + size_t StoreState(eSnapshot_SNA* s); +#endif + bool SetStateZ80(struct stream *stream); +#endif +#ifndef USE_STREAM + void UnpackPage(byte* dst, int dstlen, const byte* src, int srclen); +#else + void UnpackPage(byte* dst, int dstlen, struct stream *stream, int srclen); +#endif void SetupDevices(bool model48k) { +#ifndef NO_USE_128K devices->Get()->Mode48k(model48k); devices->Get()->Mode48k(model48k); devices->Get()->Mode48k(model48k); +#else + assert(model48k); +#endif devices->Init(); } }; -bool eZ80Accessor::SetState(const eSnapshot_SNA* s, size_t buf_size) +#ifndef NO_USE_SNA +#ifndef USE_STREAM +bool eZ80Accessor::SetState(const eSnapshot_SNA* sna, size_t buf_size) +{ + const eSnapshot_SNA_header *s = &sna->header; +#else +bool eZ80Accessor::SetStateSNA(struct stream *stream) { + eSnapshot_SNA_header snap; + stream_read(stream, (uint8_t *)&snap, sizeof(eSnapshot_SNA_header), true); + size_t buf_size = stream->size; + const eSnapshot_SNA_header *s = &snap; +#endif bool sna48 = (buf_size == eSnapshot_SNA::S_48); +#ifndef NO_USE_128K bool sna128 = (buf_size == eSnapshot_SNA::S_128_5) || (buf_size == eSnapshot_SNA::S_128_6); if(!sna48 && !sna128) return false; +#else + if (!sna48) return false; +#endif SetupDevices(sna48); +#ifndef USE_Z80_ARM alt.af = SwapWord(s->alt_af); alt.bc = SwapWord(s->alt_bc); alt.de = SwapWord(s->alt_de); @@ -102,31 +167,90 @@ bool eZ80Accessor::SetState(const eSnapshot_SNA* s, size_t buf_size) ix = SwapWord(s->ix); iy = SwapWord(s->iy); sp = SwapWord(s->sp); - pc = SwapWord(s->pc); i = s->i; r_low = s->r; r_hi = s->r & 0x80; im = s->im; iff1 = s->iff1 ? 1 : 0; - devices->IoWrite(0xfe, s->pFE, t); +#else + auto &rs = z80a_resting_state; + rs.alt_af = SwapWord(s->alt_af); + rs.alt_bc = SwapWord(s->alt_bc); + rs.alt_de = SwapWord(s->alt_de); + rs.alt_hl = SwapWord(s->alt_hl); + rs.af = SwapWord(s->af); + rs.bc = SwapWord(s->bc); + rs.de = SwapWord(s->de); + rs.hl = SwapWord(s->hl); + rs.ix = SwapWord(s->ix); + rs.iy = SwapWord(s->iy); + rs.sp = SwapWord(s->sp); + rs.i = s->i; + rs.r_low = s->r; + rs.r_hi = s->r & 0x80; + rs.im = s->im; + rs.iff1 = s->iff1 ? 1 : 0; + devices->IoWrite(0xfe, s->pFE, rs.t); +#endif + int p_size = eMemory::PAGE_SIZE; - memcpy(memory->Get(eMemory::P_RAM5), s->page5, p_size); - memcpy(memory->Get(eMemory::P_RAM2), s->page2, p_size); - int p = sna48 ? 0 : (s->p7FFD & 7); - memcpy(memory->Get(eMemory::P_RAM0 + p), s->page, p_size); + +#ifndef USE_STREAM +#ifndef NO_USE_128K + uint p = sna48 ? 0 : (s128->p7FFD & 7u); +#else + uint p = 0; +#endif + memcpy(memory->Get(eMemory::P_RAM5), sna->page5, p_size); + memcpy(memory->Get(eMemory::P_RAM2), sna->page2, p_size); + memcpy(memory->Get(eMemory::P_RAM0 + p), sna->page, p_size); +#else + stream_read(stream, memory->Get(eMemory::P_RAM5), p_size, true); + stream_read(stream, memory->Get(eMemory::P_RAM2), p_size, true); + stream_read(stream, memory->Get(eMemory::P_RAM0), p_size, true); +#endif if(sna48) { +#ifndef USE_Z80_ARM pc = memory->Read(sp) + 0x100 * memory->Read(sp+1); sp += 2; +#else + rs.pc = memory->Read(rs.sp) + 0x100 * memory->Read(rs.sp+1); + rs.sp += 2; +#endif devices->Get()->SelectPage(eRom::ROM_48); return true; } - devices->IoWrite(0x7ffd, s->p7FFD, t); - devices->Get()->SelectPage(s->trdos ? eRom::ROM_DOS : eRom::ROM_128_0); - const byte* page = s->pages; - byte mapped = 0x24 | (1 << (s->p7FFD & 7)); + +#ifndef USE_STREAM + const eSnaphsot_SNA128_header *s128 = &sna->header_128; +#else + eSnaphsot_SNA128_header snap128; + stream_read(stream, (uint8_t *)&snap128, sizeof(snap128), true); +#ifndef NO_USE_128K + const eSnaphsot_SNA128_header *s128 = &snap128; +#endif +#endif + +#ifndef NO_USE_128K +#ifndef USE_Z80_ARM + pc = SwapWord(s128->pc); + devices->IoWrite(0x7ffd, s128->p7FFD, t); +#else + rs.pc = SwapWord(s128->pc); + devices->IoWrite(0x7ffd, s128->p7FFD, rs.t); +#endif +#ifndef NO_USE_DOS + devices->Get()->SelectPage(s128->trdos ? eRom::ROM_DOS : eRom::ROM_128_0); +#else + assert(!s128->trdos); // todo error + devices->Get()->SelectPage(eRom::ROM_128_0); +#endif + byte mapped = 0x24 | (1 << (s128->p7FFD & 7)); +#ifndef USE_STREAM + const byte* page = sna->pages; for(int i = 0; i < 8; ++i) { if(!(mapped & (1 << i))) @@ -135,8 +259,19 @@ bool eZ80Accessor::SetState(const eSnapshot_SNA* s, size_t buf_size) page += p_size; } } +#else + for(int i = 0; i < 8; ++i) + { + if(!(mapped & (1 << i))) + { + stream_read(stream, memory->Get(eMemory::P_RAM0 + i), p_size, true); + } + } +#endif +#endif return true; } +#ifndef NO_USE_SAVE size_t eZ80Accessor::StoreState(eSnapshot_SNA* s) { s->trdos = devices->Get()->DosSelected(); @@ -197,67 +332,131 @@ size_t eZ80Accessor::StoreState(eSnapshot_SNA* s) } return eSnapshot_SNA::S_128_5; } -bool eZ80Accessor::SetState(const eSnapshot_Z80* s, size_t buf_size) +#endif +#endif +#ifndef USE_STREAM +bool eZ80Accessor::SetState(const eSnapshot_Z80* snap, size_t buf_size) +{ + const struct eSnapshot_Z80_v1 *s = &snap->v1; +#else +bool eZ80Accessor::SetStateZ80(struct stream *stream) { + eSnapshot_Z80_v1 snap; + static_assert(sizeof(snap) == 30, ""); + stream_read(stream, (uint8_t *)&snap, sizeof(snap), true); + const eSnapshot_Z80_v1 *s = &snap; + size_t buf_size = stream->size; +#endif bool model48k = true; byte flags = s->flags; if(flags == 0xFF) flags = 1; - byte* ptr = (byte*)s + 30; + const byte* ptr = (byte*)s + 30; word reg_pc = s->pc; + int val7ffd = 0x30; + eSnapshot_Z80_v2 *s2 = 0; if(reg_pc == 0) { // 2.01 - model48k = (s->model < 3); - SetupDevices(model48k); - word len = SwapWord(s->len); +#ifndef USE_STREAM + s2 = &((eSnapshot_Z80 *)s)->v2; + word len = SwapWord(s2->len); ptr += 2 + len; - reg_pc = s->newpc; +#else + eSnapshot_Z80_v2 snap2; + stream_read(stream, (uint8_t *)&snap2, sizeof(snap2), true); + word len = SwapWord(snap2.len); + s2 = &snap2; + stream_skip(stream, 2 + len - sizeof(snap2)); +#endif + model48k = (s2->model < 3); +#ifdef NO_USE_128K + if (!model48k) return false; +#endif + SetupDevices(model48k); + reg_pc = s2->newpc; byte* p48[] = { 0, 0, 0, 0, memory->Get(eMemory::P_RAM2), memory->Get(eMemory::P_RAM0), 0, 0, memory->Get(eMemory::P_RAM5), 0, 0, 0 }; +#ifndef NO_USE_128K byte* p128[] = { 0, 0, 0, memory->Get(eMemory::P_RAM0), memory->Get(eMemory::P_RAM1), memory->Get(eMemory::P_RAM2), memory->Get(eMemory::P_RAM3), memory->Get(eMemory::P_RAM4), memory->Get(eMemory::P_RAM5), memory->Get(eMemory::P_RAM6), memory->Get(eMemory::P_RAM7), 0 }; +#endif + val7ffd = model48k ? 0x30 : s2->p7FFD; +#ifndef USE_STREAM while(ptr < (byte*)s + buf_size) +#else + while((ptr = stream_peek(stream, 3))) +#endif { word len = ptr[0] | word(ptr[1]) << 8; - if(ptr[2] > 11) + if (ptr[2] > 11) return false; +#ifndef NO_USE_128K byte* dstpage = model48k ? p48[ptr[2]] : p128[ptr[2]]; - if(!dstpage) +#else + byte *dstpage = p48[ptr[2]]; +#endif + if (!dstpage) return false; +// printf("%d %04x\n", ptr[2], len); +#ifndef USE_STREAM ptr += 3; - if(len == 0xFFFF) +#else + stream_skip(stream, 3); +#endif + if (len == 0xFFFF) { +#ifndef USE_STREAM memcpy(dstpage, ptr, len = eMemory::PAGE_SIZE); - else +#else + stream_read(stream, dstpage, eMemory::PAGE_SIZE, true); +#endif + } else { +#ifndef USE_STREAM UnpackPage(dstpage, eMemory::PAGE_SIZE, ptr, len); - ptr += len; + ptr += len; +#else + UnpackPage(dstpage, eMemory::PAGE_SIZE, stream, len); +#endif + } } - devices->Get()->SetRegs(s->AY); +#ifndef NO_USE_AY + devices->Get()->SetRegs(s2->AY); +#endif } else { SetupDevices(true); int len = buf_size - 30; - byte* mem48 = ptr; - if(flags&0x20) - { - //data in packed format - mem48 = new byte[3*eMemory::PAGE_SIZE]; - UnpackPage(mem48, 3*eMemory::PAGE_SIZE, ptr, len); + assert(model48k); + len -= 4; // this seems to be the case! check assertions below + if (flags&0x20) { +#ifndef USE_STREAM + UnpackPage(memory->Get(eMemory::P_RAM5), 3*eMemory::PAGE_SIZE, ptr, len); +#else + UnpackPage(memory->Get(eMemory::P_RAM5), 3*eMemory::PAGE_SIZE, stream, len); +#endif + } else { +#ifndef USE_STREAM + memcpy(memory->Get(eMemory::P_RAM5), ptr, MAX(3*eMemory::PAGE_SIZE, len)); +#else + stream_read(stream, memory->Get(eMemory::P_RAM5), MAX(3*eMemory::PAGE_SIZE, len), true); +#endif } - memcpy(memory->Get(eMemory::P_RAM5), mem48, eMemory::PAGE_SIZE); - memcpy(memory->Get(eMemory::P_RAM2), mem48 + eMemory::PAGE_SIZE, eMemory::PAGE_SIZE); - memcpy(memory->Get(eMemory::P_RAM0), mem48 + 2*eMemory::PAGE_SIZE, eMemory::PAGE_SIZE); - if(flags&0x20) - SAFE_DELETE_ARRAY(mem48); +#ifndef NO_USE_128K + static_assert(eMemory::P_RAM6 == eMemory::P_RAM5 + 1, ""); + static_assert(eMemory::P_RAM7 == eMemory::P_RAM6 + 1, ""); + memcpy(memory->Get(eMemory::P_RAM2), memory->Get(eMemory::P_RAM6), eMemory::PAGE_SIZE); + memcpy(memory->Get(eMemory::P_RAM0), memory->Get(eMemory::P_RAM7), eMemory::PAGE_SIZE); +#endif } +#ifndef USE_Z80_ARM a = s->a, f = s->f; bc = SwapWord(s->bc), de = SwapWord(s->de), hl = SwapWord(s->hl); alt.bc = SwapWord(s->bc1), alt.de = SwapWord(s->de1), alt.hl = SwapWord(s->hl1); @@ -269,14 +468,38 @@ bool eZ80Accessor::SetState(const eSnapshot_Z80* s, size_t buf_size) byte pFE = (flags >> 1) & 7; devices->IoWrite(0xfe, pFE, t); iff1 = s->iff1, iff2 = s->iff2; im = s->im & 3; - devices->IoWrite(0x7ffd, model48k ? 0x30 : s->p7FFD, t); + devices->IoWrite(0x7ffd, val7ffd, t); +#else + auto &rs = z80a_resting_state; + rs.af = (s->a << 8) | s->f; + rs.bc = SwapWord(s->bc), rs.de = SwapWord(s->de), rs.hl = SwapWord(s->hl); + rs.alt_bc = SwapWord(s->bc1), rs.alt_de = SwapWord(s->de1), rs.alt_hl = SwapWord(s->hl1); + rs.alt_af = (s->a1 << 8) | s->f1; + rs.pc = SwapWord(reg_pc), rs.sp = SwapWord(s->sp); rs.ix = SwapWord(s->ix), rs.iy = SwapWord(s->iy); + + rs.i = s->i, rs.r_low = s->r & 0x7F; + rs.r_hi = ((flags & 1) << 7); + byte pFE = (flags >> 1) & 7; + devices->IoWrite(0xfe, pFE, rs.t); + rs.iff1 = s->iff1, rs.iff2 = s->iff2; rs.im = s->im & 3; + devices->IoWrite(0x7ffd, val7ffd, rs.t); +#endif +#ifndef NO_USE_128K if(model48k) devices->Get()->SelectPage(eRom::ROM_48); else - devices->Get()->SelectPage((s->p7FFD & 0x10) ? eRom::ROM_128_0 : eRom::ROM_128_1); + { + assert(s2); + devices->Get()->SelectPage((s2->p7FFD & 0x10) ? eRom::ROM_128_0 : eRom::ROM_128_1); + } +#else + assert(model48k); + devices->Get()->SelectPage(eRom::ROM_48); +#endif return true; } -void eZ80Accessor::UnpackPage(byte* dst, int dstlen, byte* src, int srclen) +#ifndef USE_STREAM +void eZ80Accessor::UnpackPage(byte* dst, int dstlen, const byte* src, int srclen) { memset(dst, 0, dstlen); while(srclen > 0 && dstlen > 0) @@ -295,26 +518,98 @@ void eZ80Accessor::UnpackPage(byte* dst, int dstlen, byte* src, int srclen) --srclen; } } + // added this to check compared to the streaming version; it is also off by 4 in some cases (currently handled in caller) + assert(srclen == 0); +} + +#else +void eZ80Accessor::UnpackPage(byte* dst, int dstlen, struct stream *stream, int srclen) +{ + memset(dst, 0, dstlen); +#ifndef NDEBUG + uint32_t pos_target = stream_pos(stream) + srclen; +#endif + while(dstlen > 0 && srclen > 0) + { + const byte *src = stream_peek(stream, 4); + if (src && srclen >= 4 && src[0] == 0xED && src[1] == 0xED) + { + for(byte i = src[2]; i; i--) + *dst++ = src[3], dstlen--; + srclen -= 4; + stream_skip(stream, 4); + } + else + { + if (!src) { + src = stream_peek(stream, 1); + if (!src) { + assert(false); + break; + } + } + *dst++ = src[0]; + --dstlen; + --srclen; + stream_skip(stream, 1); + } + } +#ifndef NDEBUG + uint pos = stream_pos(stream); + // seems to be 4 off, the old code didn't care + assert(pos == pos_target); +#endif } +#endif +#ifndef NO_USE_SZX bool LoadSZX(eSpeccy* speccy, const void* data, size_t data_size); +#endif +#ifndef USE_STREAM bool Load(eSpeccy* speccy, const char* type, const void* data, size_t data_size) { speccy->Devices().FrameStart(0); eZ80Accessor* z80 = (eZ80Accessor*)speccy->CPU(); bool ok = false; - if(!strcmp(type, "sna")) - ok = z80->SetState((const eSnapshot_SNA*)data, data_size); - else if(!strcmp(type, "z80")) + if(!strcmp(type, "z80")) ok = z80->SetState((const eSnapshot_Z80*)data, data_size); +#ifndef NO_USE_SNA + else if(!strcmp(type, "sna")) + ok = z80->SetState((const eSnapshot_SNA*)data, data_size); +#endif +#ifndef NO_USE_SZX else if(!strcmp(type, "szx")) ok = LoadSZX(speccy, data, data_size); +#endif speccy->Devices().FrameUpdate(); speccy->Devices().FrameEnd(z80->FrameTacts() + z80->T()); return ok; } +#else +bool Load(eSpeccy* speccy, const char* type, struct stream *stream) +{ + speccy->Devices().FrameStart(0); + eZ80Accessor* z80 = (eZ80Accessor*)speccy->CPU(); + bool ok = false; + if(!strcmp(type, "z80")) + ok = z80->SetStateZ80(stream); +#ifndef NO_USE_SNA + else if(!strcmp(type, "sna")) + ok = z80->SetStateSNA(stream); +#endif +#ifndef NO_USE_SZX + else if(!strcmp(type, "szx")) + ok = LoadSZX(speccy, data, data_size); +#endif + stream_close(stream); + speccy->Devices().FrameUpdate(); + speccy->Devices().FrameEnd(z80->FrameTacts() + z80->T()); + return ok; + } +#endif +#ifndef NO_USE_SAVE bool Store(eSpeccy* speccy, const char* file) { FILE* f = fopen(file, "wb"); @@ -330,6 +625,7 @@ bool Store(eSpeccy* speccy, const char* file) fclose(f); return ok; } +#endif } //namespace xSnapshot diff --git a/snapshot/snapshot.h b/snapshot/snapshot.h index bcb2d96..4f393af 100644 --- a/snapshot/snapshot.h +++ b/snapshot/snapshot.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -20,6 +21,9 @@ along with this program. If not, see . #define __SNAPSHOT_H__ #include "../std.h" +#ifdef USE_MU +#include "stream.h" +#endif #pragma once @@ -27,7 +31,11 @@ class eSpeccy; namespace xSnapshot { +#ifndef USE_STREAM bool Load(eSpeccy* speccy, const char* type, const void* data, size_t data_size); +#else +bool Load(eSpeccy* speccy, const char* type, struct stream *stream); +#endif bool Store(eSpeccy* speccy, const char* file); } //namespace xSnapshot diff --git a/speccy.cpp b/speccy.cpp index bb6271f..528b5f8 100644 --- a/speccy.cpp +++ b/speccy.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -24,12 +25,24 @@ along with this program. If not, see . #include "devices/memory.h" #include "devices/ula.h" #include "devices/input/keyboard.h" +#ifndef NO_USE_KEMPSTON #include "devices/input/kempston_joy.h" +#ifndef USE_MU #include "devices/input/kempston_mouse.h" +#endif +#endif +#ifndef NO_USE_TAPE #include "devices/input/tape.h" +#endif +#ifndef NO_USE_BEEPER #include "devices/sound/beeper.h" +#endif +#ifndef NO_USE_AY #include "devices/sound/ay.h" +#endif +#ifndef NO_USE_FDD #include "devices/fdd/wd1793.h" +#endif #include "tools/profiler.h" PROFILER_DECLARE(dev_e); @@ -52,23 +65,37 @@ eSpeccy::eSpeccy() : cpu(NULL), memory(NULL), frame_tacts(0) devices.Add(new eRam(memory)); devices.Add(new eUla(memory)); devices.Add(new eKeyboard); +#ifndef NO_USE_KEMPSTON devices.Add(new eKempstonJoy); +#ifndef USE_MU devices.Add(new eKempstonMouse); +#endif +#endif +#ifndef NO_USE_BEEPER devices.Add(new eBeeper); +#endif +#ifndef NO_USE_AY devices.Add(new eAY); +#endif +#ifndef NO_USE_FDD devices.Add(new eWD1793(this, Device())); +#endif +#ifndef NO_USE_TAPE devices.Add(new eTape(this)); +#endif cpu = new xZ80::eZ80(memory, &devices, frame_tacts); Reset(); } //============================================================================= // eSpeccy::~eSpeccy //----------------------------------------------------------------------------- +#ifndef NO_USE_DESTRUCTORS eSpeccy::~eSpeccy() { delete cpu; delete memory; } +#endif //============================================================================= // eSpeccy::Reset //----------------------------------------------------------------------------- @@ -78,6 +105,7 @@ void eSpeccy::Reset() devices.Init(); devices.Reset(); } +#ifndef NO_USE_128K //============================================================================= // eSpeccy::Mode48k //----------------------------------------------------------------------------- @@ -94,6 +122,7 @@ void eSpeccy::Mode48k(bool on) Device()->Mode48k(on); Device()->Mode48k(on); } +#endif //============================================================================= // eSpeccy::Update //----------------------------------------------------------------------------- @@ -105,10 +134,15 @@ void eSpeccy::Update(int* fetches) } { PROFILER_SECTION(frame); +#ifndef NO_USE_REPLAY if(fetches) cpu->Replay(*fetches); else cpu->Update(int_len, &nmi_pending); +#else + assert(fetches == nullptr); + cpu->Update(int_len, &nmi_pending); +#endif } { PROFILER_SECTION(dev); diff --git a/speccy.h b/speccy.h index 410e255..454f493 100644 --- a/speccy.h +++ b/speccy.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -33,7 +34,11 @@ class eSpeccy { public: eSpeccy(); +#ifndef NO_USE_DESTRUCTORS virtual ~eSpeccy(); +#else + ~eSpeccy() =delete; +#endif void Reset(); void Update(int* fetches = NULL); @@ -45,8 +50,10 @@ class eSpeccy qword T() const { return t_states; } +#ifndef NO_USE_128K bool Mode48k() const; void Mode48k(bool on); +#endif protected: xZ80::eZ80* cpu; diff --git a/speccy_handler.cpp b/speccy_handler.cpp index 4d9059a..11cc30a 100644 --- a/speccy_handler.cpp +++ b/speccy_handler.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2015 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -21,23 +22,43 @@ along with this program. If not, see . #include "devices/memory.h" #include "devices/ula.h" #include "devices/input/keyboard.h" +#ifndef NO_USE_KEMPSTON #include "devices/input/kempston_joy.h" +#ifndef USE_MU #include "devices/input/kempston_mouse.h" +#endif +#endif +#ifndef NO_USE_TAPE #include "devices/input/tape.h" +#endif +#include "devices/sound/device_sound.h" +#ifndef NO_USE_AY #include "devices/sound/ay.h" +#endif +#ifndef NO_USE_BEEPER #include "devices/sound/beeper.h" +#endif +#ifndef NO_USE_FDD #include "devices/fdd/wd1793.h" +#endif #include "z80/z80.h" #include "snapshot/snapshot.h" #include "platform/io.h" -#include "ui/ui_desktop.h" -#include "platform/custom_ui/ui_main.h" +#ifdef USE_UI +#include "../ui/ui_desktop.h" +#include "../platform/custom_ui/ui_main.h" +#endif #include "tools/profiler.h" #include "tools/options.h" #include "tools/io_select.h" +#include "snapshot/rzx.h" #include "options_common.h" #include "file_type.h" -#include "snapshot/rzx.h" +#ifdef USE_STREAM +extern "C" { +#include "stream.h" +}; +#endif namespace xPlatform { @@ -57,14 +78,24 @@ class eMacro int frame; }; -static struct eSpeccyHandler : public eHandler, public eRZX::eHandler, public xZ80::eZ80::eHandlerIo +static struct eSpeccyHandler : public eHandler +#ifndef NO_USE_REPLAY + , public eRZX::eHandler +#endif +#ifndef USE_HACKED_DEVICE_ABSTRACTION + ,public xZ80::eZ80::eHandlerIo +#endif { eSpeccyHandler() : speccy(NULL), macro(NULL), replay(NULL), video_paused(0), video_frame(-1), inside_replay_update(false) {} +#ifndef NO_USE_DESTRUCTORS virtual ~eSpeccyHandler() { assert(!speccy); } +#endif virtual void OnInit(); virtual void OnDone(); virtual const char* OnLoop(); +#ifndef NO_USE_SCREEN virtual void* VideoData() const { return speccy->Device()->Screen(); } +#endif virtual void* VideoDataUI() const { #ifdef USE_UI @@ -73,48 +104,84 @@ static struct eSpeccyHandler : public eHandler, public eRZX::eHandler, public xZ return NULL; #endif//USE_UI } +#ifndef USE_MU_SIMPLIFICATIONS virtual const char* WindowCaption() const { return "Unreal Speccy Portable"; } +#endif virtual void OnKey(char key, dword flags); +#ifndef NO_USE_KEMPSTON +#ifndef USE_MU virtual void OnMouse(eMouseAction action, byte a, byte b); - virtual bool FileTypeSupported(const char* name) const - { +#endif +#endif + virtual bool FileTypeSupported(const char* name) const { const eFileType* t = eFileType::FindByName(name); return t && t->AbleOpen(); } virtual bool OnOpenFile(const char* name, const void* data, size_t data_size); +#ifdef USE_STREAM + virtual bool OnOpenFileCompressed(const char* name, const void* comressed_data, size_t compressed_data_size, size_t data_size); +#endif bool OpenFile(const char* name, const void* data, size_t data_size); +#ifndef NO_USE_SAVE virtual bool OnSaveFile(const char* name); +#endif virtual eActionResult OnAction(eAction action); +#ifndef USE_MU virtual int AudioSources() const { return FullSpeed() ? 0 : SOUND_DEV_COUNT; } virtual void* AudioData(int source) const { return sound_dev[source]->AudioData(); } virtual dword AudioDataReady(int source) const { return sound_dev[source]->AudioDataReady(); } virtual void AudioDataUse(int source, dword size) { sound_dev[source]->AudioDataUse(size); } virtual void AudioSetSampleRate(dword sample_rate); +#endif virtual void VideoPaused(bool paused) { paused ? ++video_paused : --video_paused; } + virtual bool IsVideoPaused() { + return video_paused; + } +#ifndef USE_MU_SIMPLIFICATIONS virtual int VideoFrame() const { return video_frame; } +#endif - virtual bool FullSpeed() const { return speccy->CPU()->HandlerStep() != NULL; } + virtual bool FullSpeed() const final { +#ifndef NO_USE_FAST_TAPE + return speccy->CPU()->HandlerStep() != NULL; +#else + return false; +#endif + } virtual eSpeccy* Speccy() const { return speccy; } void PlayMacro(eMacro* m) { SAFE_DELETE(macro); macro = m; } + +#ifndef NO_USE_REPLAY virtual bool RZX_OnOpenSnapshot(const char* name, const void* data, size_t data_size) { return OpenFile(name, data, data_size); } +#endif + virtual byte Z80_IoRead(word port, int tact) { byte r = 0xff; + // todo if not replaying there should be no handler.io +#ifndef NO_USE_REPLAY replay->IoRead(&r); +#endif return r; } +#ifndef NO_USE_REPLAY virtual bool GetReplayProgress(dword* frame_current, dword* frames_total, dword* frames_cached) { if(replay) return replay->GetProgress(frame_current, frames_total, frames_cached) == eRZX::E_OK; return false; } +#endif +#ifndef NO_USE_RZX const char* RZXErrorDesc(eRZX::eError err) const; +#endif + +#ifndef NO_USE_REPLAY void Replay(eRZX* r) { speccy->CPU()->HandlerIo(NULL); @@ -123,6 +190,7 @@ static struct eSpeccyHandler : public eHandler, public eRZX::eHandler, public xZ if(replay) speccy->CPU()->HandlerIo(this); } +#endif eSpeccy* speccy; #ifdef USE_UI @@ -146,9 +214,15 @@ void eSpeccyHandler::OnInit() ui_desktop = new xUi::eDesktop; ui_desktop->Insert(new xUi::eMainDialog); #endif//USE_UI +#ifndef NO_USE_BEEPER sound_dev[0] = speccy->Device(); +#endif +#ifndef NO_USE_AY sound_dev[1] = speccy->Device(); +#endif +#ifndef NO_USE_TAPE sound_dev[2] = speccy->Device(); +#endif xOptions::Load(); OnAction(A_RESET); } @@ -156,8 +230,12 @@ void eSpeccyHandler::OnDone() { xOptions::Store(); SAFE_DELETE(macro); +#ifndef NO_USE_REPLAY SAFE_DELETE(replay); +#endif +#ifndef NO_USE_DESTRUCTORS SAFE_DELETE(speccy); +#endif #ifdef USE_UI SAFE_DELETE(ui_desktop); #endif//USE_UI @@ -173,6 +251,7 @@ const char* eSpeccyHandler::OnLoop() if(!macro->Update()) SAFE_DELETE(macro); } +#ifndef NO_USE_REPLAY if(replay) { int icount = 0; @@ -191,6 +270,7 @@ const char* eSpeccyHandler::OnLoop() } } else +#endif speccy->Update(NULL); ++video_frame; } @@ -227,8 +307,12 @@ void eSpeccyHandler::OnKey(char key, dword flags) } #endif//USE_UI +#ifndef NO_USE_KEMPSTON +#ifndef USE_MU if(flags&KF_KEMPSTON) speccy->Device()->OnKey(key, down); +#endif +#endif if(flags&KF_CURSOR) { switch(key) @@ -264,6 +348,8 @@ void eSpeccyHandler::OnKey(char key, dword flags) } speccy->Device()->OnKey(key, down, shift, ctrl, alt); } +#ifndef NO_USE_KEMPSTON +#ifndef USE_MU void eSpeccyHandler::OnMouse(eMouseAction action, byte a, byte b) { switch(action) @@ -273,22 +359,66 @@ void eSpeccyHandler::OnMouse(eMouseAction action, byte a, byte b) default: break; } } +#endif +#endif bool eSpeccyHandler::OnOpenFile(const char* name, const void* data, size_t data_size) { +#ifndef USE_MU_SIMPLIFICATIONS OpLastFile(name); +#endif return OpenFile(name, data, data_size); } + +#ifdef USE_STREAM +static struct stream *open_stream(const void *data, size_t data_size) { + struct stream *stream; +#if !PICO_ON_DEVICE + // copy because the data may not be owned + byte *data_owned = new byte[data_size]; + memcpy(data_owned, data, data_size); + data = data_owned; +#endif +#ifndef USE_XIP_STREAM +#if !PICO_ON_DEVICE + bool owned = true; +#else + bool owned = false; +#endif + stream = memory_stream_open((const uint8_t *)data, data_size, owned); +#else + stream = xip_stream_open((const uint8_t *)data, data_size, 512, 10); +#endif + return stream; +} + +bool eSpeccyHandler::OnOpenFileCompressed(const char* name, const void* comressed_data, size_t compressed_data_size, size_t data_size) { + const eFileType* t = eFileType::FindByName(name); + if(!t) + return false; + + struct stream *underlying = open_stream(comressed_data, compressed_data_size); + struct stream *compressed_stream = compressed_stream_open(underlying, data_size); + return t->Open(compressed_stream); +} +#endif + bool eSpeccyHandler::OpenFile(const char* name, const void* data, size_t data_size) { const eFileType* t = eFileType::FindByName(name); if(!t) return false; - if(data && data_size) + if(data && data_size) { +#ifndef USE_STREAM return t->Open(data, data_size); +#else + return t->Open(open_stream(data, data_size)); +#endif + } return t->Open(name); } +#ifndef NO_USE_SAVE bool eSpeccyHandler::OnSaveFile(const char* name) { OpLastFile(name); @@ -307,29 +437,53 @@ bool eSpeccyHandler::OnSaveFile(const char* name) return t->Store(name); return false; } - +#endif +#ifndef NO_USE_FAST_TAPE static struct eOptionTapeFast : public xOptions::eOptionBool { eOptionTapeFast() { Set(true); } - virtual const char* Name() const { return "fast tape"; } + virtual const char* Name() const { +#ifndef USE_MU + return "fast tape"; +#else + return "Fast tape"; +#endif + } virtual int Order() const { return 50; } } op_tape_fast; +#endif static struct eOptionAutoPlayImage : public xOptions::eOptionBool { eOptionAutoPlayImage() { Set(true); } - virtual const char* Name() const { return "auto play image"; } + virtual const char* Name() const { +#ifndef USE_MU + return "auto play image"; +#else + return "Autoplay image"; +#endif + } virtual int Order() const { return 55; } } op_auto_play_image; -static struct eOption48K : public xOptions::eOptionBool +#ifndef NO_USE_128K +static struct eOption48K : public xOptions::eOptionBoolWithPending { - virtual const char* Name() const { return "mode 48k"; } + virtual const char* Name() const { +#ifndef USE_MU + return "mode 48k"; +#else + return "48K mode"; +#endif + } virtual void Change(bool next = true) { eOptionBool::Change(); +#ifndef USE_MU Apply(); +#endif } + virtual void Apply() { sh.OnAction(A_RESET); @@ -337,27 +491,45 @@ static struct eOption48K : public xOptions::eOptionBool virtual int Order() const { return 65; } } op_48k; +#ifndef NO_USE_SERVICE_ROM static struct eOptionResetToServiceRom : public xOptions::eOptionBool { virtual const char* Name() const { return "reset to service rom"; } virtual int Order() const { return 79; } } op_reset_to_service_rom; +#endif +#endif eActionResult eSpeccyHandler::OnAction(eAction action) { switch(action) { case A_RESET: +#ifndef NO_USE_REPLAY if(!inside_replay_update) // can be called from replay->Update() SAFE_DELETE(replay); +#endif SAFE_DELETE(macro); +#ifndef NO_USE_128K speccy->Mode48k(op_48k); +#endif speccy->Reset(); +#ifndef NO_USE_128K if(!speccy->Mode48k()) + { +#ifndef NO_USE_SERVICE_ROM speccy->Device()->SelectPage(op_reset_to_service_rom ? eRom::ROM_SYS : eRom::ROM_128_1); +#else + speccy->Device()->SelectPage(eRom::ROM_128_1); +#endif + } +#endif +#ifndef USE_HACKED_DEVICE_ABSTRACTION if(inside_replay_update) speccy->CPU()->HandlerIo(this); +#endif return AR_OK; +#ifndef NO_USE_TAPE case A_TAPE_TOGGLE: { eTape* tape = speccy->Device(); @@ -365,16 +537,26 @@ eActionResult eSpeccyHandler::OnAction(eAction action) return AR_TAPE_NOT_INSERTED; if(!tape->Started()) { +#ifndef NO_USE_FAST_TAPE if(op_tape_fast) speccy->CPU()->HandlerStep(fast_tape_emul); else speccy->CPU()->HandlerStep(NULL); +#endif tape->Start(); } else tape->Stop(); return tape->Started() ? AR_TAPE_STARTED : AR_TAPE_STOPPED; } + case A_TAPE_REWIND: + { + eTape *tape = speccy->Device(); + if(!tape->Inserted()) + return AR_TAPE_NOT_INSERTED; + tape->Rewind(); + return tape->Started() ? AR_TAPE_STARTED : AR_TAPE_STOPPED; + } case A_TAPE_QUERY: { eTape* tape = speccy->Device(); @@ -382,32 +564,51 @@ eActionResult eSpeccyHandler::OnAction(eAction action) return AR_TAPE_NOT_INSERTED; return tape->Started() ? AR_TAPE_STARTED : AR_TAPE_STOPPED; } +#endif +#ifndef NO_USE_FDD case A_DISK_QUERY: { eWD1793* wd1793 = speccy->Device(); return wd1793->DiskChanged(OpDrive()) ? AR_DISK_CHANGED : AR_DISK_NOT_CHANGED; } +#endif } return AR_ERROR; } +#ifndef USE_MU void eSpeccyHandler::AudioSetSampleRate(dword sample_rate) { +#ifndef NO_USE_AY speccy->Device()->SetTimings(SNDR_DEFAULT_SYSTICK_RATE, SNDR_DEFAULT_AY_RATE, sample_rate); +#endif +#ifndef NO_USE_BEEPER speccy->Device()->SetTimings(SNDR_DEFAULT_SYSTICK_RATE, sample_rate); +#endif +#ifndef NO_USE_TAPE speccy->Device()->SetTimings(SNDR_DEFAULT_SYSTICK_RATE, sample_rate); +#endif } +#endif - +#ifndef NO_USE_AY static void SetupSoundChip(); static struct eOptionSoundChip : public xOptions::eOptionInt { eOptionSoundChip() { Set(SC_AY); } enum eType { SC_FIRST, SC_AY = SC_FIRST, SC_YM, SC_LAST }; +#ifndef USE_MU virtual const char* Name() const { return "sound chip"; } +#else + virtual const char* Name() const { return "Sound Chip"; } +#endif virtual const char** Values() const { +#ifndef USE_MU static const char* values[] = { "ay", "ym", NULL }; +#else + static const char* values[] = { "AY", "YM", NULL }; +#endif return values; } virtual void Change(bool next = true) @@ -419,9 +620,10 @@ static struct eOptionSoundChip : public xOptions::eOptionInt { SetupSoundChip(); } - virtual int Order() const { return 24; } + virtual int Order() const { return 33; } }op_sound_chip; +#ifndef USE_FAST_AY static struct eOptionAYStereo : public xOptions::eOptionInt { eOptionAYStereo() { Set(AS_ABC); } @@ -443,12 +645,14 @@ static struct eOptionAYStereo : public xOptions::eOptionInt } virtual int Order() const { return 25; } }op_ay_stereo; +#endif void SetupSoundChip() { eOptionSoundChip::eType chip = (eOptionSoundChip::eType)(int)op_sound_chip; - eOptionAYStereo::eMode stereo = (eOptionAYStereo::eMode)(int)op_ay_stereo; eAY* ay = sh.speccy->Device(); +#ifndef USE_FAST_AY + eOptionAYStereo::eMode stereo = (eOptionAYStereo::eMode)(int)op_ay_stereo; const SNDCHIP_PANTAB* sndr_pan = SNDR_PAN_MONO; switch(stereo) { @@ -461,10 +665,15 @@ void SetupSoundChip() case eOptionAYStereo::AS_MONO: sndr_pan = SNDR_PAN_MONO; break; case eOptionAYStereo::AS_LAST: break; } +#endif ay->SetChip(chip == eOptionSoundChip::SC_AY ? eAY::CHIP_AY : eAY::CHIP_YM); +#ifndef USE_FAST_AY ay->SetVolumes(0x7FFF, chip == eOptionSoundChip::SC_AY ? SNDR_VOL_AY : SNDR_VOL_YM, sndr_pan); +#endif } +#endif +#ifndef NO_USE_REPLAY static struct eFileTypeRZX : public eFileType { virtual bool Open(const void* data, size_t data_size) const @@ -484,48 +693,67 @@ static struct eFileTypeRZX : public eFileType } virtual const char* Type() const { return "rzx"; } } ft_rzx; - +#endif +#ifndef NO_USE_Z80 static struct eFileTypeZ80 : public eFileType { +#ifndef USE_STREAM virtual bool Open(const void* data, size_t data_size) const { sh.OnAction(A_RESET); return xSnapshot::Load(sh.speccy, Type(), data, data_size); } +#else + virtual bool Open(struct stream *stream) const + { + sh.OnAction(A_RESET); + // to save memory we close tape + sh.speccy->Device()->CloseTape(); + // todo hack ... because we only have one inflator, the A_RESET above can mix in the old stream, so we reset again. + stream_reset(stream); + return xSnapshot::Load(sh.speccy, Type(), stream); + } +#endif virtual const char* Type() const { return "z80"; } } ft_z80; +#endif +#ifndef NO_USE_SZX static struct eFileTypeSZX : public eFileTypeZ80 { virtual const char* Type() const { return "szx"; } } ft_szx; +#endif +#ifndef NO_USE_SNA static struct eFileTypeSNA : public eFileTypeZ80 { +#ifndef NO_USE_SAVE virtual bool Store(const char* name) const { return xSnapshot::Store(sh.speccy, name); } +#endif virtual const char* Type() const { return "sna"; } } ft_sna; - -class eMacroDiskRunBootable : public eMacro -{ - virtual bool Do() - { - switch(frame) - { - case 70: sh.OnKey('7', KF_DOWN|KF_SHIFT|KF_UI_SENDER); break; - case 72: sh.OnKey('7', KF_UI_SENDER); break; - case 100: sh.OnKey('e', KF_DOWN|KF_UI_SENDER); break; - case 102: sh.OnKey('e', KF_UI_SENDER); break; - case 140: sh.OnKey('R', KF_DOWN|KF_UI_SENDER); break; - case 142: sh.OnKey('R', KF_UI_SENDER); break; - case 160: sh.OnKey('e', KF_DOWN|KF_UI_SENDER); break; - case 162: sh.OnKey('e', KF_UI_SENDER); - return false; - } - return true; - } -}; +#endif +class eMacroDiskRunBootable : public eMacro +{ + virtual bool Do() + { + switch(frame) + { + case 70: sh.OnKey('7', KF_DOWN|KF_SHIFT|KF_UI_SENDER); break; + case 72: sh.OnKey('7', KF_UI_SENDER); break; + case 100: sh.OnKey('e', KF_DOWN|KF_UI_SENDER); break; + case 102: sh.OnKey('e', KF_UI_SENDER); break; + case 140: sh.OnKey('R', KF_DOWN|KF_UI_SENDER); break; + case 142: sh.OnKey('R', KF_UI_SENDER); break; + case 160: sh.OnKey('e', KF_DOWN|KF_UI_SENDER); break; + case 162: sh.OnKey('e', KF_UI_SENDER); + return false; + } + return true; + } +}; class eMacroDiskRunWithMaxPetrov : public eMacro { @@ -543,6 +771,7 @@ class eMacroDiskRunWithMaxPetrov : public eMacro } }; +#ifndef NO_USE_FDD static struct eFileTypeTRD : public eFileType { virtual bool Open(const void* data, size_t data_size) const @@ -552,21 +781,21 @@ static struct eFileTypeTRD : public eFileType if(ok && op_auto_play_image) { sh.OnAction(A_RESET); - if(sh.speccy->Mode48k()) + if(sh.speccy->Mode48k()) { - sh.speccy->Device()->SelectPage(eRom::ROM_DOS); + sh.speccy->Device()->SelectPage(eRom::ROM_DOS); } else { - if(wd->Bootable(OpDrive())) - { - sh.speccy->Device()->SelectPage(eRom::ROM_128_1); - sh.PlayMacro(new eMacroDiskRunBootable); - } - else + if(wd->Bootable(OpDrive())) + { + sh.speccy->Device()->SelectPage(eRom::ROM_128_1); + sh.PlayMacro(new eMacroDiskRunBootable); + } + else { - sh.speccy->Device()->SelectPage(eRom::ROM_SYS); - sh.PlayMacro(new eMacroDiskRunWithMaxPetrov); + sh.speccy->Device()->SelectPage(eRom::ROM_SYS); + sh.PlayMacro(new eMacroDiskRunWithMaxPetrov); } } } @@ -600,6 +829,7 @@ static struct eFileTypeTD0 : public eFileTypeTRD { virtual const char* Type() const { return "td0"; } } ft_td0; +#endif class eMacroTapeLoad : public eMacro { @@ -628,15 +858,19 @@ class eMacroTapeLoad : public eMacro break; case 122: sh.OnKey('e', KF_UI_SENDER); +#ifndef NO_USE_TAPE sh.OnAction(A_TAPE_TOGGLE); +#endif return false; } return true; } }; +#ifndef NO_USE_TAPE static struct eFileTypeTAP : public eFileType { +#ifndef USE_STREAM virtual bool Open(const void* data, size_t data_size) const { bool ok = sh.speccy->Device()->Open(Type(), data, data_size); @@ -648,16 +882,34 @@ static struct eFileTypeTAP : public eFileType } return ok; } +#else + virtual bool Open(struct stream *stream) const + { + bool ok = sh.speccy->Device()->Open(Type(), stream); + if(ok && op_auto_play_image) + { + sh.OnAction(A_RESET); + sh.speccy->Devices().Get()->SelectPage(sh.speccy->Devices().Get()->ROM_SOS()); + sh.PlayMacro(new eMacroTapeLoad); + } + return ok; + } +#endif virtual const char* Type() const { return "tap"; } } ft_tap; +#ifndef NO_USE_CSW static struct eFileTypeCSW : public eFileTypeTAP { virtual const char* Type() const { return "csw"; } } ft_csw; +#endif +#ifndef NO_USE_TZX static struct eFileTypeTZX : public eFileTypeTAP { virtual const char* Type() const { return "tzx"; } } ft_tzx; +#endif +#endif } //namespace xPlatform diff --git a/std.h b/std.h index 262af33..ec8216a 100644 --- a/std.h +++ b/std.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -19,14 +20,23 @@ along with this program. If not, see . #ifndef __STD_H__ #define __STD_H__ +#include "pico.h" + #include #include #include #include -#include #include "std_types.h" +#if defined(USE_MU) && PICO_ON_DEVICE +#define __game_data const __attribute__((section(".rodata.game_data"))) + +#else +#define __time_critical +#define __game_data const +#endif + #pragma once #endif//__STD_H__ diff --git a/stream/CMakeLists.txt b/stream/CMakeLists.txt new file mode 100644 index 0000000..40f5978 --- /dev/null +++ b/stream/CMakeLists.txt @@ -0,0 +1,12 @@ +add_library(stream INTERFACE) + +target_sources(stream INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/stream.c + ${CMAKE_CURRENT_LIST_DIR}/stream.h + ) + +target_include_directories(stream INTERFACE ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries(stream INTERFACE miniz) +if (TARGET hardware_dma) + target_link_libraries(stream INTERFACE hardware_dma) +endif() diff --git a/stream/stream.c b/stream/stream.c new file mode 100644 index 0000000..ec87e8e --- /dev/null +++ b/stream/stream.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "stream.h" +#ifdef ENABLE_COMPRESSED_STREAM +#include "miniz_tinfl.h" +#endif +//#include "mu_alloc.h" +#if PICO_ON_DEVICE +#include "hardware/dma.h" +#include "hardware/structs/xip_ctrl.h" +#endif +#include "hardware/gpio.h" + +CU_REGISTER_DEBUG_PINS(xip_stream_dma) + +// ---- select at most one --- +//CU_SELECT_DEBUG_PINS(xip_stream_dma) + +struct memory_stream { + struct stream stream; + const uint8_t *data; + size_t data_size; + uint32_t pos; + bool data_owned; +}; + +static inline struct memory_stream *to_ms(struct stream *s) { + assert(s); + assert(s->funcs->type == memory); + return (struct memory_stream *)s; +} + +int32_t memory_stream_read(struct stream *s, uint8_t *buffer, size_t len, bool all_data_required) { + struct memory_stream *ms = to_ms(s); + int32_t avail = ms->data_size - ms->pos; + if (len > avail) { + // todo we should hard_assert i.e. bkpt even with NDEBUG + assert(all_data_required); + len = avail; + } + memcpy(buffer, ms->data + ms->pos, len); + ms->pos += len; + return len; +} + +const uint8_t *memory_stream_peek(struct stream *s, size_t min_len, size_t *available_len, bool *available_reaches_eos) { + struct memory_stream *ms = to_ms(s); + size_t available = ms->data_size - ms->pos; + // we always reach the end of the stream as it is right there in memory! + if (available_reaches_eos) *available_reaches_eos = true; + if (available_len) *available_len = available; + if (available >= min_len) { + return ms->data + ms->pos; + } + return NULL; +} + +void memory_stream_reset(struct stream *s) { + to_ms(s)->pos = 0; +} + +bool memory_stream_skip(struct stream *s, size_t bytes) { + struct memory_stream *ms = to_ms(s); + uint32_t max = ms->pos + bytes; + if (max >= ms->data_size) { + bool rc = max == ms->data_size; + ms->pos = ms->data_size; + return rc; + } else { + ms->pos = max; + return true; + } +} + +void memory_stream_close(struct stream *s) { + struct memory_stream *ms = to_ms(s); + if (ms->data_owned) free((void*)ms->data); + free(ms); +} + +uint32_t memory_stream_pos(struct stream *s) { + return to_ms(s)->pos; +} + +bool memory_stream_is_eos(struct stream *s) { + struct memory_stream *ms = to_ms(s); + return ms->pos == ms->data_size; +} + +const struct stream_funcs memory_stream_funcs = { + .reset = memory_stream_reset, + .skip = memory_stream_skip, + .peek = memory_stream_peek, + .read = memory_stream_read, + .close = memory_stream_close, + .is_eos = memory_stream_is_eos, +#ifndef NDEBUG + .pos = memory_stream_pos, +#endif +#ifndef NDEBUG + .type = memory +#endif +}; + +struct stream *memory_stream_open(const uint8_t *buffer, size_t size, bool own_buffer) { + struct memory_stream *ms = (struct memory_stream *)calloc(1, sizeof(struct memory_stream)); + ms->stream.funcs = &memory_stream_funcs; + ms->stream.size = size; + ms->data = buffer; + ms->data_size = size; + ms->data_owned = own_buffer; + return &ms->stream; +} + +#ifdef ENABLE_COMPRESSED_STREAM +#ifdef USE_SINGLE_COMPRESSED_STREAM_INSTANCE +static tinfl_decompressor inflator; +static uint8_t inflator_buffer[MAX_COMPRESSED_STREAM_PEEK + TINFL_LZ_DICT_SIZE]; +#else +#error no +#endif +struct compressed_stream { + struct stream self; + struct stream *underlying; + uint8_t *logical_buffer; + int32_t buf_base_pos; + int32_t buf_current_index; // index into logical buffer (could be negative down to -MAX_PEEK) + int32_t buf_end_index; +}; + +static void compressed_stream_fill(struct compressed_stream *cs) { + cs->buf_base_pos += cs->buf_end_index; // old buffer complete + cs->buf_current_index = 0; + tinfl_status status; + size_t in_bytes, out_bytes; + bool eos; + // todo this isn't ideal; we should really allow returing when there is not enough input available and we have output + // however, we need to make sure we preserve the buffer so far exactly in that case + int decode_offset = 0; + DEBUG_PINS_SET(xip_stream_dma, 1); + DEBUG_PINS_XOR(xip_stream_dma, 2); + DEBUG_PINS_XOR(xip_stream_dma, 2); + do { + out_bytes = TINFL_LZ_DICT_SIZE - decode_offset; + const uint8_t *next_in = stream_peek_avail(cs->underlying, 1, &in_bytes, &eos); + if (!in_bytes) break; + assert(next_in); + status = tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, cs->logical_buffer, + (mz_uint8 *)cs->logical_buffer + decode_offset, &out_bytes, + (eos ? 0 : TINFL_FLAG_HAS_MORE_INPUT) | + TINFL_FLAG_PARSE_ZLIB_HEADER); + // we should always be deocompressing a whole buffer full unless we are at the end + assert(status == TINFL_STATUS_DONE || status == TINFL_STATUS_NEEDS_MORE_INPUT || (status == TINFL_STATUS_HAS_MORE_OUTPUT && out_bytes == TINFL_LZ_DICT_SIZE - decode_offset)); + stream_skip(cs->underlying, in_bytes); + decode_offset += out_bytes; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + DEBUG_PINS_SET(xip_stream_dma, 1); + DEBUG_PINS_XOR(xip_stream_dma, 4); + DEBUG_PINS_XOR(xip_stream_dma, 4); + cs->buf_end_index = decode_offset; +} + +static inline struct compressed_stream *to_cs(struct stream *s) { + assert(s); + assert(s->funcs->type == compressed); + return (struct compressed_stream *)s; +} + +int32_t compressed_stream_read_internal(struct compressed_stream *cs, uint8_t *buffer, size_t len, bool all_data_required) { + int32_t read = 0; + int32_t remaining = len; + int32_t avail = cs->buf_end_index - cs->buf_current_index; + while (remaining > 0 && avail > 0) { + int to_copy = MIN(remaining, avail); + if (buffer) memcpy(buffer + read, cs->logical_buffer + cs->buf_current_index, to_copy); + read += to_copy; + cs->buf_current_index += to_copy; + remaining -= to_copy; + avail -= to_copy; + if (!avail) { + compressed_stream_fill(cs); + avail = cs->buf_end_index - cs->buf_current_index; + } + } + if (all_data_required) assert(len == read); + return read; +} + +int32_t compressed_stream_read(struct stream *s, uint8_t *buffer, size_t len, bool all_data_required) { + struct compressed_stream *cs = to_cs(s); + return compressed_stream_read_internal(cs, buffer, len, all_data_required); +} + +const uint8_t *compressed_stream_peek(struct stream *s, size_t min_len, size_t *available_len, bool *available_reaches_eos) { + assert(min_len < MAX_COMPRESSED_STREAM_PEEK); + struct compressed_stream *cs = to_cs(s); + int32_t avail = cs->buf_end_index - cs->buf_current_index; + if (avail && avail < min_len) + { + // todo this is wrong... we should try filling more b4 we move data once + // we no longer always fill an entire buffer + // move the remaining data b4 the main buffer area + memcpy(cs->logical_buffer - avail, cs->logical_buffer + cs->buf_current_index, avail); + compressed_stream_fill(cs); + // back up a bit + cs->buf_current_index = -avail; + avail = cs->buf_end_index - cs->buf_current_index; + } + if (available_len) *available_len = avail; + if (available_reaches_eos) *available_reaches_eos = stream_is_eos(cs->underlying); + if (min_len <= avail) { + return cs->logical_buffer + cs->buf_current_index; + } else { + return NULL; + } +} + +void compressed_stream_reset(struct stream *s) { + struct compressed_stream *cs = to_cs(s); + stream_reset(cs->underlying); + tinfl_init(&inflator); + cs->buf_base_pos = 0; + cs->buf_end_index = 0; + compressed_stream_fill(cs); +} + +bool compressed_stream_skip(struct stream *s, size_t bytes) { + struct compressed_stream *cs = to_cs(s); + return bytes == compressed_stream_read_internal(cs, NULL, bytes, false); +} + +void compressed_stream_close(struct stream *s) { + struct compressed_stream *cs = to_cs(s); + stream_close(cs->underlying); + free(cs); +} + +uint32_t compressed_stream_pos(struct stream *s) { + struct compressed_stream *cs = to_cs(s); + return cs->buf_base_pos + cs->buf_current_index; +} + +bool compressed_stream_is_eos(struct stream *s) { + struct compressed_stream *cs = to_cs(s); + return cs->buf_end_index - cs->buf_current_index != 0; +} + +const struct stream_funcs compressed_stream_funcs = { + .reset = compressed_stream_reset, + .skip = compressed_stream_skip, + .peek = compressed_stream_peek, + .read = compressed_stream_read, + .close = compressed_stream_close, + .is_eos = compressed_stream_is_eos, +#ifndef NDEBUG + .pos = compressed_stream_pos, +#endif +#ifndef NDEBUG + .type = compressed +#endif +}; + +struct stream *compressed_stream_open(struct stream *underlying, size_t uncompressed_size) { + struct compressed_stream *cs = (struct compressed_stream *)calloc(1, sizeof(struct compressed_stream)); + cs->self.funcs = &compressed_stream_funcs; + cs->self.size = uncompressed_size; + cs->underlying = underlying; + cs->logical_buffer = inflator_buffer + MAX_COMPRESSED_STREAM_PEEK; + compressed_stream_reset(&cs->self); + return &cs->self; +} +#endif + +struct xip_stream { + struct stream self; + const uint8_t *src; // word aligned + int32_t transfer_start; + int32_t transfer_size; + uint8_t *buffer; // word aligned + size_t buffer_size; + int32_t buf_read; + int32_t buf_write; + int32_t buf_count; + uint32_t buf_read_absolute_offset; // offset within stream is (this + buf_read) + uint dma_channel; // todo use ptr + bool transfer_in_progress; +}; + +static inline struct xip_stream *to_xs(struct stream *s) { + assert(s); + assert(s->funcs->type == xip); + return (struct xip_stream *)s; +} + +static inline bool xip_stream_dma_busy(struct xip_stream *xs) { +#if PICO_ON_DEVICE + return dma_channel_is_busy(xs->dma_channel); +#else + return false; +#endif +} + +static void xip_stream_start_dma(struct xip_stream *xs, const uint8_t *src, uint8_t *dest, uint32_t words) { + assert(!xs->transfer_in_progress); + xs->transfer_size = words * 4; +#if PICO_ON_DEVICE + xip_ctrl_hw->stream_addr = (uintptr_t)src; + xip_ctrl_hw->stream_ctr = words; + dma_channel_transfer_to_buffer_now(xs->dma_channel, dest, words); +#else + memcpy(dest, src, words*4); +#endif + DEBUG_PINS_SET(xip_stream_dma, 2); + xs->transfer_in_progress = true; +} + +static void xip_stream_cancel_dma(struct xip_stream *xs) { +#if PICO_ON_DEVICE + while (dma_channel_is_busy(xs->dma_channel)); + xip_ctrl_hw->stream_ctr = 0; + dma_channel_abort(xs->dma_channel); + while (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY)) { + xip_ctrl_hw->stream_fifo; + } +#endif + DEBUG_PINS_CLR(xip_stream_dma, 2); + xs->transfer_in_progress = false; +} + +// return true if more data was made available +static bool xip_stream_background_fill(struct xip_stream *xs, bool block) { + bool rc = false; + bool polling = false; + if (block) { + DEBUG_PINS_SET(xip_stream_dma, 4); + } + do { + if (xs->transfer_in_progress) { + assert(xs->transfer_size); + // we take as much as we can from the stream in progress +#if PICO_ON_DEVICE + uint8_t *write_addr = (uint8_t *)dma_channel_hw_addr(xs->dma_channel)->write_addr; + int write_pos = write_addr - xs->buffer; + assert(write_pos >= xs->buf_write && write_pos <= xs->buffer_size); + int new_available = write_pos - xs->buf_write; +#else + int new_available = xs->transfer_size; +#endif + if (new_available > 0) { + if (new_available >= xs->transfer_size) { + assert(!xip_stream_dma_busy(xs)); + assert(new_available == xs->transfer_size); + // we have reached the end + xs->transfer_in_progress = false; + DEBUG_PINS_CLR(xip_stream_dma, 2); + } + xs->buf_write += new_available; + if (xs->buf_write == xs->buffer_size) { + xs->buf_write = 0; + } + xs->buf_count += new_available; + xs->transfer_start += new_available; + xs->transfer_size -= new_available; + rc = true; + } else { + // todo check for error ... + if (!polling) { + DEBUG_PINS_SET(xip_stream_dma, 1); + polling = true; + } + } + } + if (!xs->transfer_in_progress) { + int32_t space_words = (xs->buffer_size - xs->buf_count) >> 2; + // todo if we used aligned buffer, we can use wrapping DMA + int32_t linear_space_words = (xs->buffer_size - xs->buf_write) >> 2; + // adjust for buffer space + if (space_words < linear_space_words) { + linear_space_words = space_words; + } + //adjust for end of stream + if (linear_space_words * 4 > (xs->self.size - xs->transfer_start)) { + linear_space_words = (xs->self.size + 3 - xs->transfer_start) >> 2; + } + if (block || linear_space_words > 4 || linear_space_words < space_words) { + if (linear_space_words) { + xip_stream_start_dma(xs, xs->src + xs->transfer_start, xs->buffer + xs->buf_write, + linear_space_words); + } + } + } + } while (!rc && block && xs->transfer_in_progress); + if (polling) { + DEBUG_PINS_CLR(xip_stream_dma, 1); + } + if (block) { + DEBUG_PINS_CLR(xip_stream_dma, 4); + } + return rc; +} + +int32_t xip_stream_read(struct stream *s, uint8_t *buffer, size_t len, bool all_data_required) { + struct xip_stream *xs = to_xs(s); + int32_t remaining = len; + while (remaining > 0) { + // do we have any data already + int to_copy = MIN(remaining, xs->buf_count); + if (to_copy) { + if (xs->buf_read + to_copy <= xs->buffer_size) { + memcpy(buffer, xs->buffer + xs->buf_read, to_copy); + xs->buf_read += to_copy; + if (xs->buf_read == xs->buffer_size) { + xs->buf_read = 0; + xs->buf_read_absolute_offset += xs->buffer_size; // we have progressed another buffer full + } + } else { + int first_fragment_size = xs->buffer_size - xs->buf_read; + memcpy(buffer, xs->buffer + xs->buf_read, first_fragment_size); + memcpy(buffer + first_fragment_size, xs->buffer, to_copy - first_fragment_size); + xs->buf_read = to_copy - first_fragment_size; + xs->buf_read_absolute_offset += xs->buffer_size; // we have progressed another buffer full + } + buffer += to_copy; + remaining -= to_copy; + xs->buf_count -= to_copy; + xip_stream_background_fill(xs, false); + if (!all_data_required) break; + } else { + if (!xip_stream_background_fill(xs, true)) { + break; + } + } + } + if (all_data_required) assert(!remaining); + return len - remaining; +} + +void xip_stream_reset(struct stream *s) { + struct xip_stream *xs = to_xs(s); + xip_stream_cancel_dma(xs); + xs->buf_read = xs->buf_write = xs->buf_count = 0; + xs->buf_read_absolute_offset = 0; + xs->transfer_start = 0; + xs->transfer_in_progress = false; +#ifndef NDEBUG + xs->transfer_size = 0; +#endif + xip_stream_background_fill(xs, false); +} + +bool xip_stream_skip(struct stream *s, size_t len) { + struct xip_stream *xs = to_xs(s); + assert(len <= xs->buffer_size); + while (len != 0) { + int skip = MIN(xs->buf_count, len); + if (skip) { + xs->buf_read += skip; + len -= skip; + if (xs->buf_read >= xs->buffer_size) { + xs->buf_read -= xs->buffer_size; + xs->buf_read_absolute_offset += xs->buffer_size; + } + xs->buf_count -= skip; + } else { + if (xs->transfer_in_progress) { + if (skip > xs->transfer_size) { + xip_stream_cancel_dma(xs); + } else { + // not this may make a best effort attempt to return some data without necessarily waiting + // for the DMA transfer to complete + if (!xip_stream_background_fill(xs, true)) { + // there is no more data + break; + } + continue; + } + } + // now we have no transfer in progress and an empty buffer + assert(!xs->transfer_in_progress); + assert(!xs->buf_count); + xs->buf_read_absolute_offset += xs->buf_read; + xs->buf_read = xs->buf_write = 0; + skip = MIN(len, xs->self.size - xs->buf_read_absolute_offset); + len -= skip; + xs->buf_read_absolute_offset += skip; + xip_stream_background_fill(xs, false); + } + } + return len == 0; +} + +void xip_stream_close(struct stream *s) { + struct xip_stream *xs = to_xs(s); + xip_stream_cancel_dma(xs); + free(xs->buffer); + free(xs); +} + +uint32_t xip_stream_pos(struct stream *s) { + struct xip_stream *xs = to_xs(s); + return xs->buf_read + xs->buf_read_absolute_offset; +} + +const uint8_t *xip_stream_peek(struct stream *s, size_t min_len, size_t *available_len, bool *eos) { + assert(min_len == 1); // this isn't general yet because we don't handle split buffers; 1 byte is unsplittable! + struct xip_stream *xs = to_xs(s); + if (!xs->buf_count) { + xip_stream_background_fill(xs, true); + } + int avail; + if (xs->buf_count + xs->buf_read > xs->buffer_size) { + avail = xs->buffer_size - xs->buf_read; + if (eos) *eos = false; + assert(avail > 0); + } else { + avail = xs->buf_count; + if (eos) *eos = xs->self.size == xs->transfer_start; + } + if (available_len) *available_len = avail; + if (min_len <= avail) { + return xs->buffer + xs->buf_read; + } else { + return NULL; + } +} + +bool xip_stream_is_eos(struct stream *s) { + struct xip_stream *xs = to_xs(s); + return !xs->buf_count && !xs->transfer_in_progress; +} + +const struct stream_funcs xip_stream_funxs = { + .reset = xip_stream_reset, + .skip = xip_stream_skip, + .peek = xip_stream_peek, + .read = xip_stream_read, + .close = xip_stream_close, + .is_eos = xip_stream_is_eos, +#ifndef NDEBUG + .pos = xip_stream_pos, +#endif +#ifndef NDEBUG + .type = xip +#endif +}; + +struct stream *xip_stream_open(const uint8_t *src, size_t size, size_t buffer_size, uint dma_channel) { + struct xip_stream *xs = (struct xip_stream *)calloc(1, sizeof(struct xip_stream)); +#if PICO_ON_DEVICE + assert(0x1u == (((uintptr_t)src)>>28u)); +#endif + xs->self.funcs = &xip_stream_funxs; + xs->self.size = size; + xs->src = src; + xs->buffer = calloc(1, buffer_size); + xs->buffer_size = buffer_size; + xs->dma_channel = dma_channel; + +#if PICO_ON_DEVICE + xip_ctrl_hw->stream_ctr = 0; + xip_ctrl_hw->stream_addr = (uintptr_t)src; + dma_channel_config c = dma_channel_get_default_config(dma_channel); + channel_config_set_read_increment(&c, false); + channel_config_set_write_increment(&c, true); + channel_config_set_dreq(&c, DREQ_XIP_STREAM); + dma_channel_set_read_addr(dma_channel, (void *)XIP_AUX_BASE, false); + dma_channel_set_config(dma_channel, &c, false); +#endif + xip_stream_reset(&xs->self); + return &xs->self; +} diff --git a/stream/stream.h b/stream/stream.h new file mode 100644 index 0000000..de8b1a0 --- /dev/null +++ b/stream/stream.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 Graham Sanderson + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STREAM_H +#define STREAM_H + +#include "pico/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef ENABLE_COMPRESSED_STREAM +// statically allocate all state (this is the only option right now!) +#define USE_SINGLE_COMPRESSED_STREAM_INSTANCE +#define MAX_COMPRESSED_STREAM_PEEK 128 +#endif + +struct stream; + +typedef int32_t (*stream_read_func)(struct stream *stream, uint8_t *buffer, size_t len, bool all_data_required); + +typedef const uint8_t *(*stream_peek_func)(struct stream *stream, size_t min_len, size_t *available_len, bool *eof); + +typedef void (*stream_close_func)(struct stream *stream); + +typedef void (*stream_reset_func)(struct stream *stream); + +typedef bool (*stream_skip_func)(struct stream *stream, size_t bytes); + +typedef bool (*stream_is_eos_func)(struct stream *stream); + +#ifndef NDEBUG +// only used by debug atm +typedef uint32_t (*stream_pos_func)(struct stream *stream); +#endif + +enum stream_type { + memory, + compressed, + xip +}; + +struct stream_funcs { + stream_read_func read; + stream_peek_func peek; + stream_close_func close; + stream_reset_func reset; + stream_skip_func skip; + stream_is_eos_func is_eos; +#ifndef NDEBUG + stream_pos_func pos; +#endif +#ifndef NDEBUG + enum stream_type type; +#endif +}; + +struct stream { + const struct stream_funcs *funcs; + size_t size; +}; + +struct memory_stream; +struct compressed_stream; + +struct stream *memory_stream_open(const uint8_t *buffer, size_t size, bool free_buffer); + +struct stream *xip_stream_open(const uint8_t *buffer, size_t size, size_t buffer_size, uint dma_channel); + +#ifdef ENABLE_COMPRESSED_STREAM + +struct stream *compressed_stream_open(struct stream *underlying, size_t uncompressed_size); + +#endif + +// return number of bytes read, -1 for end of file +inline static int32_t stream_read(struct stream *stream, uint8_t *buffer, size_t len, bool all_data_required) +{ + return stream->funcs->read(stream, buffer, len, all_data_required); +} + +inline static const uint8_t *stream_peek(struct stream *stream, size_t min_len) +{ + return stream->funcs->peek(stream, min_len, NULL, NULL); +} + +inline static const uint8_t *stream_peek_avail(struct stream *stream, size_t min_len, size_t *available_len, bool *eof) +{ + return stream->funcs->peek(stream, min_len, available_len, eof); +} + +inline static void stream_reset(struct stream *stream) +{ + stream->funcs->reset(stream); +} + +#ifndef NDEBUG +inline static uint32_t stream_pos(struct stream *stream) { + return stream->funcs->pos(stream); +} +#endif + +inline static void stream_close(struct stream *stream) +{ + stream->funcs->close(stream); +} + +inline static bool stream_skip(struct stream *stream, size_t size) +{ + return stream->funcs->skip(stream, size); +} + +inline static bool stream_is_eos(struct stream *stream) +{ + return stream->funcs->is_eos(stream); +} + +#ifdef __cplusplus +} +#endif + +#endif //SOFTWARE_STREAM_H \ No newline at end of file diff --git a/tools/io_select.h b/tools/io_select.h index 548231f..1576d6f 100755 --- a/tools/io_select.h +++ b/tools/io_select.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2015 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -27,7 +28,9 @@ namespace xIo class eFileSelect { public: +#ifndef NO_USE_DESTRUCTORS virtual ~eFileSelect() {} +#endif virtual bool Valid() const = 0; virtual void Next() = 0; virtual const char* Name() const = 0; diff --git a/tools/list.h b/tools/list.h index 2044c6f..70133f9 100755 --- a/tools/list.h +++ b/tools/list.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -31,7 +32,8 @@ template class eList next = n; } static T* First() { return _First(); } - T* Next() { return next; } + + virtual T* Next() { return next; } const T* Next() const { return next; } protected: static T*& _First() { static T* first = NULL; return first; } diff --git a/tools/options.cpp b/tools/options.cpp index e05c09d..ad8bee1 100644 --- a/tools/options.cpp +++ b/tools/options.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -56,7 +57,7 @@ const char* eOptionInt::Value() const const char** vals = Values(); if(!vals) return NULL; - return vals[value]; + return vals[*this]; } void eOptionInt::Value(const char* v) { @@ -79,7 +80,7 @@ const char* eOptionBool::Value() const const char** vals = Values(); if(!vals) return NULL; - return vals[value ? 1 : 0]; + return vals[*this ? 1 : 0]; } void eOptionBool::Value(const char* v) { @@ -96,6 +97,12 @@ const char** eOptionBool::Values() const static const char* values[] = { "off", "on", NULL }; return values; } +const char** eOptionBoolYesNo::Values() const +{ + static const char* values[] = { "no", "yes", NULL }; + return values; +} + void eOptionString::Value(const char* v) { int s = strlen(v) + 1; diff --git a/tools/options.h b/tools/options.h index 9a9b330..451130f 100755 --- a/tools/options.h +++ b/tools/options.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -30,11 +31,25 @@ namespace xOptions class eOptionB : public eList { public: - eOptionB() : customizable(true), storeable(true) {} + eOptionB() : customizable(true), storeable(true) +#ifdef USE_MU + , is_action(false), is_change_pending(false) +#endif + {} +#ifndef NO_USE_DESTRUCTORS virtual ~eOptionB() {} +#endif bool Customizable() const { return customizable; } bool Storeable() const { return storeable; } +#ifdef USE_MU + bool IsAction() const { return is_action; } + // some options when Set or Change are called + bool IsChangePending() const { return is_change_pending; } + virtual void Complete(bool accept = true) { + if (accept) Apply(); + } +#endif virtual const char* Name() const = 0; virtual const char* Value() const { return NULL; } @@ -49,13 +64,20 @@ class eOptionB : public eList protected: bool customizable; bool storeable; +#ifdef USE_MU + bool is_action; + bool is_change_pending; +#endif }; template class eOption : public eOptionB { public: - operator const T&() const { return value; } + virtual operator const T&() const { return value; } virtual void Set(const T& v) { value = v; } +#ifdef USE_MU + virtual void SetNow(const T& v) { Set(v); Complete(); } +#endif static eOption* Find(const char* name) { return (eOption*)eOptionB::Find(name); } protected: @@ -72,6 +94,31 @@ class eOptionInt : public eOption void Change(int f, int l, bool next = true); }; +class eOptionIntWithPending : public eOptionInt { +public: + virtual operator const int&() const override { return is_change_pending ? pending_value : value; } + + void Complete(bool accept) override { + if (accept && is_change_pending) { + SetNow(pending_value); + } + is_change_pending = false; + } + + void Set(const int& v) override { + pending_value = v; + is_change_pending = (v != value); + } + + void SetNow(const int & v) override { + value = v; + is_change_pending = false; + Apply(); + } +protected: + int pending_value; +}; + class eOptionBool : public eOption { public: @@ -79,14 +126,47 @@ class eOptionBool : public eOption virtual const char* Value() const; virtual void Value(const char* v); virtual const char** Values() const; - virtual void Change(bool next = true) { Set(!value); } + virtual void Change(bool next = true) { Set(!(bool)*this); } +}; + +class eOptionBoolWithPending : public eOptionBool { +public: + virtual operator const bool&() const override { return is_change_pending ? pending_value : value; } + + void Complete(bool accept) override { + if (accept && is_change_pending) { + SetNow(pending_value); + } + is_change_pending = false; + } + + void Set(const bool& v) override { + pending_value = v; + is_change_pending = (v != value); + } + + void SetNow(const bool & v) override { + value = v; + is_change_pending = false; + Apply(); + } +protected: + bool pending_value; +}; + +class eOptionBoolYesNo : public eOptionBool +{ +public: + virtual const char** Values() const; }; class eOptionString : public eOption { public: eOptionString() : alloc_size(32) { value = new char[alloc_size]; Value(""); } +#ifndef NO_USE_DESTRUCTORS virtual ~eOptionString() { SAFE_DELETE_ARRAY(value); } +#endif virtual const char* Value() const { return value; } virtual void Value(const char* v); virtual void Set(const char*& v) { Value(v); } diff --git a/z80/z80.cpp b/z80/z80.cpp index 68cf6dd..1a5ca86 100644 --- a/z80/z80.cpp +++ b/z80/z80.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -32,11 +33,18 @@ namespace xZ80 eZ80::eZ80(eMemory* _m, eDevices* _d, dword _frame_tacts) : memory(_m), rom(_d->Get()), ula(_d->Get()), devices(_d) , t(0), im(0), eipos(0) - , frame_tacts(_frame_tacts), fetches(0), reg_unused(0) + , frame_tacts(_frame_tacts), +#ifndef NO_USE_REPLAY + fetches(0), +#endif + reg_unused(0) { pc = sp = ir = memptr = ix = iy = 0; bc = de = hl = af = alt.bc = alt.de = alt.hl = alt.af = 0; int_flags = 0; +#ifndef NDEBUG + bp_addr = -1; +#endif InitOpNoPrefix(); InitOpCB(); @@ -58,7 +66,9 @@ eZ80::eZ80(eMemory* _m, eDevices* _d, dword _frame_tacts) //----------------------------------------------------------------------------- void eZ80::Reset() { +#ifndef NO_USE_FAST_TAPE handler.step = NULL; +#endif handler.io = NULL; int_flags = 0; ir = 0; @@ -72,13 +82,44 @@ inline byte eZ80::Read(word addr) const { return memory->Read(addr); } + +inline byte eZ80::ReadInc(int& addr) const +{ + return memory->Read(addr++); +} + +inline int eZ80::Read2(word addr) const +{ + unsigned r = memory->Read(addr); + r += memory->Read(addr+1) << 8u; + return r; +} + +inline int eZ80::Read2Inc(int& addr) const +{ + unsigned r = memory->Read(addr++); + r += memory->Read(addr++) << 8u; + return r; +} + + //============================================================================= // eZ80::StepF //----------------------------------------------------------------------------- void eZ80::StepF() { +#ifndef NDEBUG +#ifdef ENABLE_BREAKPOINT_IN_DEBUG + if (pc == bp_addr) { + breakpoint_hit(); + } + last_pc = pc; +#endif +#endif rom->Read(pc); +#ifndef NO_USE_FAST_TAPE SAFE_CALL(handler.step)->Z80_Step(this); +#endif (this->*normal_opcodes[Fetch()])(); } //============================================================================= @@ -86,6 +127,14 @@ void eZ80::StepF() //----------------------------------------------------------------------------- void eZ80::Step() { +#ifndef NDEBUG +#ifdef ENABLE_BREAKPOINT_IN_DEBUG + if (pc == bp_addr) { + breakpoint_hit(); + } + last_pc = pc; +#endif +#endif rom->Read(pc); (this->*normal_opcodes[Fetch()])(); } @@ -94,6 +143,8 @@ void eZ80::Step() //----------------------------------------------------------------------------- void eZ80::Update(int int_len, int* nmi_pending) { +//#define NO_USE_INTERRUPTS +#ifndef NO_USE_INTERRUPTS if(!iff1 && halted) return; // INT check separated from main Z80 loop to improve emulation speed @@ -108,7 +159,9 @@ void eZ80::Update(int int_len, int* nmi_pending) if(halted) break; } +#endif eipos = -1; +#ifndef NO_USE_FAST_TAPE if(handler.step) { while(t < frame_tacts) @@ -117,6 +170,7 @@ void eZ80::Update(int int_len, int* nmi_pending) } } else +#endif { while(t < frame_tacts) { @@ -135,6 +189,7 @@ void eZ80::Update(int int_len, int* nmi_pending) t -= frame_tacts; eipos -= frame_tacts; } +#ifndef NO_USE_REPLAY //============================================================================= // eZ80::Replay //----------------------------------------------------------------------------- @@ -151,6 +206,7 @@ void eZ80::Replay(int _fetches) Int(); fetches = 0; } +#endif //============================================================================= // eZ80::Int //----------------------------------------------------------------------------- @@ -164,8 +220,7 @@ void eZ80::Int() intad = Read(vec) + 0x100*Read(vec+1); } t += (im < 2) ? 13 : 19; - Write(--sp, pc_h); - Write(--sp, pc_l); + push(pc); pc = intad; memptr = intad; halted = 0; @@ -177,10 +232,21 @@ void eZ80::Int() //----------------------------------------------------------------------------- void eZ80::Nmi() { - Write(--sp, pc_h); - Write(--sp, pc_l); + push(pc); pc = 0x66; iff1 = halted = 0; } +#ifndef NDEBUG +#ifdef ENABLE_BREAKPOINT_IN_DEBUG +void eZ80::set_breakpoint(int _bp_addr) { + bp_addr = _bp_addr; +} +void eZ80::breakpoint_hit() { + static int i = 0; + printf("%d %d %04x %04x\n", i++, t, hl, af); +} +#endif +#endif + }//namespace xZ80 diff --git a/z80/z80.h b/z80/z80.h index 15b39b2..07e8bd8 100644 --- a/z80/z80.h +++ b/z80/z80.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -19,8 +20,12 @@ along with this program. If not, see . #ifndef __Z80_H__ #define __Z80_H__ -#include "z80_op_tables.h" #include "../platform/endian.h" +#ifdef USE_Z80_ARM +#include "z80arm.h" +#else + +#include "z80_op_tables.h" #pragma once @@ -31,7 +36,6 @@ class eDevices; namespace xZ80 { - #ifdef USE_BIG_ENDIAN #define DECLARE_REG16(reg, low, high)\ union\ @@ -72,6 +76,7 @@ enum eFlags SF = 0x80 }; + //***************************************************************************** // eZ80 //----------------------------------------------------------------------------- @@ -81,7 +86,9 @@ class eZ80 eZ80(eMemory* m, eDevices* d, dword frame_tacts = 0); void Reset(); void Update(int int_len, int* nmi_pending); +#ifndef NO_USE_REPLAY void Replay(int fetches); +#endif dword FrameTacts() const { return frame_tacts; } dword T() const { return t; } @@ -99,29 +106,50 @@ class eZ80 public: virtual void Z80_Step(eZ80* z80) = 0; }; +#ifndef NO_USE_FAST_TAPE void HandlerStep(eHandlerStep* h) { handler.step = h; } eHandlerStep* HandlerStep() const { return handler.step; } +#endif -protected: +//protected: void Int(); void Nmi(); void Step(); void StepF(); byte Fetch() { +#ifndef NO_USE_REPLAY --fetches; +#endif +#ifndef NO_UPDATE_RLOW_IN_FETCH ++r_low;// = (cpu->r & 0x80) + ((cpu->r+1) & 0x7F); +#endif t += 4; - return Read(pc++); + return ReadInc(pc); } + + typedef byte temp8; + typedef byte temp8_xy; + typedef int temp16; + typedef unsigned temp16_2; + typedef unsigned temp16_xy; + typedef unsigned scratch16; + typedef signed char address_delta; + byte IoRead(word port) const; void IoWrite(word port, byte v); byte Read(word addr) const; + byte ReadInc(int& addr) const; + int Read2(word addr) const; + int Read2Inc(int& addr) const; void Write(word addr, byte v); + inline void WriteXY(word addr, byte v) { Write(addr, v); } + void Write2(word addr, int v); typedef void (eZ80::*CALLFUNC)(); typedef byte (eZ80::*CALLFUNCI)(byte); +public: #include "z80_op.h" #include "z80_op_noprefix.h" #include "z80_op_cb.h" @@ -137,17 +165,73 @@ class eZ80 void InitOpFD(); void InitOpDDCB(); -protected: +#ifndef NDEBUG + void set_breakpoint(int bp_addr); +#endif + protected: eMemory* memory; eRom* rom; eUla* ula; eDevices* devices; + // z80arm codegen needs to capture the if/else + template void if_nonzero(int v, A&& a, B&& b) { + if (v) a(); else b(); + } + template void if_nonzero(int v, A&& a) { + if (v) a(); + } + + template void if_equal(int v, int cmp, A&& a) { + if (v == cmp) a(); + } + + template void if_zero(int v, A&& a) { + if (!v) a(); + } + // z80arm codegen needs to capture the if/else + template void if_flag_set(byte flag, A&& a, B&& b) { + if (f&flag) a(); else b(); + } + + template void if_flag_set(byte flag, A&& a) { + if (f&flag) a(); + } + + // z80arm codegen needs to capture the if/else + template void if_flag_clear(byte flag, A&& a, B&& b) { + if (!(f&flag)) a(); else b(); + } + template void if_flag_clear(byte flag, A&& a) { + if (!(f&flag)) a(); + } + + inline void set_a35_flags_preserve_set(byte preserve, byte set) { + f = (f & (preserve)) | (a & (F3|F5)) | set; + } + + inline void set_logic_flags_preserve(byte value, byte preserve_flags) { + set_logic_flags_preserve_reset(value, preserve_flags, 0); + } + + inline void set_logic_flags_preserve_reset(byte value, byte preserve_flags, byte reset_flags) { + f = log_f[value] | (f & preserve_flags); + if (reset_flags) { + f &= ~reset_flags; + } + } + struct eHandler { - eHandler() : io(NULL), step(NULL) {} + eHandler() : io(NULL) +#ifndef NO_USE_FAST_TAPE + ,step(NULL) +#endif + {} eHandlerIo* io; +#ifndef NO_USE_FAST_TAPE eHandlerStep* step; +#endif }; eHandler handler; @@ -155,7 +239,14 @@ class eZ80 int im; int eipos; int frame_tacts; // t-states per frame +#ifndef NO_USE_REPLAY int fetches; // .rzx replay fetches +#endif +#ifndef NDEBUG + int bp_addr; + int last_pc; + void breakpoint_hit(); +#endif DECLARE_REG16(pc, pc_l, pc_h) DECLARE_REG16(sp, sp_l, sp_h) @@ -197,8 +288,27 @@ class eZ80 typedef byte (eZ80::*REGP); REGP reg_offset[8]; byte reg_unused; + + inline word get_caller_pc() const { return pc; } + inline void set_caller_pc(word v) { pc = v; } + inline byte get_caller_a() const { return a; } + inline void set_caller_a(byte v) { a = v; } + inline void set_caller_flag(byte flags) { f |= flags; } + inline byte get_caller_b() const { return b; } + inline void set_caller_b(byte v) { b = v; } + inline byte get_caller_c() const { return c; } + inline void set_caller_bc(word v) { bc = v; } + inline void set_caller_h(byte v) { h = v; } + inline byte get_caller_l() const { return l; } + inline void set_caller_l(byte v) { l = v; } + inline word get_caller_ix() const { return ix; } + inline void set_caller_ix(word v) { ix = v; } + inline word get_caller_de() const { return de; } + inline void set_caller_de(word v) { de = v; } + inline void delta_caller_t(int delta) { t += delta; } }; }//namespace xZ80 +#endif #endif//__Z80_H__ diff --git a/z80/z80_op.h b/z80/z80_op.h index c6ec7e3..a2b37ae 100644 --- a/z80/z80_op.h +++ b/z80/z80_op.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -76,6 +77,38 @@ void bit(byte src, byte bit) { f = log_f[src & (1 << bit)] | HF | (f & CF) | (src & (F3|F5)); } +void rlc8(byte &x) { + f = rlcf[x]; + x = rol[x]; +} +void rrc8(byte& x) { + f = rrcf[x]; + x = ror[x]; +} +void rl8(byte &x) { + if (f & CF) + f = rl1[x], x = (x << 1) + 1; + else + f = rl0[x], x = (x << 1); +} +void rr8(byte& x) { + if (f & CF) + f = rr1[x], x = (x >> 1) + 0x80; + else + f = rr0[x], x = (x >> 1); +} +void sla8(byte& x) { + f = rl0[x], x = (x << 1); +} +void sra8(byte& x) { + f = sraf[x], x = (x >> 1) + (x & 0x80); +} +void sli8(byte& x) { + f = rl1[x], x = (x << 1) + 1; +} +void srl8(byte& x) { + f = rr0[x], x = (x >> 1); +} void bitmem(byte src, byte bit) { f = log_f[src & (1 << bit)] | HF | (f & CF); @@ -97,5 +130,45 @@ byte setbyte(byte src, byte bit) const { return src | (1 << bit); } - +void add16(int& r1, int& r2) { + memptr = r1+1; + f = (f & ~(NF | CF | F5 | F3 | HF)); + f |= (((r1 & 0x0FFF) + (r2 & 0x0FFF)) >> 8) & 0x10; /* HF */ + r1 = (r1 & 0xFFFF) + (r2 & 0xFFFF); + if (r1 & 0x10000) f |= CF; + f |= ((r1>>8) & (F5 | F3)); + t += 7; +} +void adc16(int& reg) { + memptr = hl+1; + byte fl = (((hl & 0x0FFF) + (reg & 0x0FFF) + (af & CF)) >> 8) & 0x10; /* HF */ + unsigned tmp = (hl & 0xFFFF) + (reg & 0xFFFF) + (af & CF); + if (tmp & 0x10000) fl |= CF; + if (!(unsigned short)tmp) fl |= ZF; + int ri = (int)(short)hl + (int)(short)reg + (int)(af & CF); + if (ri < -0x8000 || ri >= 0x8000) fl |= PV; + hl = tmp; + f = fl | (h & (F3|F5|SF)); + t += 7; +} +// hl, reg +void sbc16(int& reg) { + memptr = hl+1; + byte fl = NF; + fl |= (((hl & 0x0FFF) - (reg & 0x0FFF) - (af & CF)) >> 8) & 0x10; /* HF */ + unsigned tmp = (hl & 0xFFFF) - (reg & 0xFFFF) - (af & CF); + if (tmp & 0x10000) fl |= CF; + if (!(tmp & 0xFFFF)) fl |= ZF; + int ri = (int)(short)hl - (int)(short)reg - (int)(af & CF); + if (ri < -0x8000 || ri >= 0x8000) fl |= PV; + hl = tmp; + f = fl | (h & (F3|F5|SF)); + t += 7; +} +void push(word val) { + sp -= 2; + Write2(sp, val); +// Write(--sp, val>>8); +// Write(--sp, val&0xff); +} #endif//__Z80_OP_H__ diff --git a/z80/z80_op_cb.h b/z80/z80_op_cb.h index dac388c..4fedc0a 100644 --- a/z80/z80_op_cb.h +++ b/z80/z80_op_cb.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -24,282 +25,220 @@ along with this program. If not, see . #pragma once void Opl00() { // rlc b - f = rlcf[b]; - b = rol[b]; + rlc8(b); } void Opl01() { // rlc c - f = rlcf[c]; - c = rol[c]; + rlc8(c); } void Opl02() { // rlc d - f = rlcf[d]; - d = rol[d]; + rlc8(d); } void Opl03() { // rlc e - f = rlcf[e]; - e = rol[e]; + rlc8(e); } void Opl04() { // rlc h - f = rlcf[h]; - h = rol[h]; + rlc8(h); } void Opl05() { // rlc l - f = rlcf[l]; - l = rol[l]; + rlc8(l); } void Opl06() { // rlc (hl) - byte v = Read(hl); - f = rlcf[v]; - Write(hl, rol[v]); + temp8 v = Read(hl); + rlc8(v); + Write(hl, v); t += 7; } void Opl07() { // rlc a - f = rlcf[a]; - a = rol[a]; + rlc8(a); } void Opl08() { // rrc b - f = rrcf[b]; - b = ror[b]; + rrc8(b); } void Opl09() { // rrc c - f = rrcf[c]; - c = ror[c]; + rrc8(c); } void Opl0A() { // rrc d - f = rrcf[d]; - d = ror[d]; + rrc8(d); } void Opl0B() { // rrc e - f = rrcf[e]; - e = ror[e]; + rrc8(e); } void Opl0C() { // rrc h - f = rrcf[h]; - h = ror[h]; + rrc8(h); } void Opl0D() { // rrc l - f = rrcf[l]; - l = ror[l]; + rrc8(l); } void Opl0E() { // rrc (hl) - byte v = Read(hl); - f = rrcf[v]; - Write(hl, ror[v]); + temp8 v = Read(hl); + rrc8(v); + Write(hl, v); t += 7; } void Opl0F() { // rrc a - f = rrcf[a]; - a = ror[a]; + rrc8(a); } void Opl10() { // rl b - if (f & CF) - f = rl1[b], b = (b << 1) + 1; - else - f = rl0[b], b = (b << 1); + rl8(b); } void Opl11() { // rl c - if (f & CF) - f = rl1[c], c = (c << 1) + 1; - else - f = rl0[c], c = (c << 1); + rl8(c); } void Opl12() { // rl d - if (f & CF) - f = rl1[d], d = (d << 1) + 1; - else - f = rl0[d], d = (d << 1); + rl8(d); } void Opl13() { // rl e - if (f & CF) - f = rl1[e], e = (e << 1) + 1; - else - f = rl0[e], e = (e << 1); + rl8(e); } void Opl14() { // rl h - if (f & CF) - f = rl1[h], h = (h << 1) + 1; - else - f = rl0[h], h = (h << 1); + rl8(h); } void Opl15() { // rl l - if (f & CF) - f = rl1[l], l = (l << 1) + 1; - else - f = rl0[l], l = (l << 1); + rl8(l); } void Opl16() { // rl (hl) - byte v = Read(hl); - if (f & CF) - f = rl1[v], v = (v << 1) + 1; - else - f = rl0[v], v = (v << 1); + temp8 v = Read(hl); + rl8(v); Write(hl, v); t += 7; } void Opl17() { // rl a - if (f & CF) - f = rl1[a], a = (a << 1) + 1; - else - f = rl0[a], a = (a << 1); + rl8(a); } void Opl18() { // rr b - if (f & CF) - f = rr1[b], b = (b >> 1) + 0x80; - else - f = rr0[b], b = (b >> 1); + rr8(b); } void Opl19() { // rr c - if (f & CF) - f = rr1[c], c = (c >> 1) + 0x80; - else - f = rr0[c], c = (c >> 1); + rr8(c); } void Opl1A() { // rr d - if (f & CF) - f = rr1[d], d = (d >> 1) + 0x80; - else - f = rr0[d], d = (d >> 1); + rr8(d); } void Opl1B() { // rr e - if (f & CF) - f = rr1[e], e = (e >> 1) + 0x80; - else - f = rr0[e], e = (e >> 1); + rr8(e); } void Opl1C() { // rr h - if (f & CF) - f = rr1[h], h = (h >> 1) + 0x80; - else - f = rr0[h], h = (h >> 1); + rr8(h); } void Opl1D() { // rr l - if (f & CF) - f = rr1[l], l = (l >> 1) + 0x80; - else - f = rr0[l], l = (l >> 1); + rr8(l); } void Opl1E() { // rr (hl) - byte v = Read(hl); - if (f & CF) - f = rr1[v], v = (v >> 1) | 0x80; - else - f = rr0[v], v = (v >> 1); + temp8 v = Read(hl); + rr8(v); Write(hl, v); t += 7; } void Opl1F() { // rr a - if (f & CF) - f = rr1[a], a = (a >> 1) + 0x80; - else - f = rr0[a], a = (a >> 1); + rr8(a); } void Opl20() { // sla b - f = rl0[b], b = (b << 1); + sla8(b); } void Opl21() { // sla c - f = rl0[c], c = (c << 1); + sla8(c); } void Opl22() { // sla d - f = rl0[d], d = (d << 1); + sla8(d); } void Opl23() { // sla e - f = rl0[e], e = (e << 1); + sla8(e); } void Opl24() { // sla h - f = rl0[h], h = (h << 1); + sla8(h); } void Opl25() { // sla l - f = rl0[l], l = (l << 1); + sla8(l); } void Opl26() { // sla (hl) - byte v = Read(hl); - f = rl0[v], v = (v << 1); + temp8 v = Read(hl); + sla8(v); Write(hl, v); t += 7; } void Opl27() { // sla a - f = rl0[a], a = (a << 1); + sla8(a); } void Opl28() { // sra b - f = sraf[b], b = (b >> 1) + (b & 0x80); + sra8(b); } void Opl29() { // sra c - f = sraf[c], c = (c >> 1) + (c & 0x80); + sra8(c); } void Opl2A() { // sra d - f = sraf[d], d = (d >> 1) + (d & 0x80); + sra8(d); } void Opl2B() { // sra e - f = sraf[e], e = (e >> 1) + (e & 0x80); + sra8(e); } void Opl2C() { // sra h - f = sraf[h], h = (h >> 1) + (h & 0x80); + sra8(h); } void Opl2D() { // sra l - f = sraf[l], l = (l >> 1) + (l & 0x80); + sra8(l); } void Opl2E() { // sra (hl) - byte v = Read(hl); - f = sraf[v], v = (v >> 1) + (v & 0x80); + temp8 v = Read(hl); + sra8(v); Write(hl, v); t += 7; } void Opl2F() { // sra a - f = sraf[a], a = (a >> 1) + (a & 0x80); + sra8(a); } void Opl30() { // sli b - f = rl1[b], b = (b << 1) + 1; + sli8(b); } void Opl31() { // sli c - f = rl1[c], c = (c << 1) + 1; + sli8(c); } void Opl32() { // sli d - f = rl1[d], d = (d << 1) + 1; + sli8(d); } void Opl33() { // sli e - f = rl1[e], e = (e << 1) + 1; + sli8(e); } void Opl34() { // sli h - f = rl1[h], h = (h << 1) + 1; + sli8(h); } void Opl35() { // sli l - f = rl1[l], l = (l << 1) + 1; + sli8(l); } void Opl36() { // sli (hl) - byte v = Read(hl); - f = rl1[v], v = (v << 1) + 1; + temp8 v = Read(hl); + sli8(v); Write(hl, v); t += 7; } void Opl37() { // sli a - f = rl1[a], a = (a << 1) + 1; + sli8(a); } void Opl38() { // srl b - f = rr0[b], b = (b >> 1); + srl8(b); } void Opl39() { // srl c - f = rr0[c], c = (c >> 1); + srl8(c); } void Opl3A() { // srl d - f = rr0[d], d = (d >> 1); + srl8(d); } void Opl3B() { // srl e - f = rr0[e], e = (e >> 1); + srl8(e); } void Opl3C() { // srl h - f = rr0[h], h = (h >> 1); + srl8(h); } void Opl3D() { // srl l - f = rr0[l], l = (l >> 1); + srl8(l); } void Opl3E() { // srl (hl) - byte v = Read(hl); - f = rr0[v], v = (v >> 1); + temp8 v = Read(hl); + srl8(v); Write(hl, v); t += 7; } void Opl3F() { // srl a - f = rr0[a], a = (a >> 1); + srl8(a); } void Opl40() { // bit 0,b bit(b, 0); @@ -520,7 +459,7 @@ void Opl85() { // res 0,l res(l, 0); } void Opl86() { // res 0,(hl) - byte v = Read(hl); res(v, 0); Write(hl, v); + temp8 v = Read(hl); res(v, 0); Write(hl, v); t += 7; } void Opl87() { // res 0,a @@ -545,7 +484,7 @@ void Opl8D() { // res 1,l res(l, 1); } void Opl8E() { // res 1,(hl) - byte v = Read(hl); res(v, 1); Write(hl, v); + temp8 v = Read(hl); res(v, 1); Write(hl, v); t += 7; } void Opl8F() { // res 1,a @@ -570,7 +509,7 @@ void Opl95() { // res 2,l res(l, 2); } void Opl96() { // res 2,(hl) - byte v = Read(hl); res(v, 2); Write(hl, v); + temp8 v = Read(hl); res(v, 2); Write(hl, v); t += 7; } void Opl97() { // res 2,a @@ -595,7 +534,7 @@ void Opl9D() { // res 3,l res(l, 3); } void Opl9E() { // res 3,(hl) - byte v = Read(hl); res(v, 3); Write(hl, v); + temp8 v = Read(hl); res(v, 3); Write(hl, v); t += 7; } void Opl9F() { // res 3,a @@ -620,7 +559,7 @@ void OplA5() { // res 4,l res(l, 4); } void OplA6() { // res 4,(hl) - byte v = Read(hl); res(v, 4); Write(hl, v); + temp8 v = Read(hl); res(v, 4); Write(hl, v); t += 7; } void OplA7() { // res 4,a @@ -645,7 +584,7 @@ void OplAD() { // res 5,l res(l, 5); } void OplAE() { // res 5,(hl) - byte v = Read(hl); res(v, 5); Write(hl, v); + temp8 v = Read(hl); res(v, 5); Write(hl, v); t += 7; } void OplAF() { // res 5,a @@ -670,7 +609,7 @@ void OplB5() { // res 6,l res(l, 6); } void OplB6() { // res 6,(hl) - byte v = Read(hl); res(v, 6); Write(hl, v); + temp8 v = Read(hl); res(v, 6); Write(hl, v); t += 7; } void OplB7() { // res 6,a @@ -695,7 +634,7 @@ void OplBD() { // res 7,l res(l, 7); } void OplBE() { // res 7,(hl) - byte v = Read(hl); res(v, 7); Write(hl, v); + temp8 v = Read(hl); res(v, 7); Write(hl, v); t += 7; } void OplBF() { // res 7,a @@ -720,7 +659,7 @@ void OplC5() { // set 0,l set(l, 0); } void OplC6() { // set 0,(hl) - byte v = Read(hl); set(v, 0); Write(hl, v); + temp8 v = Read(hl); set(v, 0); Write(hl, v); t += 7; } void OplC7() { // set 0,a @@ -745,7 +684,7 @@ void OplCD() { // set 1,l set(l, 1); } void OplCE() { // set 1,(hl) - byte v = Read(hl); set(v, 1); Write(hl, v); + temp8 v = Read(hl); set(v, 1); Write(hl, v); t += 7; } void OplCF() { // set 1,a @@ -770,7 +709,7 @@ void OplD5() { // set 2,l set(l, 2); } void OplD6() { // set 2,(hl) - byte v = Read(hl); set(v, 2); Write(hl, v); + temp8 v = Read(hl); set(v, 2); Write(hl, v); t += 7; } void OplD7() { // set 2,a @@ -795,7 +734,7 @@ void OplDD() { // set 3,l set(l, 3); } void OplDE() { // set 3,(hl) - byte v = Read(hl); set(v, 3); Write(hl, v); + temp8 v = Read(hl); set(v, 3); Write(hl, v); t += 7; } void OplDF() { // set 3,a @@ -820,7 +759,7 @@ void OplE5() { // set 4,l set(l, 4); } void OplE6() { // set 4,(hl) - byte v = Read(hl); set(v, 4); Write(hl, v); + temp8 v = Read(hl); set(v, 4); Write(hl, v); t += 7; } void OplE7() { // set 4,a @@ -845,7 +784,7 @@ void OplED() { // set 5,l set(l, 5); } void OplEE() { // set 5,(hl) - byte v = Read(hl); set(v, 5); Write(hl, v); + temp8 v = Read(hl); set(v, 5); Write(hl, v); t += 7; } void OplEF() { // set 5,a @@ -870,7 +809,7 @@ void OplF5() { // set 6,l set(l, 6); } void OplF6() { // set 6,(hl) - byte v = Read(hl); set(v, 6); Write(hl, v); + temp8 v = Read(hl); set(v, 6); Write(hl, v); t += 7; } void OplF7() { // set 6,a @@ -895,17 +834,19 @@ void OplFD() { // set 7,l set(l, 7); } void OplFE() { // set 7,(hl) - byte v = Read(hl); set(v, 7); Write(hl, v); + temp8 v = Read(hl); set(v, 7); Write(hl, v); t += 7; } void OplFF() { // set 7,a set(a, 7); } +#ifndef USE_Z80T inline void OpCB() { byte opcode = Fetch(); (this->*logic_opcodes[opcode])(); } +#endif #endif//__Z80_OP_CB_H__ diff --git a/z80/z80_op_dd.h b/z80/z80_op_dd.h index e5c5cf4..7dc7539 100644 --- a/z80/z80_op_dd.h +++ b/z80/z80_op_dd.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -23,358 +24,15 @@ along with this program. If not, see . #pragma once -void Opx09() { // add ix,bc - memptr = ix+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((ix & 0x0FFF) + (bc & 0x0FFF)) >> 8) & 0x10; /* HF */ - ix = (ix & 0xFFFF) + (bc & 0xFFFF); - if (ix & 0x10000) f |= CF; - f |= (xh & (F5 | F3)); - t += 7; -} -void Opx19() { // add ix,de - memptr = ix+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((ix & 0x0FFF) + (de & 0x0FFF)) >> 8) & 0x10; /* HF */ - ix = (ix & 0xFFFF) + (de & 0xFFFF); - if (ix & 0x10000) f |= CF; - f |= (xh & (F5 | F3)); - t += 7; -} -void Opx21() { // ld ix,nnnn - xl = Read(pc++); - xh = Read(pc++); - t += 6; -} -void Opx22() { // ld (nnnn),ix - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; - memptr = adr+1; - Write(adr, xl); - Write(adr+1, xh); - t += 12; -} -void Opx23() { // inc ix - ix++; - t += 2; -} -void Opx24() { // inc xh - inc8(xh); -} -void Opx25() { // dec xh - dec8(xh); -} -void Opx26() { // ld xh,nn - xh = Read(pc++); - t += 3; -} -void Opx29() { // add ix,ix - memptr = ix+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= ((ix >> 7) & 0x10); /* HF */ - ix = (ix & 0xFFFF)*2; - if (ix & 0x10000) f |= CF; - f |= (xh & (F5 | F3)); - t += 7; -} -void Opx2A() { // ld ix,(nnnn) - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; - memptr = adr+1; - xl = Read(adr); - xh = Read(adr+1); - t += 12; -} -void Opx2B() { // dec ix - ix--; - t += 2; -} -void Opx2C() { // inc xl - inc8(xl); -} -void Opx2D() { // dec xl - dec8(xl); -} -void Opx2E() { // ld xl,nn - xl = Read(pc++); - t += 3; -} -void Opx34() { // inc (ix+nn) - signed char ofs = Read(pc++); - byte v = Read(ix + ofs); - inc8(v); - Write(ix + ofs, v); - t += 15; -} -void Opx35() { // dec (ix+nn) - signed char ofs = Read(pc++); - byte v = Read(ix + ofs); - dec8(v); - Write(ix + ofs, v); - t += 15; -} -void Opx36() { // ld (ix+nn),nn - signed char ofs = Read(pc++); - Write(ix + ofs, Read(pc++)); - t += 11; -} -void Opx39() { // add ix,sp - memptr = ix+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((ix & 0x0FFF) + (sp & 0x0FFF)) >> 8) & 0x10; /* HF */ - ix = (ix & 0xFFFF) + (sp & 0xFFFF); - if (ix & 0x10000) f |= CF; - f |= (xh & (F5 | F3)); - t += 7; -} -void Opx44() { // ld b,xh - b = xh; -} -void Opx45() { // ld b,xl - b = xl; -} -void Opx46() { // ld b,(ix+nn) - signed char ofs = Read(pc++); - b = Read(ix + ofs); - t += 11; -} -void Opx4C() { // ld c,xh - c = xh; -} -void Opx4D() { // ld c,xl - c = xl; -} -void Opx4E() { // ld c,(ix+nn) - signed char ofs = Read(pc++); - c = Read(ix + ofs); - t += 11; -} -void Opx54() { // ld d,xh - d = xh; -} -void Opx55() { // ld d,xl - d = xl; -} -void Opx56() { // ld d,(ix+nn) - signed char ofs = Read(pc++); - d = Read(ix + ofs); - t += 11; -} -void Opx5C() { // ld e,xh - e = xh; -} -void Opx5D() { // ld e,xl - e = xl; -} -void Opx5E() { // ld e,(ix+nn) - signed char ofs = Read(pc++); - e = Read(ix + ofs); - t += 11; -} -void Opx60() { // ld xh,b - xh = b; -} -void Opx61() { // ld xh,c - xh = c; -} -void Opx62() { // ld xh,d - xh = d; -} -void Opx63() { // ld xh,e - xh = e; -} -void Opx65() { // ld xh,xl - xh = xl; -} -void Opx66() { // ld h,(ix+nn) - signed char ofs = Read(pc++); - h = Read(ix + ofs); - t += 11; -} -void Opx67() { // ld xh,a - xh = a; -} -void Opx68() { // ld xl,b - xl = b; -} -void Opx69() { // ld xl,c - xl = c; -} -void Opx6A() { // ld xl,d - xl = d; -} -void Opx6B() { // ld xl,e - xl = e; -} -void Opx6C() { // ld xl,xh - xl = xh; -} -void Opx6E() { // ld l,(ix+nn) - signed char ofs = Read(pc++); - l = Read(ix + ofs); - t += 11; -} -void Opx6F() { // ld xl,a - xl = a; -} -void Opx70() { // ld (ix+nn),b - signed char ofs = Read(pc++); - Write(ix + ofs, b); - t += 11; -} -void Opx71() { // ld (ix+nn),c - signed char ofs = Read(pc++); - Write(ix + ofs, c); - t += 11; -} -void Opx72() { // ld (ix+nn),d - signed char ofs = Read(pc++); - Write(ix + ofs, d); - t += 11; -} -void Opx73() { // ld (ix+nn),e - signed char ofs = Read(pc++); - Write(ix + ofs, e); - t += 11; -} -void Opx74() { // ld (ix+nn),h - signed char ofs = Read(pc++); - Write(ix + ofs, h); - t += 11; -} -void Opx75() { // ld (ix+nn),l - signed char ofs = Read(pc++); - Write(ix + ofs, l); - t += 11; -} -void Opx77() { // ld (ix+nn),a - signed char ofs = Read(pc++); - Write(ix + ofs, a); - t += 11; -} -void Opx7C() { // ld a,xh - a = xh; -} -void Opx7D() { // ld a,xl - a = xl; -} -void Opx7E() { // ld a,(ix+nn) - signed char ofs = Read(pc++); - a = Read(ix + ofs); - t += 11; -} -void Opx84() { // add a,xh - add8(xh); -} -void Opx85() { // add a,xl - add8(xl); -} -void Opx86() { // add a,(ix+nn) - signed char ofs = Read(pc++); - add8(Read(ix + ofs)); - t += 11; -} -void Opx8C() { // adc a,xh - adc8(xh); -} -void Opx8D() { // adc a,xl - adc8(xl); -} -void Opx8E() { // adc a,(ix+nn) - signed char ofs = Read(pc++); - adc8(Read(ix + ofs)); - t += 11; -} -void Opx94() { // sub xh - sub8(xh); -} -void Opx95() { // sub xl - sub8(xl); -} -void Opx96() { // sub (ix+nn) - signed char ofs = Read(pc++); - sub8(Read(ix + ofs)); - t += 11; -} -void Opx9C() { // sbc a,xh - sbc8(xh); -} -void Opx9D() { // sbc a,xl - sbc8(xl); -} -void Opx9E() { // sbc a,(ix+nn) - signed char ofs = Read(pc++); - sbc8(Read(ix + ofs)); - t += 11; -} -void OpxA4() { // and xh - and8(xh); -} -void OpxA5() { // and xl - and8(xl); -} -void OpxA6() { // and (ix+nn) - signed char ofs = Read(pc++); - and8(Read(ix + ofs)); - t += 11; -} -void OpxAC() { // xor xh - xor8(xh); -} -void OpxAD() { // xor xl - xor8(xl); -} -void OpxAE() { // xor (ix+nn) - signed char ofs = Read(pc++); - xor8(Read(ix + ofs)); - t += 11; -} -void OpxB4() { // or xh - or8(xh); -} -void OpxB5() { // or xl - or8(xl); -} -void OpxB6() { // or (ix+nn) - signed char ofs = Read(pc++); - or8(Read(ix + ofs)); - t += 11; -} -void OpxBC() { // cp xh - cp8(xh); -} -void OpxBD() { // cp xl - cp8(xl); -} -void OpxBE() { // cp (ix+nn) - signed char ofs = Read(pc++); - cp8(Read(ix + ofs)); - t += 11; -} -void OpxE1() { // pop ix - xl = Read(sp++); - xh = Read(sp++); - t += 6; -} -void OpxE3() { // ex (sp),ix - unsigned tmp = Read(sp) + 0x100*Read(sp + 1); - Write(sp, xl); - Write(sp+1, xh); - memptr = tmp; - ix = tmp; - t += 15; -} -void OpxE5() { // push ix - Write(--sp, xh); - Write(--sp, xl); - t += 7; -} -void OpxE9() { // jp (ix) - pc = ix; -} -void OpxF9() { // ld sp,ix - sp = ix; - t += 2; -} +#undef OPXY_FUNC +#define OPXY_FUNC(a) Opx##a +#undef xy_reg +#define xy_reg ix +#undef xy_reg_lo +#define xy_reg_lo xl +#undef xy_reg_hi +#define xy_reg_hi xh + +#include "z80_op_xy.h" #endif//__Z80_OP_DD_H__ diff --git a/z80/z80_op_ddcb.h b/z80/z80_op_ddcb.h index f077960..7301927 100644 --- a/z80/z80_op_ddcb.h +++ b/z80/z80_op_ddcb.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -24,111 +25,112 @@ along with this program. If not, see . #pragma once -byte Oplx00(byte v) { // rlc (ix+nn) - f = rlcf[v]; return rol[v]; +temp8 Oplx00(temp8 v) { // rlc (ix+nn) + rlc8(v); + return v; } -byte Oplx08(byte v) { // rrc (ix+nn) - f = rrcf[v]; return ror[v]; +temp8 Oplx08(temp8 v) { // rrc (ix+nn) + rrc8(v); + return v; } -byte Oplx10(byte v) { // rl (ix+nn) - if (f & CF) { - f = rl1[v]; return (v << 1) + 1; - } else { - f = rl0[v]; return (v << 1); - } +temp8 Oplx10(temp8 v) { // rl (ix+nn) + rl8(v); + return v; } -byte Oplx18(byte v) { // rr (ix+nn) - if (f & CF) { - f = rr1[v]; return (v >> 1) + 0x80; - } else { - f = rr0[v]; return (v >> 1); - } +temp8 Oplx18(temp8 v) { // rr (ix+nn) + rr8(v); + return v; } -byte Oplx20(byte v) { // sla (ix+nn) - f = rl0[v]; return (v << 1); +temp8 Oplx20(temp8 v) { // sla (ix+nn) + sla8(v); + return v; } -byte Oplx28(byte v) { // sra (ix+nn) - f = sraf[v]; return (v >> 1) + (v & 0x80); +temp8 Oplx28(temp8 v) { // sra (ix+nn) + sra8(v); + return v; } -byte Oplx30(byte v) { // sli (ix+nn) - f = rl1[v]; return (v << 1) + 1; +temp8 Oplx30(temp8 v) { // sli (ix+nn) + sli8(v); + return v; } -byte Oplx38(byte v) { // srl (ix+nn) - f = rr0[v]; return (v >> 1); +temp8 Oplx38(temp8 v) { // srl (ix+nn) + srl8(v); + return v; } -byte Oplx40(byte v) { // bit 0,(ix+nn) +temp8 Oplx40(temp8 v) { // bit 0,(ix+nn) bitmem(v, 0); return v; } -byte Oplx48(byte v) { // bit 1,(ix+nn) +temp8 Oplx48(temp8 v) { // bit 1,(ix+nn) bitmem(v, 1); return v; } -byte Oplx50(byte v) { // bit 2,(ix+nn) +temp8 Oplx50(temp8 v) { // bit 2,(ix+nn) bitmem(v, 2); return v; } -byte Oplx58(byte v) { // bit 3,(ix+nn) +temp8 Oplx58(temp8 v) { // bit 3,(ix+nn) bitmem(v, 3); return v; } -byte Oplx60(byte v) { // bit 4,(ix+nn) +temp8 Oplx60(temp8 v) { // bit 4,(ix+nn) bitmem(v, 4); return v; } -byte Oplx68(byte v) { // bit 5,(ix+nn) +temp8 Oplx68(temp8 v) { // bit 5,(ix+nn) bitmem(v, 5); return v; } -byte Oplx70(byte v) { // bit 6,(ix+nn) +temp8 Oplx70(temp8 v) { // bit 6,(ix+nn) bitmem(v, 6); return v; } -byte Oplx78(byte v) { // bit 7,(ix+nn) +temp8 Oplx78(temp8 v) { // bit 7,(ix+nn) bitmem(v, 7); return v; } -byte Oplx80(byte v) { // res 0,(ix+nn) +temp8 Oplx80(temp8 v) { // res 0,(ix+nn) return resbyte(v, 0); } -byte Oplx88(byte v) { // res 1,(ix+nn) +temp8 Oplx88(temp8 v) { // res 1,(ix+nn) return resbyte(v, 1); } -byte Oplx90(byte v) { // res 2,(ix+nn) +temp8 Oplx90(temp8 v) { // res 2,(ix+nn) return resbyte(v, 2); } -byte Oplx98(byte v) { // res 3,(ix+nn) +temp8 Oplx98(temp8 v) { // res 3,(ix+nn) return resbyte(v, 3); } -byte OplxA0(byte v) { // res 4,(ix+nn) +temp8 OplxA0(temp8 v) { // res 4,(ix+nn) return resbyte(v, 4); } -byte OplxA8(byte v) { // res 5,(ix+nn) +temp8 OplxA8(temp8 v) { // res 5,(ix+nn) return resbyte(v, 5); } -byte OplxB0(byte v) { // res 6,(ix+nn) +temp8 OplxB0(temp8 v) { // res 6,(ix+nn) return resbyte(v, 6); } -byte OplxB8(byte v) { // res 7,(ix+nn) +temp8 OplxB8(temp8 v) { // res 7,(ix+nn) return resbyte(v, 7); } -byte OplxC0(byte v) { // set 0,(ix+nn) +temp8 OplxC0(temp8 v) { // set 0,(ix+nn) return setbyte(v, 0); } -byte OplxC8(byte v) { // set 1,(ix+nn) +temp8 OplxC8(temp8 v) { // set 1,(ix+nn) return setbyte(v, 1); } -byte OplxD0(byte v) { // set 2,(ix+nn) +temp8 OplxD0(temp8 v) { // set 2,(ix+nn) return setbyte(v, 2); } -byte OplxD8(byte v) { // set 3,(ix+nn) +temp8 OplxD8(temp8 v) { // set 3,(ix+nn) return setbyte(v, 3); } -byte OplxE0(byte v) { // set 4,(ix+nn) +temp8 OplxE0(temp8 v) { // set 4,(ix+nn) return setbyte(v, 4); } -byte OplxE8(byte v) { // set 5,(ix+nn) +temp8 OplxE8(temp8 v) { // set 5,(ix+nn) return setbyte(v, 5); } -byte OplxF0(byte v) { // set 6,(ix+nn) +temp8 OplxF0(temp8 v) { // set 6,(ix+nn) return setbyte(v, 6); } -byte OplxF8(byte v) { // set 7,(ix+nn) +temp8 OplxF8(temp8 v) { // set 7,(ix+nn) return setbyte(v, 7); } +#ifndef USE_Z80T inline void DDFD(byte opcode) { byte op1; // last DD/FD prefix @@ -141,10 +143,10 @@ inline void DDFD(byte opcode) if(opcode == 0xCB) { dword ptr; // pointer to DDCB operand - ptr = ((op1 == 0xDD) ? ix : iy) + (signed char)Read(pc++); + ptr = ((op1 == 0xDD) ? ix : iy) + (signed char)ReadInc(pc); memptr = ptr; // DDCBnnXX,FDCBnnXX increment R by 2, not 3! - opcode = Read(pc++); + opcode = ReadInc(pc); t += 4; byte v = (this->*logic_ix_opcodes[opcode])(Read(ptr)); if((opcode & 0xC0) == 0x40)// bit n,rm @@ -166,6 +168,8 @@ inline void DDFD(byte opcode) // one prefix: DD/FD op1 == 0xDD ? (this->*ix_opcodes[opcode])() : (this->*iy_opcodes[opcode])(); } +#endif +#ifndef USE_Z80T inline void OpDD() { DDFD(0xDD); @@ -174,5 +178,6 @@ inline void OpFD() { DDFD(0xFD); } +#endif #endif//__Z80_OP_DDCB_H__ diff --git a/z80/z80_op_ed.h b/z80/z80_op_ed.h index e72f1af..77c31a0 100644 --- a/z80/z80_op_ed.h +++ b/z80/z80_op_ed.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2013 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -27,7 +28,8 @@ void Ope40() { // in b,(c) t += 4; memptr = bc+1; b = IoRead(bc); - f = log_f[b] | (f & CF); +// f = log_f[b] | (f & CF); + set_logic_flags_preserve(b, CF); } void Ope41() { // out (c),b t += 4; @@ -35,34 +37,23 @@ void Ope41() { // out (c),b IoWrite(bc, b); } void Ope42() { // sbc hl,bc - memptr = hl+1; - byte fl = NF; - fl |= (((hl & 0x0FFF) - (bc & 0x0FFF) - (af & CF)) >> 8) & 0x10; /* HF */ - unsigned tmp = (hl & 0xFFFF) - (bc & 0xFFFF) - (af & CF); - if (tmp & 0x10000) fl |= CF; - if (!(tmp & 0xFFFF)) fl |= ZF; - int ri = (int)(short)hl - (int)(short)bc - (int)(af & CF); - if (ri < -0x8000 || ri >= 0x8000) fl |= PV; - hl = tmp; - f = fl | (h & (F3|F5|SF)); - t += 7; + sbc16(bc); } void Ope43() { // ld (nnnn),bc - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; + temp16 adr = Read2Inc(pc); memptr = adr+1; - Write(adr, c); - Write(adr+1, b); + Write2(adr, bc); t += 12; } +#ifndef USE_Z80T void Ope44() { // neg - f = sbcf[a]; + f = sbcf[a]; a = -a; } +#endif void Ope45() { // retn iff1 = iff2; - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); + temp16 addr = Read2Inc(sp); pc = addr; memptr = addr; t += 6; @@ -78,7 +69,8 @@ void Ope48() { // in c,(c) t += 4; memptr = bc+1; c = IoRead(bc); - f = log_f[c] | (f & CF); +// f = log_f[c] | (f & CF); + set_logic_flags_preserve(c, CF); } void Ope49() { // out (c),c t += 4; @@ -86,30 +78,18 @@ void Ope49() { // out (c),c IoWrite(bc, c); } void Ope4A() { // adc hl,bc - memptr = hl+1; - byte fl = (((hl & 0x0FFF) + (bc & 0x0FFF) + (af & CF)) >> 8) & 0x10; /* HF */ - unsigned tmp = (hl & 0xFFFF) + (bc & 0xFFFF) + (af & CF); - if (tmp & 0x10000) fl |= CF; - if (!(tmp & 0xFFFF)) fl |= ZF; - int ri = (int)(short)hl + (int)(short)bc + (int)(af & CF); - if (ri < -0x8000 || ri >= 0x8000) fl |= PV; - hl = tmp; - f = fl | (h & (F3|F5|SF)); - t += 7; + adc16(bc); } void Ope4B() { // ld bc,(nnnn) - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; + temp16 adr = Read2Inc(pc); memptr = adr+1; - c = Read(adr); - b = Read(adr+1); + bc = Read2(adr); t += 12; } void Ope4C() { Ope44(); } // neg void Ope4D() { // reti iff1 = iff2; - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); + temp16 addr = Read2Inc(sp); pc = addr; memptr = addr; t += 6; @@ -126,7 +106,8 @@ void Ope50() { // in d,(c) t += 4; memptr = bc+1; d = IoRead(bc); - f = log_f[d] | (f & CF); +// f = log_f[d] | (f & CF); + set_logic_flags_preserve(d, CF); } void Ope51() { // out (c),d t += 4; @@ -134,40 +115,31 @@ void Ope51() { // out (c),d IoWrite(bc, d); } void Ope52() { // sbc hl,de - memptr = hl+1; - byte fl = NF; - fl |= (((hl & 0x0FFF) - (de & 0x0FFF) - (af & CF)) >> 8) & 0x10; /* HF */ - unsigned tmp = (hl & 0xFFFF) - (de & 0xFFFF) - (af & CF); - if (tmp & 0x10000) fl |= CF; - if (!(tmp & 0xFFFF)) fl |= ZF; - int ri = (int)(short)hl - (int)(short)de - (int)(af & CF); - if (ri < -0x8000 || ri >= 0x8000) fl |= PV; - hl = tmp; - f = fl | (h & (F3|F5|SF)); - t += 7; + sbc16(de); } void Ope53() { // ld (nnnn),de - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; + temp16 adr = Read2Inc(pc); memptr = adr+1; - Write(adr, e); - Write(adr+1, d); + Write2(adr, de); t += 12; } void Ope54() { Ope44(); } // neg void Ope55() { Ope45(); } // retn void Ope56() { Ope4E(); } // im 1 +#ifndef USE_Z80T void Ope57() { // ld a,i - a = i; - f = (log_f[a] | (f & CF)) & ~PV; + a = i; + f = (log_f[a] | (f & CF)) & ~PV; + t++; if (iff1 && (t+10 < frame_tacts)) f |= PV; - t++; } +#endif void Ope58() { // in e,(c) t += 4; memptr = bc+1; e = IoRead(bc); - f = log_f[e] | (f & CF); +// f = log_f[e] | (f & CF); + set_logic_flags_preserve(e, CF); } void Ope59() { // out (c),e t += 4; @@ -175,23 +147,11 @@ void Ope59() { // out (c),e IoWrite(bc, e); } void Ope5A() { // adc hl,de - memptr = hl+1; - byte fl = (((hl & 0x0FFF) + (de & 0x0FFF) + (af & CF)) >> 8) & 0x10; /* HF */ - unsigned tmp = (hl & 0xFFFF) + (de & 0xFFFF) + (af & CF); - if (tmp & 0x10000) fl |= CF; - if (!(tmp & 0xFFFF)) fl |= ZF; - int ri = (int)(short)hl + (int)(short)de + (int)(af & CF); - if (ri < -0x8000 || ri >= 0x8000) fl |= PV; - hl = tmp; - f = fl | (h & (F3|F5|SF)); - t += 7; + adc16(de); } void Ope5B() { // ld de,(nnnn) - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; - memptr = adr+1; - e = Read(adr); - d = Read(adr+1); + temp16 adr = Read2Inc(pc); + de = Read2(adr); t += 12; } void Ope5C() { Ope44(); } // neg @@ -199,17 +159,25 @@ void Ope5D() { Ope4D(); } // reti void Ope5E() { // im 2 im = 2; } +#ifndef USE_Z80T void Ope5F() { // ld a,r - a = (r_low & 0x7F) | r_hi; +#ifdef NO_UPDATE_RLOW_IN_FETCH + // save on counting r_low + a = ((t>>2) & 0x7F) | r_hi; +#else + a = (r_low & 0x7F) | r_hi; +#endif f = (log_f[a] | (f & CF)) & ~PV; if (iff2 && ((t+10 < frame_tacts) || eipos+8==t)) f |= PV; t++; } +#endif void Ope60() { // in h,(c) t += 4; memptr = bc+1; h = IoRead(bc); - f = log_f[h] | (f & CF); +// f = log_f[h] | (f & CF); + set_logic_flags_preserve(h, CF); } void Ope61() { // out (c),h t += 4; @@ -217,34 +185,28 @@ void Ope61() { // out (c),h IoWrite(bc, h); } void Ope62() { // sbc hl,hl - memptr = hl+1; - byte fl = NF; - fl |= (f & CF) << 4; /* HF - copy from CF */ - unsigned tmp = 0-(af & CF); - if (tmp & 0x10000) fl |= CF; - if (!(tmp & 0xFFFF)) fl |= ZF; - // never set PV - hl = tmp; - f = fl | (h & (F3|F5|SF)); - t += 7; + sbc16(hl); } void Ope63() { Op22(); } // ld (nnnn),hl void Ope64() { Ope44(); } // neg void Ope65() { Ope45(); } // retn void Ope66() { Ope46(); } // im 0 +#ifndef USE_Z80T void Ope67() { // rrd - byte tmp = Read(hl); + byte tmp = Read(hl); memptr = hl+1; Write(hl, (a << 4) | (tmp >> 4)); a = (a & 0xF0) | (tmp & 0x0F); f = log_f[a] | (f & CF); t += 10; } +#endif void Ope68() { // in l,(c) t += 4; memptr = bc+1; l = IoRead(bc); - f = log_f[l] | (f & CF); +// f = log_f[l] | (f & CF); + set_logic_flags_preserve(l, CF); } void Ope69() { // out (c),l t += 4; @@ -252,33 +214,27 @@ void Ope69() { // out (c),l IoWrite(bc, l); } void Ope6A() { // adc hl,hl - memptr = hl+1; - byte fl = ((h << 1) & 0x10); /* HF */ - unsigned tmp = (hl & 0xFFFF)*2 + (af & CF); - if (tmp & 0x10000) fl |= CF; - if (!(tmp & 0xFFFF)) fl |= ZF; - int ri = 2*(int)(short)hl + (int)(af & CF); - if (ri < -0x8000 || ri >= 0x8000) fl |= PV; - hl = tmp; - f = fl | (h & (F3|F5|SF)); - t += 7; + adc16(hl); } void Ope6B() { Op2A(); } // ld hl,(nnnn) void Ope6C() { Ope44(); } // neg void Ope6D() { Ope4D(); } // reti void Ope6E() { Ope56(); } // im 0/1 -> im 1 +#ifndef USE_Z80T void Ope6F() { // rld - byte tmp = Read(hl); + byte tmp = Read(hl); memptr = hl+1; Write(hl, (a & 0x0F) | (tmp << 4)); a = (a & 0xF0) | (tmp >> 4); f = log_f[a] | (f & CF); t += 10; } +#endif void Ope70() { // in (c) - t += 4; + t += 4; memptr = bc+1; - f = log_f[IoRead(bc)] | (f & CF); +// f = log_f[IoRead(bc)] | (f & CF); + set_logic_flags_preserve(IoRead(bc), CF); } void Ope71() { // out (c),0 t += 4; @@ -286,24 +242,12 @@ void Ope71() { // out (c),0 IoWrite(bc, 0); } void Ope72() { // sbc hl,sp - memptr = hl+1; - byte fl = NF; - fl |= (((hl & 0x0FFF) - (sp & 0x0FFF) - (af & CF)) >> 8) & 0x10; /* HF */ - unsigned tmp = (hl & 0xFFFF) - (sp & 0xFFFF) - (af & CF); - if (tmp & 0x10000) fl |= CF; - if (!(tmp & 0xFFFF)) fl |= ZF; - int ri = (int)(short)hl - (int)(short)sp - (int)(af & CF); - if (ri < -0x8000 || ri >= 0x8000) fl |= PV; - hl = tmp; - f = fl | (h & (F3|F5|SF)); - t += 7; + sbc16(sp); } void Ope73() { // ld (nnnn),sp - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; + temp16 adr = Read2Inc(pc); memptr = adr+1; - Write(adr, sp_l); - Write(adr+1, sp_h); + Write2(adr, sp); t += 12; } void Ope74() { Ope44(); } // neg @@ -316,7 +260,8 @@ void Ope78() { // in a,(c) t += 4; memptr = bc+1; a = IoRead(bc); - f = log_f[a] | (f & CF); + // f = log_f[a] | (f & CF); + set_logic_flags_preserve(a, CF); } void Ope79() { // out (c),a t += 4; @@ -324,45 +269,39 @@ void Ope79() { // out (c),a IoWrite(bc, a); } void Ope7A() { // adc hl,sp - memptr = hl+1; - byte fl = (((hl & 0x0FFF) + (sp & 0x0FFF) + (af & CF)) >> 8) & 0x10; /* HF */ - unsigned tmp = (hl & 0xFFFF) + (sp & 0xFFFF) + (af & CF); - if (tmp & 0x10000) fl |= CF; - if (!(unsigned short)tmp) fl |= ZF; - int ri = (int)(short)hl + (int)(short)sp + (int)(af & CF); - if (ri < -0x8000 || ri >= 0x8000) fl |= PV; - hl = tmp; - f = fl | (h & (F3|F5|SF)); - t += 7; + adc16(sp); } void Ope7B() { // ld sp,(nnnn) - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; + temp16 adr = Read2Inc(pc); memptr = adr+1; - sp_l = Read(adr); - sp_h = Read(adr+1); + sp = Read2(adr); t += 12; } void Ope7C() { Ope44(); } // neg void Ope7D() { Ope4D(); } // reti void Ope7E() { Ope5E(); } // im 2 void Ope7F() { Op00(); } // nop +#ifndef USE_Z80T void OpeA0() { // ldi - t += 8; - byte tempbyte = Read(hl++); + t += 8; + temp8 tempbyte = ReadInc(hl); Write(de++, tempbyte); tempbyte += a; tempbyte = (tempbyte & F3) + ((tempbyte << 4) & F5); f = (f & ~(NF|HF|PV|F3|F5)) + tempbyte; if (--bc & 0xFFFF) f |= PV; //??? } +#endif +#ifndef USE_Z80T void OpeA1() { // cpi - t += 8; - byte cf = f & CF; - byte tempbyte = Read(hl++); - f = cpf8b[a*0x100 + tempbyte] + cf; - if (--bc & 0xFFFF) f |= PV; //??? - memptr++; -} + t += 8; + byte cf = f & CF; + byte tempbyte = ReadInc(hl); + f = cpf8b[a*0x100 + tempbyte] + cf; + if (--bc & 0xFFFF) f |= PV; //??? + memptr++; +} +#endif +#ifndef USE_Z80T void OpeA2A3AAABFlags(byte val, byte tmp) { f = log_f[b] & ~PV; @@ -370,131 +309,163 @@ void OpeA2A3AAABFlags(byte val, byte tmp) if(tmp < val) f |= (HF|CF); if(val & 0x80) f |= NF; } +#endif void OpeA2() { // ini memptr = bc+1; t += 8; - byte val = IoRead(bc); + temp8 val = IoRead(bc); --b; - Write(hl++, val); + Write(hl, val); + hl++; OpeA2A3AAABFlags(val, val + c + 1); } void OpeA3() { // outi - t += 8; - byte val = Read(hl++); + t += 8; + temp8 val = ReadInc(hl); --b; IoWrite(bc, val); OpeA2A3AAABFlags(val, val + l); memptr = bc+1; } +#ifndef USE_Z80T void OpeA8() { // ldd - t += 8; - byte tempbyte = Read(hl--); + t += 8; + temp8 tempbyte = Read(hl--); Write(de--, tempbyte); tempbyte += a; tempbyte = (tempbyte & F3) + ((tempbyte << 4) & F5); f = (f & ~(NF|HF|PV|F3|F5)) + tempbyte; if (--bc & 0xFFFF) f |= PV; //??? } +#endif +#ifndef USE_Z80T void OpeA9() { // cpd - t += 8; - byte cf = f & CF; - byte tempbyte = Read(hl--); - f = cpf8b[a*0x100 + tempbyte] + cf; - if (--bc & 0xFFFF) f |= PV; //??? - memptr--; -} + t += 8; + byte cf = f & CF; + byte tempbyte = Read(hl--); + f = cpf8b[a*0x100 + tempbyte] + cf; + if (--bc & 0xFFFF) f |= PV; //??? + memptr--; +} +#endif void OpeAA() { // ind - memptr = bc-1; + memptr = bc-1; t += 8; - byte val = IoRead(bc); + temp8 val = IoRead(bc); --b; - Write(hl--, val); + Write(hl, val); + --hl; OpeA2A3AAABFlags(val, val + c - 1); } void OpeAB() { // outd - t += 8; - byte val = Read(hl--); + t += 8; + temp8 val = Read(hl); + --hl; --b; IoWrite(bc, val); OpeA2A3AAABFlags(val, val + l); memptr = bc-1; } void OpeB0() { // ldir - t += 8; - byte tempbyte = Read(hl++); - Write(de++, tempbyte); - tempbyte += a; tempbyte = (tempbyte & F3) + ((tempbyte << 4) & F5); - f = (f & ~(NF|HF|PV|F3|F5)) + tempbyte; - if (--bc & 0xFFFF) f |= PV, pc -= 2, t += 5, memptr = pc+1; //??? + OpeA0(); + if_flag_set(PV, [&] { + pc -= 2, t += 5, memptr = pc+1; //??? + }); } void OpeB1() { // cpir - memptr++; - t += 8; - byte cf = f & CF; - byte tempbyte = Read(hl++); - f = cpf8b[a*0x100 + tempbyte] + cf; - if (--bc & 0xFFFF) { //??? - f |= PV; - if (!(f & ZF)) pc -= 2, t += 5, memptr = pc+1; - } + OpeA1(); + if_flag_set(PV, [&] { + return if_flag_clear(ZF, + [&] { + pc -= 2, t += 5, memptr = pc+1; + }); + }); } void OpeB2() { // inir - t += 8; + t += 8; memptr = bc+1; - Write(hl++, IoRead(bc)); + Write(hl, IoRead(bc)); + hl++; dec8(b); - if (b) f |= PV, pc -= 2, t += 5; - else f &= ~PV; + + if_nonzero(b, [&] { + f |= PV, pc -= 2, t += 5; + }, [&] { + f &= ~PV; + }); } void OpeB3() { // otir - t += 8; + t += 8; dec8(b); - IoWrite(bc, Read(hl++)); - if (b) f |= PV, pc -= 2, t += 5; - else f &= ~PV; - f &= ~CF; if (!l) f |= CF; - memptr = bc+1; + IoWrite(bc, Read(hl)); + hl++; + memptr = bc+1; + return if_nonzero(b, [&] { + f |= PV, pc -= 2, t += 5; + f &= ~CF; + return if_zero(l, [&] { + f |= CF; + }); + }, [&] { + f &= ~PV; + return if_zero(l, [&] { + f |= CF; + }); + }); } void OpeB8() { // lddr - t += 8; - byte tempbyte = Read(hl--); - Write(de--, tempbyte); - tempbyte += a; tempbyte = (tempbyte & F3) + ((tempbyte << 4) & F5); - f = (f & ~(NF|HF|PV|F3|F5)) + tempbyte; - if (--bc & 0xFFFF) f |= PV, memptr = pc - 1, pc -= 2, t += 5; //??? + OpeA8(); + return if_flag_set(PV, [&] { + pc -= 2, t += 5, memptr = pc+1; //??? + }); } void OpeB9() { // cpdr - memptr--; - t += 8; - byte cf = f & CF; - byte tempbyte = Read(hl--); - f = cpf8b[a*0x100 + tempbyte] + cf; - if (--bc & 0xFFFF) { //??? - f |= PV; - if (!(f & ZF)) pc -= 2, t += 5, memptr = pc+1; - } + OpeA9(); + return if_flag_set(PV, [&] { + return if_flag_clear(ZF, + [&] { + pc -= 2, t += 5, memptr = pc+1; + }); + }); } + void OpeBA() { // indr - t += 8; + t += 8; memptr = bc-1; - Write(hl--, IoRead(bc)); + Write(hl, IoRead(bc)); + --hl; dec8(b); - if (b) f |= PV, pc -= 2, t += 5; - else f &= ~PV; + if_nonzero(b, [&] { + f |= PV, pc -= 2, t += 5; + }, [&] { + f &= ~PV; + }); } void OpeBB() { // otdr - t += 8; - dec8(b); - IoWrite(bc, Read(hl--)); - if (b) f |= PV, pc -= 2, t += 5; - else f &= ~PV; - f &= ~CF; if (l == 0xFF) f |= CF; - memptr = bc-1; + t += 8; + dec8(b); + IoWrite(bc, Read(hl)); + --hl; + memptr = bc-1; + return if_nonzero(b, [&] { + f |= PV, pc -= 2, t += 5; + f &= ~CF; + return if_equal(l, 255, [&] { + f |= CF; + }); + }, [&] { + f &= ~PV; + return if_equal(l, 255, [&] { + f |= CF; + }); + }); } +#ifndef USE_Z80T void OpED() { byte opcode = Fetch(); (this->*ext_opcodes[opcode])(); } +#endif #endif//__Z80_OP_ED_H__ diff --git a/z80/z80_op_fd.h b/z80/z80_op_fd.h index d432ee9..0a51b98 100644 --- a/z80/z80_op_fd.h +++ b/z80/z80_op_fd.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -23,358 +24,15 @@ along with this program. If not, see . #pragma once -void Opy09() { // add iy,bc - memptr = iy+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((iy & 0x0FFF) + (bc & 0x0FFF)) >> 8) & 0x10; /* HF */ - iy = (iy & 0xFFFF) + (bc & 0xFFFF); - if (iy & 0x10000) f |= CF; - f |= (yh & (F5 | F3)); - t += 7; -} -void Opy19() { // add iy,de - memptr = iy+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((iy & 0x0FFF) + (de & 0x0FFF)) >> 8) & 0x10; /* HF */ - iy = (iy & 0xFFFF) + (de & 0xFFFF); - if (iy & 0x10000) f |= CF; - f |= (yh & (F5 | F3)); - t += 7; -} -void Opy21() { // ld iy,nnnn - yl = Read(pc++); - yh = Read(pc++); - t += 6; -} -void Opy22() { // ld (nnnn),iy - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; - memptr = adr+1; - Write(adr, yl); - Write(adr+1, yh); - t += 12; -} -void Opy23() { // inc iy - iy++; - t += 2; -} -void Opy24() { // inc yh - inc8(yh); -} -void Opy25() { // dec yh - dec8(yh); -} -void Opy26() { // ld yh,nn - yh = Read(pc++); - t += 3; -} -void Opy29() { // add iy,iy - memptr = iy+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= ((iy >> 7) & 0x10); /* HF */ - iy = (iy & 0xFFFF)*2; - if (iy & 0x10000) f |= CF; - f |= (yh & (F5 | F3)); - t += 7; -} -void Opy2A() { // ld iy,(nnnn) - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; - memptr = adr+1; - yl = Read(adr); - yh = Read(adr+1); - t += 12; -} -void Opy2B() { // dec iy - iy--; - t += 2; -} -void Opy2C() { // inc yl - inc8(yl); -} -void Opy2D() { // dec yl - dec8(yl); -} -void Opy2E() { // ld yl,nn - yl = Read(pc++); - t += 3; -} -void Opy34() { // inc (iy+nn) - signed char ofs = Read(pc++); - byte v = Read(iy + ofs); - inc8(v); - Write(iy + ofs, v); - t += 15; -} -void Opy35() { // dec (iy+nn) - signed char ofs = Read(pc++); - byte v = Read(iy + ofs); - dec8(v); - Write(iy + ofs, v); - t += 15; -} -void Opy36() { // ld (iy+nn),nn - signed char ofs = Read(pc++); - Write(iy + ofs, Read(pc++)); - t += 11; -} -void Opy39() { // add iy,sp - memptr = iy+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((iy & 0x0FFF) + (sp & 0x0FFF)) >> 8) & 0x10; /* HF */ - iy = (iy & 0xFFFF) + (sp & 0xFFFF); - if (iy & 0x10000) f |= CF; - f |= (yh & (F5 | F3)); - t += 7; -} -void Opy44() { // ld b,yh - b = yh; -} -void Opy45() { // ld b,yl - b = yl; -} -void Opy46() { // ld b,(iy+nn) - signed char ofs = Read(pc++); - b = Read(iy + ofs); - t += 11; -} -void Opy4C() { // ld c,yh - c = yh; -} -void Opy4D() { // ld c,yl - c = yl; -} -void Opy4E() { // ld c,(iy+nn) - signed char ofs = Read(pc++); - c = Read(iy + ofs); - t += 11; -} -void Opy54() { // ld d,yh - d = yh; -} -void Opy55() { // ld d,yl - d = yl; -} -void Opy56() { // ld d,(iy+nn) - signed char ofs = Read(pc++); - d = Read(iy + ofs); - t += 11; -} -void Opy5C() { // ld e,yh - e = yh; -} -void Opy5D() { // ld e,yl - e = yl; -} -void Opy5E() { // ld e,(iy+nn) - signed char ofs = Read(pc++); - e = Read(iy + ofs); - t += 11; -} -void Opy60() { // ld yh,b - yh = b; -} -void Opy61() { // ld yh,c - yh = c; -} -void Opy62() { // ld yh,d - yh = d; -} -void Opy63() { // ld yh,e - yh = e; -} -void Opy65() { // ld yh,yl - yh = yl; -} -void Opy66() { // ld h,(iy+nn) - signed char ofs = Read(pc++); - h = Read(iy + ofs); - t += 11; -} -void Opy67() { // ld yh,a - yh = a; -} -void Opy68() { // ld yl,b - yl = b; -} -void Opy69() { // ld yl,c - yl = c; -} -void Opy6A() { // ld yl,d - yl = d; -} -void Opy6B() { // ld yl,e - yl = e; -} -void Opy6C() { // ld yl,yh - yl = yh; -} -void Opy6E() { // ld l,(iy+nn) - signed char ofs = Read(pc++); - l = Read(iy + ofs); - t += 11; -} -void Opy6F() { // ld yl,a - yl = a; -} -void Opy70() { // ld (iy+nn),b - signed char ofs = Read(pc++); - Write(iy + ofs, b); - t += 11; -} -void Opy71() { // ld (iy+nn),c - signed char ofs = Read(pc++); - Write(iy + ofs, c); - t += 11; -} -void Opy72() { // ld (iy+nn),d - signed char ofs = Read(pc++); - Write(iy + ofs, d); - t += 11; -} -void Opy73() { // ld (iy+nn),e - signed char ofs = Read(pc++); - Write(iy + ofs, e); - t += 11; -} -void Opy74() { // ld (iy+nn),h - signed char ofs = Read(pc++); - Write(iy + ofs, h); - t += 11; -} -void Opy75() { // ld (iy+nn),l - signed char ofs = Read(pc++); - Write(iy + ofs, l); - t += 11; -} -void Opy77() { // ld (iy+nn),a - signed char ofs = Read(pc++); - Write(iy + ofs, a); - t += 11; -} -void Opy7C() { // ld a,yh - a = yh; -} -void Opy7D() { // ld a,yl - a = yl; -} -void Opy7E() { // ld a,(iy+nn) - signed char ofs = Read(pc++); - a = Read(iy + ofs); - t += 11; -} -void Opy84() { // add a,yh - add8(yh); -} -void Opy85() { // add a,yl - add8(yl); -} -void Opy86() { // add a,(iy+nn) - signed char ofs = Read(pc++); - add8(Read(iy + ofs)); - t += 11; -} -void Opy8C() { // adc a,yh - adc8(yh); -} -void Opy8D() { // adc a,yl - adc8(yl); -} -void Opy8E() { // adc a,(iy+nn) - signed char ofs = Read(pc++); - adc8(Read(iy + ofs)); - t += 11; -} -void Opy94() { // sub yh - sub8(yh); -} -void Opy95() { // sub yl - sub8(yl); -} -void Opy96() { // sub (iy+nn) - signed char ofs = Read(pc++); - sub8(Read(iy + ofs)); - t += 11; -} -void Opy9C() { // sbc a,yh - sbc8(yh); -} -void Opy9D() { // sbc a,yl - sbc8(yl); -} -void Opy9E() { // sbc a,(iy+nn) - signed char ofs = Read(pc++); - sbc8(Read(iy + ofs)); - t += 11; -} -void OpyA4() { // and yh - and8(yh); -} -void OpyA5() { // and yl - and8(yl); -} -void OpyA6() { // and (iy+nn) - signed char ofs = Read(pc++); - and8(Read(iy + ofs)); - t += 11; -} -void OpyAC() { // xor yh - xor8(yh); -} -void OpyAD() { // xor yl - xor8(yl); -} -void OpyAE() { // xor (iy+nn) - signed char ofs = Read(pc++); - xor8(Read(iy + ofs)); - t += 11; -} -void OpyB4() { // or yh - or8(yh); -} -void OpyB5() { // or yl - or8(yl); -} -void OpyB6() { // or (iy+nn) - signed char ofs = Read(pc++); - or8(Read(iy + ofs)); - t += 11; -} -void OpyBC() { // cp yh - cp8(yh); -} -void OpyBD() { // cp yl - cp8(yl); -} -void OpyBE() { // cp (iy+nn) - signed char ofs = Read(pc++); - cp8(Read(iy + ofs)); - t += 11; -} -void OpyE1() { // pop iy - yl = Read(sp++); - yh = Read(sp++); - t += 6; -} -void OpyE3() { // ex (sp),iy - unsigned tmp = Read(sp) + 0x100*Read(sp + 1); - Write(sp, yl); - Write(sp+1, yh); - memptr = tmp; - iy = tmp; - t += 15; -} -void OpyE5() { // push iy - Write(--sp, yh); - Write(--sp, yl); - t += 7; -} -void OpyE9() { // jp (iy) - pc = iy; -} -void OpyF9() { // ld sp,iy - sp = iy; - t += 2; -} +#undef OPXY_FUNC +#define OPXY_FUNC(a) Opy##a +#undef xy_reg +#define xy_reg iy +#undef xy_reg_lo +#define xy_reg_lo yl +#undef xy_reg_hi +#define xy_reg_hi yh + +#include "z80_op_xy.h" #endif//__Z80_OP_FD_H__ diff --git a/z80/z80_op_noprefix.h b/z80/z80_op_noprefix.h index e40f08a..f3c4ddd 100644 --- a/z80/z80_op_noprefix.h +++ b/z80/z80_op_noprefix.h @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2013 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -23,12 +24,17 @@ along with this program. If not, see . #pragma once +void rst_idone(temp16 addr) { + push(pc); + pc = addr; + memptr = addr; + t += 7; +} void Op00() { // nop /* note: don't inc t: 4 cycles already wasted in m1_cycle() */ } void Op01() { // ld bc,nnnn - c = Read(pc++); - b = Read(pc++); + bc = Read2Inc(pc); t += 6; } void Op02() { // ld (bc),a @@ -48,26 +54,23 @@ void Op05() { // dec b dec8(b); } void Op06() { // ld b,nn - b = Read(pc++); + b = ReadInc(pc); t += 3; } +#ifndef USE_Z80T void Op07() { // rlca f = rlcaf[a] | (f & (SF | ZF | PV)); a = rol[a]; } +#endif void Op08() { // ex af,af' - unsigned tmp = af; + temp16 tmp; + tmp = af; af = alt.af; alt.af = tmp; } void Op09() { // add hl,bc - memptr = hl+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((hl & 0x0FFF) + (bc & 0x0FFF)) >> 8) & 0x10; /* HF */ - hl = (hl & 0xFFFF) + (bc & 0xFFFF); - if (hl & 0x10000) f |= CF; - f |= (h & (F5 | F3)); - t += 7; + add16(hl, bc); } void Op0A() { // ld a,(bc) memptr = bc+1; @@ -75,7 +78,7 @@ void Op0A() { // ld a,(bc) t += 3; } void Op0B() { // dec bc - bc--; + --bc; t += 2; } void Op0C() { // inc c @@ -85,22 +88,29 @@ void Op0D() { // dec c dec8(c); } void Op0E() { // ld c,nn - c = Read(pc++); + c = ReadInc(pc); t += 3; } +#ifndef USE_Z80T void Op0F() { // rrca f = rrcaf[a] | (f & (SF | ZF | PV)); a = ror[a]; } +#endif void Op10() { // djnz rr - if (--b) { - signed char offs = (char)Read(pc); - memptr = pc += offs+1, t += 9; - } else pc++, t += 4; + --b; + return if_nonzero(b, + [&] { + address_delta offs = (address_delta)Read(pc); + memptr = pc += offs+1, t += 9; + }, + [&] { + pc++, t += 4; + }); + } void Op11() { // ld de,nnnn - e = Read(pc++); - d = Read(pc++); + de = Read2Inc(pc); t += 6; } void Op12() { // ld (de),a @@ -120,28 +130,24 @@ void Op15() { // dec d dec8(d); } void Op16() { // ld d,nn - d = Read(pc++); + d = ReadInc(pc); t += 3; } +#ifndef USE_Z80T void Op17() { // rla byte new_a = (a << 1) + (f & 1); f = rlcaf[a] | (f & (SF | ZF | PV)); // use same table with rlca a = new_a; } +#endif void Op18() { // jr rr - signed char offs = (char)Read(pc); - pc += offs+1; + address_delta offs = (address_delta)Read(pc); + pc += offs + 1; memptr = pc; t += 8; } void Op19() { // add hl,de - memptr = hl+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((hl & 0x0FFF) + (de & 0x0FFF)) >> 8) & 0x10; /* HF */ - hl = (hl & 0xFFFF) + (de & 0xFFFF); - if (hl & 0x10000) f |= CF; - f |= (h & (F5 | F3)); - t += 7; + add16(hl, de); } void Op1A() { // ld a,(de) memptr = de+1; @@ -149,7 +155,7 @@ void Op1A() { // ld a,(de) t += 3; } void Op1B() { // dec de - de--; + --de; t += 2; } void Op1C() { // inc e @@ -159,33 +165,35 @@ void Op1D() { // dec e dec8(e); } void Op1E() { // ld e,nn - e = Read(pc++); + e = ReadInc(pc); t += 3; } +#ifndef USE_Z80T void Op1F() { // rra byte new_a = (a >> 1) + (f << 7); f = rrcaf[a] | (f & (SF | ZF | PV)); // use same table with rrca a = new_a; } +#endif void Op20() { // jr nz, rr - if (!(f & ZF)) { - signed char offs = (char)Read(pc); + return if_flag_clear(ZF, + [&] { + address_delta offs = (address_delta)Read(pc); memptr = pc += offs+1, t += 8; - } else pc++, t += 3; + }, + [&] { + pc++, t += 3; + }); } void Op21() { // ld hl,nnnn - l = Read(pc++); - h = Read(pc++); + hl = Read2Inc(pc); t += 6; } void Op22() { // ld (nnnn),hl - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; + temp16 adr = Read2Inc(pc); memptr = adr+1; - t += 9; - Write(adr, l); - t += 3; - Write(adr+1, h); + Write2(adr, hl); + t += 12; } void Op23() { // inc hl hl++; @@ -198,38 +206,36 @@ void Op25() { // dec h dec8(h); } void Op26() { // ld h,nn - h = Read(pc++); + h = ReadInc(pc); t += 3; } +#ifndef USE_Z80T void Op27() { // daa af = daatab[a + 0x100*((f & 3) + ((f >> 2) & 4))]; } +#endif void Op28() { // jr z,rr - if ((f & ZF)) { - signed char offs = (char)Read(pc); - memptr = pc += offs+1, t += 8; - } else pc++, t += 3; + return if_flag_set(ZF, + [&] { + address_delta offs = (address_delta)Read(pc); + memptr = pc += offs+1, t += 8; + }, + [&] { + pc++, t += 3; + }); } void Op29() { // add hl,hl - memptr = hl+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= ((hl >> 7) & 0x10); /* HF */ - hl = (hl & 0xFFFF)*2; - if (hl & 0x10000) f |= CF; - f |= (h & (F5 | F3)); - t += 7; + add16(hl, hl); } void Op2A() { // ld hl,(nnnn) - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; + temp16 adr = Read2Inc(pc); memptr = adr+1; - l = Read(adr); - h = Read(adr+1); + hl = Read2(adr); t += 12; } void Op2B() { // dec hl - hl--; + --hl; t += 2; } void Op2C() { // inc l @@ -239,28 +245,31 @@ void Op2D() { // dec l dec8(l); } void Op2E() { // ld l,nn - l = Read(pc++); + l = ReadInc(pc); t += 3; } void Op2F() { // cpl a ^= 0xFF; - f = (f & ~(F3|F5)) | NF | HF | (a & (F3|F5)); + set_a35_flags_preserve_set(PV|ZF|SF|CF, NF|HF); +// f = (f & ~(F3|F5)) | NF | HF | (a & (F3|F5)); } void Op30() { // jr nc, rr - if (!(f & CF)) { - signed char offs = (char)Read(pc); - memptr = pc += offs+1, t += 8; - } else pc++, t += 3; + return if_flag_clear(CF, + [&] { + address_delta offs = (address_delta)Read(pc); + memptr = pc += offs+1, t += 8; + }, + [&] { + pc++, t += 3; + }); } void Op31() { // ld sp,nnnn - sp_l = Read(pc++); - sp_h = Read(pc++); + sp = Read2Inc(pc); t += 6; } void Op32() { // ld (nnnn),a - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; - memptr = ((adr+1) & 0xFF) + (a << 8); + temp16 adr = Read2Inc(pc); + mem_l = adr+1; mem_h = a; t += 9; Write(adr, a); @@ -270,48 +279,46 @@ void Op33() { // inc sp t += 2; } void Op34() { // inc (hl) - byte v = Read(hl); + temp8 v = Read(hl); inc8(v); t += 7; Write(hl, v); } void Op35() { // dec (hl) - byte v = Read(hl); + temp8 v = Read(hl); dec8(v); t += 7; Write(hl, v); } void Op36() { // ld (hl),nn t += 6; - Write(hl, Read(pc++)); + Write(hl, ReadInc(pc)); } void Op37() { // scf - f = (f & (PV|ZF|SF)) | (a & (F3|F5)) | CF; + // f = (f & (PV|ZF|SF)) | (a & (F3|F5)) | CF; + set_a35_flags_preserve_set(PV|ZF|SF, CF); } void Op38() { // jr c,rr - if ((f & CF)) { - signed char offs = (char)Read(pc); - memptr = pc += offs+1, t += 8; - } else pc++, t += 3; + return if_flag_set(CF, + [&] { + address_delta offs = (address_delta)Read(pc); + memptr = pc += offs+1, t += 8; + }, + [&] { + pc++, t += 3; + }); } void Op39() { // add hl,sp - memptr = hl+1; - f = (f & ~(NF | CF | F5 | F3 | HF)); - f |= (((hl & 0x0FFF) + (sp & 0x0FFF)) >> 8) & 0x10; /* HF */ - hl = (hl & 0xFFFF) + (sp & 0xFFFF); - if (hl & 0x10000) f |= CF; - f |= (h & (F5 | F3)); - t += 7; + add16(hl, sp); } void Op3A() { // ld a,(nnnn) - unsigned adr = Read(pc++); - adr += Read(pc++)*0x100; + temp16 adr = Read2Inc(pc); memptr = adr+1; a = Read(adr); t += 9; } void Op3B() { // dec sp - sp--; + --sp; t += 2; } void Op3C() { // inc a @@ -321,12 +328,14 @@ void Op3D() { // dec a dec8(a); } void Op3E() { // ld a,nn - a = Read(pc++); + a = ReadInc(pc); t += 3; } +#ifndef USE_Z80T void Op3F() { // ccf f = (f & (PV|ZF|SF)) | ((f & CF) ? HF : CF) | (a & (F3|F5)); } +#endif void Op40() {} // ld b,b void Op49() {} // ld c,c void Op52() {} // ld d,d @@ -490,10 +499,12 @@ void Op75() { // ld (hl),l t += 3; Write(hl, l); } +#ifndef USE_Z80T void Op76() { // halt halted = 1; unsigned int st = (frame_tacts - t-1)/4+1; t += 4*st; +#ifndef NO_USE_REPLAY if(handler.io) // replay is active { r_low += fetches; @@ -501,7 +512,13 @@ void Op76() { // halt } else r_low += st; +#else +#ifndef NO_UPDATE_RLOW_IN_FETCH + r_low += st; +#endif +#endif } +#endif void Op77() { // ld (hl),a t += 3; Write(hl, a); @@ -729,402 +746,390 @@ void OpBF() { // cp a cp8(a); // can't optimize: F3,F5 depends on A } void OpC0() { // ret nz - if (!(f & ZF)) { - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; - t += 7; - } else t += 1; + return if_flag_clear(ZF, + [&] { + pc = memptr = Read2Inc(sp); + t += 7; + }, + [&] { + t += 1; + }); } void OpC1() { // pop bc - c = Read(sp++); - b = Read(sp++); + bc = Read2Inc(sp); t += 6; } void OpC2() { // jp nz,nnnn t += 6; - unsigned addr = Read(pc); - addr += 0x100*Read(pc+1); - memptr = addr; - if (!(f & ZF)) { - pc = addr; - } else pc += 2; + memptr = Read2(pc); + return if_flag_clear(ZF, + [&] { + pc = memptr; + }, + [&] { + pc += 2; + }); } void OpC3() { // jp nnnn - unsigned lo = Read(pc++); - pc = lo + 0x100*Read(pc); + pc = Read2(pc); memptr = pc; t += 6; } void OpC4() { // call nz,nnnn - pc += 2; - unsigned addr = Read(pc-2); - addr += 0x100*Read(pc-1); - memptr = addr; - if (!(f & ZF)) { - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = addr; - t += 13; - } else t += 6; + memptr = Read2Inc(pc); + return if_flag_clear(ZF, + [&]{ + push(pc); + pc = memptr; + t += 13; + }, + [&]{ + t += 6; + }); } void OpC5() { // push bc t += 7; - Write(--sp, b); - Write(--sp, c); + push(bc); } void OpC6() { // add a,nn - add8(Read(pc++)); + add8(ReadInc(pc)); t += 3; } void OpC7() { // rst 00 - Write(--sp, pc_h); - Write(--sp, pc_l); + push(pc); pc = 0x00; memptr = 0x00; t += 7; } void OpC8() { // ret z - if (f & ZF) { - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; - t += 7; - } else t += 1; + return if_flag_set(ZF, + [&] { + pc = memptr = Read2Inc(sp); + t += 7; + }, + [&] { + t += 1; + }); } void OpC9() { // ret - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; + pc = memptr = Read2Inc(sp); t += 6; } void OpCA() { // jp z,nnnn t += 6; - unsigned addr = Read(pc); - addr += 0x100*Read(pc+1); - memptr = addr; - if(f & ZF) - { - pc = addr; - } - else - pc += 2; + memptr = Read2(pc); + return if_flag_set(ZF, + [&] { + pc = memptr; + }, + [&] { + pc += 2; + }); + } void OpCC() { // call z,nnnn - pc += 2; - unsigned addr = Read(pc-2); - addr += 0x100*Read(pc-1); - memptr = addr; - if (f & ZF) { - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = addr; - t += 13; - } else t += 6; + memptr = Read2Inc(pc); + return if_flag_set(ZF, + [&]{ + push(pc); + pc = memptr; + t += 13; + }, + [&]{ + t += 6; + }); } void OpCD() { // call - unsigned addr = Read(pc++); - addr += 0x100*Read(pc++); - Write(--sp, pc_h); - Write(--sp, pc_l); + temp16 addr = Read2Inc(pc); + push(pc); pc = addr; memptr = addr; t += 13; } void OpCE() { // adc a,nn - adc8(Read(pc++)); + adc8(ReadInc(pc)); t += 3; } void OpCF() { // rst 08 - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = 0x08; - memptr = 0x08; - mem_h = 0; - t += 7; + rst_idone(0x8); } void OpD0() { // ret nc - if (!(f & CF)) { - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; - t += 7; - } else t += 1; + return if_flag_clear(CF, + [&] { + pc = memptr = Read2Inc(sp); + t += 7; + }, + [&] { + t += 1; + }); } void OpD1() { // pop de - e = Read(sp++); - d = Read(sp++); + de = Read2Inc(sp); t += 6; } void OpD2() { // jp nc,nnnn t += 6; - unsigned addr = Read(pc); - addr += 0x100*Read(pc+1); - memptr = addr; - if (!(f & CF)) { - pc = addr; - } else pc += 2; + memptr = Read2(pc); + return if_flag_clear(CF, + [&] { + pc = memptr; + }, + [&] { + pc += 2; + }); } void OpD3() { // out (nn),a - unsigned port = Read(pc++); + temp16 port = ReadInc(pc); t += 7; - memptr = ((port+1) & 0xFF) + (a << 8); - IoWrite(port + (a << 8), a); + temp16_2 a_hi = a << 8; + memptr = ((port+1) & 0xFF) | a_hi; + IoWrite(port | a_hi, a); } void OpD4() { // call nc,nnnn - pc += 2; - unsigned addr = Read(pc-2); - addr += 0x100*Read(pc-1); - memptr = addr; - if (!(f & CF)) { - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = addr; - t += 13; - } else t += 6; + memptr = Read2Inc(pc); + return if_flag_clear(CF, + [&]{ + push(pc); + pc = memptr; + t += 13; + }, + [&]{ + t += 6; + }); } void OpD5() { // push de t += 7; - Write(--sp, d); - Write(--sp, e); + push(de); } void OpD6() { // sub nn - sub8(Read(pc++)); + sub8(ReadInc(pc)); t += 3; } void OpD7() { // rst 10 - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = 0x10; - memptr = 0x10; - t += 7; + rst_idone(0x10); } + void OpD8() { // ret c - if (f & CF) { - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; - t += 7; - } else t += 1; + return if_flag_set(CF, + [&] { + pc = memptr = Read2Inc(sp); + t += 7; + }, + [&] { + t += 1; + }); } + +#ifndef USE_Z80T void OpD9() { // exx unsigned tmp = bc; bc = alt.bc; alt.bc = tmp; tmp = de; de = alt.de; alt.de = tmp; tmp = hl; hl = alt.hl; alt.hl = tmp; } +#endif + void OpDA() { // jp c,nnnn t += 6; - unsigned addr = Read(pc); - addr += 0x100*Read(pc+1); - memptr = addr; - if (f & CF) { - pc = addr; - } else pc += 2; + memptr = Read2(pc); + return if_flag_set(CF, + [&] { + pc = memptr; + }, + [&] { + pc += 2; + }); } void OpDB() { // in a,(nn) - unsigned port = Read(pc++) + (a << 8); + temp16 port = ReadInc(pc); + port |= (a << 8); memptr = port+1; t += 7; a = IoRead(port); } void OpDC() { // call c,nnnn - pc += 2; - unsigned addr = Read(pc-2); - addr += 0x100*Read(pc-1); - memptr = addr; - if (f & CF) { - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = addr; - t += 13; - } else t += 6; + memptr = Read2Inc(pc); + return if_flag_set(CF, + [&]{ + push(pc); + pc = memptr; + t += 13; + }, + [&]{ + t += 6; + }); } void OpDE() { // sbc a,nn - sbc8(Read(pc++)); + sbc8(ReadInc(pc)); t += 3; } void OpDF() { // rst 18 - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = 0x18; - memptr = 0x18; - t += 7; + rst_idone(0x18); } void OpE0() { // ret po - if (!(f & PV)) { - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; - t += 7; - } else t += 1; + return if_flag_clear(PV, + [&] { + pc = memptr = Read2Inc(sp); + t += 7; + }, + [&] { + t += 1; + }); } void OpE1() { // pop hl - l = Read(sp++); - h = Read(sp++); + hl = Read2Inc(sp); t += 6; } void OpE2() { // jp po,nnnn t += 6; - unsigned addr = Read(pc); - addr += 0x100*Read(pc+1); - memptr = addr; - if (!(f & PV)) { - pc = addr; - } else pc += 2; + memptr = Read2(pc); + return if_flag_clear(PV, + [&] { + pc = memptr; + }, + [&] { + pc += 2; + }); } void OpE3() { // ex (sp),hl - unsigned tmp = Read(sp) + 0x100*Read(sp + 1); - Write(sp, l); - Write(sp+1, h); + temp16 tmp = Read2(sp); + Write2(sp, hl); memptr = tmp; hl = tmp; t += 15; } void OpE4() { // call po,nnnn - pc += 2; - unsigned addr = Read(pc-2); - addr += 0x100*Read(pc-1); - memptr = addr; - if (!(f & PV)) { - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = addr; - t += 13; - } else t += 6; + memptr = Read2Inc(pc); + return if_flag_clear(PV, + [&]{ + push(pc); + pc = memptr; + t += 13; + }, + [&]{ + t += 6; + }); } void OpE5() { // push hl t += 7; - Write(--sp, h); - Write(--sp, l); + push(hl); } void OpE6() { // and nn - and8(Read(pc++)); + and8(ReadInc(pc)); t += 3; } void OpE7() { // rst 20 - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = 0x20; - memptr = 0x20; - t += 7; + rst_idone(0x20); } void OpE8() { // ret pe - if (f & PV) { - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; - t += 7; - } else t += 1; + return if_flag_set(PV, + [&] { + pc = memptr = Read2Inc(sp); + t += 7; + }, + [&] { + t += 1; + }); } void OpE9() { // jp (hl) pc = hl; } void OpEA() { // jp pe,nnnn t += 6; - unsigned addr = Read(pc); - addr += 0x100*Read(pc+1); - memptr = addr; - if (f & PV) { - pc = addr; - } else pc += 2; + memptr = Read2(pc); + return if_flag_set(PV, + [&] { + pc = memptr; + }, + [&] { + pc += 2; + }); } void OpEB() { // ex de,hl - unsigned tmp = de; + temp16 tmp; + tmp = de; de = hl; hl = tmp; } void OpEC() { // call pe,nnnn - pc += 2; - unsigned addr = Read(pc-2); - addr += 0x100*Read(pc-1); - memptr = addr; - if (f & PV) { - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = addr; - t += 13; - } else t += 6; + memptr = Read2Inc(pc); + return if_flag_set(PV, + [&]{ + push(pc); + pc = memptr; + t += 13; + }, + [&]{ + t += 6; + }); } void OpEE() { // xor nn - xor8(Read(pc++)); + xor8(ReadInc(pc)); t += 3; } void OpEF() { // rst 28 - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = 0x28; - memptr = 0x28; - t += 7; + rst_idone(0x28); } void OpF0() { // ret p - if (!(f & SF)) { - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; - t += 7; - } else t += 1; + return if_flag_clear(SF, + [&] { + pc = memptr = Read2Inc(sp); + t += 7; + }, + [&] { + t += 1; + }); } void OpF1() { // pop af - f = Read(sp++); - a = Read(sp++); + af = Read2Inc(sp); t += 6; } void OpF2() { // jp p,nnnn t += 6; - unsigned addr = Read(pc); - addr += 0x100*Read(pc+1); - memptr = addr; - if (!(f & SF)) { - pc = addr; - } else pc += 2; + memptr = Read2(pc); + return if_flag_clear(SF, + [&] { + pc = memptr; + }, + [&] { + pc += 2; + }); } void OpF3() { // di - iff1 = iff2 = 0; + iff1 = 0; + iff2 = 0; } void OpF4() { // call p,nnnn - pc += 2; - unsigned addr = Read(pc-2); - addr += 0x100*Read(pc-1); - memptr = addr; - if (!(f & SF)) { - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = addr; + memptr = Read2Inc(pc); + return if_flag_clear(SF, + [&]{ + push(pc); + pc = memptr; t += 13; - } else t += 6; + }, + [&]{ + t += 6; + }); } void OpF5() { // push af t += 7; - Write(--sp, a); - Write(--sp, f); + push(af); } void OpF6() { // or nn - or8(Read(pc++)); + or8(ReadInc(pc)); t += 3; } void OpF7() { // rst 30 - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = 0x30; - memptr = 0x30; - t += 7; + rst_idone(0x30); } void OpF8() { // ret m - if (f & SF) { - unsigned addr = Read(sp++); - addr += 0x100*Read(sp++); - pc = addr; - memptr = addr; - t += 7; - } else t += 1; + return if_flag_set(SF, + [&] { + pc = memptr = Read2Inc(sp); + t += 7; + }, + [&] { + t += 1; + }); } void OpF9() { // ld sp,hl sp = hl; @@ -1132,39 +1137,37 @@ void OpF9() { // ld sp,hl } void OpFA() { // jp m,nnnn t += 6; - unsigned addr = Read(pc); - addr += 0x100*Read(pc+1); - memptr = addr; - if (f & SF) { - pc = addr; - } else pc += 2; + memptr = Read2(pc); + return if_flag_set(SF, + [&] { + pc = memptr; + }, + [&] { + pc += 2; + }); } void OpFB() { // ei iff1 = iff2 = 1; eipos = t; } void OpFC() { // call m,nnnn - pc += 2; - unsigned addr = Read(pc-2); - addr += 0x100*Read(pc-1); - memptr = addr; - if (f & SF) { - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = addr; - t += 13; - } else t += 6; + memptr = Read2Inc(pc); + return if_flag_set(SF, + [&]{ + push(pc); + pc = memptr; + t += 13; + }, + [&]{ + t += 6; + }); } void OpFE() { // cp nn - cp8(Read(pc++)); + cp8(ReadInc(pc)); t += 3; } void OpFF() { // rst 38 - Write(--sp, pc_h); - Write(--sp, pc_l); - pc = 0x38; - memptr = 0x38; - t += 7; + rst_idone(0x38); } #endif//__Z80_OP_NOPREFIX_H__ diff --git a/z80/z80_op_xy.h b/z80/z80_op_xy.h new file mode 100644 index 0000000..b0f2fdd --- /dev/null +++ b/z80/z80_op_xy.h @@ -0,0 +1,330 @@ +/* +Portable ZX-Spectrum emulator. +Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson + +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 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +/* DD prefix opcodes */ + +void OPXY_FUNC(09)() { // add ix,bc + add16(xy_reg, bc); +} + +void OPXY_FUNC(19)() { // add ix,de + add16(xy_reg, de); +} +void OPXY_FUNC(21)() { // ld ix,nnnn + xy_reg = Read2Inc(pc); + t += 6; +} +void OPXY_FUNC(22)() { // ld (nnnn),ix + temp16_xy adr = Read2Inc(pc); + memptr = adr+1; + Write2(adr, xy_reg); + t += 12; +} +void OPXY_FUNC(23)() { // inc ix + xy_reg++; + t += 2; +} +void OPXY_FUNC(24)() { // inc xh + inc8(xy_reg_hi); +} +void OPXY_FUNC(25)() { // dec xh + dec8(xy_reg_hi); +} +void OPXY_FUNC(26)() { // ld xh,nn + xy_reg_hi = ReadInc(pc); + t += 3; +} +void OPXY_FUNC(29)() { // add ix,ix +// memptr = xy_reg+1; +// f = (f & ~(NF | CF | F5 | F3 | HF)); +// f |= ((xy_reg >> 7) & 0x10); /* HF */ +// xy_reg = (xy_reg & 0xFFFF)*2; +// if (xy_reg & 0x10000) f |= CF; +// f |= (xy_reg_hi & (F5 | F3)); +// t += 7; + add16(xy_reg, xy_reg); +} +void OPXY_FUNC(2A)() { // ld ix,(nnnn) + temp16_xy adr = Read2Inc(pc); + xy_reg = Read2(adr); + memptr = adr+1; + t += 12; +} +void OPXY_FUNC(2B)() { // dec ix + --xy_reg; + t += 2; +} +void OPXY_FUNC(2C)() { // inc xl + inc8(xy_reg_lo); +} +void OPXY_FUNC(2D)() { // dec xl + dec8(xy_reg_lo); +} +void OPXY_FUNC(2E)() { // ld xl,nn + xy_reg_lo = ReadInc(pc); + t += 3; +} +void OPXY_FUNC(34)() { // inc (ix+nn) + scratch16 scratch = xy_reg + (address_delta)ReadInc(pc); + temp8_xy v = Read(scratch); + inc8(v); + WriteXY(scratch, v); + t += 15; +} +void OPXY_FUNC(35)() { // dec (ix+nn) + scratch16 scratch = xy_reg + (address_delta)ReadInc(pc); + temp8_xy v = Read(scratch); + dec8(v); + WriteXY(scratch, v); + t += 15; +} +void OPXY_FUNC(36)() { // ld (ix+nn),nn + scratch16 scratch = xy_reg + (address_delta)ReadInc(pc); + temp8_xy v = ReadInc(pc); + WriteXY(scratch, v); + t += 11; +} +void OPXY_FUNC(39)() { // add ix,sp + add16(xy_reg, sp); +} + +void OPXY_FUNC(44)() { // ld b,xh + b = xy_reg_hi; +} +void OPXY_FUNC(45)() { // ld b,xl + b = xy_reg_lo; +} +void OPXY_FUNC(46)() { // ld b,(ix+nn) + b = Read(xy_reg + (address_delta)ReadInc(pc)); + t += 11; +} +void OPXY_FUNC(4C)() { // ld c,xh + c = xy_reg_hi; +} +void OPXY_FUNC(4D)() { // ld c,xl + c = xy_reg_lo; +} +void OPXY_FUNC(4E)() { // ld c,(ix+nn) + c = Read(xy_reg + (address_delta)ReadInc(pc)); + t += 11; +} +void OPXY_FUNC(54)() { // ld d,xh + d = xy_reg_hi; +} +void OPXY_FUNC(55)() { // ld d,xl + d = xy_reg_lo; +} +void OPXY_FUNC(56)() { // ld d,(ix+nn) + d = Read(xy_reg + (address_delta)ReadInc(pc)); + t += 11; +} +void OPXY_FUNC(5C)() { // ld e,xh + e = xy_reg_hi; +} +void OPXY_FUNC(5D)() { // ld e,xl + e = xy_reg_lo; +} +void OPXY_FUNC(5E)() { // ld e,(ix+nn) + e = Read(xy_reg + (address_delta)ReadInc(pc)); + t += 11; +} +void OPXY_FUNC(60)() { // ld xh,b + xy_reg_hi = b; +} +void OPXY_FUNC(61)() { // ld xh,c + xy_reg_hi = c; +} +void OPXY_FUNC(62)() { // ld xh,d + xy_reg_hi = d; +} +void OPXY_FUNC(63)() { // ld xh,e + xy_reg_hi = e; +} +void OPXY_FUNC(65)() { // ld xh,xl + xy_reg_hi = xy_reg_lo; +} +void OPXY_FUNC(66)() { // ld h,(ix+nn) + h = Read(xy_reg + (address_delta)ReadInc(pc)); + t += 11; +} +void OPXY_FUNC(67)() { // ld xh,a + xy_reg_hi = a; +} +void OPXY_FUNC(68)() { // ld xl,b + xy_reg_lo = b; +} +void OPXY_FUNC(69)() { // ld xl,c + xy_reg_lo = c; +} +void OPXY_FUNC(6A)() { // ld xl,d + xy_reg_lo = d; +} +void OPXY_FUNC(6B)() { // ld xl,e + xy_reg_lo = e; +} +void OPXY_FUNC(6C)() { // ld xl,xh + xy_reg_lo = xy_reg_hi; +} +void OPXY_FUNC(6E)() { // ld l,(ix+nn) + l = Read(xy_reg + (address_delta)ReadInc(pc)); + t += 11; +} +void OPXY_FUNC(6F)() { // ld xl,a + xy_reg_lo = a; +} +void OPXY_FUNC(70)() { // ld (ix+nn),b + WriteXY(xy_reg + ReadInc(pc), b); + t += 11; +} +void OPXY_FUNC(71)() { // ld (ix+nn),c + WriteXY(xy_reg + (address_delta)ReadInc(pc), c); + t += 11; +} +void OPXY_FUNC(72)() { // ld (ix+nn),d + WriteXY(xy_reg + (address_delta)ReadInc(pc), d); + t += 11; +} +void OPXY_FUNC(73)() { // ld (ix+nn),e + WriteXY(xy_reg + (address_delta)ReadInc(pc), e); + t += 11; +} +void OPXY_FUNC(74)() { // ld (ix+nn),h + WriteXY(xy_reg + (address_delta)ReadInc(pc), h); + t += 11; +} +void OPXY_FUNC(75)() { // ld (ix+nn),l + WriteXY(xy_reg + (address_delta)ReadInc(pc), l); + t += 11; +} +void OPXY_FUNC(77)() { // ld (ix+nn),a + WriteXY(xy_reg + (address_delta)ReadInc(pc), a); + t += 11; +} +void OPXY_FUNC(7C)() { // ld a,xh + a = xy_reg_hi; +} +void OPXY_FUNC(7D)() { // ld a,xl + a = xy_reg_lo; +} +void OPXY_FUNC(7E)() { // ld a,(ix+nn) + a = Read(xy_reg + (address_delta)ReadInc(pc)); + t += 11; +} +void OPXY_FUNC(84)() { // add a,xh + add8(xy_reg_hi); +} +void OPXY_FUNC(85)() { // add a,xl + add8(xy_reg_lo); +} +void OPXY_FUNC(86)() { // add a,(ix+nn) + add8(Read(xy_reg + (address_delta)ReadInc(pc))); + t += 11; +} +void OPXY_FUNC(8C)() { // adc a,xh + adc8(xy_reg_hi); +} +void OPXY_FUNC(8D)() { // adc a,xl + adc8(xy_reg_lo); +} +void OPXY_FUNC(8E)() { // adc a,(ix+nn) + adc8(Read(xy_reg + (address_delta)ReadInc(pc))); + t += 11; +} +void OPXY_FUNC(94)() { // sub xh + sub8(xy_reg_hi); +} +void OPXY_FUNC(95)() { // sub xl + sub8(xy_reg_lo); +} +void OPXY_FUNC(96)() { // sub (ix+nn) + sub8(Read(xy_reg + (address_delta)ReadInc(pc))); + t += 11; +} +void OPXY_FUNC(9C)() { // sbc a,xh + sbc8(xy_reg_hi); +} +void OPXY_FUNC(9D)() { // sbc a,xl + sbc8(xy_reg_lo); +} +void OPXY_FUNC(9E)() { // sbc a,(ix+nn) + sbc8(Read(xy_reg + (address_delta)ReadInc(pc))); + t += 11; +} +void OPXY_FUNC(A4)() { // and xh + and8(xy_reg_hi); +} +void OPXY_FUNC(A5)() { // and xl + and8(xy_reg_lo); +} +void OPXY_FUNC(A6)() { // and (ix+nn) + and8(Read(xy_reg + (address_delta)ReadInc(pc))); + t += 11; +} +void OPXY_FUNC(AC)() { // xor xh + xor8(xy_reg_hi); +} +void OPXY_FUNC(AD)() { // xor xy_reg_lo + xor8(xy_reg_lo); +} +void OPXY_FUNC(AE)() { // xor (ix+nn) + xor8(Read(xy_reg + (address_delta)ReadInc(pc))); + t += 11; +} +void OPXY_FUNC(B4)() { // or xh + or8(xy_reg_hi); +} +void OPXY_FUNC(B5)() { // or xl + or8(xy_reg_lo); +} +void OPXY_FUNC(B6)() { // or (ix+nn) + or8(Read(xy_reg + (address_delta)ReadInc(pc))); + t += 11; +} +void OPXY_FUNC(BC)() { // cp xh + cp8(xy_reg_hi); +} +void OPXY_FUNC(BD)() { // cp xl + cp8(xy_reg_lo); +} +void OPXY_FUNC(BE)() { // cp (ix+nn) + cp8(Read(xy_reg + (address_delta)ReadInc(pc))); + t += 11; +} +void OPXY_FUNC(E1)() { // pop ix + xy_reg = Read2Inc(sp); + t += 6; +} +void OPXY_FUNC(E3)() { // ex (sp),ix + memptr = Read2(sp); + Write2(sp, xy_reg); + xy_reg = memptr; + t += 15; +} +void OPXY_FUNC(E5)() { // push ix + push(xy_reg); + t += 7; +} +void OPXY_FUNC(E9)() { // jp (ix) + pc = xy_reg; +} +void OPXY_FUNC(F9)() { // ld sp,ix + sp = xy_reg; + t += 2; +} + diff --git a/z80/z80_opcodes.cpp b/z80/z80_opcodes.cpp index 1f5bc54..a1439f3 100644 --- a/z80/z80_opcodes.cpp +++ b/z80/z80_opcodes.cpp @@ -1,6 +1,7 @@ /* Portable ZX-Spectrum emulator. Copyright (C) 2001-2010 SMT, Dexus, Alone Coder, deathsoft, djdron, scor +Copyright (C) 2023 Graham Sanderson 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 @@ -48,6 +49,14 @@ void eZ80::Write(word addr, byte v) ula->Write(t); memory->Write(addr, v); } + +void eZ80::Write2(word addr, int v) +{ + Write(addr, v&0xff); + Write(addr+1, v>>8); +} + + //============================================================================= // eZ80::Read //----------------------------------------------------------------------------- @@ -56,6 +65,26 @@ inline byte eZ80::Read(word addr) const return memory->Read(addr); } +inline byte eZ80::ReadInc(int& addr) const +{ + return memory->Read(addr++); +} + +inline int eZ80::Read2(word addr) const +{ + unsigned r = memory->Read(addr); + r += memory->Read(addr+1) << 8u; + return r; +} + +inline int eZ80::Read2Inc(int& addr) const +{ + unsigned r = memory->Read(addr++); + r += memory->Read(addr++) << 8u; + return r; +} + + //============================================================================= // eZ80::InitOpNoPrefix //----------------------------------------------------------------------------- @@ -151,7 +180,7 @@ void eZ80::InitOpCB() //----------------------------------------------------------------------------- void eZ80::InitOpDD() { - CALLFUNC const opcodes[] = + CALLFUNC const opcodes[] = { &eZ80::Op00 , &eZ80::Op01 , &eZ80::Op02 , &eZ80::Op03 , &eZ80::Op04 , &eZ80::Op05 , &eZ80::Op06 , &eZ80::Op07 , &eZ80::Op08 , &eZ80::Opx09, &eZ80::Op0A , &eZ80::Op0B , &eZ80::Op0C , &eZ80::Op0D , &eZ80::Op0E , &eZ80::Op0F , @@ -196,7 +225,7 @@ void eZ80::InitOpDD() //----------------------------------------------------------------------------- void eZ80::InitOpED() { - CALLFUNC const opcodes[] = + CALLFUNC const opcodes[] = { &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 , &eZ80::Op00 ,