diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 244fe3b..ee251e0 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -29,4 +29,4 @@ jobs: esp_idf_version: release/v5.4 # (Optional) cppcheck args - cppcheck_args: -i$GITHUB_WORKSPACE/components/gbc/gnuboy -i$GITHUB_WORKSPACE/components/nes/nofrendo -i$GITHUB_WORKSPACE/components/sms/smsplus -i$GITHUB_WORKSPACE/components/genesis/gwenesis -i$GITHUB_WORKSPACE/components/gui/generated -i$GITHUB_WORKSPACE/components/menu/generated -i$GITHUB_WORKSPACE/components/jpegdec --check-level=exhaustive --force --enable=all --inline-suppr --inconclusive --platform=mips32 --std=c++17 --suppressions-list=$GITHUB_WORKSPACE/suppressions.txt + cppcheck_args: -i$GITHUB_WORKSPACE/components/gbc/gnuboy -i$GITHUB_WORKSPACE/components/nes/nofrendo -i$GITHUB_WORKSPACE/components/msx/fmsx -i$GITHUB_WORKSPACE/components/sms/smsplus -i$GITHUB_WORKSPACE/components/genesis/gwenesis -i$GITHUB_WORKSPACE/components/gui/generated -i$GITHUB_WORKSPACE/components/menu/generated -i$GITHUB_WORKSPACE/components/jpegdec --check-level=exhaustive --force --enable=all --inline-suppr --inconclusive --platform=mips32 --std=c++17 --suppressions-list=$GITHUB_WORKSPACE/suppressions.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index c1108b1..ccbcfbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ set(SMS_COMPONENTS "sms") # set(SNES_COMPONENTS "snes") ### MSX ### -# set(MSX_COMPONENTS "msx") +set(MSX_COMPONENTS "msx") ### GENESIS ### set(GENESIS_COMPONENTS "genesis") diff --git a/README.md b/README.md index 0c36a6c..d4de786 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ ESP32-S3-BOX-3 which provides: - NES Emulator (nofrendo) - Regular Controls (D-Pad/A/B/Start/Select) - Unlocked mode (fastest execution), toggled with the X button + - MSX I / II Emulator (fmsx) + - Regular Controls (D-Pad/A/B/Start/Select) - Gameboy / Gameboy Color emulator (gnuboy) - Regular Controls (D-Pad/A/B/Start/Select) - Unlocked mode (fastest execution), toggled with the X button @@ -75,13 +77,14 @@ This project has the following features (still WIP): - [x] User input with d-pad + buttons (a/b/x/y, start/select) (using MCP23x17 [v0 hardware] or AW9523 [v1 hardware]) - [x] Interaction with touchscreen (using [tt21100 component](./components/tt21100)) - [x] Navigation of LVGL rom menu with controller (up,down,a,b,start) + - [x] Shared memory system shared between emulators to allow for many emulators to compile in together while still enabling their main state to be stored in fast internal memory. - [x] Runnable emulators (automatically selected by rom extension): - [x] NES emulator - [x] GB/GBC emulator - [x] Sega Master System (SMS) / GameGear (GG) emulator - - [ ] MSX emulator (WIP) - - [x] Sega Mega Drive / Genesis emulator (WIP) - - [ ] SNES emulator + - [x] MSX emulator + - [x] Sega Mega Drive / Genesis emulator + - [ ] SNES emulator (WIP) - [ ] Doom (WIP) - [x] uSD card (FAT) filesystem over SPI - [x] TinyUSB MSC device for optionally exposing the uSD to the attached USB host diff --git a/components/espp b/components/espp index 6d102d8..6d0d0f6 160000 --- a/components/espp +++ b/components/espp @@ -1 +1 @@ -Subproject commit 6d102d80595b0ec6bb14d5f9b028143d8a90d1a6 +Subproject commit 6d0d0f687c97fe1e6921acb4fe1e16394cd831e7 diff --git a/components/gbc/src/gameboy.cpp b/components/gbc/src/gameboy.cpp index f8c7fc0..179f0ae 100644 --- a/components/gbc/src/gameboy.cpp +++ b/components/gbc/src/gameboy.cpp @@ -215,9 +215,7 @@ std::vector get_gameboy_video_buffer() { auto width = GAMEBOY_SCREEN_WIDTH; auto height = GAMEBOY_SCREEN_HEIGHT; std::vector new_frame_buffer(width * 2 * height); - for (int y = 0; y < height; ++y) { - memcpy(&new_frame_buffer[y * width * 2], &frame_buffer[y * width * 2], width * 2); - } + memcpy(new_frame_buffer.data(), frame_buffer, width * 2 * height); return new_frame_buffer; } diff --git a/components/msx/CMakeLists.txt b/components/msx/CMakeLists.txt new file mode 100644 index 0000000..d8d0022 --- /dev/null +++ b/components/msx/CMakeLists.txt @@ -0,0 +1,20 @@ +idf_component_register( + INCLUDE_DIRS "include" + SRC_DIRS "src" "fmsx" "fmsx/src/EMULib" "fmsx/src/fMSX" "fmsx/src/Z80" + PRIV_INCLUDE_DIRS "fmsx" "fmsx/src/EMULib" "fmsx/src/fMSX" "fmsx/src/Z80" + REQUIRES box-emu statistics shared_memory + ) +# target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-char-subscripts -Wno-attributes -Wno-implicit-fallthrough -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers) +target_compile_options(${COMPONENT_LIB} PRIVATE + -Wno-error=stringop-truncation + -Wno-error=format-overflow + -Wno-implicit-fallthrough + -Wno-format-overflow + -Wno-stringop-truncation + -Wno-unused-but-set-variable + -Wno-unused-variable + -Wno-register + -DBPS16 -DUNIX -DLSB_FIRST -DNARROW -O2 +) +file(GLOB_RECURSE c_sources "fmsx/src/*.c") +set_source_files_properties(${c_sources} PROPERTIES COMPILE_FLAGS "-include msxfix.h") diff --git a/components/msx/fmsx/CMakeLists.txt b/components/msx/fmsx/CMakeLists.txt new file mode 100644 index 0000000..94aa76b --- /dev/null +++ b/components/msx/fmsx/CMakeLists.txt @@ -0,0 +1,16 @@ +set(COMPONENT_SRCDIRS ". src/EMULib src/fMSX src/Z80") +set(COMPONENT_ADD_INCLUDEDIRS ". src/EMULib src/fMSX src/Z80") +set(COMPONENT_REQUIRES "retro-go") +register_component() +rg_setup_compile_options( + -Wno-error=stringop-truncation + -Wno-error=format-overflow + -Wno-implicit-fallthrough + -Wno-format-overflow + -Wno-stringop-truncation + -Wno-unused-but-set-variable + -Wno-unused-variable + -DBPS16 -DUNIX -DLSB_FIRST -DNARROW -O2 +) +file(GLOB_RECURSE c_sources "src/*.c") +set_source_files_properties(${c_sources} PROPERTIES COMPILE_FLAGS "-include msxfix.h") diff --git a/components/msx/fmsx/LibUnix.h b/components/msx/fmsx/LibUnix.h new file mode 100644 index 0000000..ba3d569 --- /dev/null +++ b/components/msx/fmsx/LibUnix.h @@ -0,0 +1,51 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** LibUnix.h **/ +/** **/ +/** This file contains Unix-dependent definitions and **/ +/** declarations for the emulation library. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef LIBUNIX_H +#define LIBUNIX_H + +#define XImage void + +#define SND_CHANNELS 16 /* Number of sound channels */ +#define SND_BITS 8 +#define SND_BUFSIZE (1<>16)&0xFF)+((P>>8)&0xFF)+(P&0xFF))/3) +#define RMASK 0xFF0000 +#define GMASK 0x00FF00 +#define BMASK 0x0000FF + +#elif defined(BPP16) +#define PIXEL(R,G,B) (pixel)(((31*(R)/255)<<11)|((63*(G)/255)<<5)|(31*(B)/255)) +#define PIXEL2MONO(P) (522*(((P)&31)+(((P)>>5)&63)+(((P)>>11)&31))>>8) +#define RMASK 0xF800 +#define GMASK 0x07E0 +#define BMASK 0x001F + +#elif defined(BPP8) +#define PIXEL(R,G,B) (pixel)(((7*(R)/255)<<5)|((7*(G)/255)<<2)|(3*(B)/255)) +#define PIXEL2MONO(P) (3264*((((P)<<1)&7)+(((P)>>2)&7)+(((P)>>5)&7))>>8) +#define RMASK 0xE0 +#define GMASK 0x1C +#define BMASK 0x03 +#endif + +unsigned int InitAudio(unsigned int Rate,unsigned int Latency); +void TrashAudio(void); +int PauseAudio(int Switch); + +#endif /* LIBUNIX_H */ diff --git a/components/msx/fmsx/README.md b/components/msx/fmsx/README.md new file mode 100644 index 0000000..e8efcce --- /dev/null +++ b/components/msx/fmsx/README.md @@ -0,0 +1,3 @@ +In the `src` folder is unmodified fMSX 6.0 by Marat Fayzullin from https://fms.komkon.org/fMSX/. + +`msxfix.c` wraps certain functions to allow it to work well within retro-go/esp-idf. diff --git a/components/msx/fmsx/carts_sha.h b/components/msx/fmsx/carts_sha.h new file mode 100644 index 0000000..d254ef1 --- /dev/null +++ b/components/msx/fmsx/carts_sha.h @@ -0,0 +1,752 @@ +const unsigned char carts_sha[] = { + "0733cd627467a866846e15caf1770a5594eaf4cc 4\n" + "da397e783d677d1a78fff222d9d6cb48b915dada 4\n" + "ba07b1b585386f887d4c7e457210b3fce819709a 3\n" + "dd1e87a16e5fb38d9d729ef7edc6da21146a99fe 3\n" + "9c544426a02312b7148249ef20ae6e2021b45db4 3\n" + "937464eb371c68add2236bcef91d24a8ce7c4ed1 2\n" + "d76c7a5c2d28684f0d67114cc7b9707b296e68a2 4\n" + "f9e8cc6e47d5632e3b28500197f8e9187295c52b 4\n" + "91d3f1463dac4d5fed8d4ff34bed0d388c61f164 2\n" + "5380e913d8dac23470446844cab21f6921101af8 5\n" + "0d9c472cf7687b86f3fe2e5af6545a26a0efd5fc 5\n" + "2639792df6f7c7cfaffc2616b0e1849f18897ace 5\n" + "1e7ac21796642c3ccb66b0ef0f00ba4752428aca 5\n" + "6bde4e6761286a2909858ecef04155e17072996e 4\n" + "495876c504bdc4de24860ea15b25dda8f3b06c49 4\n" + "db33011d006201b3bd3bbc4c7c952da2990f36e4 0\n" + "72815a7213f899a454bcf76733834ba499d35cd8 5\n" + "f0fee046198a076e664811f4e3a788d5f0b47183 2\n" + "709fb35338f21897e275237cc4c5615d0a5c2753 4\n" + "3739d841abfef971db76ba10915b19b9df833476 4\n" + "908bb09ba2883c3ecdec96ed7e0211ccb05c5018 2\n" + "2588b9ade775b93f03fd4b17fd3f78ba70b556d6 5\n" + "482cd650220e6931f85ee8532c61dac561365e30 4\n" + "16c3ced0fb2e360bc7c43d372a0a30eb6bd3963d 5\n" + "855cabd1ad1abae3a9d26999d48c66de8096d7c5 5\n" + "c1219e0735d31f1c41995795e58b04b97bf1b71a 2\n" + "e32d7334ace26a570a08fe7c01ad01a2c08b1991 4\n" + "10907d8e9845d8f9d8b5283affd7bb824963194f 4\n" + "9e0312e72f30a20f556b64fe37dbbfe0d4471823 5\n" + "a731d3d3b5badf33c7602febd32cc4e6ec98c646 3\n" + "25b28cfe8d6d51f619d182c774f6ceb05b577eeb 5\n" + "bb902e82a2bdda61101a9b3646462adecdd18c8d 7\n" + "2418c0302abbce8b0f8556b63169c60a849f60ee 4\n" + "6159cdfc79c68bf7dfe2653fc0b5893ae913c72b 4\n" + "1833ffc252d43d3d8239e57d5ac2c015b4367988 3\n" + "e6419519c2d3247ea395e4feaa494a2e23e469ce 4\n" + "d82135a5e28b750c44995af116db890a15f6428a 4\n" + "d5b164797bc969b55c1a6f4006a4535c3fb03cf0 3\n" + "03b42b77b1a7412f1d7bd0998cf8f2f003f77d0a 3\n" + "86fdfe9e26d6e77f41cbcb47e10e2f32e5518549 3\n" + "7b94a728a5945a53d518c18994e1e09a09ec3c1b 4\n" + "3df7c19f739d74d6efdfd8151343e5a55d4ac842 4\n" + "d7b46aece68c924e09f07e3df45711f337d35d6a 4\n" + "68691348a29ce59046f993e9abaf3c8651bdda3c 3\n" + "6c1814c70d69a50ec60e39ef281f0b8cd7bf8598 4\n" + "88cd90595fbb7dbabd73ac9c0985c0a013992dc6 4\n" + "fcdbc5e15dd6b973e0f1112f4599dad985f48042 3\n" + "2f0db48fbcf3444f52b9c7c76ba9c4bd38bc2a15 5\n" + "a52c37c1f16ba13d3f39bb5403c82c0187cbff51 5\n" + "f100a76117e95ab0335e89a901e47d844bbc0ab6 4\n" + "52a9173b22c8a72376003764d73e07579dc2015d 2\n" + "d1fbdbdf2e830139584d7dc796806aa3327720dd 5\n" + "2799d221d5486d48226bbbd3941207e1fc7c985e 5\n" + "d7109cf20a22558f923c833ff0b4e2311340acb1 5\n" + "3880b064dcb851ca221ff67e435137a7cf1141f8 4\n" + "6e5acdfb1c1610257a7aabf3d5aa858866dbcf2e 2\n" + "ff72a1788d47f9876d9fabd720f6a289fb409090 2\n" + "42fbb18722df3e34e5b0f935a2dc0ce0d85099e9 2\n" + "8b252fe784692fc15290a04fa59848f609216f16 2\n" + "f0be97ceca47b65b46c9518e6157b295f8c99c38 2\n" + "e11afc03db4e1d03976d02796b29da9c65d4ff3d 4\n" + "2bb837a9051277ba574d8351a9f91f9e57033074 4\n" + "84566b5f37ab4f04a2e5b950c5beecbd27b88ea0 3\n" + "21380bee0748625e9951a7a924016cc6cec46fb8 3\n" + "2b10234debd2a6a9a02e0750ba6563768bc4a2f3 4\n" + "dccdb2d18c70a94e48b3ec5e0cb986c5d708bbc9 3\n" + "048737f995eecb1dd8dd341d750efd005267796f 4\n" + "6868e7050d989d1ecbd13393ed360addefa4bcd3 4\n" + "442a39b196f19a22fc7fb8f14bf17386a292b60e 3\n" + "6d800787f5f5d63827b841aa29fe26ed89ea2cef 4\n" + "75eb441c8298b7fd3982999f772e4c8b983ef17f 2\n" + "59e35d24ab5f8e3b812ba4a1a4feb5be1ab269fe 2\n" + "a553c3f204c105c23b227a1e5aeb290671ccdbeb 4\n" + "90ea059c57f011a4fb33a558e868ee639882fe5e 3\n" + "50422ba24fd158ca5f0b9728b8e41829048cfdfd 5\n" + "a3fc0a55a91ed0041221f8da788d824d9d795913 3\n" + "de95b99dffeab5c2b453778dbdc8723dc15cbfd7 3\n" + "453eac7568d5d04c8cf7da86f2e8dc79777343da 4\n" + "2bd311a4baf59cb85b839cca1f55b7462aa96952 3\n" + "e8ab46785e75e19dac497c4a82ac2f5b37ac0580 5\n" + "3425eea336140da03d7d7f09a94fd928d70e2212 5\n" + "abe52920e895c148851649ecb9c029fdc41a275f 5\n" + "2117a3375684fa3c5bedebced4d7c1019a1ccdca 5\n" + "c3c67787e2a69e2fdc739d8d1542726ec4ee7c47 5\n" + "89073c052b0fe29b6de077c8bdf5373474081edf 4\n" + "b2cca92b7a51a43c310e58e011f3ede1e7125a96 5\n" + "3f0a7b0cbaa3d4c3c6ce744708c68c89eff82289 7\n" + "2de7891988a868f89f0c02fc84f762597d542d6f 7\n" + "5f912637565932c2bd7c0d7b9be6ad3dc971f96f 7\n" + "30b41d0e9b3c59e8826112c618206e12e187fb5d 4\n" + "a4a75ed32c2af3c5f009661bb76be4f65e2e9a1f 7\n" + "edd109e4b518c41eb30a2b6f2a492598c9a08008 7\n" + "12b3c31f0fd10ff5823dcc8bf6dfeb785a8af2f7 7\n" + "8a25afaed4bf87468ad7a0c5cf12375554d67741 4\n" + "f416b424fb913ca067fe75279c924a20fac5c6a1 4\n" + "7393f677e0fae5fc83071c6b74756117b7d75e2d 2\n" + "0413bb3aeacb0c28429b8c85b42796dbe48bef6d 2\n" + "40a0f35dccc7572ae53bcd4be70abfe477d49bc9 2\n" + "5692e41b3a4c5e767cf290fd6c24942d0fd7b2e3 2\n" + "b4788d85e67950177882ea4cf6a5fa3219f3fbd0 2\n" + "520739caa1e0aac1b8eabc4305556aa75f3f5a3b 2\n" + "c84ea9e7a02609c71ff75893cbb948fc1f1cf21c 2\n" + "edc238c1eb254cbf90f1dfdc276ff7ccc93a940e 2\n" + "d605458c96bbb538c4624deeeed3294ea489a61e 2\n" + "7a4126934f9e68c34bf00dd3d9a9e753c05ee73f 5\n" + "930df58762e7b5bcf2d362462f03335bad732398 5\n" + "d2ccc4a8de5da21745209d0731671e8f3cbea515 5\n" + "8b7e4bc408a8c715943bfa00069f2d517b39a748 5\n" + "05767fec34beaaee390a86be496d98d2b27de641 5\n" + "678efe599b8085bd27f9d3d42cfbbd7ca74ceb89 5\n" + "91505fccdcc43230550d101967010bed27f9b573 3\n" + "959a2ddefc9dc1a751cf30779b1aaac775f1612f 5\n" + "50efb7040339632cf8bddbc1d3eaae1fb2e2188f 4\n" + "e96888c96c3044ca7ce57dbdf1744df7469f7465 4\n" + "e31ac6520e912c27ce96431a1dfb112bf71cb7b9 3\n" + "f0e4168ea18188fca2581526c2503223b9a28581 3\n" + "98748c364e7bff50cf073c1a421ebe5b5d8b7025 3\n" + "910b156b562880ace789fb3f9b11848163c2df20 3\n" + "f4a1e82c533677ed9923b11a0b970264d9ecf8ca 3\n" + "30a7f3ea1612f20c0f62b1a121bc7d79ae515e89 3\n" + "fd7b23a4f1c2058b966b5ddd52cf7ae44a0eebb0 4\n" + "ab30cdeaacbdf14e6366d43d881338178fc665cb 2\n" + "d63e20369f98487767810a0c57603bef6a2a07e5 2\n" + "076c45e152598777cdb1ace82b11994018bfed83 2\n" + "2ae97f3c8152f56695c3d3a2dedaa781bf0a15f1 2\n" + "c66483cd0d83292e4f2b54a3e89bd96b8bf9abb2 2\n" + "4127844955388f812e33437f618936dc98944c0a 2\n" + "4c15375910461a7a34a2fd54c7f51e712f06bc69 2\n" + "76666da5870d1901026e3dded3f22ce7a706689d 2\n" + "d441ccfede2c089fbef54e20b90c0d4b513c5196 2\n" + "06aa7bdc1d8f01e67d1a1736084384393074e613 2\n" + "4c2f015685a17db7a8c3893e868e0a84a8dbf1e5 2\n" + "6844758ff5c2c410115d4d7cf12498c42e931732 2\n" + "87b7c80069a15fdb90b40dfed736fb8492104cd9 2\n" + "d09d75c7f242a696931407458e717c9bdaadb7d9 5\n" + "44b23a175d04ca21236a5fef18600b01b12aaf4d 4\n" + "10758fb5d42b97a05b537db9b009bd644c153c93 4\n" + "26a7b0118d158e9bd3ea947fbe68f57b54e0b847 5\n" + "0dbc7defd96b71c1fe2d63cbc725f5e58aea2db0 7\n" + "a18b1d49df954a3316ed215b6facfc1ec020ce13 7\n" + "b40b178c6876cf3dedf4ae48262a5914c5f674fd 7\n" + "b5bcbe2dfeaf7cc156c0d8bbf4fdcbe97b2007d4 2\n" + "a3de07612da7986387a4f5c41bbbc7e3b244e077 5\n" + "3626b5dd3188ec2a16e102d05c79f8f242fbd892 7\n" + "da4b44c734029f60388b7cea4ab97c3d5c6a09e9 5\n" + "a3537934a4d9dfbf27aca5aaf42e0f18e4975366 5\n" + "b18d36cc60d0e3b325138bb98472b685cca89f90 5\n" + "552a758f77a7c35969523dbaca74587f0b59eb2b 3\n" + "74e9ea381e2fed07d989d1056002de5737125aaf 4\n" + "217b21d10d80fe2d5d04636ee8c2f97f6edfe34a 4\n" + "77f2d9d27724a7c71c688a598d34bd0c8a8e56d0 2\n" + "c654b842cff7227d9e85fda25279259e837cee9e 2\n" + "093d5ab39878ed4cdd42fb7c2a1502b7e65b17c0 2\n" + "46f995308d736da235ba7f17841d8afeff403c99 2\n" + "32cdc5c98915ba035865fb134e08ce5b75084099 2\n" + "1aeae1180471e9a6e8e866993031b881a341f921 5\n" + "6fefbe448674b6ea846d0c6b9c8a0d57a11aa410 5\n" + "3b6200f88561a59ae5d2b3e94515b89cdb96044b 3\n" + "842009e0f7d0e977e47be7a56fe60707f477ed93 3\n" + "4180544158a57c99162269e33e4f2c77c9fce84e 5\n" + "1c462c3629d43297a006ba9055b39a2dccba9f6c 4\n" + "122f659250a0ae10ce0be0dde626dd3e384affa7 5\n" + "a2ca7e6e216f8b450eb8db10a4120f0353275b6b 5\n" + "46062d3393c49884f84c2dc437ff27854e9d2e49 5\n" + "438bbb3367db4938b3d90fa9d2cfb1f08c072bb7 3\n" + "f3306e6f25d111da21ce66db3404f5f48acb25a1 4\n" + "cfd872d005b7bd4cdd6e06c4c0162191f0b0415d 2\n" + "0f298581b85e838c1fbaa24273d292217f81662c 2\n" + "ee60e88ae409ddd93d4259b79586dacb2e5ee372 4\n" + "4d51d3c5036311392b173a576bc7d91dc9fed6cb 3\n" + "d94327745bbb865a394e31e150a6c77263ee95da 3\n" + "7a786959d8fc0b518b35341422f096dd6019468d 3\n" + "e7273feaa30b676941b25061ef0631ec09ccdb7c 3\n" + "7d07fdf58c7a86d9f93af35a35acf2d7ded02ff1 3\n" + "0c425d8fc1ad36c9b8e248e477b1796cd62d9bed 3\n" + "fb053becf87f12e196e00b3e153ec8029ebf1ff8 3\n" + "240e15fb5d918aa821f226002772dc800d9f20a4 4\n" + "d33508c89656b18063893cadb74573e0b656792d 4\n" + "f999e0187023413d839a67337d0595e150b2398f 4\n" + "1ff0afe393f89b6517025dc39a282c11c7da87ca 4\n" + "25f5adeca8a2ddb754d25eb18cff0d84e5b003bc 3\n" + "3b6f140c99cba5bfa510200df49d1e573d687e4d 3\n" + "4323eb37b9a795be32a2b64824fc4acd7afe4e9d 3\n" + "d0dfc1927461d9473d0ed471375dcedddba42279 3\n" + "8c609b8bee4245a1bb81e37d888ac5efb66533cf 3\n" + "64321138e03337d0e6178ac30bb98e6623c640ba 5\n" + "63d4e39c59f24f880809caa534d7a46ae83f4c9f 5\n" + "728340e95ad58ab989a59f61317a45ab1f6b0bdc 2\n" + "ee533b8e4bdd6cdf2c273f7c80753cf8db60f545 3\n" + "f2eef7402d2e6e7f7c15a1f3d8bd251603eabf12 2\n" + "0099492bd11dfa3ddf87af3aeb4c92b29f78bd82 4\n" + "fe74b4df9698a61dffd3ac88f47619675514ba1c 6\n" + "5c00f9e3a48411e08fac89b1dfdd2a6e95853919 6\n" + "f02281aa13cb34d2f10add301529b95adff7f768 6\n" + "720b629e2e22e0af15b482b154a1473e8ab6627c 2\n" + "0a7bed652592ed6325acd037f1f5bbbd1d31b239 4\n" + "2cea9cdd501d77f2b6bd78ae2ae9a63aba64cfed 4\n" + "6a98d5787dd0e76f04283ce0aec55d45ba81565d 3\n" + "598eb258cbe532e98b4a3c8d87ecd9536d09ce56 3\n" + "e4401a78869ffa879a55f239e5da4990cb6797a6 2\n" + "0442dd29ea0f7b78c9cd5849ec7ef5bb21ec0bf5 5\n" + "0f2c5a7c6cc9ff591c911e4f3f204e425bd3cf50 7\n" + "079bacc9708f711af90310f3e08f2abafa8fc919 7\n" + "e348e62e3c4c02d87ff2aa4d55ddebb60c3ea507 7\n" + "145552b3f62885efcfe7484008fa4cebe2feae84 7\n" + "9c5c1c40ec30c34b1b436cf8cc494c0b509e81fc 5\n" + "3243a5cc562451de527917e9ca85657286b2852f 5\n" + "f7dd6841d280cbffa9f0f2da7af3549f23270ddb 4\n" + "db7e4c8dc56a0e8e0e3df08b14a65030738d5ac4 2\n" + "7dc7f7e3966943280f34836656a7d1bd3ace67cd 4\n" + "0cb11c766bd357d203879bd6bee041a4690cc3df 5\n" + "e92baa5fdfb2715e68700024d964098ef35704d9 5\n" + "98b7a6ac44b82ccfc45eb51595e2905adabac1c7 5\n" + "cbe6c0db6201f8ac09310c788adc634873251164 3\n" + "cf344e0f58fd918c089c4d4575caec8843944ff6 5\n" + "bf0014ca7dd0815b2385a778c5328ae63c692229 2\n" + "abeccfe49a6874702d65eb4d06a970d8fcf55123 2\n" + "7abf89652396a648a84ae06e6dabc09735a75798 4\n" + "176ec8e65a9fdbf59edc245b9e8388cc94195db9 4\n" + "7341efc039394ec159feebcfaa9d4a61ebf08a18 3\n" + "0ec71916791e05d207d7fe0a461a79a76eab52c5 3\n" + "1c238009e77c5536d0059631954c2c4a52527283 2\n" + "ca45da2f9cd8edf4ab04ffd542b9c60ef6a9a254 2\n" + "aa6055ba0e433aa9c93df4444783cacb9628a7e2 2\n" + "7b8015f919c8dd95db683f3f3d0799cf7ef1bc22 2\n" + "9f2fb5f31c2bcff949e08449f197b0c31bdebe50 2\n" + "b493dd7d082e25fc40ad1cccc364e1945590a2f9 2\n" + "3ff276af654c7dc712abed79d9123317ce79b4e4 2\n" + "65160f0d59068dbbf76b11bc79e17a9369c8dcee 2\n" + "72783ba5a0b9f33b4573952a1c721fc5d5025fd5 4\n" + "168d59fca3170f56858f39d0b53067c0c0d414a8 2\n" + "473bc0ea85ed1b218974796d819416e711ac193f 4\n" + "5d55fc272184d160af6318855c9d2f728b3f25c0 3\n" + "8557dcdb4cf1e15180d17acc8e6d515bf7bf7e7c 4\n" + "7752b6624fb80d9499aaad28dd5776ec74565576 4\n" + "c2f4df1bd91f7dee1536774b9d7089086b9bf835 4\n" + "ec1bb5c36952b12e6c0805e9a8d5bf31c20a68c4 4\n" + "22f9b0238ff48840fb7d45ac93a7f15495393920 3\n" + "4b138e041947990309c228890914689908d46685 3\n" + "76b7a5e75d5091f0482746e5a0c0a71de07ddf83 4\n" + "ce3f9356eaa5fd6c1b5ff1ade244ebd9130f1ceb 4\n" + "e6de8bbd60123444de2a90928853985ceb0b4cbf 7\n" + "66802dd98370da809250367365df234f9a6422b7 5\n" + "0b02dda5316a318a7f75a811aa54200ddd7abc30 4\n" + "7c066cb763f7a4fec0474b5a09e3ef43bbf9248b 4\n" + "2220363ae56ef707ab2471fcdb36f4816ad1d32c 2\n" + "75d3b72d9ceeaa55c76223d935629a30ae4124d6 2\n" + "ee66334fbac1c926ed05c1c3f38df3920f501fe6 2\n" + "2111cec4b5ea698d772bb80664f6be690b47391c 2\n" + "04e4eb1cae7cf379977d238b74d727bb929c8d23 2\n" + "f1de76dfcd8c4e354a8a8058a76191167fd112e3 2\n" + "240a23b63e901b988951dda1ded561c281ccbbc1 2\n" + "8c218c317e1e213e1c5e9439870929009d645a47 4\n" + "390fb09a9d944d1d17c328ae138325751d1b4203 2\n" + "5b25d93259a358f2064adc44f6382c91c9394861 4\n" + "c95f8edb24edca9f5d38c26d0e8a34c6b61efb0c 5\n" + "ac109dac24df4dae813ca5d9f0938cc48a162bb1 5\n" + "16c692a2c9babfdadd8408d2f0f8fae3a8d96fd5 4\n" + "4e864cc0045c85c4f5c5c0efcdf26340a178686a 2\n" + "37bd4680a36c3a1a078e2bc47b631d858d9296b8 5\n" + "9b6cb224f2a7a4af69221f1280d3344a5c88450e 7\n" + "9c886fff02779267041efe45dadefc5fd7f4b9a2 7\n" + "0b379610cb7085005a56b24a0890b79dd5a7e817 7\n" + "441dacb74a47ca4b7d1ce47300d182bc7424ffac 2\n" + "74ae85d44cb8ef1bae428c90200cb74be6d56d3a 4\n" + "94d7a756e199457ea9958c8f5d6860c9bf334126 2\n" + "9a611007b58a001639676728614ffea61cd5ff85 2\n" + "b7f36450d1ac76834046fd582d6e77a6d3881a8c 2\n" + "c960c1b895557ef1b336035029d5c6d553c64085 2\n" + "46c98a3143f5a80b2090311a770f9b73000881c0 5\n" + "f6e0dff9f8674383a866902fddb72a280d331d9d 3\n" + "64c49fdabbdd5e13c4310b03a183ea1a018154a4 4\n" + "0d76a069726fec7326541f75b809b8b72148ed3a 4\n" + "3b1fc45b04bd03f21d5b88330a0c5d16a9bd2d65 4\n" + "a676173780dc3ef4523ff3123fdf179279d22932 2\n" + "0d459788b6c464b50cbc2436e67a2cef248e0c4a 2\n" + "a1830ad66feb55f471d52210c6f07937f38238fe 2\n" + "c19f67882b2586c4ba577ffc5d04197e757e19a5 2\n" + "655b15e8fd81866492bcf2b1d6609211b30efce1 2\n" + "d30c17109fa1c4a81e39dca57b79464b0aa0b7c2 2\n" + "5261006ca4245653f5153074dfcc8ca27315bb59 2\n" + "df15daf1e68e9c8cd95c4431803d86fe6c2f985e 2\n" + "14ff6fe464362c6b7dbb47b2ecda3a8c5f05ef79 4\n" + "3f741ba2ab08c5e9fb658882b36b8e3d01682f58 4\n" + "96e2a7163c755fbea77abfd9d09a798687a5a993 3\n" + "7ad40ae512bbf4ba688467ba23e16354b8421d0a 3\n" + "fd0e46e4aef2d9713d20b51d2ba57d55bde7dcee 2\n" + "84831671935bf80c250aff223a99684669a0ce72 2\n" + "1ec94d049fcbf59c5493d5de76bdfb891d24249e 2\n" + "54a1d356fd7bc6098f5c3d94f9d8bfcc2c6d5935 2\n" + "0b3f5dbdbf5636e22b3a6bc12eeb4ac64b0501e5 2\n" + "3b4143e5769d6235bca8977b41241295493eaf5e 2\n" + "2b255c3e5615b2f5e2365419a35e4115a060e93c 4\n" + "7a0a1c5570a3361ad75406eea07c93fdad9fde02 3\n" + "e6ba7a6ecda80a1c844542be0b2a44a4a33dc113 3\n" + "e7351ef9606bd6d5f38acb7c17aed8a00170b85d 4\n" + "6715aea31525869220d7acb638b6a432875e85a1 4\n" + "5a12439f74ca5d1c2664f01ff6a8302d3ce907a8 4\n" + "3caeec19423a960d98e4719b301a3d276339e5ae 4\n" + "d554710317482127f2e1597e91e72be5ee0726ce 4\n" + "fe39af5eb457bba8c5b96b5b500120cfc05c265e 2\n" + "0809b47cf385328c3faa6a80f8378fa5c6690829 2\n" + "1d129bde09c9db2433b334d36762704de94983ba 3\n" + "72afbebb12098ade1ec26dcd65890b60b2c705d0 2\n" + "65da0e5c5de057a1ed836ec19b1f07e10f4b738f 4\n" + "dacc9ed6a80fb15c3e82c4081c07211e033ba820 3\n" + "a3e8e076b9c8d4060082233a2f16184cf4911a85 3\n" + "9bafce699964f4aabc6b60c71a71c9ff5b0cc82d 4\n" + "14faf5da6ab76de0b62f1ede91e27d46b9db77a2 4\n" + "5d65a308bb9ec8c4b4ce334b18d699b1f53227ef 3\n" + "2dfbca8f5cc3a9e9d151382ebe0da410f5393eaf 4\n" + "c69165e84e8350a6726901f2ab6e4688f38f03fc 3\n" + "244399d67d7851f3daa9bb87a14f5b8ef6d8c160 3\n" + "cf6fe49b28fcf81511f39fdf68f37af82bd724c0 3\n" + "81b85b1152a59bb10d0e0118f80f0a4ed363c785 3\n" + "0d2f86dbb70f4b4a4e4dc1bc95df232f48856037 7\n" + "cf088b4eefe7f426e438dd2fe7a3433140abba52 4\n" + "16351e6a7d7fc38c23b54ee15ac6f0275621ba96 4\n" + "a8ac24a702d2c850c7a15b5196893817b0c93123 4\n" + "ebceb3de22e4c0444a969d83ee487f8ee34204e4 5\n" + "16f06109c57de5a39fbe150de9c25754483ee9fb 4\n" + "fd1eecd9c89f47741276433b99b75eca2930b58d 2\n" + "d147f2cac0600527ce49bfffc865c54eb783e5e5 5\n" + "500d7a1d81621adf8c6914c6a42d65aa44ad4588 2\n" + "bfe8046f8ccc6d6016d7752c04f0654420ef81e7 5\n" + "22d8ac99ef692675210f101eb4e688cde9b2d7f5 2\n" + "b550757c8f5b55dbde16b4a7888f10999f01acfd 3\n" + "4340a2580c949d498d2c8e71699fff860214e9ea 5\n" + "47de7524c32e72e849daee4d7f3e6cf393e3b7dc 4\n" + "b188980c998d1dfb5dfbc21837ffbbb5affdbb3c 4\n" + "badcb43e490e3713bf291e83cff204e1c57ecdfe 4\n" + "f50d0c66b08c47b59925952276243e2008668185 4\n" + "818d91505ad39bba2eaf7f4857c7d41e95fcb233 4\n" + "e19cfcbe92ec3682bee869dfad5f6b4058b1f2ce 4\n" + "97e173dac64dbde7d6a60de7606cba0c860813db 4\n" + "b6a5552effcee708b665fa74e5ce7b0fa2541c03 3\n" + "d7e85888117188a6d6f7914e9649fc4821c594a2 3\n" + "fa6c059e14092d023b1f9f2df28e482f966287db 4\n" + "d53e0c8bcd98820afe820f756af35cc97911bfe4 3\n" + "632bdb164acab52ee715c7eefed60e0b1ae629e0 3\n" + "d0706fd10e418eba2929d515cc0994f49376a63f 3\n" + "d3d411c8b7891aef9c59cbc20bb4fa3ff0ca03ea 3\n" + "898bda19f882c6d1dffdb2173db84a97dc21f7d6 3\n" + "bae8548faded657cca06c2b02a59e00cf1e55484 3\n" + "4f35f676d2f382078078343921374c2889d06e11 3\n" + "05a531043d13ba261b0db37e4ef8b257986ce05f 3\n" + "ad3d8fc2201a245e352875918e421b42764e4148 4\n" + "c9135b1e451193bd4c06d8143eaca934cc89fe05 4\n" + "d9826cb2044ed0224124f2f31d0bdc1fb840d81c 4\n" + "2fc15ef2c728a2472cfa3a34053e07f821dc18d4 3\n" + "66508bbdc3b133b9a69ea96789bc134eea46796a 5\n" + "5fd07ed5b7c0492d42ed91a54a0fe80ce6e4e40d 5\n" + "1f17e46190635a3214057d33ad4a768939d5a071 5\n" + "cb544661b6da6a35c20bfe503974f2b79a3c4550 4\n" + "5ef7d03b138a2023f6def241b671c666f97ed83b 3\n" + "eb58b57f468e12c4300d3a88337e6c45e532c7d3 3\n" + "5460a88c25386b1b950b57fc1325fd75587cc825 3\n" + "b58e77f819dfbde72845801cff4b4e0a2f20ef5f 3\n" + "e78511a2b8cca37a96aa396c938863c92a20c797 3\n" + "43084db72dd13112450a7af4b513e13e9266defb 3\n" + "aa13b346e11644f8426fc546b359bc6fd7c6b357 3\n" + "90b57b582675360916ad68ba65e9284926d93f4a 3\n" + "12f6f31f495bfb384c9ca9067bfbf8f98af6adf9 5\n" + "fda4422b86daa233fa4e6104e45305e7cb383053 5\n" + "11b712633bd0e44e8d59ceba5a2d6b1c20a43ef9 5\n" + "2761df1ff6bf826e00dbd4f9a346aa145e91c6c6 5\n" + "be2d023f5f1ee4cfbdc712c8ef9eda9d07278d88 5\n" + "f4f7c789dd320192fd544c666ceab720c7f3a747 5\n" + "4b506234dfd3b6aa1c315b40cd8ddca024dd15c9 3\n" + "030557706d72199acf17df4480f6b1a8ff798d1e 3\n" + "54b05b9c064232d75299fd31b53335f530c20aea 3\n" + "937f8d412e9810c8aeb9d92182cd3b007ace30d2 3\n" + "07793d57b00ad8927a3c1f0b1ed61c606441dbbb 4\n" + "1545566bd7decb1612009daa2daa71758fa6e4a1 4\n" + "4a8eed643993b86e71ea46098a580ec15038cbba 4\n" + "55183b7b7df66950dbb65ef1065d7d987e0fdf5a 5\n" + "a8186cd3ef78662c87665df52026ecbae4866bfd 5\n" + "ae3c3c2c022f566d2e790aba193470d5826f36cf 5\n" + "875a262be44a27a8357f170080fb8466c9599b62 5\n" + "1064aa5c8a884dddc0a27147ca70881e04988725 5\n" + "e8e416977f5dae3635362c513bba004ea89a0bf4 5\n" + "9c89215dd36eded769691708eb8c82dec1ce5109 5\n" + "1642891b693df616f27431299352e1d7750ac94b 4\n" + "452deac5b3341437e70c5b2726aa26cc1d074fba 4\n" + "9e790499af6916f82809e35d68cc159efe580f99 3\n" + "c843b8b0e5ed98d3bae68e701fb709ea178ed968 3\n" + "b210047af5ccc352cc044bf6c1419552223bf8dc 3\n" + "c4a919f31d20008b5d6540aa5d2be59aa4cef5cf 4\n" + "1aee947b1ad6d478c0def550868a3851c2910809 4\n" + "a3fbf7c2392c1f97d3a0946a5d1654bf4f7d9b0a 3\n" + "e89fc7437d8f9aed1640689a32ebbf047699b147 4\n" + "ae8172bcf76adfa3d401467cbc89cbec2db86ee2 4\n" + "5d82713f36e9395092f16663084fbc85f05c664a 4\n" + "a8059dc1820b461418713acf2af14f3a25734bd3 4\n" + "e839f3ac7d52c427ecc011d01e6e241f88ca850c 4\n" + "f57b46154cf0711e3f5218e00f67c375d95d1d5d 4\n" + "a8eeac428a5992a2390b1827db1358f4c733e200 3\n" + "bd1435a19ba895149fa5b88425316171d165c323 3\n" + "efdf63f01293251dc593163a42f31556fd042282 4\n" + "4edb5ed12c1b9bc7efa571ca67ffc732f53d3a4a 0\n" + "e58c1dd46067e2abaaf3ee6a70a7e5541a32017e 3\n" + "32210a9cbbb51e30ff59ed44bf32e628fdf6c42c 3\n" + "6fd90e7573abdb5a7bd6e4e8dc44db80fc163e30 4\n" + "bf96a360083c5c26430940546827e91a69ade63e 4\n" + "2e388978f765bc5fe1fa9a42e1f059de5053721d 3\n" + "8a65786d9c6b2fe7c1643c27316d6cbb35fbfd24 3\n" + "022a53d79bb72c736a5fdad6d82434a98f33e6e7 4\n" + "599c824ec92be0369accaaaf31340df6f471f202 4\n" + "e73946f1f26589d77926276ca4f9dbee60c53fde 4\n" + "c5edd1c74e72ddaeba1027da1e848e2c4fda94f5 4\n" + "a40674b5e0ba449078cbb07406edfd2627306122 3\n" + "d66068f5a8b6cad09ed57893ceb0f3dd37ce8715 3\n" + "e36e16acfdfa76fa72b218da2bebc668db39d21e 5\n" + "1302d258c952e93666ecec12429d6d2c2f841f43 4\n" + "90003c78975d00b1e5612fd00dffabb70d616ecd 2\n" + "cc46a737acd729b2839ecd237d38b4a63cfb16cb 2\n" + "7964ba4c3c27b6a32d397157fd38dd1dc2f1e543 2\n" + "bc06bd3d6f138da8f5d38b47e459b4d1942e49e4 2\n" + "424320762bcbbb6081b1e186e21accf758aeb935 2\n" + "a21adba681956ec35816af673a9d4f6c7743253a 4\n" + "2a4aef32db28c059c30f5f972f1c09708ef6ce62 7\n" + "10c38f91d8f5db2438414ea58f26dcac78352088 5\n" + "3f072673752badfbd234d6005ddad60a494a9438 4\n" + "d4da7236bd09d735dcb92127c73e43b3ca94dd7f 4\n" + "3bb2f9f4be38b5b08da03ee190c39caec30e2f08 4\n" + "fc9adbd816109f7705f342c01f168bb09ca4b2ff 4\n" + "35c02d8d590d92053f083b0c0c897277a5375af6 4\n" + "772ad4eabbf0941a9b4655f5615f2a5cf22246b1 4\n" + "1ba6b699b4e34c23ee60db4e58e1265dc03f0b50 4\n" + "87a7fa43602f74c47151e79f1aaf7f641331ba51 4\n" + "ecbf33a376a7a108176f8d1009d042e01519241c 4\n" + "e0a8aff980ed84ec12acbf63b40cf9895997faf3 4\n" + "81befaa45ee3f1196b0e1ea657b0161495601a87 3\n" + "6030005c4401b3cbcb09f802cd48f5c1c585303b 3\n" + "5577e483caca553cfbfc0cba26f3ab2444569f81 4\n" + "7e9a9ce7c18206b325830e9cdcbb27179118de96 3\n" + "7c3aa9cca2f1f6e81ae9d24b3a3c675f08a203ea 3\n" + "27f9144fb24b434f7a75737aa694283084782766 0\n" + "05280f0a26ac5c42ab63b80a487bb909683e7461 4\n" + "77fcce7b7c9c915fb0adff059141d4d4307f5a22 4\n" + "028eb2de64ee129f9403d114b3059e8b62fef6c2 4\n" + "b63fc17c1d004ca4d7f3b1ef8962210a779ca665 5\n" + "23f3e1b597bee95375f380638340995b55232e7c 3\n" + "3f87c3413c8cc2a25e1f64cfdcd01155a13711f0 4\n" + "fa2d5f56273a0faa5b39096448f82998655de60d 3\n" + "6d7a8ea1b283ecc1cc94c359e5f053a6e82ab3f5 4\n" + "b11c0ded529af18c29ec46b9e0a795ed0405e7ec 4\n" + "af3524caccd33ccb7a5b42b1ce4314d055e01672 4\n" + "d0bbf730725806384756f5306ceccd91dc01d975 3\n" + "bd70b00ccb8fafca883c7160585e9c5920cca58f 5\n" + "a6d285e515ad1e2c15b4352d9b699ebe060b1dba 5\n" + "cd8ee800822ff0cd295621faafafc73be4bacad0 4\n" + "caeca37113c56e696d7053d90f5f9f3e02de8ad1 4\n" + "98f6769741a1c9df4fd203ed19d70b283c94e41a 3\n" + "475c3af7495922759a5f424d2e22d7e4073d5992 7\n" + "39bc111c953bdf25683db7ff4c0bad79b5c49958 4\n" + "f12ad34dc3b4279da0fc7094f3d50d6b8bb517dc 4\n" + "06132ead4caadb2395302381bf72c60831500dce 4\n" + "f04f5c6936301eed31a79c23571f8fc497864dbb 3\n" + "b45172e4628f68c32cd13c73cf17c4dd1bed1bb2 3\n" + "18eada2178fc1a89895087aa513305d1ba73c926 3\n" + "924fcf87b3747dae06e621975903ee73c42016f2 4\n" + "6d72a04ef018fa1fa3708c8b5ed3333b2c5e8ead 4\n" + "c74c0a9b973f4dbfc132b60fe4d666600ae12552 3\n" + "d78d763e72e23418f31dc3ce91deea1e8ee10490 4\n" + "66620207ca6c21bcbae8f868bb55532e72e521a6 3\n" + "31dbd1c08659c649c49da8d2e6b80328cb6b59e8 3\n" + "970e26f3c2c085e39267115991add6ccc6e9ff80 5\n" + "a58ca651f4c12bf07cf3840d5e37b534f5fc075e 5\n" + "1f39c472f1e02511fa7eaaf8ed9fcce4d45861b9 5\n" + "df10b3c8470acebbc445f7c24a50b4f24db0187d 5\n" + "cdeadd6cf7bf5b53aa7d901446b0ce586542c580 4\n" + "8cbfd94324f297a418e7eec6920dfead7b9bed08 4\n" + "32dd4df2b39461c0694f6bb84de0709478a69d31 5\n" + "1f3f586255dd49b0f912bf41e07e88fe3eb3fb61 4\n" + "273a3dab994b5b9c975fd12711dd19e0c6367caa 4\n" + "fd71df03b0e9f3caf701ecfc6d5232060cab3b13 3\n" + "f5009c44ba1e72b64e29fc734741d518b4167aea 3\n" + "55bad6c00e6a676efc7ff349fa1b5422d624f5ef 3\n" + "f924980511d2ea191481c1e4ff5ab7adc3f082ab 3\n" + "02cfc41132d518e2b9fc632ec6ad00656bd9ec92 3\n" + "f4da2be2ee712ba2e7cccf56fcd15483fe0023ed 5\n" + "4b872dac11a720e2b9e86f22df1e2214c1059281 3\n" + "1f26dfa026b116c80c951a995633b4c2f7fa033d 4\n" + "2a1c91bcc9b1b621773232061d74b7098420f1d4 4\n" + "4690b2f6ce28e55d56ab185c573fc27a9b414468 4\n" + "b8f4ee146c3cdb8f506371d001e6398ecda85251 2\n" + "e22a6317747903475095008d0d5ad9427ddb34af 2\n" + "ad995ede603a28c72dad06c245fc7d4dad85d230 2\n" + "d89922ed43ea4fc0b87d7e5baef3f072bb566849 2\n" + "93b07e3cd5ae2f00b36824a74f83a3ef0d65d0d5 2\n" + "dbed87a53c604f7140577d6707ca0fc8490463a6 2\n" + "faa5f0bf79fde9ce74b91a7091d203771cae8499 2\n" + "74d1c7f66242335c736da29413037bd23384efaa 7\n" + "8a4bcf0f38c9f243443a4d4865278f695bd83dc8 7\n" + "bb99fe7702733ab0f9e8856e91f5ddcb55d30b5f 5\n" + "b74f759ba3837968a04e9b64321b47cf087e76ff 4\n" + "920f141db0d42952130855ac1dce446c327e7d9c 5\n" + "14403ca71d287084569e6c2f4124d2712c665219 4\n" + "90722d413913ab18462aa741f85b820e751bb50f 3\n" + "c34a1c225d1a5fc41a998e23d1209b59b3b759bb 3\n" + "f5e199a5b39bad10256d8e963f0da272ad359517 3\n" + "4376342bf42334d98dbd3e35ef35460974269c58 3\n" + "25022c4f5cfc805e93026fd5fda781e9a1807474 7\n" + "3ff54468a5acf1ad44e85bdc993044aaa3165a63 7\n" + "d08d4e2a8d92c01551ff012a71a1f3e57fe2d09c 7\n" + "751571d21457d89e5c52c45657bfe9a617ae8e14 7\n" + "32151ddc4303290c09889bf8107493dd34296043 4\n" + "4ee3e785e6949bd535804a32c6a1a31eeef30031 3\n" + "ead699582641d103a94775a08121ecc336b99eae 4\n" + "6e1d2d5b0688860b0a04d53fe9e5e3939dfba547 4\n" + "13fbb273a95481330d33c9edc4263e30ccf1774d 4\n" + "45760316a4ebc74ddd55ee163c5aeaaafe965d2c 4\n" + "d723f2eb1c887db5448fd56be5e7c57e60d88e63 3\n" + "612c18616bc800d9eb69b9da3cd29eca31f50c1c 3\n" + "300f20adde3941ab10eb158db48250e8e856a911 3\n" + "357c0c2e9b3b2970c587c96f86a6b7272160f54a 3\n" + "875bbb27f6a54e5d123dc8a6df4bf46f97c23a57 3\n" + "fed52c0455ce1e7cef80cd144c3d0ef3636134e5 3\n" + "7e5024e6e6aecd05ec6c885786a4aac287ed110b 3\n" + "3837d3a72498caa070866d947956a2e30fb94fbe 5\n" + "facb967717c21fed9b375481908aa337b3361c45 5\n" + "14537ad9a551245261776bf9b64068fe4921ad47 5\n" + "f182d4785e3eed00292f7ff4795c9dcc6c990a0a 4\n" + "5d2062ecc7176ca2b100724c2a17f877878d721d 3\n" + "219e9acc2c023c25a8bbe9fb7d1764ffcff66034 4\n" + "e065238580b9e118b6c9e789d797cdd99e0a1c69 5\n" + "25b80aa670e7fdc84b51628b6dbe930eb234beb7 5\n" + "fe8aae62fdec28631d8f7b6659dd95135c1bcbb7 5\n" + "0cc6c30c6918fcf780e57d40f58a4cf2a82225cd 7\n" + "97f2cc1cd4c51dc20b86cd190016b8f0c006a968 7\n" + "6420d61002da325403fcb6f41afb1d348eeba3b4 7\n" + "9afdee40e7b243f99cc50dd1ad710ab0928dd820 4\n" + "0eb3a48a8ab90826726f0c06daadf4bf13390758 5\n" + "d78293133b075f48c9dc5c4cf0c4a985ae964b08 5\n" + "f4dba5ba267b5dd1a35eb74a6ec4fd4d5ca9a1b4 5\n" + "a0c486d0f25a3dc586717d5d3e1f5a46d36735fc 5\n" + "6dd28797f9d1fe7cc041077ebe77d08f2ccc0937 4\n" + "11f1b9fda9a44f59a6de479aebc18fbc6347f5a7 4\n" + "38a103e540eb5fbe2627fab2991bda08092635e0 4\n" + "6d3008b97b48d99522e45056f52ed944e3d8e208 3\n" + "dcc1ecdd0e3a2a33b994e472ea367a1637cc3799 3\n" + "99330820cf8aef8bb34d56a666accb4b1ca90e30 4\n" + "ecb538238fc7c5e374d723f4d6120d1b2ef5d80e 4\n" + "fe4bd35e87dd50fddd255172129b9bec38691188 3\n" + "c6d2aa3e7303e0e2244256bfcfc113bafe2d6b59 3\n" + "e178917d1974ac85fd58cb93b936eb9a6f44ba0d 3\n" + "2b4c386a56dddd2a3354bdf1a0041d3faa0ccc72 3\n" + "e178917d1974ac85fd58cb93b936eb9a6f44ba0d 3\n" + "bd9852ea114e8b8ff781115d1d6e6da6f8dfafff 3\n" + "65a5bda34d1e1b22cfa697e4e7fdf1829cf2238d 3\n" + "502d7cf7bc2595af6f83bb2b95baa4db5b0f6a4d 3\n" + "de2349227d765db73cdc44efc6338f6b530eb133 3\n" + "a5963fa6c95fbe34482862b0f7221b7766e3a194 3\n" + "2e0436303648097e8e936c5467574aff5e3f1517 2\n" + "f25628e474035e6bf0440b274c5df04f94c49fb8 4\n" + "5ec8811254dc762c6852289a08acf22954404f0d 2\n" + "b5acea41aeabb5daa4183953f800d47db0c39fb5 2\n" + "2e2543ac99193f76b4f35463142294c016f0cf3d 4\n" + "f90064fe4ffed170f9e9c79196aa0b17e0bbd86b 2\n" + "523330d609ca0e527f3e28a078635b3d28489ec9 5\n" + "f9486dd810d6321bad4bc9d7703a67d630167f98 4\n" + "22f5998b4de47bf2ccc077d5881ac448e6b0484d 7\n" + "e003d57d2102e8fdf5d9eea7bd78a3cf67d75778 4\n" + "51e4231e245ffd733590e3205fa3ffea92ad509f 4\n" + "68158290666f4d7ac1ada40d674a1160c162b193 4\n" + "d57fc4121d716182a091bb53c666b46c10142668 4\n" + "13c59dd8f7320856b15c834038af3c846db33e4e 3\n" + "db1513dff6dd456fc973286816129ace81bcdfdd 5\n" + "4c44939a95a20d701f60830499254e5dabedeeaa 4\n" + "a53feafa4c70d2378998f0dcb837905e5070c541 4\n" + "ee501fde62b25ddd1c82798ee1b233b8813e717b 4\n" + "4fe1d66e8f3cbd5ecd9b22b6aea2b37a604f2492 3\n" + "55fb4e3d3a762b613b635ee9745d92f0f3711272 4\n" + "de0eb1c46c47924879e2a49f9eafba0ab344fe5f 5\n" + "e6c01bd90cf0b2793282fc53e9a3150a6d7804c5 5\n" + "c5e3845c74da6aea2cb68ccd765e4f606f10b5b6 5\n" + "e3b9241291b5f19ab239726360258a31e02565a3 7\n" + "845b9263d75d89ef02c23fd05cccc13db5d512ea 4\n" + "495eb918ced94ef47885df40ceceef8b56e96691 4\n" + "31b8b69de88d84aabdb762af1adeac815723562c 4\n" + "5ff1254dccd5dde58d3475a468178738d6260b05 3\n" + "a52021f1b257c7c35d626d5d642134091c45e4f4 3\n" + "7d685547c2a4b92fa62e00854e4a9689d495f93d 3\n" + "b656bd19df58fc1ba4628342b87beeec5948e0b3 3\n" + "7b959c609deaf67d29e3f79f55a03f53961f129e 3\n" + "c461efa942e1a3a1e580f9d04bf8d15c5e834f8b 3\n" + "804be21493a151a0be2075e86c94759dd96f0bf7 3\n" + "8dfe236a6a5734824035725a4f513b8fd31b310a 3\n" + "aa881a2936f368ab887c296b837a8f7f553a3fb2 3\n" + "847841ad88ff2eff6602f03b7231cd1e03f89ffc 3\n" + "8449cdf161babec85508c35c8ca64a5c1fd7671d 3\n" + "1417f9e8ca99deb3b422c5968fdfa03cfa633ec6 3\n" + "65040fb1e9bc4b2d027d7d81e44be30e7620e368 3\n" + "356a936987e62967597ec64a0d35a1e8ad1aa20b 4\n" + "e7b8574a42b1029539632ce0f5161b1c38d5e95f 2\n" + "4ad4898db3bdcf87b2acd5755e06d45c73d338cd 4\n" + "8262ac069e395ba020025caf0519e8eb5a63a0b8 4\n" + "ac71117eb20ec8a84035acbbd380df8235a5c665 4\n" + "af567ea6e27912d6d5bf1c8e9bc18e9b393fd1ab 2\n" + "983eb5deeb61af5ce82ad598692192b8ae8a2a08 2\n" + "ad621247f95f80a1a32e67def5e978c845d19208 2\n" + "a7b196f7e934faa76602c8c00bd2f6e6d2d70787 2\n" + "7be2f14fd0df5718cdb69b972c5f7c1f284b03e6 2\n" + "53e1fb8ee77757cd20bf4c7aa4a5c6c786c664d6 2\n" + "c18adae0ca1243e13a15d381a431cc0dfc68223b 2\n" + "ea0824ea0692d2cdff816a2b3c3eb9a587a5f536 2\n" + "e9e778c3d103cc8c32093cd6880fa8d7238bbd9e 2\n" + "4b78514753008cabda2ff3d3cb537b2429efd7d2 2\n" + "21ac0ae7da47abf30986b479489134c79405f95d 2\n" + "3a0b0bdd77a1f3a11533f5ef030370bcc90c6334 2\n" + "1c17e58f1a602e07f185bce1aa8589fae76e8b44 4\n" + "d53a8efefe3714269538a91e5256c2938fe13239 4\n" + "1cbd5c7849f97dea1e3375a842f784ff9283310c 7\n" + "ab087d04eb5c2c8894758c7cee32ccafd80989a7 4\n" + "4c2a8cde9b40c4936f512365a7cd6c8cef92095e 3\n" + "b1cf453206beef8ab2ede2b061ed95a3cd91eae3 3\n" + "04a135910588ef8753b04a49c66d138c081284d2 3\n" + "721a2ded692d34d864d0440ac347c60880942d19 4\n" + "1d97922a5ab63e73611d0edbe952f041af3385b3 4\n" + "688b70e60dc272654ac6b0a72237fefdce2ef9ff 4\n" + "6075cd96bc85864f22c643b023302f006b565049 3\n" + "5c278deb1390eb45c79b7667b1ea1c54183123ac 4\n" + "cb6bd26961387def757c7faeec7563d77d404d01 7\n" + "89943ae938fb308f4b5149ebc0cbea597b9b0223 7\n" + "fbfdd89ec183ceae63d3d66b0ab45faa951b525a 7\n" + "c9c272ffa69d3e0af5be42e7636b02dc2af6ea8c 5\n" + "82397096d5ef436b99b943c2a52d6eff87694183 5\n" + "8e66731c99f4d041ddcd33088d0ab0d4cae8bf75 5\n" + "0f10d77d1536993b8455b3b47278ca5782ef9275 4\n" + "6933de9a128e90a34383f3a9b2660d5bae5d9fe3 4\n" + "d5902afbd8dfb1ea96fc8cf4d62cf6e4fa40d4f7 4\n" + "78507f3f8b8601d99bbedb6517bec6cddf0bd5b4 4\n" + "4607ed284bd4602affa00600c749ce43b2b89b6d 5\n" + "b7104477c8801fe54d1ba91f3a77ac6e4f399f52 5\n" + "788c53657d9ea180d04f29ccaf1f1c29a9b75519 5\n" + "ce4c5ce07afd297156899eb6c1056eef3f83b803 5\n" + "cbec713b32517a503ae6c02497357a8361eccb76 3\n" + "b20e02cbffef692c7aee6281eb36729ee1e928ef 4\n" + "ae6ad52d5ebcad7722ea070109a286fe13b06cb7 4\n" + "d7f8c34e3ae9dea51d0c87857e0ff78028d402b7 4\n" + "f52690157a51c662ffdf4ae3592ada93d7be4032 4\n" + "81143d2a8ef7cc593a604f3cc890312b42bdff01 5\n" + "8afe57c287adc0f93f234e8e8c136ae3c00947c1 5\n" + "418f6248221c79039fe6c44fc5a9514842e3ec71 4\n" + "07c4c39c6e6c03241d2353300f630b367322e78a 4\n" + "91afe2ee3a5b8e80d67f6c7a30bafbc2d63b2421 3\n" + "29a42c2b679bac55a0318ce8c97708e402e2779b 4\n" + "0c62591033aa4e5e6b9eeb21eb2e5e60dcd8ee8e 2\n" + "2312bff70c83e60d1ca0fb9ca78a50637d02771c 2\n" + "1ccac7caeaaa1c5a89503221b0a5eec2f34b1c91 2\n" + "c82c6e054313f4c8864e64897baea8b63776020d 4\n" + "f787271597abbcb1297e4e8a4a9561959a195649 4\n" + "c08a3c1478e51db62624814acbf36c198d6561e6 4\n" + "b81dd702afaaa5946e4ec065042c891ad2f76c2b 3\n" + "02c3ec94190c6fce6fa4984d92be48b2697e7706 4\n" + "4bd533cd7859489a3b26787fb5c7e4d8662cc420 4\n" + "b5749b4cac530e261da9acc0d494a81766d2acc3 3\n" + "afb693fd0a138cfc59a2bf81eabd7ca2abd4e162 4\n" + "81bbbd4ea86350ebba03e8d8cb29f050a3d93a83 4\n" + "d17acc97bf2f853a5fff58da733e4cd53cdaa3ed 5\n" + "59f679c5ebf75de98b5e909da9182dbd80413364 4\n" + "a80b5426fd0f4ab19eba1c06620f5efb8fbade3a 3\n" + "a57f8ec4f89ab84936f46821969de6bca9f46d8e 4\n" + "d3a936e5ef24fdbc7a02a81644ab6cd4b88119d3 3\n" + "522a53034cf40448d441683f3308fcb4b65e6d7c 4\n" + "dab5a9f93a35fcb92c01e4250b01928fe8bd294b 7\n" + "25ffc7e8ca90274566c31e10b5998cca6522673a 7\n" + "c1923ab5c5dcaf67af3e23fd1ac42e2d4c61aca7 4\n" + "24384e63bfe450bce6abd327eae3e27969025379 4\n" + "3f9d72bdafecfa640639cf66721e7380e41c6e6d 3\n" + "c5b4cd4b49144e8f77455e9ff9077ff840ab278c 3\n" + "c74a5a6164536750d6b3ea90e19798ab6fad79d7 4\n" + "510f6d854f813269f67ee15a4cc2ed77d96d2903 4\n" + "a5f3ada0610ad32f59d2f114167612d4f62bec4e 3\n" + "d076d7185078b8adddbacfee7e46c61e3e02fe7e 3\n" + "77d95640c80bc4868d04a913f9dbf4b5f6d877d2 5\n" + "dbcfa55870a267112c9a71bcf85b481b11d98e04 3\n" + "ee095ee6361f0a767250fe19c9bc805bba8da4e9 3\n" + "39d500fbf22168be2a12ac89ff5a50131bb1c30b 4\n" + "3194153a9c93aa24b37804a764f112bdef091bf8 4\n" + "eca36fe80a916eb6b196ec56edfc1296d793e350 3\n" + "d452a79b25f18e6a6eadc5c592d5101f115ef0f0 3\n" + "ebed19269b7625c93eccefc2bd758555e2027431 3\n" + "e388e8d8546e329d2c58269d4fa026624a640f1b 4\n" + "f6199f48ff994fc9a8e33a8581bb3bb16dd301ab 2\n" + "9447e50d1266b7cb96a414eaf9ee16c0f99e55f3 2\n" + "ad1081f398ac69c0c92e4afadde482574fe875bb 2\n" + "17583adb63d16d64ae51820055cd32c0e935425a 2\n" + "2fc050dbc44ba9dfb72299129d48e5e5e1c892c3 4\n" + "2bfa08db95502ee9bde61c5fe7b6557c0e4aef5a 4\n" + "dc56cff1211bacd94f59e188e44a58d6f40a0c26 5\n" + "de251a0af1df3be18469578dc00d414e572ee33e 7\n" + "d1cc2a77fb17608d542c50e2a1404cf5b4231521 5\n" + "8666f3109ad01093b0afab11986cfd60ba37d2a0 5\n" + "fbb22cea9227f6f40080a42a1a169c0747129c95 5\n" + "e171d0cec1d32aaf084e3c8150705c177522cbe6 3\n" + "4164dcdfe0613d0f4c53b6f3581548bbbad5eb43 7\n" + "ef6c6cde14eb551b74640ed6b38660572f121b86 7\n" + "b9d46267d081882dfd89342d3e09b82d7e8f618d 7\n" + "2a3c7ad70e34989fa5170028bad760e65ebe9d57 5\n" + "3202af113265e67e130a0b948dc167a9f9e9cc79 4\n" + "1ab38b014480745eaae143c9e6f67b542e62442f 3\n" + "795d47c6aae378d0d5bedc9f104a597bcc1ee16b 4\n" + "414fd2a22076f45502c2329b45bda9f8730dcaae 4\n" + "b2ce0bc86f82f727db956dfbbe2a3fd0cb72f5fc 7\n" + "00c70cf37c71cb8f99567ae407ff6dd52c4774c2 4\n" + "c3ab3edb7f9ba31124092dc0f1dd2f13d3b52f8f 4\n" + "aba839d554733850902f3073f7974e2e87c488ba 4\n" + "19e79a79be13ac62bb002ed3323502be97886e17 3\n" + "db22cd03db61a495d55c460cfaa266271c8a5926 3\n" + "17dcbe8e9ab792483a542b0230897baf0240dcc9 3\n" + "af310ee21b5534f32ea92c0ae34a44d828d157a7 4\n" + "e3036a7bc6ed3ece1c85d5e37fb2ff39ba98ba47 4\n" + "2300aa82bfcbcf95329eb04e58bcd7e4ca0a7889 3\n" + "7fe5f17ad7e6b70dd56cad2c8bdc516a86f03207 4\n" + "632cb0834441626d20cc113cfad2e3a7ec0e0dc7 3\n" + "d7fb2ca6004c5fdf77c42a5eab102b95a9a862d7 3\n" + "4ff2aad8371e382e203c7f29b665612a8c9d937c 3\n" + "7c7d0bff4a51bb0f1f0cdb4132fdb11f8a71c623 3\n" + "216b2c181c9ee0fb48e2d9efdb88ea918c8ff04f 3\n" + "338460029d65ebce0d9119d811187a35c866a958 4\n" + "108e9164ef6cf1ffe6e3c5303bfbbc3bd807b6df 4\n" + "18be429f8b5c7bc4775c99292110aa09341cd644 4\n" + "92bada2d019fcd6eee6d332a8068a9ddfa30d518 4\n" + "c7377eaeb7f91639296c3df095ba403dbf4f8e79 2\n" + "2f2c7559a5ba7fb938aba152bd84879c0bd3cae2 5\n" + "7ed1308179c41ae62cfb46124ab0ce22d8b0513b 5\n" + "6a95b7f3a4cff9c9b3d7b8b30b0b0f752dadaa37 5\n" + "34635bf3518c7b33164f3d96da769c7a01102d28 4\n" + "ebe20c85c5314957fafc223c26bf393847e3bb03 5\n" + "f2361230358bd945690ca1c2a9dcc1cbce83941b 5\n" + "ab3f375b6890b2a9bb1b6124f0fa690950c749f2 5\n" + "f84d7fc089b31440cac1f4fbee3d45b2f6c90e37 5\n" + "28b54294007ec345aef7322a2fcaa18d3363a73e 5\n" + "3e57fad130be79223068ab29f1e77ecb7f2ad237 4\n" + "efed48854beb2803d9f2b5644edff2aacf7c6af9 4\n" + "9347890b025cc8c521a4abbff309ffc0466f22b3 4\n" + "6fe7a6bb86897e579393e70961a3a3204adfa48b 3\n" + "c570eccb1bf0c993e375e094d803ef112d0763ad 3\n" + "2724871629b46e05a0c8b5554a962211e700de0e 3\n" + "9200fd6f1e92f78e3d9c6d082837f719ad0500d4 5\n" + "23e702084e3031158a50a0560746767bbc1b7bee 5\n" + "289ac36381905685dda39946127ab2af3317a010 5\n" + "36eda820b734ac7a42653af370a475f36bd36b1b 5\n" + "d526249c1a35cf1174300df75bc28662f579290a 5\n" + "a322d4e572467be5e1b452214143f88db526b49f 4\n" + "67fa6e9005d23d9c49f77884a856cfb376194083 3\n" + "4cc5f197d2bd3f0306c07a880590b4b4a5574e12 4\n" + "06c527db4fbe929b4da005e7463078c05944f477 4\n" + "094d11ebf8a20b6bfc07902c7f136bdd18b1e7ca 4\n"}; diff --git a/components/msx/fmsx/msxfix.c b/components/msx/fmsx/msxfix.c new file mode 100644 index 0000000..ec8fa81 --- /dev/null +++ b/components/msx/fmsx/msxfix.c @@ -0,0 +1,113 @@ +// Unused but needed because we define UNIX +int UseSound, UseZoom, SyncFreq, UseEffects; +int ARGC; +char **ARGV; + +#ifdef ESP_PLATFORM +/** + * esp-idf has no concept of a current working directory, which fMSX relies heavily upon. + * So we must emulate it. I've also added optimizations to bypass stat calls because + * it makes using the built-in menu incredibly slow. It also returns NULL on certain filenames + * to speed up boot significantly. + * + * To make it work, add `-include msxfix.h` to fMSX's cflags +*/ +#include +#include +#include +#include +#include +#include + +#include "carts_sha.h" + +static char path_buffer[100]; +static char path_cwd[100] = "/sdcard/"; +static struct dirent dirent_cache; + +const char *msx_ignore_files = ""; // "DEFAULT.FNT DEFAULT.CAS DRIVEA.DSK DRIVEB.DSK CARTA.ROM CARTB.ROM"; + +static const char *get_path(const char *path) +{ + if (strcmp(path, ".") == 0) + strcpy(path_buffer, path_cwd); + else if (path[0] == '/') + strcpy(path_buffer, path); + else + sprintf(path_buffer, "%s/%s", path_cwd, path); + return path_buffer; +} + +int msx_chdir(const char *path) +{ + strcpy(path_cwd, get_path(path)); + return 0; +} + +char *msx_getcwd(char *buf, size_t size) +{ + if (!buf) + return strdup(path_cwd); + if (strlen(path_cwd) < size) + return strcpy(buf, path_cwd); + return NULL; +} + +DIR *msx_opendir(const char *path) +{ + return opendir(get_path(path)); +} + +struct dirent *msx_readdir(DIR *dir) +{ + struct dirent *ent = readdir(dir); + if (ent == NULL) + memset(&dirent_cache, 0, sizeof(dirent_cache)); + else + dirent_cache = *ent; + return ent; +} + +FILE *msx_fopen(const char *path, const char *mode) +{ + if (strstr(msx_ignore_files, path)) + return NULL; + /* if (strcmp(path, "CARTS.SHA") == 0) */ + /* { */ + /* path = RG_BASE_PATH_CACHE "/MSX_CARTS.SHA"; */ + /* if (!rg_storage_exists(path)) */ + /* { */ + /* FILE *fp = fopen(path, "wb"); */ + /* fwrite(carts_sha, sizeof(carts_sha), 1, fp); */ + /* fclose(fp); */ + /* } */ + /* } */ + return fopen(get_path(path), mode); +} + +int msx_stat(const char *path, struct stat *sbuf) +{ + if (strstr(msx_ignore_files, path)) + return -1; +#if defined(DT_REG) && defined(DT_DIR) + // fMSX uses stat right after readdir just to check type + // This is ultra slow on esp32, so we cache the last readdir + // and if it has the same name, we fake sbuf + if (sbuf && strcmp(path, dirent_cache.d_name) == 0) + { + sbuf->st_mode = 0; + if (dirent_cache.d_type == DT_REG) + sbuf->st_mode |= S_IFREG; + if (dirent_cache.d_type == DT_DIR) + sbuf->st_mode |= S_IFDIR; + return 0; + } +#endif + return stat(get_path(path), sbuf); +} + +int msx_unlink(const char *path) +{ + return unlink(get_path(path)); +} +#endif diff --git a/components/msx/fmsx/msxfix.h b/components/msx/fmsx/msxfix.h new file mode 100644 index 0000000..ed0ab71 --- /dev/null +++ b/components/msx/fmsx/msxfix.h @@ -0,0 +1,46 @@ +#ifndef _MSXFIX_H_ +#define _MSXFIX_H_ + +#include +#include + +// Misc fixes +#define main(x, y) fmsx_main(x, y) +#define abs(x) __abs(x) +#define GenericSetVideo SetVideo + +int fmsx_main(int argc, char *argv[]); + + +#ifdef ESP_PLATFORM +/** + * esp-idf has no concept of a current working directory, which fMSX relies heavily on. + * So we must emulate it. I've also added optimizations to bypass stat calls because + * it makes using the built-in menu incredibly slow. It also bypass some fopen() only + * during startup, to speed up boot. + * + * To make it work, add `-include msxfix.h` to fMSX's cflags +*/ +#include +#include +#include + +DIR *msx_opendir(const char *); +struct dirent *msx_readdir(DIR *); +FILE *msx_fopen(const char *, const char *); +int msx_stat(const char *, struct stat *); +int msx_unlink(const char *); +int msx_rename(const char *, const char *); +int msx_chdir(const char *); +char *msx_getcwd(char *buf, size_t size); + +#define chdir(x) msx_chdir(x) +#define getcwd(x, y) msx_getcwd(x, y) +#define opendir(x) msx_opendir(x) +#define readdir(x) msx_readdir(x) +#define fopen(x, y) msx_fopen(x, y) +#define stat(x, y) msx_stat(x, y) +#define unlink(x) msx_unlink(x) +#endif + +#endif diff --git a/components/msx/fmsx/src/EMULib/AY8910.c b/components/msx/fmsx/src/EMULib/AY8910.c new file mode 100644 index 0000000..20925b3 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/AY8910.c @@ -0,0 +1,314 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** AY8910.c **/ +/** **/ +/** This file contains emulation for the AY8910 sound chip **/ +/** produced by General Instruments, Yamaha, etc. See **/ +/** AY8910.h for declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +#include "AY8910.h" +#include "Sound.h" +#include + +static const unsigned char Envelopes[16][32] = +{ + { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 }, + { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }, + { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }, + { 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 }, + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 }, + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15 }, + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0 }, + { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } +}; + +static const int Volumes[16] = +{ 0,1,2,4,6,8,11,16,23,32,45,64,90,128,180,255 }; +//{ 0,16,33,50,67,84,101,118,135,152,169,186,203,220,237,254 }; + +/** Reset8910() **********************************************/ +/** Reset the sound chip and use sound channels from the **/ +/** one given in First. **/ +/*************************************************************/ +void Reset8910(register AY8910 *D,int ClockHz,int First) +{ + static byte RegInit[16] = + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFD, + 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00 + }; + int J; + + /* Reset state */ + memcpy(D->R,RegInit,sizeof(D->R)); + D->EPhase = 0; + D->Clock = ClockHz>>4; + D->First = First; + D->Sync = AY8910_ASYNC; + D->Changed = 0x00; + D->EPeriod = -1; + D->ECount = 0; + D->Latch = 0x00; + + /* Set sound types */ + SetSound(0+First,SND_MELODIC); + SetSound(1+First,SND_MELODIC); + SetSound(2+First,SND_MELODIC); + SetSound(3+First,SND_NOISE); + SetSound(4+First,SND_NOISE); + SetSound(5+First,SND_NOISE); + + /* Configure noise generator */ + SetNoise(0x10000,16,14); + + /* Silence all channels */ + for(J=0;JFreq[J]=D->Volume[J]=0; + Sound(J+First,0,0); + } +} + +/** WrCtrl8910() *********************************************/ +/** Write a value V to the PSG Control Port. **/ +/*************************************************************/ +void WrCtrl8910(AY8910 *D,byte V) +{ + D->Latch=V&0x0F; +} + +/** WrData8910() *********************************************/ +/** Write a value V to the PSG Data Port. **/ +/*************************************************************/ +void WrData8910(AY8910 *D,byte V) +{ + Write8910(D,D->Latch,V); +} + +/** RdData8910() *********************************************/ +/** Read a value from the PSG Data Port. **/ +/*************************************************************/ +byte RdData8910(AY8910 *D) +{ + return(D->R[D->Latch]); +} + +/** Write8910() **********************************************/ +/** Call this function to output a value V into the sound **/ +/** chip. **/ +/*************************************************************/ +void Write8910(register AY8910 *D,register byte R,register byte V) +{ + register int J; + + switch(R) + { + case 1: + case 3: + case 5: + V&=0x0F; + /* Fall through */ + case 0: + case 2: + case 4: + if(V!=D->R[R]) + { + /* Compute changed channels mask */ + D->Changed|=(1<<(R>>1))&~D->R[7]; + /* Write value */ + D->R[R]=V; + } + break; + + case 6: + V&=0x1F; + if(V!=D->R[R]) + { + /* Compute changed channels mask */ + D->Changed|=0x38&~D->R[7]; + /* Write value */ + D->R[R]=V; + } + break; + + case 7: + /* Compute changed channels mask */ + D->Changed|=(V^D->R[R])&0x3F; + /* Write value */ + D->R[R]=V; + break; + + case 8: + case 9: + case 10: + V&=0x1F; + if(V!=D->R[R]) + { + /* Compute changed channels mask */ + D->Changed|=(0x09<<(R-8))&~D->R[7]; + /* Write value */ + D->R[R]=V; + } + break; + + case 11: + case 12: + if(V!=D->R[R]) + { + /* Will need to recompute EPeriod next time */ + D->EPeriod = -1; + /* Write value */ + D->R[R]=V; + /* No channels changed */ + } + return; + + case 13: + /* Write value */ + D->R[R]=V&=0x0F; + /* Reset envelope */ + D->ECount = 0; + D->EPhase = 0; + /* Compute changed channels mask */ + for(J=0;JR[J+8]&0x10) D->Changed|=(0x09<R[7]; + break; + + case 14: + case 15: + /* Write value */ + D->R[R]=V; + /* No channels changed */ + return; + + default: + /* Wrong register, do nothing */ + return; + } + + /* For asynchronous mode, make Sound() calls right away */ + if(!D->Sync&&D->Changed) Sync8910(D,AY8910_FLUSH); +} + +/** Loop8910() ***********************************************/ +/** Call this function periodically to update volume **/ +/** envelopes. Use uSec to pass the time since the last **/ +/** call of Loop8910() in microseconds. **/ +/*************************************************************/ +void Loop8910(register AY8910 *D,int uSec) +{ + register int J; + + /* If envelope period was updated... */ + if(D->EPeriod<0) + { + /* Compute envelope period (why not <<4?) */ + J=((int)D->R[12]<<8)+D->R[11]; + D->EPeriod=(int)(1000000L*(J? J:0x10000)/D->Clock); + } + + /* Exit if no envelope running */ + if(!D->EPeriod) return; + + /* Count microseconds */ + D->ECount += uSec; + if(D->ECountEPeriod) return; + + /* Count steps */ + J = D->ECount/D->EPeriod; + D->ECount -= J*D->EPeriod; + + /* Count phases */ + D->EPhase += J; + if(D->EPhase>31) + D->EPhase = (D->R[13]&0x09)==0x08? (D->EPhase&0x1F):31; + + /* Compute changed channels mask */ + for(J=0;JR[J+8]&0x10) D->Changed|=(0x09<R[7]; + + /* For asynchronous mode, make Sound() calls right away */ + if(!D->Sync&&D->Changed) Sync8910(D,AY8910_FLUSH); +} + +/** Sync8910() ***********************************************/ +/** Flush all accumulated changes by issuing Sound() calls **/ +/** and set the synchronization on/off. The second argument **/ +/** should be AY8910_SYNC/AY8910_ASYNC to set/reset sync, **/ +/** or AY8910_FLUSH to leave sync mode as it is. To emulate **/ +/** noise channels with MIDI drums, OR second argument with **/ +/** AY8910_DRUMS. **/ +/*************************************************************/ +void Sync8910(register AY8910 *D,register byte Sync) +{ + register int J,I,K; + int Freq,Volume,Drums; + + /* Update sync/async mode */ + I = Sync&~AY8910_DRUMS; + if(I!=AY8910_FLUSH) D->Sync=I; + + /* If hitting drums, always compute noise channels */ + I = D->Changed|(Sync&AY8910_DRUMS? 0x38:0x00); + + /* For every changed channel... */ + for(J=0,Drums=0;I&&(J>=1) + { + if(I&1) + { + if(D->R[7]&(1<R[J+8]&0x1F; + K = Volumes[K&0x10? Envelopes[D->R[13]&0x0F][D->EPhase]:(K&0x0F)]; + Volume = K; + /* Compute melodic channel frequency */ + K = J<<1; + K = ((int)(D->R[K+1]&0x0F)<<8)+D->R[K]; + // Eggerland Mystery 1 (MSX) uses 0 to silence sound + //Freq = D->Clock/(K? K:0x1000); + Freq = K? D->Clock/K:0; + } + else + { + /* Compute noise channel volume */ + K = D->R[J+5]&0x1F; + K = Volumes[K&0x10? Envelopes[D->R[13]&0x0F][D->EPhase]:(K&0x0F)]; + Volume = (K+1)>>1; + Drums += Volume; + /* Compute noise channel frequency */ + /* Shouldn't do <<2, but need to keep frequency down */ + K = D->R[6]&0x1F; + Freq = D->Clock/((K&0x1F? (K&0x1F):0x20)<<2); + } + } + + /* Play sound */ + Sound(D->First+J,Freq,Volume); + } + } + + /* Hit MIDI drums for noise channels, if requested */ + if(Drums&&(Sync&AY8910_DRUMS)) Drum(DRM_MIDI|28,Drums>255? 255:Drums); + + /* Done with all the changes */ + D->Changed=0x00; +} diff --git a/components/msx/fmsx/src/EMULib/AY8910.h b/components/msx/fmsx/src/EMULib/AY8910.h new file mode 100644 index 0000000..d37fd64 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/AY8910.h @@ -0,0 +1,102 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** AY8910.h **/ +/** **/ +/** This file contains emulation for the AY8910 sound chip **/ +/** produced by General Instruments, Yamaha, etc. See **/ +/** AY8910.c for the actual code. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef AY8910_H +#define AY8910_H +#ifdef __cplusplus +extern "C" { +#endif + +#define AY8910_CHANNELS 6 /* 3 melodic + 3 noise chanls */ + +#define AY8910_ASYNC 0 /* Asynchronous emulation */ +#define AY8910_SYNC 1 /* Synchronous emulation */ +#define AY8910_FLUSH 2 /* Flush buffers only */ +#define AY8910_DRUMS 0x80 /* Hit drums for noise chnls */ + +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif + +/** AY8910 ***************************************************/ +/** This data structure stores AY8910 state. **/ +/*************************************************************/ +#pragma pack(4) +typedef struct +{ + byte R[16]; /* PSG registers contents */ + + /* THESE VALUES ARE NOT USED BUT KEPT FOR BACKWARD COMPATIBILITY */ + int Freq[AY8910_CHANNELS]; /* Frequencies (0 for off) */ + int Volume[AY8910_CHANNELS]; /* Volumes (0..255) */ + + int Clock; /* Base clock rate (Fin/16) */ + int First; /* First used Sound() channel */ + byte Changed; /* Bitmap of changed channels */ + byte Sync; /* AY8910_SYNC/AY8910_ASYNC */ + byte Latch; /* Latch for the register num */ + int EPeriod; /* Envelope step in microsecs */ + int ECount; /* Envelope step counter */ + int EPhase; /* Envelope phase */ +} AY8910; +#pragma pack() + +/** Reset8910() **********************************************/ +/** Reset the sound chip and use sound channels from the **/ +/** one given in First. **/ +/*************************************************************/ +void Reset8910(register AY8910 *D,int ClockHz,int First); + +/** Write8910() **********************************************/ +/** Call this function to output a value V into the sound **/ +/** chip. **/ +/*************************************************************/ +void Write8910(register AY8910 *D,register byte R,register byte V); + +/** WrCtrl8910() *********************************************/ +/** Write a value V to the PSG Control Port. **/ +/*************************************************************/ +void WrCtrl8910(AY8910 *D,byte V); + +/** WrData8910() *********************************************/ +/** Write a value V to the PSG Data Port. **/ +/*************************************************************/ +void WrData8910(AY8910 *D,byte V); + +/** RdData8910() *********************************************/ +/** Read a value from the PSG Data Port. **/ +/*************************************************************/ +byte RdData8910(AY8910 *D); + +/** Sync8910() ***********************************************/ +/** Flush all accumulated changes by issuing Sound() calls **/ +/** and set the synchronization on/off. The second argument **/ +/** should be AY8910_SYNC/AY8910_ASYNC to set/reset sync, **/ +/** or AY8910_FLUSH to leave sync mode as it is. To emulate **/ +/** noise channels with MIDI drums, OR second argument with **/ +/** AY8910_DRUMS. **/ +/*************************************************************/ +void Sync8910(register AY8910 *D,register byte Sync); + +/** Loop8910() ***********************************************/ +/** Call this function periodically to update volume **/ +/** envelopes. Use uSec to pass the time since the last **/ +/** call of Loop8910() in microseconds. **/ +/*************************************************************/ +void Loop8910(register AY8910 *D,int uSec); + +#ifdef __cplusplus +} +#endif +#endif /* AY8910_H */ diff --git a/components/msx/fmsx/src/EMULib/Console.c b/components/msx/fmsx/src/EMULib/Console.c new file mode 100644 index 0000000..3998b54 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Console.c @@ -0,0 +1,1073 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Console.c **/ +/** **/ +/** This file contains platform-independent implementation **/ +/** for the EMULib-based console. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2005-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#if !defined(BPP32) && !defined(BPP24) && !defined(BPP16) && !defined(BPP8) +#include "ConsoleMux.h" +#else + +#include "Console.h" +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#include "dirent.h" +#define S_ISDIR(Attrs) ((Attrs)&S_IFDIR) +#else +#include +#endif + +#if defined(MSDOS) || defined(WINDOWS) +#include +#else +#include +#endif + +static pixel FG = PIXEL(255,255,255); +static pixel BG = PIXEL(0,0,0); + +#ifndef DEFINE_ONCE +#define DEFINE_ONCE + +#define EOLN(C) (!(C)||((C)=='\n')) +#define TAG_SELEFILE 0x5E7EF17E + +static const unsigned char NormalFont[] = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x00 */ + 0x00,0xC0,0xF0,0xFC,0xFF,0xFC,0xF0,0xC0, + 0x03,0x03,0x06,0x06,0xCC,0x6C,0x38,0x18, + 0x00,0x00,0xF0,0xFE,0x82,0x82,0x82,0xFE, + 0x00,0xFE,0x82,0xBA,0x82,0xBA,0x82,0xFE, + 0x00,0x00,0x10,0x38,0x7C,0x00,0x00,0x00, + 0x00,0x00,0x7C,0x38,0x10,0x00,0x00,0x00, + 0x00,0x00,0x00,0x78,0x78,0xFC,0x00,0x00, + 0x00,0x10,0x30,0x7E,0x30,0x10,0x00,0x00, /* 0x08 */ + 0x00,0x48,0x4C,0x7E,0x4C,0x48,0x00,0x00, + 0x00,0x02,0x12,0x32,0x7E,0x30,0x10,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xAA, + 0x88,0x89,0x8A,0xAA,0xDB,0x8A,0x8A,0x00, + 0x00,0x02,0x12,0x32,0x7E,0x30,0x10,0x00, + 0x00,0x08,0x24,0x42,0x42,0x24,0x08,0x00, + 0x00,0x42,0x24,0x08,0x08,0x24,0x42,0x00, + 0x00,0x64,0x4C,0x44,0x64,0x44,0x4E,0x00, /* 0x10 */ + 0x00,0x64,0x4A,0x42,0x64,0x48,0x4E,0x00, + 0x00,0x6C,0x42,0x44,0x62,0x42,0x4C,0x00, + 0x00,0x6A,0x4A,0x4E,0x62,0x42,0x42,0x00, + 0x00,0x6E,0x48,0x4C,0x62,0x42,0x4C,0x00, + 0x00,0x66,0x48,0x4C,0x6A,0x4A,0x44,0x00, + 0x00,0x6E,0x4A,0x42,0x64,0x48,0x48,0x00, + 0x00,0x6E,0x4A,0x44,0x6A,0x4A,0x4E,0x00, + 0x07,0x04,0x04,0x07,0x04,0x04,0x07,0x00, /* 0x18 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x24,0x24,0x24,0x24,0x24,0x24,0x00, + 0x00,0x70,0x60,0x50,0x08,0x04,0x02,0x00, + 0x91,0x51,0x51,0x8F,0x41,0x51,0x8E,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x41,0x21,0xC2,0x6C,0x30,0x60,0x78,0xE6, + 0xD1,0xC9,0xC5,0xC4,0x84,0x84,0x40,0x00, + 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, +}; + +static const unsigned char BoldFont[] = +{ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x00 */ +0x00,0xC0,0xF0,0xFC,0xFF,0xFC,0xF0,0xC0, +0x03,0x03,0x06,0x06,0xCC,0x6C,0x38,0x18, +0x00,0x00,0xF0,0xFE,0x82,0x82,0x82,0xFE, +0x00,0xFE,0x82,0xBA,0x82,0xBA,0x82,0xFE, +0x00,0x00,0x10,0x38,0x7C,0x00,0x00,0x00, +0x00,0x00,0x7C,0x38,0x10,0x00,0x00,0x00, +0x00,0x00,0x00,0x78,0x78,0xFC,0x00,0x00, +0xBE,0x08,0x08,0x08,0x08,0x08,0x08,0x00, +0x00,0xFC,0xFC,0xFE,0xFE,0xFF,0x7F,0x7F, +0x7B,0x7B,0x79,0x79,0x78,0xFC,0xFC,0xFC, +0xFC,0x00,0xFC,0xFC,0xFC,0x00,0xFC,0xFC, +0x88,0x89,0x8A,0xAA,0xDB,0x8A,0x8A,0x00, +0x00,0x3F,0x3F,0x7F,0x7F,0xFF,0xFE,0xFE, +0xDE,0xDE,0x9E,0x9E,0x1E,0x3F,0x3F,0x3F, +0x3F,0x00,0x3F,0x3F,0x3F,0x00,0x3F,0x3F, +0xEF,0x28,0x28,0x2F,0xEA,0x29,0x28,0x00, /* 0x10 */ +0x00,0x07,0x0F,0x0F,0x1E,0x1E,0x1E,0x0F, +0x0F,0x07,0x03,0x00,0x1E,0x1E,0x1F,0x0F, +0x07,0x00,0x07,0x07,0x07,0x00,0x07,0x07, +0x3E,0xA0,0xA0,0x38,0x20,0x20,0xBE,0x00, +0x00,0xFC,0xFE,0xFF,0x0F,0x0F,0x00,0xF8, +0xFC,0xFE,0xFE,0x0F,0x0F,0x0F,0xFE,0xFE, +0xFC,0x00,0xFC,0xFC,0xFC,0x00,0xFC,0xFC, +0x07,0x04,0x04,0x07,0x04,0x04,0x07,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x41,0x42,0x21,0x1B,0x06,0x03,0x0F,0x33, +0x45,0x49,0x51,0x11,0x10,0x10,0x01,0x00, +0x91,0x51,0x51,0x8F,0x41,0x51,0x8E,0x80, +0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, +0x41,0x21,0xC2,0x6C,0x30,0x60,0x78,0xE6, +0xD1,0xC9,0xC5,0xC4,0x84,0x84,0x40,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0x20 */ +0x00,0x00,0x04,0x00,0x00,0x00,0x40,0x00, +0x00,0x36,0x36,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00, +0x00,0x20,0x00,0x02,0x00,0x00,0x00,0x00, +0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00, +0x6C,0xBE,0xBE,0xFE,0x7C,0x38,0x10,0x00, +0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00, +0x00,0x00,0x18,0x18,0x7E,0x7E,0x18,0x18, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40, +0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18, +0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0x40, /* / */ +0x00,0x1C,0x22,0x63,0x63,0x63,0x22,0x1C, /* 0x30 */ +0x00,0x18,0x38,0x18,0x18,0x18,0x18,0x7E, +0x00,0x3E,0x63,0x03,0x0E,0x3C,0x70,0x7F, +0x00,0x3E,0x63,0x03,0x0E,0x03,0x63,0x3E, +0x00,0x0E,0x1E,0x36,0x66,0x66,0x7F,0x06, +0x00,0x7F,0x60,0x7E,0x63,0x03,0x63,0x3E, +0x00,0x3E,0x63,0x60,0x7E,0x63,0x63,0x3E, +0x00,0x7F,0x63,0x06,0x0C,0x18,0x18,0x18, +0x00,0x3E,0x63,0x63,0x3E,0x63,0x63,0x3E, +0x00,0x3E,0x63,0x63,0x3F,0x03,0x63,0x3E, +0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18, /* : */ +0xF0,0xF0,0xF0,0xF8,0xF8,0x7E,0x3F,0x0F, +0xFF,0xFF,0x00,0x00,0x00,0x00,0xFF,0xFF, +0xFF,0xFE,0x00,0x00,0x00,0x01,0xFF,0xFF, +0x00,0x60,0x30,0x18,0x0C,0x18,0x30,0x60, +0x1E,0x33,0x33,0x06,0x0C,0x00,0x0C,0x0C, +0xDB,0xBD,0xA5,0x66,0x3C,0x18,0x18,0x18, /* 0x40 */ +0x00,0x1C,0x36,0x63,0x63,0x7F,0x63,0x63, +0x00,0x7E,0x63,0x63,0x7E,0x63,0x63,0x7E, +0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E, +0x00,0x7C,0x62,0x63,0x63,0x63,0x62,0x7C, +0x00,0x7F,0x60,0x60,0x7E,0x60,0x60,0x7F, +0x00,0x7F,0x60,0x60,0x7E,0x60,0x60,0x60, +0x00,0x3E,0x63,0x60,0x67,0x63,0x63,0x3E, +0x00,0x63,0x63,0x63,0x7F,0x63,0x63,0x63, +0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x3C, +0x00,0x1F,0x06,0x06,0x06,0x06,0x66,0x3C, +0x00,0x63,0x66,0x6C,0x78,0x7C,0x6E,0x67, +0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x7F, +0x00,0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63, +0x00,0x63,0x73,0x7B,0x7F,0x6F,0x67,0x63, +0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x3E, +0x00,0x7E,0x63,0x63,0x63,0x7E,0x60,0x60, /* 0x50 */ +0x00,0x3E,0x63,0x63,0x63,0x6F,0x66,0x3D, +0x00,0x7E,0x63,0x63,0x62,0x7C,0x66,0x63, +0x00,0x3E,0x63,0x60,0x3E,0x03,0x63,0x3E, +0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x18, +0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x3E, +0x00,0x63,0x63,0x63,0x63,0x36,0x1C,0x08, +0x00,0x63,0x63,0x6B,0x6B,0x7F,0x77,0x22, +0x00,0x63,0x76,0x3C,0x1C,0x1E,0x37,0x63, +0x00,0x63,0x63,0x33,0x1E,0x0C,0x18,0x70, +0x00,0x7F,0x07,0x0E,0x1C,0x38,0x70,0x7F, +0x38,0x44,0xBA,0xA2,0xA2,0xBA,0x44,0x38, +0x00,0x40,0x60,0x30,0x18,0x0C,0x06,0x02, /* \ */ +0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00, +0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0x7F, +0x34,0x34,0x34,0x34,0x34,0x34,0x3C,0x18, /* 0x60 */ +0x00,0x1C,0x36,0x63,0x63,0x7F,0x63,0x63, +0x00,0x7E,0x63,0x63,0x7E,0x63,0x63,0x7E, +0x00,0x3E,0x63,0x60,0x60,0x60,0x63,0x3E, +0x00,0x7C,0x62,0x63,0x63,0x63,0x62,0x7C, +0x00,0x7F,0x60,0x60,0x7E,0x60,0x60,0x7F, +0x00,0x7F,0x60,0x60,0x7E,0x60,0x60,0x60, +0x00,0x3E,0x63,0x60,0x67,0x63,0x63,0x3E, +0x00,0x63,0x63,0x63,0x7F,0x63,0x63,0x63, +0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x3C, +0x00,0x1F,0x06,0x06,0x06,0x06,0x66,0x3C, +0x00,0x63,0x66,0x6C,0x78,0x7C,0x6E,0x67, +0x00,0x60,0x60,0x60,0x60,0x60,0x60,0x7F, +0x00,0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63, +0x00,0x63,0x73,0x7B,0x7F,0x6F,0x67,0x63, +0x00,0x3E,0x63,0x63,0x63,0x63,0x63,0x3E, +0x00,0x7E,0x63,0x63,0x63,0x7E,0x60,0x60, /* 0x70 */ +0x00,0x3E,0x63,0x63,0x63,0x6F,0x66,0x3D, +0x00,0x7E,0x63,0x63,0x62,0x7C,0x66,0x63, +0x00,0x3E,0x63,0x60,0x3E,0x03,0x63,0x3E, +0x00,0x7E,0x18,0x18,0x18,0x18,0x18,0x18, +0x00,0x63,0x63,0x63,0x63,0x63,0x63,0x3E, +0x00,0x63,0x63,0x63,0x63,0x36,0x1C,0x08, +0x00,0x63,0x63,0x6B,0x6B,0x7F,0x77,0x22, +0x00,0x63,0x76,0x3C,0x1C,0x1E,0x37,0x63, +0x00,0x63,0x63,0x33,0x1E,0x0C,0x18,0x70, +0x00,0x7F,0x07,0x0E,0x1C,0x38,0x70,0x7F, +0x66,0x3C,0x00,0x00,0x66,0x66,0x00,0xFF, +0x3F,0x40,0x80,0x98,0x9C,0x8C,0x80,0x80, +0x80,0x80,0x8C,0x9C,0x98,0x80,0x40,0x3F, +0x00,0x00,0x36,0x7E,0x6C,0x00,0x00,0x00, +0x01,0x01,0x31,0x39,0x19,0x01,0x02,0xFC, +0x00,0x08,0x50,0x20,0x04,0x45,0x02,0x00, /* 0x80 */ +0x04,0x50,0x20,0x01,0x01,0x4A,0x04,0x00, +0x3C,0x5E,0xBF,0xBF,0xFF,0xFF,0x7E,0x3C, +0x04,0x21,0x44,0x00,0x09,0x40,0x02,0x91, +0xFF,0x80,0xA2,0x80,0x88,0x81,0x90,0x84, +0x80,0x88,0x80,0xA1,0x84,0xA8,0x80,0xFF, +0xFF,0x01,0x21,0x05,0x81,0x11,0x49,0x21, +0x01,0x25,0x41,0x11,0x01,0x45,0x01,0xFF, +0x80,0xA0,0x82,0x80,0x90,0x89,0xA0,0x80, +0xFF,0x00,0x08,0x81,0x20,0x02,0x48,0x00, +0x01,0x11,0x85,0x09,0x01,0x21,0x05,0x41, +0x00,0x0A,0x40,0x08,0x12,0x00,0x00,0xFF, +0x88,0x44,0x22,0x11,0x88,0x44,0x22,0x11, +0xFE,0x7C,0x38,0x10,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x10,0x38,0x7C,0xFE, +0x80,0xC0,0xE0,0xF0,0xE0,0xC0,0x80,0x00, +0x01,0x03,0x07,0x0F,0x07,0x03,0x01,0x00, +0xFF,0x7E,0x3C,0x18,0x18,0x3C,0x7E,0xFF, +0x81,0xC3,0xE7,0xFF,0xFF,0xE7,0xC3,0x81, +0xF0,0xF0,0xF0,0xF0,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x0F,0x0F,0x0F,0x0F, +0x0F,0x0F,0x0F,0x0F,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xF0, +0x33,0x33,0xCC,0xCC,0x33,0x33,0xCC,0xCC, +0x00,0x20,0x20,0x50,0x50,0x88,0xF8,0x00, +0x20,0x20,0x70,0x20,0x70,0x20,0x20,0x00, +0x00,0x00,0x00,0x50,0x88,0xA8,0x50,0x00, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF, +0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0,0xF0, +0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, +0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00, +0x00,0x00,0x68,0x90,0x90,0x90,0x68,0x00, +0x30,0x48,0x48,0x70,0x48,0x48,0x70,0xC0, +0xF8,0x88,0x80,0x80,0x80,0x80,0x80,0x00, +0xF8,0x50,0x50,0x50,0x50,0x50,0x98,0x00, +0xF8,0x88,0x40,0x20,0x40,0x88,0xF8,0x00, +0x00,0x00,0x78,0x90,0x90,0x90,0x60,0x00, +0x00,0x50,0x50,0x50,0x50,0x68,0x80,0x80, +0x00,0x50,0xA0,0x20,0x20,0x20,0x20,0x00, +0xF8,0x20,0x70,0xA8,0xA8,0x70,0x20,0xF8, +0x20,0x50,0x88,0xF8,0x88,0x50,0x20,0x00, +0x70,0x88,0x88,0x88,0x50,0x50,0xD8,0x00, +0x30,0x40,0x40,0x20,0x50,0x50,0x50,0x20, +0x00,0x00,0x00,0x50,0xA8,0xA8,0x50,0x00, +0x08,0x70,0xA8,0xA8,0xA8,0x70,0x80,0x00, +0x38,0x40,0x80,0xF8,0x80,0x40,0x38,0x00, +0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x00, +0x00,0xF8,0x00,0xF8,0x00,0xF8,0x00,0x00, +0x20,0x20,0xF8,0x20,0x20,0x00,0xF8,0x00, +0xC0,0x30,0x08,0x30,0xC0,0x00,0xF8,0x00, +0x18,0x60,0x80,0x60,0x18,0x00,0xF8,0x00, +0x10,0x28,0x20,0x20,0x20,0x20,0x20,0x20, +0x20,0x20,0x20,0x20,0x20,0x20,0xA0,0x40, +0x00,0x20,0x00,0xF8,0x00,0x20,0x00,0x00, +0x00,0x50,0xA0,0x00,0x50,0xA0,0x00,0x00, +0x00,0x18,0x24,0x24,0x18,0x00,0x00,0x00, +0x00,0x30,0x78,0x78,0x30,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00, +0x3E,0x20,0x20,0x20,0xA0,0x60,0x20,0x00, +0xA0,0x50,0x50,0x50,0x00,0x00,0x00,0x00, +0x40,0xA0,0x20,0x40,0xE0,0x00,0x00,0x00, +0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x00, +0x88,0x70,0x88,0x88,0x70,0x88,0x00,0x00, +0x07,0x1C,0x30,0x67,0x48,0xD4,0x91,0x93, +0x93,0x91,0xD4,0x48,0x67,0x30,0x1C,0x07, +0xE0,0x38,0x0C,0xE6,0x12,0x2B,0x89,0xC9, +0xC9,0x89,0x2B,0x12,0xE6,0x0C,0x38,0xE0, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x41,0xC1,0x19,0x31,0x26,0x08,0x0C,0xF3, +0xF1,0x09,0x0A,0x26,0x31,0x19,0xC1,0x41, +0x82,0x83,0x98,0x8C,0x64,0x50,0x90,0x8F, +0xCF,0x30,0x10,0x64,0x8C,0x98,0x83,0x82, +0x00,0x24,0x5A,0x3C,0x3C,0x5A,0x24,0x00, +0x00,0x00,0x88,0xD8,0xA8,0x88,0x88,0x00, +0x00,0x00,0x88,0x88,0xF8,0x88,0x88,0x00, +0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00, +0x02,0x48,0x00,0x82,0x10,0x00,0x44,0x10, +0x00,0x41,0x04,0x11,0x40,0x04,0x41,0x08, +0xC3,0x99,0x2C,0x5E,0x7E,0x3C,0x99,0xC3, +0xC3,0x99,0x2C,0x62,0x46,0x34,0x99,0xC3, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x40,0x40,0x70,0x48,0x70,0x00, +0x00,0x00,0x88,0x88,0xC8,0xA8,0xC8,0x00, +0x00,0x00,0xF0,0x08,0x70,0x08,0xF0,0x00, +0x00,0x00,0xA8,0xA8,0xA8,0xA8,0xF8,0x00, +0x00,0x00,0x70,0x88,0x38,0x88,0x70,0x00, +0x00,0x00,0xA8,0xA8,0xA8,0xF8,0x08,0x00, +0x00,0x00,0x48,0x48,0x78,0x08,0x08,0x00, +0x00,0x00,0xC0,0x40,0x70,0x48,0x70,0x00, +0x90,0xA8,0xA8,0xE8,0xA8,0xA8,0x90,0x00, +0x20,0x50,0x88,0x88,0xF8,0x88,0x88,0x00, +0xF8,0x88,0x80,0xF0,0x88,0x88,0xF0,0x00, +0x90,0x90,0x90,0x90,0x90,0xF8,0x08,0x00, +0x38,0x28,0x28,0x48,0x48,0xF8,0x88,0x00, +0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00, +0x20,0x70,0xA8,0xA8,0xA8,0x70,0x20,0x00, +0xF8,0x88,0x88,0x80,0x80,0x80,0x80,0x00, +0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00, +0x88,0x88,0x98,0xA8,0xC8,0x88,0x88,0x00, +0x50,0x20,0x88,0x98,0xA8,0xC8,0x88,0x00, +0x88,0x90,0xA0,0xC0,0xA0,0x90,0x88,0x00, +0x18,0x28,0x48,0x48,0x48,0x48,0x88,0x00, +0x88,0xD8,0xA8,0xA8,0x88,0x88,0x88,0x00, +0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00, +0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00, +0xF8,0x88,0x88,0x88,0x88,0x88,0x88,0x00, +0x78,0x88,0x88,0x78,0x28,0x48,0x88,0x00, +0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00, +0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00, +0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x00, +0x88,0x88,0x88,0x50,0x20,0x40,0x80,0x00, +0xA8,0xA8,0x70,0x20,0x70,0xA8,0xA8,0x00, +0xF0,0x48,0x48,0x70,0x48,0x48,0xF0,0x00, +0x80,0x80,0x80,0xF0,0x88,0x88,0xF0,0x00, +0x88,0x88,0x88,0xC8,0xA8,0xA8,0xC8,0x00, +0xF0,0x08,0x08,0x30,0x08,0x08,0xF0,0x00, +0xA8,0xA8,0xA8,0xA8,0xA8,0xA8,0xF8,0x00, +0x70,0x88,0x08,0x78,0x08,0x88,0x70,0x00, +0xA8,0xA8,0xA8,0xA8,0xA8,0xF8,0x08,0x00, +0x88,0x88,0x88,0x88,0x78,0x08,0x08,0x00, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +static unsigned char ShadowFont[8*128]; +static const unsigned char *CurFont = NormalFont; +static const unsigned char *CurShadow = 0; +static char Result[256]; + +static const char *nth(const char *S,int N) +{ + while(N) { if(!*S++) { N--;if(!*S) return(0); } } + return(S); +} + +#if !defined(_MSC_VER) && !defined(__WATCOMC__) +static int stricmp(const char *S1,const char *S2) +{ + while(*S1&&(toupper(*S1)==toupper(*S2))) { ++S1;++S2; } + return(toupper(*S1)-toupper(*S2)); +} +#endif /* !_MSC_VER & !__WATCOMC__ */ + +const unsigned char *CONGetFont() +{ + return(CurFont); +} + +void CONSetFont(const unsigned char *Font) +{ + CurFont = Font==FNT_NORMAL? NormalFont + : Font==FNT_BOLD? BoldFont + : Font; +} + +#endif /* DEFINE_ONCE */ + +void CONSetColor(pixel FGColor,pixel BGColor) +{ + FG=FGColor; + BG=BGColor; +} + +void CONClear(pixel BGColor) +{ + pixel *P; + int X,Y; + + if(!VideoImg) return; + + P=(pixel *)VideoImg->Data+VideoY*VideoImg->L+VideoX; + for(Y=VideoH;Y;--Y,P+=VideoImg->L) + for(X=0;X=VideoW? VideoW-1:X; + Y = Y<0? 0:Y>=VideoH? VideoH-1:Y; + Width = Width>VideoW-X? VideoW-X:Width; + Height = Height>VideoH-Y? VideoH-Y:Height; + IMGFillRect(VideoImg,VideoX+X,VideoY+Y,Width,Height,BGColor); +} + +void CONFrame(int X,int Y,int Width,int Height,pixel FGColor) +{ + if(!VideoImg) return; + + X = X<0? 0:X>=VideoW? VideoW-1:X; + Y = Y<0? 0:Y>=VideoH? VideoH-1:Y; + Width = Width>VideoW-X? VideoW-X:Width; + Height = Height>VideoH-Y? VideoH-Y:Height; + IMGDrawRect(VideoImg,VideoX+X,VideoY+Y,Width,Height,FGColor); +} + +void PrintXY(Image *Img,const char *S,int X,int Y,pixel FG,int BG) +{ + const unsigned char *C; + pixel *P; + int I,J,K,N; + + X = X<0? 0:X>Img->W-8? Img->W-8:X; + Y = Y<0? 0:Y>Img->H-8? Img->H-8:Y; + + for(K=X;*S;S++) + switch(*S) + { + case '\n': + K=X;Y+=8; + if(Y>Img->H-8) Y=0; + break; + default: + P=(pixel *)Img->Data+Img->L*Y+K; + if(BG<0) + { + for(C=CurFont+(*S<<3),J=8;J;P+=Img->L,++C,--J) + for(I=0,N=(int)*C<<24;N&&(I<8);++I,N<<=1) + if(N&0x80000000) P[I]=FG; + } + else + { + for(C=CurFont+(*S<<3),J=8;J;P+=Img->L,++C,--J) + for(I=0,N=*C;I<8;++I) + P[I]=N&(0x80>>I)? FG:BG; + } + K+=8; + if(X>Img->W-8) + { + K=0;Y+=8; + if(Y>Img->H-8) Y=0; + } + break; + } +} + +void ShadowPrintXY(Image *Img,const char *S,int X,int Y,pixel FG,int BG) +{ + /* When BG is transparent, do not show shadow at all */ + if(BG<0) { PrintXY(Img,S,X,Y,FG,BG);return; } + + /* Update shadow font as required */ + if(CurShadow!=CurFont) + { + const unsigned char *SRC; + unsigned char *DST; + int J; + + /* Make vertical shadow */ + for(J=sizeof(ShadowFont)>>3,SRC=CurFont,DST=ShadowFont;J;--J,SRC+=8,DST+=8) + { + DST[0] = SRC[0]|SRC[1]; + DST[1] = SRC[1]|SRC[2]|SRC[0]; + DST[2] = SRC[2]|SRC[3]|SRC[1]; + DST[3] = SRC[3]|SRC[4]|SRC[2]; + DST[4] = SRC[4]|SRC[5]|SRC[3]; + DST[5] = SRC[5]|SRC[6]|SRC[4]; + DST[6] = SRC[6]|SRC[7]|SRC[5]; + DST[7] = SRC[7]|SRC[6]; + } + + /* Make horizontal shadow */ + for(J=0;J>1); + + /* Shadow font updated */ + CurShadow = CurFont; + } + + /* Print shadows, then actual letters */ + CONSetFont(ShadowFont); + PrintXY(Img,S,X,Y,BG,-1); + CONSetFont(CurShadow); + PrintXY(Img,S,X,Y,FG,-1); +} + +void CONChar(int X,int Y,char V) +{ + const unsigned char *C; + pixel *P; + int I,J,K; + + if(!VideoImg) return; + + X<<= 3; + Y<<= 3; + X = X<0? 0:X>=VideoW-8? VideoW-8:X; + Y = Y<0? 0:Y>=VideoH-8? VideoH-8:Y; + P = (pixel *)VideoImg->Data+VideoImg->L*(VideoY+Y)+VideoX+X; + for(C=CurFont+(V<<3),J=8;J;P+=VideoImg->L,++C,--J) + for(I=0,K=*C;I<8;++I) P[I]=K&(0x80>>I)? FG:BG; +} + +void CONPrintN(int X,int Y,const char *S,int N) +{ + int J,X1; + + /* Truncate N to accommodate the screen size */ + J = (VideoW>>3)-X; + N = N<=J? N:J; + + for(;*S&&(Y<(VideoH>>3));++Y,S+=J) + { + /* Print N-1 characters */ + for(X1=X,J=0;!EOLN(S[J])&&(J=VideoW-8? VideoW-8:X; + Y = Y<0? 0:Y>=VideoH-8? VideoH-8:Y; + + for(K=X;*S;S++) + switch(*S) + { + case '\n': + K=X;Y+=8; + if(Y>=VideoH) Y=0; + break; + default: + P=(pixel *)VideoImg->Data+VideoImg->L*(VideoY+Y)+VideoX+K; + for(C=CurFont+(*S<<3),J=8;J;P+=VideoImg->L,++C,--J) + for(I=0;I<8;++I) P[I]=*C&(0x80>>I)? FG:BG; + K+=8; + if(X>=VideoW) + { + K=0;Y+=8; + if(Y>=VideoH) Y=0; + } + break; + } +} + +void CONWindow(int X,int Y,int W,int H,pixel FGColor,pixel BGColor,const char *Title) +{ + int J; + + if(!VideoImg) return; + + /* Draw menu box */ + CONBox(X<<3,Y<<3,W<<3,8,FGColor); + CONBox(X<<3,(Y+1)<<3,W<<3,(H-1)<<3,BGColor); + + /* Draw title */ + CONSetColor(BGColor,FGColor); + J=strlen(Title); + if(J>W-2) J=W-2; + CONPrintN(X+((W-J)>>1),Y,Title,J); + + /* Draw frame */ + CONFrame(X<<3,(Y+1)<<3,W<<3,(H-1)<<3,FGColor); + CONFrame((X<<3)-1,(Y<<3)-1,(W<<3)+2,(H<<3)+2,BGColor); + + /* Set "normal" colors for window body */ + CONSetColor(FGColor,BGColor); +} + +void CONMsg(int X,int Y,int W,int H,pixel FGColor,pixel BGColor,const char *Title,const char *Text) +{ + const char *P; + int I,J,Total,Wait; + + if(!VideoImg) return; + + /* Check if it is a no-wait message */ + if(Title[0]=='~') { ++Title;Wait=0; } else Wait=1; + + /* Compute message dimensions */ + for(P=Text,I=strlen(Title),Total=0;*P;Total++,P++) + { + for(J=0;*P;P++,J++); + if(J>I) I=J; + } + + /* Update message box coordinates and dimensions */ + J = VideoW>>3; + W = W>2? W:I+2; + W = W>J-2? J-2:W; + X = X<0? (J-W)/2:X+W>J? J-W:X; + J = VideoH>>3; + H = H>3? H:Total+3; + H = H>J-2? J-2:H; + Y = Y<0? (J-H)/2:Y+H>J? J-H:Y; + + /* Print text in portions, until it ends */ + for(P=Text;*P&&VideoImg;) + { + /* Draw window */ + CONWindow(X,Y,W,H,FGColor,BGColor,Title); + + /* Draw button icon if waiting for a button */ + if(Wait) CONChar(X+W-1,Y+H-1,CON_BUTTON); + + /* Draw frame */ + CONFrame(X<<3,(Y+1)<<3,W<<3,(H-1)<<3,FGColor); + CONFrame((X<<3)-1,(Y<<3)-1,(W<<3)+2,(H<<3)+2,BGColor); + + /* Draw text */ + for(J=0;*P&&(J>3; + W = (I>Length? I:Length)+2; + W = W>J-2? J-2:W; + X = X<0? (J-W)/2:X+W>J? J-W:X; + J = VideoH>>3; + H = 4; + H = H>J-2? J-2:H; + Y = Y<0? (J-H)/2:Y+H>J? J-H:Y; + + /* Correct length */ + if(Length>W-2) Length=W-2; + + /* Draw window */ + CONWindow(X,Y,W,H,FGColor,BGColor,Title); + + for(J=I=0;(J>=0)&&(I!=CON_OK)&&(I!=CON_EXIT);) + { + CONChar(X+J+1,Y+H-2,'_'); + ShowVideo(); + I=WaitKey(); + if(!I||!VideoImg) return(0); + if((I>=0x20)&&(I<0x80)&&(JI) I=J; + } + + /* Update menu coordinates and dimensions */ + J = VideoW>>3; + W = W>3? W:I+3; + W = W>J-2? J-2:W; + X = X<0? (J-W)/2:X+W>J? J-W:X; + + J = VideoH>>3; + H = H>3? H:Total+3; + H = H>J-2? J-2:H; + Y = Y<0? (J-H)/2:Y+H>J? J-H:Y; + + /* Prepare for selection loop */ + Item = FileSelect? 0:Item<1? 0:Item>Total? Total-1:Item-1; + Top = Item-Item%(H-3); + Item = Item%(H-3); + Draw = 1; + + /* Selection loop */ + do + { + if(Draw) + { + /* Draw window */ + CONWindow(X,Y,W,H,FGColor,BGColor,Items); + + /* Draw arrows */ + if(Top) CONChar(X+1,Y+1,CON_LESS); + if(Top+H-3>16; + /* If a menu item has been clicked... */ + if((I>=((X+1)<<3)) && (I<=((X+W-1)<<3)) && (J>=((Y+1)<<3)) && (J<=((Y+H)<<3))) + { + /* Position inside menu in characters */ + J = (J>>3)-Y; + if(J<2) { Item=0;J=CON_UP; } + else if(J>=H-1) { Item=H-4;J=CON_DOWN; } + else { Item=J-2;J=CON_OK; } + } + else + { + /* Outside click cancels the menu */ + J = CON_EXIT; + } + } + + /* Drop out if console closed */ + if(!J||!VideoImg) return(FileSelect? 0:Result); + + /* SPACE, ENTER, TAB treated as "OK" */ + if((J==' ')||(J==0x0A)||(J==0x0D)||(J==0x09)) J=CON_OK; + + /* ESCAPE, BS treated as "EXIT" */ + if((J==0x1B)||(J==0x08)) J=CON_EXIT; + + /* Erase arrow */ + CONChar(X+1,Y+2+Item,' '); + + /* When selecting a filename and a letter has been typed... */ + if(FileSelect&&(J>0x20)&&(J<0x80)) + { + /* Draw input box */ + CONWindow(X,Y+H-4,W,4,FGColor,BGColor,"Enter Filename:"); + + /* Input text */ + for(I=0;(I>=0)&&(J!=CON_OK)&&(J!=CON_EXIT);) + { + if((J>=0x20)&&(J<0x80)&&(I0) Item--; + else + { + Draw=1; + if(Top>0) Top--; + else + { + Top=Total>(H-3)? Total-H+3:0; + Item=Total-Top>0? Total-Top-1:0; + } + } + } + + if(J==CON_DOWN) + { + if((Itemd_name)+2); + + /* Reallocate buffer if needed */ + if((I>BufSize)&&(T=malloc(I))) + { + if(Buf) free(Buf); + BufSize = I; + Buf = T; + } + + /* If failed to allocate buffer, drop out */ + if(!Buf) { closedir(D);return(0); } + + /* Create title from the current pathname */ + if(!getcwd(Buf,BufSize-2)) strcpy(Buf,"Choose File"); + J=strlen(Buf)+1; + + /* Scan subdirectories */ + for(rewinddir(D);(DP=readdir(D));) + if(!stat(DP->d_name,&ST)&&S_ISDIR(ST.st_mode)) + { + I=strlen(DP->d_name)+1; + if(J+I+1d_name); + J+=I; + } + } + + /* Scan files */ + for(rewinddir(D);(DP=readdir(D));) + if(!stat(DP->d_name,&ST)&&!S_ISDIR(ST.st_mode)) + { + I=strlen(DP->d_name)+1; + if(J+I+1strlen(P))&&!stricmp(DP->d_name+I-1-strlen(P),P)) + { + Buf[J++]=CON_FILE; + strcpy(Buf+J,DP->d_name); + J+=I; + break; + } + } + + /* Terminate directory listing */ + Buf[J]='\0'; + + /* Done with directory */ + closedir(D); + + /* Show menu */ + P=CONSelector(-1,-1,(VideoW>>3)-2,(VideoH>>3)-2,FGColor,BGColor,Buf,TAG_SELEFILE); + + /* Handle menu selection */ + if(P) + switch(*P) + { + case CON_FOLDER: + /* Check that the folder is accessible */ + if(!(D=opendir(P+1))) { /* Something went wrong */ } + else + { + /* Folder accessible, close it for now */ + closedir(D); + /* Change to selected folder */ + if(chdir(P+1)) { /* Something went wrong */ } + } + break; + case CON_FILE: + /* File selected, return it */ + strncpy(Result,P+1,sizeof(Result)); + Result[sizeof(Result)-1]='\0'; + free(Buf); + return(Result); + default: + /* Filename entered into Result[] */ + free(Buf); + return(Result); + } + } + while(P); + + free(Buf); + return(0); +} + +#endif /* BPP32||BPP24||BPP16||BPP8 */ diff --git a/components/msx/fmsx/src/EMULib/Console.h b/components/msx/fmsx/src/EMULib/Console.h new file mode 100644 index 0000000..afc2bad --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Console.h @@ -0,0 +1,137 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Console.h **/ +/** **/ +/** This file contains platform-independent definitions and **/ +/** declarations for the EMULib-based console. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2005-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef CONSOLE_H +#define CONSOLE_H + +#include "EMULib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Special Characters ***************************************/ +/** These are special characters implemented in the default **/ +/** font and used in various standard dialogs. **/ +/*************************************************************/ +#define CON_ARROW 0x01 +#define CON_CHECK 0x02 +#define CON_FOLDER 0x03 +#define CON_FILE 0x04 +#define CON_LESS 0x05 +#define CON_MORE 0x06 +#define CON_BUTTON 0x07 +#define CON_BS 0x08 +#define CON_TAB 0x09 +#define CON_DOTS 0x0B +#define CON_ENTER 0x0D +#define CON_INSERT 0x0E +#define CON_DELETE 0x0F +#define CON_FUNC 0x10 /* 10 keys */ +#define CON_STOP 0x1A +#define CON_ESCAPE 0x1B + +/** CONSetFont Modes *****************************************/ +/** Special font designators passed to CONSetFont(). **/ +/*************************************************************/ +#define FNT_NORMAL (const unsigned char *)0 +#define FNT_BOLD (const unsigned char *)1 + +/** CONInput() Modes *****************************************/ +/** These are passed to CONInput(). **/ +/*************************************************************/ +#define CON_TEXT 0x00000000 +#define CON_DEC 0x80000000 +#define CON_HEX 0x40000000 +#define CON_HIDE 0x20000000 + +/** CONSetColor()/CONSetFont() *******************************/ +/** Set current foreground and background colors, and font. **/ +/*************************************************************/ +void CONSetColor(pixel FGColor,pixel BGColor); +void CONSetFont(const unsigned char *Font); +const unsigned char *CONGetFont(); + +/** CONClear() ***********************************************/ +/** Clear screen with a given color. **/ +/*************************************************************/ +void CONClear(pixel BGColor); + +/** CONBox() *************************************************/ +/** Draw a filled box with a given color. **/ +/*************************************************************/ +void CONBox(int X,int Y,int Width,int Height,pixel BGColor); + +/** CONFrame() ***********************************************/ +/** Draw a frame with a given color. **/ +/*************************************************************/ +void CONFrame(int X,int Y,int Width,int Height,pixel BGColor); + +/** CONChar() ************************************************/ +/** Print a character at given coordinates. **/ +/*************************************************************/ +void CONChar(int X,int Y,char V); + +/** PrintXY() ************************************************/ +/** Print text at given pixel coordinates in given colors. **/ +/** When BG=-1, use transparent background. **/ +/*************************************************************/ +void PrintXY(Image *Img,const char *S,int X,int Y,pixel FG,int BG); + +/** ShadowPrintXY() ******************************************/ +/** Print contrast text at given pixel coordinates in given **/ +/** text and contrast shadow colors. When BG=-1, do not **/ +/** show shadow. **/ +/*************************************************************/ +void ShadowPrintXY(Image *Img,const char *S,int X,int Y,pixel FG,int BG); + +/** CONPrint() ***********************************************/ +/** Print a text at given coordinates with current colors. **/ +/*************************************************************/ +void CONPrint(int X,int Y,const char *S); + +/** CONPrintN() **********************************************/ +/** Print a text at given coordinates with current colors. **/ +/** Truncate with "..." if text length exceeds N. **/ +/*************************************************************/ +void CONPrintN(int X,int Y,const char *S,int N); + +/** CONMsg() *************************************************/ +/** Show a message box. **/ +/*************************************************************/ +void CONMsg(int X,int Y,int W,int H,pixel FGColor,pixel BGColor,const char *Title,const char *Text); + +/** CONInput() ***********************************************/ +/** Show an input box. Input modes (text/hex/dec) are ORed **/ +/** to the Length argument. **/ +/*************************************************************/ +char *CONInput(int X,int Y,pixel FGColor,pixel BGColor,const char *Title,char *Input,unsigned int Length); + +/** CONWindow() **********************************************/ +/** Show a titled window. **/ +/*************************************************************/ +void CONWindow(int X,int Y,int W,int H,pixel FGColor,pixel BGColor,const char *Title); + +/** CONMenu() ************************************************/ +/** Show a menu. **/ +/*************************************************************/ +int CONMenu(int X,int Y,int W,int H,pixel FGColor,pixel BGColor,const char *Items,int Item); + +/** CONFile() ************************************************/ +/** Show a file selector. **/ +/*************************************************************/ +const char *CONFile(pixel FGColor,pixel BGColor,const char *Ext); + +#ifdef __cplusplus +} +#endif +#endif /* CONSOLE_H */ diff --git a/components/msx/fmsx/src/EMULib/ConsoleMux.h b/components/msx/fmsx/src/EMULib/ConsoleMux.h new file mode 100644 index 0000000..4c7ce6b --- /dev/null +++ b/components/msx/fmsx/src/EMULib/ConsoleMux.h @@ -0,0 +1,381 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** ConsoleMux.h **/ +/** **/ +/** This file wraps Console.c routines for multiple display **/ +/** depths (BPP8,BPP16,BPP32). It is used automatically **/ +/** when none of BPP* values are defined. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2008-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef CONSOLEMUX_H +#define CONSOLEMUX_H + +#include "EMULib.h" +#include "Console.h" + +#undef BPP8 +#undef BPP16 +#undef BPP24 +#undef BPP32 +#undef PIXEL + +#define BPP8 +#define pixel unsigned char +#define PIXEL(R,G,B) (pixel)(((7*(R)/255)<<5)|((7*(G)/255)<<2)|(3*(B)/255)) +#define FG FG_8 +#define BG BG_8 +#define CONSetColor CONSetColor_8 +#define CONClear CONClear_8 +#define CONBox CONBox_8 +#define CONFrame CONFrame_8 +#define CONChar CONChar_8 +#define PrintXY PrintXY_8 +#define ShadowPrintXY ShadowPrintXY_8 +#define CONPrint CONPrint_8 +#define CONPrintN CONPrintN_8 +#define CONMsg CONMsg_8 +#define CONInput CONInput_8 +#define CONWindow CONWindow_8 +#define CONSelector CONSelector_8 +#define CONMenu CONMenu_8 +#define CONFile CONFile_8 +#include "Console.c" +#undef pixel +#undef PIXEL +#undef FG +#undef BG +#undef CONSetColor +#undef CONClear +#undef CONBox +#undef CONFrame +#undef CONChar +#undef PrintXY +#undef ShadowPrintXY +#undef CONPrint +#undef CONPrintN +#undef CONMsg +#undef CONInput +#undef CONWindow +#undef CONSelector +#undef CONMenu +#undef CONFile +#undef BPP8 + +#define BPP16 +#define pixel unsigned short +#if defined(UNIX) || defined(ANDROID) || defined(S60) || defined(UIQ) || defined(NXC2600) || defined(STMP3700) +/* Symbian and Unix use true 16BPP color */ +#define PIXEL(R,G,B) (pixel)(((31*(R)/255)<<11)|((63*(G)/255)<<5)|(31*(B)/255)) +#else +/* Other platforms use 15BPP color */ +#define PIXEL(R,G,B) (pixel)(((31*(R)/255)<<10)|((31*(G)/255)<<5)|(31*(B)/255)) +#endif +#define FG FG_16 +#define BG BG_16 +#define CONSetColor CONSetColor_16 +#define CONClear CONClear_16 +#define CONBox CONBox_16 +#define CONFrame CONFrame_16 +#define CONChar CONChar_16 +#define PrintXY PrintXY_16 +#define ShadowPrintXY ShadowPrintXY_16 +#define CONPrint CONPrint_16 +#define CONPrintN CONPrintN_16 +#define CONMsg CONMsg_16 +#define CONInput CONInput_16 +#define CONWindow CONWindow_16 +#define CONSelector CONSelector_16 +#define CONMenu CONMenu_16 +#define CONFile CONFile_16 +#include "Console.c" +#undef pixel +#undef PIXEL +#undef FG +#undef BG +#undef CONSetColor +#undef CONClear +#undef CONBox +#undef CONFrame +#undef CONChar +#undef PrintXY +#undef ShadowPrintXY +#undef CONPrint +#undef CONPrintN +#undef CONMsg +#undef CONInput +#undef CONWindow +#undef CONSelector +#undef CONMenu +#undef CONFile +#undef BPP16 + +#define BPP32 +#define pixel unsigned int +#if defined(ANDROID) +#define PIXEL(R,G,B) (pixel)(((int)R<<16)|((int)G<<8)|B|0xFF000000) +#else +#define PIXEL(R,G,B) (pixel)(((int)R<<16)|((int)G<<8)|B) +#endif +#define FG FG_32 +#define BG BG_32 +#define CONSetColor CONSetColor_32 +#define CONClear CONClear_32 +#define CONBox CONBox_32 +#define CONFrame CONFrame_32 +#define CONChar CONChar_32 +#define PrintXY PrintXY_32 +#define ShadowPrintXY ShadowPrintXY_32 +#define CONPrint CONPrint_32 +#define CONPrintN CONPrintN_32 +#define CONMsg CONMsg_32 +#define CONInput CONInput_32 +#define CONWindow CONWindow_32 +#define CONSelector CONSelector_32 +#define CONMenu CONMenu_32 +#define CONFile CONFile_32 +#include "Console.c" +#undef pixel +#undef PIXEL +#undef FG +#undef BG +#undef CONSetColor +#undef CONClear +#undef CONBox +#undef CONFrame +#undef CONChar +#undef PrintXY +#undef ShadowPrintXY +#undef CONPrint +#undef CONPrintN +#undef CONMsg +#undef CONInput +#undef CONWindow +#undef CONSelector +#undef CONMenu +#undef CONFile +#undef BPP32 + +/** CONSetColor() ********************************************/ +/** Set current foreground and background colors. **/ +/*************************************************************/ +void CONSetColor(pixel FGColor,pixel BGColor) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONSetColor_8(FGColor,BGColor);break; + case 16: CONSetColor_16(FGColor,BGColor);break; + case 24: + case 32: CONSetColor_32(FGColor,BGColor);break; + } +} + +/** CONClear() ***********************************************/ +/** Clear screen with a given color. **/ +/*************************************************************/ +void CONClear(pixel BGColor) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONClear_8(BGColor);break; + case 16: CONClear_16(BGColor);break; + case 24: + case 32: CONClear_32(BGColor);break; + } +} + +/** CONBox() *************************************************/ +/** Draw a filled box with a given color. **/ +/*************************************************************/ +void CONBox(int X,int Y,int Width,int Height,pixel BGColor) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONBox_8(X,Y,Width,Height,BGColor);break; + case 16: CONBox_16(X,Y,Width,Height,BGColor);break; + case 24: + case 32: CONBox_32(X,Y,Width,Height,BGColor);break; + } +} + +/** CONFrame() ***********************************************/ +/** Draw a frame with a given color. **/ +/*************************************************************/ +void CONFrame(int X,int Y,int Width,int Height,pixel BGColor) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONFrame_8(X,Y,Width,Height,BGColor);break; + case 16: CONFrame_16(X,Y,Width,Height,BGColor);break; + case 24: + case 32: CONFrame_32(X,Y,Width,Height,BGColor);break; + } +} + +/** CONChar() ************************************************/ +/** Print a character at given coordinates. **/ +/*************************************************************/ +void CONChar(int X,int Y,char V) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONChar_8(X,Y,V);break; + case 16: CONChar_16(X,Y,V);break; + case 24: + case 32: CONChar_32(X,Y,V);break; + } +} + +/** PrintXY() ************************************************/ +/** Print text at given pixel coordinates in given colors. **/ +/** When BG=-1, use transparent background. **/ +/*************************************************************/ +void PrintXY(Image *Img,const char *S,int X,int Y,pixel FG,int BG) +{ + switch(Img->D) + { + case 8: PrintXY_8(Img,S,X,Y,FG,BG);break; + case 16: PrintXY_16(Img,S,X,Y,FG,BG);break; + case 24: + case 32: PrintXY_32(Img,S,X,Y,FG,BG);break; + } +} + +/** ShadowPrintXY() ******************************************/ +/** Print contrast text at given pixel coordinates in given **/ +/** text and contrast shadow colors. When BG=-1, do not **/ +/** show shadow. **/ +/*************************************************************/ +void ShadowPrintXY(Image *Img,const char *S,int X,int Y,pixel FG,int BG) +{ + switch(Img->D) + { + case 8: ShadowPrintXY_8(Img,S,X,Y,FG,BG);break; + case 16: ShadowPrintXY_16(Img,S,X,Y,FG,BG);break; + case 24: + case 32: ShadowPrintXY_32(Img,S,X,Y,FG,BG);break; + } +} + +/** CONPrint() ***********************************************/ +/** Print a text at given coordinates with current colors. **/ +/*************************************************************/ +void CONPrint(int X,int Y,const char *S) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONPrint_8(X,Y,S);break; + case 16: CONPrint_16(X,Y,S);break; + case 24: + case 32: CONPrint_32(X,Y,S);break; + } +} + +/** CONPrintN() **********************************************/ +/** Print a text at given coordinates with current colors. **/ +/** Truncate with "..." if text length exceeds N. **/ +/*************************************************************/ +void CONPrintN(int X,int Y,const char *S,int N) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONPrintN_8(X,Y,S,N);break; + case 16: CONPrintN_16(X,Y,S,N);break; + case 24: + case 32: CONPrintN_32(X,Y,S,N);break; + } +} + +/** CONMsg() *************************************************/ +/** Show a message box. **/ +/*************************************************************/ +void CONMsg(int X,int Y,int W,int H,pixel FGColor,pixel BGColor,const char *Title,const char *Text) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONMsg_8(X,Y,W,H,FGColor,BGColor,Title,Text);break; + case 16: CONMsg_16(X,Y,W,H,FGColor,BGColor,Title,Text);break; + case 24: + case 32: CONMsg_32(X,Y,W,H,FGColor,BGColor,Title,Text);break; + } +} + +/** CONInput() ***********************************************/ +/** Show an input box. Input modes (text/hex/dec) are ORed **/ +/** to the Length argument. **/ +/*************************************************************/ +char *CONInput(int X,int Y,pixel FGColor,pixel BGColor,const char *Title,char *Input,unsigned int Length) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: return(CONInput_8(X,Y,FGColor,BGColor,Title,Input,Length)); + case 16: return(CONInput_16(X,Y,FGColor,BGColor,Title,Input,Length)); + case 24: + case 32: return(CONInput_32(X,Y,FGColor,BGColor,Title,Input,Length)); + } + + return(0); +} + +/** CONWindow() **********************************************/ +/** Show a titled window. **/ +/*************************************************************/ +void CONWindow(int X,int Y,int W,int H,pixel FGColor,pixel BGColor,const char *Title) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: CONWindow_8(X,Y,W,H,FGColor,BGColor,Title);break; + case 16: CONWindow_16(X,Y,W,H,FGColor,BGColor,Title);break; + case 24: + case 32: CONWindow_32(X,Y,W,H,FGColor,BGColor,Title);break; + } +} + +/** CONMenu() ************************************************/ +/** Show a menu. **/ +/*************************************************************/ +int CONMenu(int X,int Y,int W,int H,pixel FGColor,pixel BGColor,const char *Items,int Item) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: return(CONMenu_8(X,Y,W,H,FGColor,BGColor,Items,Item)); + case 16: return(CONMenu_16(X,Y,W,H,FGColor,BGColor,Items,Item)); + case 24: + case 32: return(CONMenu_32(X,Y,W,H,FGColor,BGColor,Items,Item)); + } + + return(0); +} + +/** CONFile() ************************************************/ +/** Show a file selector. **/ +/*************************************************************/ +const char *CONFile(pixel FGColor,pixel BGColor,const char *Ext) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: return(CONFile_8(FGColor,BGColor,Ext)); + case 16: return(CONFile_16(FGColor,BGColor,Ext)); + case 24: + case 32: return(CONFile_32(FGColor,BGColor,Ext)); + } + + return(0); +} + +#endif /* CONSOLEMUX_H */ diff --git a/components/msx/fmsx/src/EMULib/EMULib.c b/components/msx/fmsx/src/EMULib/EMULib.c new file mode 100644 index 0000000..db0dcf7 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/EMULib.c @@ -0,0 +1,283 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** EMULib.c **/ +/** **/ +/** This file contains platform-independent implementation **/ +/** part of the emulation library. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "EMULib.h" +#include "Console.h" +#include +#include +#include +#include + +#ifndef _MSC_VER +#include +#endif + +#if defined(WINDOWS) || defined(UNIX) || defined(MAEMO) || defined(MEEGO) || defined(ANDROID) +#define NewImage GenericNewImage +#endif + +#if defined(WINDOWS) || defined(UNIX) || defined(MAEMO) || defined(MEEGO) +#define FreeImage GenericFreeImage +#define CropImage GenericCropImage +extern Image BigScreen; +#endif + +#if defined(UNIX) || defined(MAEMO) || defined(MEEGO) || defined(ANDROID) +#define SetVideo GenericSetVideo +#endif + +/** Current Video Image **************************************/ +/** These parameters are set with SetVideo() and used by **/ +/** ShowVideo() to show a WxH fragment from of Img. **/ +/*************************************************************/ +Image *VideoImg = 0; /* Current ShowVideo() image */ +int VideoX; /* X for ShowVideo() */ +int VideoY; /* Y for ShowVideo() */ +int VideoW; /* Width for ShowVideo() */ +int VideoH; /* Height for ShowVideo() */ + +/** KeyHandler ***********************************************/ +/** This function receives key presses and releases. **/ +/*************************************************************/ +void (*KeyHandler)(unsigned int Key) = 0; + +/** NewImage() ***********************************************/ +/** Create a new image of the given size. Returns pointer **/ +/** to the image data on success, 0 on failure. **/ +/*************************************************************/ +pixel *NewImage(Image *Img,int Width,int Height) +{ + Img->Data = (pixel *)malloc(Width*Height*sizeof(pixel)); + Img->Cropped = 0; + + if(!Img->Data) Img->W=Img->H=Img->L=Img->D=0; + else + { + memset(Img->Data,0,Width*Height*sizeof(pixel)); + Img->D = sizeof(pixel)<<3; + Img->W = Width; + Img->H = Height; + Img->L = Width; + } + + return(Img->Data); +} + +/** FreeImage() **********************************************/ +/** Free previously allocated image. **/ +/*************************************************************/ +void FreeImage(Image *Img) +{ + /* If image is used for video, unselect it */ + if(VideoImg==Img) VideoImg=0; + /* Image gone */ + if(Img->Data&&!Img->Cropped) free(Img->Data); + Img->Data = 0; + Img->Cropped = 0; + Img->W = 0; + Img->H = 0; + Img->L = 0; + Img->D = 0; +} + +/** CropImage() **********************************************/ +/** Create a subimage Dst of the image Src. Returns Dst. **/ +/*************************************************************/ +Image *CropImage(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + Dst->Data = (void *)((char *)Src->Data+(Src->L*Y+X)*(Src->D>>3)); + Dst->Cropped = 1; + Dst->W = W; + Dst->H = H; + Dst->L = Src->L; + Dst->D = Src->D; + return(Dst); +} + +/** SetVideo() ***********************************************/ +/** Set part of the image as "active" for display. **/ +/*************************************************************/ +void SetVideo(Image *Img,int X,int Y,int W,int H) +{ + VideoImg = Img; + VideoX = X<0? 0:X>=Img->W? Img->W-1:X; + VideoY = Y<0? 0:Y>=Img->H? Img->H-1:Y; + VideoW = VideoX+W>Img->W? Img->W-VideoX:W; + VideoH = VideoY+H>Img->H? Img->H-VideoY:H; +} + +/** WaitJoystick() *******************************************/ +/** Wait until one or more of the given buttons have been **/ +/** pressed. Returns the bitmask of pressed buttons. Refer **/ +/** to BTN_* #defines for the button mappings. **/ +/*************************************************************/ +unsigned int WaitJoystick(unsigned int Mask) +{ + unsigned int I; + +#if defined(ANDROID) + /* Wait for all requested buttons to be released first */ + while((GetJoystick()&Mask)&&VideoImg) usleep(100000); + /* Wait for any of the buttons to become pressed */ + do { I=GetJoystick()&Mask;usleep(100000); } while(!I&&VideoImg); +#elif defined(UNIX) || defined(MAEMO) || defined(NXC2600) || defined(STMP3700) || defined(ANDROID) + /* Wait for all requested buttons to be released first */ + while((GetJoystick()&Mask)&&VideoImg) usleep(100000); + /* Wait for any of the buttons to become pressed */ + do { I=GetJoystick()&Mask;usleep(100000); } while(!I&&VideoImg); +#else + /* Wait for all requested buttons to be released first */ + while((GetJoystick()&Mask)&&VideoImg); + /* Wait for any of the buttons to become pressed */ + do I=GetJoystick()&Mask; while(!I&&VideoImg); +#endif + + /* Return pressed buttons */ + return(I); +} + +/** SetKeyHandler() ******************************************/ +/** Attach keyboard handler that will be called when a key **/ +/** is pressed or released. **/ +/*************************************************************/ +void SetKeyHandler(void (*Handler)(unsigned int Key)) +{ + KeyHandler=Handler; +} + +/** GetFilePath() ********************************************/ +/** Extracts pathname from filename and returns a pointer **/ +/** to the internal buffer containing just the path name **/ +/** ending with "\". **/ +/*************************************************************/ +const char *GetFilePath(const char *Name) +{ + static char Path[256]; + const char *P; + char *T; + + P=strrchr(Name,'\\'); + + /* If path not found or too long, assume current */ + if(!P||(P-Name>200)) { strcpy(Path,"");return(Path); } + + /* Copy and return the pathname */ + for(T=Path;Name200) { strcpy(Name,"");return(Name); } + + /* Make up the format string */ + for(T=S,P=Pattern;*P&&(*P!='.');) *T++=*P++; + *T='\0'; + strcat(S,"%04d"); + strcat(S,P); + + /* Scan through the filenames */ + for(J=0;J<10000;J++) + { + sprintf(Name,S,J); + if(stat(Name,&FInfo)) break; + } + + if(J==10000) strcpy(Name,""); + return(Name); +} + +/** ParseEffects() *******************************************/ +/** Parse command line visual effect options, removing them **/ +/** from Args[] and applying to the initial Effects value. **/ +/*************************************************************/ +unsigned int ParseEffects(char *Args[],unsigned int Effects) +{ + static const struct { const char *Name; unsigned int Bit,Mask; } Opts[] = + { + { "tv", EFF_TVLINES, EFF_RASTER_ALL }, + { "notv", -EFF_TVLINES, EFF_RASTER_ALL }, /* Backward compat only */ + { "lcd", EFF_LCDLINES,EFF_RASTER_ALL }, + { "nolcd", -EFF_LCDLINES,EFF_RASTER_ALL }, /* Backward compat only */ + { "raster", EFF_RASTER, EFF_RASTER_ALL }, + { "noraster",-EFF_RASTER, EFF_RASTER_ALL }, /* Backward compat only */ + { "cmy", EFF_CMYMASK, EFF_MASK_ALL }, + { "rgb", EFF_RGBMASK, EFF_MASK_ALL }, + { "mono", EFF_MONO, EFF_MASK_ALL }, + { "sepia", EFF_SEPIA, EFF_MASK_ALL }, + { "green", EFF_GREEN, EFF_MASK_ALL }, + { "amber", EFF_AMBER, EFF_MASK_ALL }, + { "soft", EFF_2XSAI, EFF_SOFTEN_ALL }, + { "2xsai", EFF_2XSAI, EFF_SOFTEN_ALL }, + { "epx", EFF_EPX, EFF_SOFTEN_ALL }, + { "eagle", EFF_EAGLE, EFF_SOFTEN_ALL }, + { "scale2x", EFF_SCALE2X, EFF_SOFTEN_ALL }, + { "hq4x", EFF_HQ4X, EFF_SOFTEN_ALL }, + { "nearest", EFF_NEAREST, EFF_SOFTEN_ALL }, /* Disable hw softening */ + { "linear", EFF_LINEAR, EFF_SOFTEN_ALL }, /* Force hw softening */ + { "vignette", EFF_VIGNETTE,EFF_VIGNETTE }, + { "4x3", EFF_4X3, EFF_4X3 }, +// { "sync", EFF_SYNC, 0 }, +// { "nosync", -EFF_SYNC, 0 }, + { "saver", EFF_SAVECPU, 0 }, + { "nosaver", -EFF_SAVECPU, 0 }, +// { "scale", EFF_SCALE, 0 }, +// { "vsync", EFF_VSYNC, 0 } +#if defined(UNIX) && defined(MITSHM) + { "shm", EFF_MITSHM, 0 }, + { "noshm", -EFF_MITSHM, 0 }, +#endif + { 0,0,0 } + }; + + char **S,**D; + unsigned int NewEffects,J; + + /* For all arguments... */ + for(S=D=Args,NewEffects=Effects ; *S ; ++S) + if(*S[0]!='-') *D++=*S; + else + { + /* Search for the option */ + for(J=0 ; Opts[J].Name && strcmp(*S+1,Opts[J].Name) ; ++J); + + /* If option not found, it is not ours */ + if(!Opts[J].Name) *D++=*S; + else + { + /* Clear the masked bits first */ + NewEffects &= ~Opts[J].Mask; + + /* Now set or reset required bit(s) */ + if(Opts[J].Bit>0) NewEffects |= Opts[J].Bit; + else NewEffects &= ~(-Opts[J].Bit); + } + } + + /* Terminate edited arguments list */ + *D = *S; + + /* Return modified Effects value */ + return(NewEffects); +} diff --git a/components/msx/fmsx/src/EMULib/EMULib.h b/components/msx/fmsx/src/EMULib/EMULib.h new file mode 100644 index 0000000..94190fb --- /dev/null +++ b/components/msx/fmsx/src/EMULib/EMULib.h @@ -0,0 +1,579 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** EMULib.h **/ +/** **/ +/** This file contains platform-independent definitions and **/ +/** declarations for the emulation library. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef EMULIB_H +#define EMULIB_H + +/** ARMScaleImage() Attributes *******************************/ +/** These values can be ORed and passed to ARMScaleImage(). **/ +/*************************************************************/ +#define ARMSI_VFILL 0x01 +#define ARMSI_HFILL 0x02 + +/** SetEffects() Attributes **********************************/ +/** These values can be ORed and passed to SetEffects(). **/ +/*************************************************************/ +#define EFF_NONE 0x0000 /* No special effects */ +#define EFF_SCALE 0x0001 /* Scale video to fill screen */ +#define EFF_SOFTEN 0x0002 /* Scale + soften video */ +#define EFF_TVLINES 0x0004 /* Apply TV scanlines effect */ +#define EFF_SAVECPU 0x0008 /* Suspend when inactive */ +#define EFF_SYNC 0x0010 /* Wait for sync timer */ +#define EFF_PENCUES 0x0020 /* Draw pen input cue lines */ +#define EFF_DIALCUES 0x0040 /* Draw dialpad (with PENCUES)*/ +#define EFF_VERBOSE 0x0080 /* Report problems via printf */ +#define EFF_STRETCH 0x0100 /* Stretch video, fill screen */ +#define EFF_SHOWFPS 0x0200 /* Show frames-per-sec count */ +#define EFF_LCDLINES 0x0400 /* Apply LCD scanlines effect */ +#define EFF_VKBD 0x0800 /* Draw virtual keyboard */ +#define EFF_SOFTEN2 0x1000 /* Softening algorithm select */ +#define EFF_FULLJOY 0x2000 /* Use full screen controls */ +#define EFF_TILTJOY 0x4000 /* Use accelerometer controls */ +#define EFF_PENJOY 0x8000 /* Use touchpad controls */ +#define EFF_VSYNC 0x10000 /* Wait for VBlanks */ +#define EFF_DIRECT 0x20000 /* Copy whole VideoImg */ +#define EFF_CMYMASK 0x40000 /* Apply vertical CMY mask */ +#define EFF_RGBMASK 0x80000 /* Apply vertical RGB mask */ +#define EFF_SOFTEN3 0x1000000 /* Softening algorithm select */ +#define EFF_MONO 0x2000000 /* Apply monochrome color */ +#define EFF_VIGNETTE 0x4000000 /* Apply CRT-like vignetting */ +#define EFF_4X3 0x8000000 /* Stretch video to 4x3 ratio */ +#if defined(ANDROID) +#define EFF_FIXFFWD 0x100000 /* Persistent FFWD button */ +#define EFF_GLES 0x200000 /* OpenGLES video rendering */ +#define EFF_EXTVKBD 0x400000 /* Java virtual keyboard */ +#define EFF_LOCKED 0x80000000 /* SetEffects() now disabled */ +#elif defined(MAEMO) +#define EFF_NOVOLUME 0x100000 /* No volume control on F7/F8 */ +#define EFF_FULLSCR 0x200000 /* Force full-screen mode */ +#define EFF_WINDOWED 0x400000 /* Force windowed mode */ +#define EFF_HAA 0x800000 /* Use HildonAnimationActor */ +#define EFF_ALWAYS1 0x80000000 /* 1: InitMaemo() success */ +#elif defined(S60) || defined(UIQ) +#define EFF_FILL 0x100000 /* Fill display vertically */ +#define EFF_LIGHT 0x200000 /* Keep backlight on */ +#define EFF_USEHAL 0x400000 /* Use direct image rendering without DSB */ +#elif defined(UNIX) +#define EFF_MITSHM 0x100000 /* Use MITSHM X11 extension */ +#define EFF_VARBPP 0x200000 /* X11 determines Image depth */ +#endif + +#define EFF_SOFTEN_ALL (EFF_SOFTEN|EFF_SOFTEN2|EFF_SOFTEN3) +#define EFF_2XSAI (EFF_SOFTEN) +#define EFF_EPX (EFF_SOFTEN2) +#define EFF_EAGLE (EFF_SOFTEN|EFF_SOFTEN2) +#define EFF_SCALE2X (EFF_SOFTEN3) +#define EFF_HQ4X (EFF_SOFTEN|EFF_SOFTEN3) +#define EFF_NEAREST (EFF_SOFTEN2|EFF_SOFTEN3) /* Disable hw interpolation */ +#define EFF_LINEAR (EFF_SOFTEN|EFF_SOFTEN2|EFF_SOFTEN3) /* Force hw interpolation */ + +#define EFF_RASTER_ALL (EFF_TVLINES|EFF_LCDLINES) +#define EFF_RASTER (EFF_TVLINES|EFF_LCDLINES) + +#define EFF_MASK_ALL (EFF_CMYMASK|EFF_RGBMASK|EFF_MONO) +#define EFF_GREEN (EFF_MONO|EFF_CMYMASK) +#define EFF_AMBER (EFF_MONO|EFF_RGBMASK) +#define EFF_SEPIA (EFF_MONO|EFF_CMYMASK|EFF_RGBMASK) + +/** Button Bits **********************************************/ +/** Bits returned by GetJoystick() and WaitJoystick(). **/ +/*************************************************************/ +#define BTN_LEFT 0x0001 +#define BTN_RIGHT 0x0002 +#define BTN_UP 0x0004 +#define BTN_DOWN 0x0008 +#define BTN_FIREA 0x0010 +#define BTN_FIREB 0x0020 +#define BTN_FIREL 0x0040 +#define BTN_FIRER 0x0080 +#define BTN_START 0x0100 +#define BTN_SELECT 0x0200 +#define BTN_EXIT 0x0400 +#define BTN_FIREX 0x0800 +#define BTN_FIREY 0x1000 +#define BTN_FFWD 0x2000 +#define BTN_MENU 0x4000 +#define BTN_ALL 0x7FFF +#define BTN_OK (BTN_FIREA|BTN_START) +#define BTN_FIRE (BTN_FIREA|BTN_FIREB|BTN_FIREL|BTN_FIRER|BTN_FIREX|BTN_FIREY) +#define BTN_ARROWS (BTN_LEFT|BTN_RIGHT|BTN_UP|BTN_DOWN) +#define BTN_SHIFT CON_SHIFT +#define BTN_CONTROL CON_CONTROL +#define BTN_ALT CON_ALT +#define BTN_MODES (BTN_SHIFT|BTN_CONTROL|BTN_ALT) + +/** Mouse Bits ***********************************************/ +/** Bits returned by GetMouse() and WaitKeyOrMouse(). **/ +/*************************************************************/ +#define MSE_RIGHT 0x80000000 +#define MSE_LEFT 0x40000000 +#define MSE_BUTTONS (MSE_RIGHT|MSE_LEFT) +#define MSE_YPOS 0x3FFF0000 +#define MSE_XPOS 0x0000FFFF + +/** Special Key Codes ****************************************/ +/** Modifiers returned by GetKey() and WaitKey(). **/ +/*************************************************************/ +#define CON_KEYCODE 0x03FFFFFF /* Key code */ +#define CON_MODES 0xFC000000 /* Mode bits, as follows: */ +#define CON_CLICK 0x04000000 /* Key click (LiteS60 only) */ +#define CON_CAPS 0x08000000 /* CapsLock held */ +#define CON_SHIFT 0x10000000 /* SHIFT held */ +#define CON_CONTROL 0x20000000 /* CONTROL held */ +#define CON_ALT 0x40000000 /* ALT held */ +#define CON_RELEASE 0x80000000 /* Key released (going up) */ + +#define CON_F1 0xEE +#define CON_F2 0xEF +#define CON_F3 0xF0 +#define CON_F4 0xF1 +#define CON_F5 0xF2 +#define CON_F6 0xF3 +#define CON_F7 0xF4 +#define CON_F8 0xF5 +#define CON_F9 0xF6 +#define CON_F10 0xF7 +#define CON_F11 0xF8 +#define CON_F12 0xF9 +#define CON_LEFT 0xFA +#define CON_RIGHT 0xFB +#define CON_UP 0xFC +#define CON_DOWN 0xFD +#define CON_OK 0xFE +#define CON_EXIT 0xFF + +#ifdef WINDOWS +#include "LibWin.h" +#endif +#ifdef MSDOS +#include "LibMSDOS.h" +#endif +#ifdef UNIX +#include "LibUnix.h" +#endif +#ifdef MAEMO +#define ARM_CPU +#include "LibMaemo.h" +#endif +#ifdef MEEGO +#define ARM_CPU +#include "LibMeego.h" +#endif +#ifdef NXC2600 +#include "LibNXC2600.h" +#endif +#ifdef STMP3700 +#define ARM_CPU +#include "LibSTMP3700.h" +#endif +#ifdef ANDROID +#include "LibAndroid.h" +#endif +#ifdef IOS +#define ARM_CPU +#include "LibApple.h" +#endif +#if defined(S60) || defined(UIQ) +#define ARM_CPU +#include "LibSym.h" +#include "LibSym.rh" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** pixel ****************************************************/ +/** Pixels may be either 8bit, or 16bit, or 32bit. When no **/ +/** BPP* specified, we assume the pixel to have the largest **/ +/** size and default to GetColor(). **/ +/*************************************************************/ +#ifndef PIXEL_TYPE_DEFINED +#define PIXEL_TYPE_DEFINED +#if defined(BPP32) || defined(BPP24) +typedef unsigned int pixel; +#elif defined(BPP16) +typedef unsigned short pixel; +#elif defined(BPP8) +typedef unsigned char pixel; +#else +typedef unsigned int pixel; +#define PIXEL(R,G,B) GetColor(R,G,B) +#endif +#endif + +/** sample ***************************************************/ +/** Audio samples may be either 8bit or 16bit. **/ +/*************************************************************/ +#ifndef SAMPLE_TYPE_DEFINED +#define SAMPLE_TYPE_DEFINED +#ifdef BPS16 +typedef signed short sample; +#else +typedef signed char sample; +#endif +#endif + +/** Image ****************************************************/ +/** This data type encapsulates a bitmap. **/ +/*************************************************************/ +typedef struct +{ + pixel *Data; /* Buffer containing WxH pixels */ + int W,H,L,D; /* Image size, pitch, depth */ + char Cropped; /* 1: Cropped, do not free() */ +#ifdef WINDOWS + HDC hDC; /* Handle to device context */ + HBITMAP hBMap; /* Handle to bitmap */ +#endif +#ifdef MAEMO + GdkImage *GImg; /* Pointer to GdkImage object */ +#endif +#ifdef MEEGO + QImage *QImg; /* Pointer to QImage object */ +#endif +#ifdef UNIX + XImage *XImg; /* Pointer to XImage structure */ + int Attrs; /* USE_SHM and other attributes */ +#ifdef MITSHM + XShmSegmentInfo SHMInfo; /* Shared memory information */ +#endif +#endif +} Image; + +/** Current Video Image **************************************/ +/** These parameters are set with SetVideo() and used by **/ +/** ShowVideo() to show a WxH fragment from of Img. **/ +/*************************************************************/ +extern Image *VideoImg; /* Current ShowVideo() image */ +extern int VideoX; /* X for ShowVideo() */ +extern int VideoY; /* Y for ShowVideo() */ +extern int VideoW; /* Width for ShowVideo() */ +extern int VideoH; /* Height for ShowVideo() */ + +/** KeyHandler ***********************************************/ +/** This function receives key presses and releases. **/ +/*************************************************************/ +extern void (*KeyHandler)(unsigned int Key); + +/** NewImage() ***********************************************/ +/** Create a new image of the given size. Returns pointer **/ +/** to the image data on success, 0 on failure. **/ +/*************************************************************/ +pixel *NewImage(Image *Img,int Width,int Height); + +/** FreeImage() **********************************************/ +/** Free previously allocated image. **/ +/*************************************************************/ +void FreeImage(Image *Img); + +/** CropImage() **********************************************/ +/** Create a subimage Dst of the image Src. Returns Dst. **/ +/*************************************************************/ +Image *CropImage(Image *Dst,const Image *Src,int X,int Y,int W,int H); + +#if defined(WINDOWS) || defined(UNIX) || defined(MAEMO) || defined(MEEGO) +Image *GenericCropImage(Image *Dst,const Image *Src,int X,int Y,int W,int H); +#endif + +/** ScaleImage() *********************************************/ +/** Copy Src into Dst, scaling as needed. **/ +/*************************************************************/ +void ScaleImage(Image *Dst,const Image *Src,int X,int Y,int W,int H); + +/** ARMScaleImage() ******************************************/ +/** Copy Src into Dst using ARM-optimized assembly code. **/ +/** Returns 0 if this function is not supported or there is **/ +/** an alignment problem. Returns destination height and **/ +/** width on success in format. **/ +/*************************************************************/ +unsigned int ARMScaleImage(Image *Dst,Image *Src,int X,int Y,int W,int H,int Attrs); + +/** InterpolateImage() ***************************************/ +/** Scale image Src into Dst using linear interpolation. **/ +/*************************************************************/ +void InterpolateImage(Image *Dst,const Image *Src,int X,int Y,int W,int H); + +/** TelevizeImage() ******************************************/ +/** Create televizion effect on the image. **/ +/*************************************************************/ +void TelevizeImage(Image *Img,int X,int Y,int W,int H); + +/** LcdizeImage() ********************************************/ +/** Create LCD effect on the image. **/ +/*************************************************************/ +void LcdizeImage(Image *Img,int X,int Y,int W,int H); + +/** RasterizeImage() *****************************************/ +/** Create raster effect on the image. **/ +/*************************************************************/ +void RasterizeImage(Image *Img,int X,int Y,int W,int H); + +/** CMYizeImage() ********************************************/ +/** Apply vertical CMY stripes to the image. **/ +/*************************************************************/ +void CMYizeImage(Image *Img,int X,int Y,int W,int H); + +/** RGBizeImage() ********************************************/ +/** Apply vertical RGB stripes to the image. **/ +/*************************************************************/ +void RGBizeImage(Image *Img,int X,int Y,int W,int H); + +/** MonoImage() **********************************************/ +/** Turn image into monochrome. **/ +/*************************************************************/ +void MonoImage(Image *Img,int X,int Y,int W,int H); + +/** SepiaImage() *********************************************/ +/** Turn image into sepia tones. **/ +/*************************************************************/ +void SepiaImage(Image *Img,int X,int Y,int W,int H); + +/** GreenImage() *********************************************/ +/** Simulate green CRT phosphor. **/ +/*************************************************************/ +void GreenImage(Image *Img,int X,int Y,int W,int H); + +/** AmberImage() *********************************************/ +/** Simulate amber CRT phosphor. **/ +/*************************************************************/ +void AmberImage(Image *Img,int X,int Y,int W,int H); + +/** SoftenImage() ********************************************/ +/** Uses softening algorithm to interpolate image Src into **/ +/** a bigger image Dst. **/ +/*************************************************************/ +void SoftenImage(Image *Dst,const Image *Src,int X,int Y,int W,int H); + +/** SoftenSCALE2X() ******************************************/ +/** Uses SCALE2X softening algorithm to interpolate image **/ +/** Src into a bigger image Dst. **/ +/*************************************************************/ +void SoftenSCALE2X(Image *Dst,const Image *Src,int X,int Y,int W,int H); + +/** SoftenEPX() **********************************************/ +/** Uses EPX softening algorithm to interpolate image Src **/ +/** into a bigger image Dst. **/ +/*************************************************************/ +void SoftenEPX(Image *Dst,const Image *Src,int X,int Y,int W,int H); + +/** SoftenEAGLE() ********************************************/ +/** Uses EAGLE softening algorithm to interpolate image Src **/ +/** into a bigger image Dst. **/ +/*************************************************************/ +void SoftenEAGLE(Image *Dst,const Image *Src,int X,int Y,int W,int H); + +/** ClearImage() *********************************************/ +/** Clear image with a given color. **/ +/*************************************************************/ +void ClearImage(Image *Img,pixel Color); + +/** IMGCopy() ************************************************/ +/** Copy one image into another. Skips pixels of given **/ +/** color unless TColor=-1. **/ +/*************************************************************/ +void IMGCopy(Image *Dst,int DX,int DY,const Image *Src,int SX,int SY,int W,int H,int TColor); + +/** IMGDrawRect()/IMGFillRect() ******************************/ +/** Draw filled/unfilled rectangle in a given image. **/ +/*************************************************************/ +void IMGDrawRect(Image *Img,int X,int Y,int W,int H,pixel Color); +void IMGFillRect(Image *Img,int X,int Y,int W,int H,pixel Color); + +/** IMGPrint() ***********************************************/ +/** Print text in a given image. **/ +/*************************************************************/ +//@@@ NOT YET +//void IMGPrint(Image *Img,const char *S,int X,int Y,int FG,int BG); + +/** SetVideo() ***********************************************/ +/** Set part of the image as "active" for display. **/ +/*************************************************************/ +void SetVideo(Image *Img,int X,int Y,int W,int H); + +#if defined(UNIX) || defined(MAEMO) || defined(MEEGO) || defined(ANDROID) +void GenericSetVideo(Image *Img,int X,int Y,int W,int H); +#endif + +/** ShowVideo() **********************************************/ +/** Show "active" image at the actual screen or window. **/ +/*************************************************************/ +int ShowVideo(void); + +/** GetColor() ***********************************************/ +/** Return pixel corresponding to the given value. **/ +/** This only works for non-palletized modes. **/ +/*************************************************************/ +pixel GetColor(unsigned char R,unsigned char G,unsigned char B); + +/** SetPalette() *********************************************/ +/** Set color N to the given value. This only works **/ +/** for palette-based modes. **/ +/*************************************************************/ +void SetPalette(pixel N,unsigned char R,unsigned char G,unsigned char B); + +/** GetFreeAudio() *******************************************/ +/** Get the amount of free samples in the audio buffer. **/ +/*************************************************************/ +unsigned int GetFreeAudio(void); + +/** GetTotalAudio() ******************************************/ +/** Get total amount of samples in the audio buffer. **/ +/*************************************************************/ +unsigned int GetTotalAudio(void); + +/** WriteAudio() *********************************************/ +/** Write up to a given number of samples to audio buffer. **/ +/** Returns the number of samples written. **/ +/*************************************************************/ +unsigned int WriteAudio(sample *Data,unsigned int Length); + +/** PauseAudio() *********************************************/ +/** Pause/resume audio playback. Returns current playback **/ +/** state. **/ +/************************************************* OPTIONAL **/ +int PauseAudio(int Switch); + +/** GetJoystick() ********************************************/ +/** Get the state of joypad buttons (1="pressed"). Refer to **/ +/** the BTN_* #defines for the button mappings. Notice that **/ +/** on Windows this function calls ProcessEvents() thus **/ +/** automatically handling all Windows messages. **/ +/*************************************************************/ +unsigned int GetJoystick(void); + +/** WaitJoystick() *******************************************/ +/** Wait until one or more of the given buttons have been **/ +/** pressed. Returns the bitmask of pressed buttons. Refer **/ +/** to BTN_* #defines for the button mappings. **/ +/*************************************************************/ +unsigned int WaitJoystick(unsigned int Mask); + +/** GetMouse() ***********************************************/ +/** Get mouse position and button states in the following **/ +/** format: RMB.LMB.Y[29-16].X[15-0] **/ +/*************************************************************/ +unsigned int GetMouse(void); + +/** GetKey() *************************************************/ +/** Get currently pressed key or 0 if none pressed. Returns **/ +/** CON_* definitions for arrows and special keys. **/ +/*************************************************************/ +unsigned int GetKey(void); + +/** WaitKey() ************************************************/ +/** Wait for a key to be pressed. Returns CON_* definitions **/ +/** for arrows and special keys. **/ +/*************************************************************/ +unsigned int WaitKey(void); + +/** WaitKeyOrMouse() *****************************************/ +/** Wait for a key or a mouse button to be pressed. Returns **/ +/** the same result as GetMouse(). If no mouse buttons **/ +/** reported to be pressed, call GetKey() to fetch a key. **/ +/*************************************************************/ +unsigned int WaitKeyOrMouse(void); + +/** SetKeyHandler() ******************************************/ +/** Attach keyboard handler that will be called when a key **/ +/** is pressed or released. **/ +/*************************************************************/ +void SetKeyHandler(void (*Handler)(unsigned int Key)); + +/** WaitSyncTimer() ******************************************/ +/** Wait for the timer to become ready. Returns number of **/ +/** times timer has been triggered after the last call to **/ +/** WaitSyncTimer(). **/ +/*************************************************************/ +int WaitSyncTimer(void); + +/** SyncTimerReady() *****************************************/ +/** Return 1 if sync timer ready, 0 otherwise. **/ +/*************************************************************/ +int SyncTimerReady(void); + +/** SetSyncTimer() *******************************************/ +/** Set synchronization timer to a given frequency in Hz. **/ +/*************************************************************/ +int SetSyncTimer(int Hz); + +/** ProcessEvents() ******************************************/ +/** Process UI (X11,GTK,Qt,...) event messages. When Wait=1 **/ +/** and there are no messages, wait for some. Returns 1 for **/ +/** continued execution, 0 if application has been closed. **/ +/*************************************************************/ +int ProcessEvents(int Wait); + +/** SetEffects() *********************************************/ +/** Set visual effects applied to video in ShowVideo(). **/ +/*************************************************************/ +void SetEffects(unsigned int NewEffects); + +/** ParseEffects() *******************************************/ +/** Parse command line visual effect options, removing them **/ +/** from Args[] and applying to the initial Effects value. **/ +/*************************************************************/ +unsigned int ParseEffects(char *Args[],unsigned int Effects); + +/** GetFilePath() ********************************************/ +/** Extracts pathname from filename and returns a pointer **/ +/** to the internal buffer containing just the path name **/ +/** ending with "\". **/ +/*************************************************************/ +const char *GetFilePath(const char *Name); + +/** NewFile() ************************************************/ +/** Given pattern NAME.EXT, generates a new filename in the **/ +/** NAMEnnnn.EXT (nnnn = 0000..9999) format and returns a **/ +/** pointer to the internal buffer containing new filename. **/ +/*************************************************************/ +const char *NewFile(const char *Pattern); + +/** ChangeDir() **********************************************/ +/** This function is a wrapper for chdir(). Unlike chdir(), **/ +/** it changes *both* the drive and the directory in MSDOS, **/ +/** exactly like the "normal" chdir() should. Returns 0 on **/ +/** success, -1 on failure, just like chdir(). **/ +/*************************************************************/ +int ChangeDir(const char *Name); + +/** MicroSleep() *********************************************/ +/** Wait for a given number of microseconds. **/ +/*************************************************************/ +void MicroSleep(unsigned int uS); + +#ifdef GIFLIB +/** LoadGIF() ************************************************/ +/** Load screen from .GIF file. Returns the number of used **/ +/** colors or 0 if failed. **/ +/*************************************************************/ +int LoadGIF(const char *File); + +/** SaveGIF() ************************************************/ +/** Save screen to .GIF file. Returns the number of written **/ +/** scanlines or 0 if failed. **/ +/*************************************************************/ +int SaveGIF(const char *File); +#endif /* GIFLIB */ + +#ifdef LIBPNG +/** LoadPNG() ************************************************/ +/** Load image from a .PNG file. Returns 1 on success, 0 on **/ +/** failure. **/ +/*************************************************************/ +int LoadPNG(Image *Img,const char *FileName); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* EMULIB_H */ diff --git a/components/msx/fmsx/src/EMULib/FDIDisk.c b/components/msx/fmsx/src/EMULib/FDIDisk.c new file mode 100644 index 0000000..17413a5 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/FDIDisk.c @@ -0,0 +1,944 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** FDIDisk.c **/ +/** **/ +/** This file contains routines to load, save, and access **/ +/** disk images in various formats. The internal format is **/ +/** always .FDI. See FDIDisk.h for declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2007-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "FDIDisk.h" +#include +#include +#include +#include +#include + +#ifdef ZLIB +#include +#endif + +#define IMAGE_SIZE(Fmt) \ + (Formats[Fmt].Sides*Formats[Fmt].Tracks* \ + Formats[Fmt].Sectors*Formats[Fmt].SecSize) + +#define FDI_SIDES(P) ((P)[6]+((int)((P)[7])<<8)) +#define FDI_TRACKS(P) ((P)[4]+((int)((P)[5])<<8)) +#define FDI_DIR(P) ((P)+(P)[12]+((int)((P)[13])<<8)+14) +#define FDI_DATA(P) ((P)+(P)[10]+((int)((P)[11])<<8)) +#define FDI_INFO(P) ((P)+(P)[8]+((int)((P)[9])<<8)) +#define FDI_SECTORS(T) ((T)[6]) +#define FDI_TRACK(P,T) (FDI_DATA(P)+(T)[0]+((int)((T)[1])<<8)+((int)((T)[2])<<16)+((int)((T)[3])<<24)) +#define FDI_SECSIZE(S) (SecSizes[(S)[3]<=4? (S)[3]:4]) +#define FDI_SECTOR(P,T,S) (FDI_TRACK(P,T)+(S)[5]+((int)((S)[6])<<8)) + +static const struct { int Sides,Tracks,Sectors,SecSize; } Formats[] = +{ + { 2,80,16,256 }, /* Dummy format */ + { 2,80,10,512 }, /* FMT_IMG can be 256 */ + { 2,80,10,512 }, /* FMT_MGT can be 256 */ + { 2,80,16,256 }, /* FMT_TRD - ZX Spectrum TRDOS disk */ + { 2,80,10,512 }, /* FMT_FDI - Generic FDI image */ + { 2,80,16,256 }, /* FMT_SCL - ZX Spectrum TRDOS disk */ + { 2,80,16,256 }, /* FMT_HOBETA - ZX Spectrum HoBeta disk */ + { 2,80,9,512 }, /* FMT_MSXDSK - MSX disk */ + { 2,80,9,512 }, /* FMT_CPCDSK - CPC disk */ + { 1,40,16,256 }, /* FMT_SF7000 - Sega SF-7000 disk */ + { 2,80,10,512 }, /* FMT_SAMDSK - Sam Coupe disk */ + { 1,40,8,512 }, /* FMT_ADMDSK - Coleco Adam disk */ + { 1,32,16,512 }, /* FMT_DDP - Coleco Adam tape */ + { 2,80,10,512 }, /* FMT_SAD - Sam Coupe disk */ + { 2,80,9,512 } /* FMT_DSK - Assuming 720kB MSX format */ +}; + +static const int SecSizes[] = +{ 128,256,512,1024,4096,0 }; + +static const char FDIDiskLabel[] = +"Disk image created by EMULib (C)Marat Fayzullin"; + +static const byte TRDDiskInfo[] = +{ + 0x01,0x16,0x00,0xF0,0x09,0x10,0x00,0x00, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x00,0x00,0x64,0x69,0x73,0x6B,0x6E, + 0x61,0x6D,0x65,0x00,0x00,0x00,0x46,0x55 +}; + +static int stricmpn(const char *S1,const char *S2,int Limit) +{ + for(;*S1&&*S2&&Limit&&(toupper(*S1)==toupper(*S2));++S1,++S2,--Limit); + return(Limit? toupper(*S1)-toupper(*S2):0); +} + +/** InitFDI() ************************************************/ +/** Clear all data structure fields. **/ +/*************************************************************/ +void InitFDI(FDIDisk *D) +{ + D->Format = 0; + D->Data = 0; + D->DataSize = 0; + D->Sides = 0; + D->Tracks = 0; + D->Sectors = 0; + D->SecSize = 0; +} + +/** EjectFDI() ***********************************************/ +/** Eject disk image. Free all allocated memory. **/ +/*************************************************************/ +void EjectFDI(FDIDisk *D) +{ + if(D->Data) free(D->Data); + InitFDI(D); +} + +/** NewFDI() *************************************************/ +/** Allocate memory and create new .FDI disk image of given **/ +/** dimensions. Returns disk data pointer on success, 0 on **/ +/** failure. **/ +/*************************************************************/ +byte *NewFDI(FDIDisk *D,int Sides,int Tracks,int Sectors,int SecSize) +{ + byte *P,*DDir; + int I,J,K,L,N; + + /* Find sector size code */ + for(L=0;SecSizes[L]&&(SecSizes[L]!=SecSize);++L); + if(!SecSizes[L]) return(0); + + /* Allocate memory */ + K = Sides*Tracks*Sectors*SecSize+sizeof(FDIDiskLabel); + I = Sides*Tracks*(Sectors+1)*7+14; + if(!(P=(byte *)malloc(I+K))) return(0); + memset(P,0x00,I+K); + + /* Eject previous disk image */ + EjectFDI(D); + + /* Set disk dimensions according to format */ + D->Format = FMT_FDI; + D->Data = P; + D->DataSize = I+K; + D->Sides = Sides; + D->Tracks = Tracks; + D->Sectors = Sectors; + D->SecSize = SecSize; + + /* .FDI magic number */ + memcpy(P,"FDI",3); + /* Disk description */ + memcpy(P+I,FDIDiskLabel,sizeof(FDIDiskLabel)); + /* Write protection (1=ON) */ + P[3] = 0; + P[4] = Tracks&0xFF; + P[5] = Tracks>>8; + P[6] = Sides&0xFF; + P[7] = Sides>>8; + /* Disk description offset */ + P[8] = I&0xFF; + P[9] = I>>8; + I += sizeof(FDIDiskLabel); + /* Sector data offset */ + P[10] = I&0xFF; + P[11] = I>>8; + /* Track directory offset */ + P[12] = 0; + P[13] = 0; + + /* Create track directory */ + for(J=K=0,DDir=P+14;J>8)&0xFF; + DDir[2] = (K>>16)&0xFF; + DDir[3] = (K>>24)&0xFF; + /* Reserved bytes */ + DDir[4] = 0; + DDir[5] = 0; + DDir[6] = Sectors; + /* For all sectors on a track... */ + for(I=N=0,DDir+=7;I>8; + } + } + + /* Done */ + return(FDI_DATA(P)); +} + +#ifdef ZLIB +#define fopen(N,M) (FILE *)gzopen(N,M) +#define fclose(F) gzclose((gzFile)(F)) +#define fread(B,L,N,F) gzread((gzFile)(F),B,(L)*(N)) +#define fwrite(B,L,N,F) gzwrite((gzFile)(F),B,(L)*(N)) +#define fgets(B,L,F) gzgets((gzFile)(F),B,L) +#define fseek(F,O,W) gzseek((gzFile)(F),O,W) +#define rewind(F) gzrewind((gzFile)(F)) +#define fgetc(F) gzgetc((gzFile)(F)) +#define ftell(F) gztell((gzFile)(F)) +#endif + +/** LoadFDI() ************************************************/ +/** Load a disk image from a given file, in a given format **/ +/** (see FMT_* #defines). Guess format from the file name **/ +/** when Format=FMT_AUTO. Returns format ID on success or **/ +/** 0 on failure. When FileName=0, ejects the disk freeing **/ +/** memory and returns 0. **/ +/*************************************************************/ +int LoadFDI(FDIDisk *D,const char *FileName,int Format) +{ + byte Buf[256],*P,*DDir; + const char *T; + int J,I,K,L,N; + FILE *F; + + /* If just ejecting a disk, drop out */ + if(!FileName) { EjectFDI(D);return(0); } + + /* If requested automatic format recognition... */ + if(!Format) + { + /* Recognize disk image format */ + T = strrchr(FileName,'\\'); + T = T? T:strrchr(FileName,'/'); + T = T? T+1:FileName; + T = strchr(T,'.'); + Format = !T? 0 + : !stricmpn(T,".FDI",4)? FMT_FDI + : !stricmpn(T,".IMG",4)? FMT_IMG + : !stricmpn(T,".MGT",4)? FMT_MGT + : !stricmpn(T,".TRD",4)? FMT_TRD + : !stricmpn(T,".SCL",4)? FMT_SCL + : !stricmpn(T,".DSK",4)? FMT_DSK + : !stricmpn(T,".DDP",4)? FMT_DDP + : !stricmpn(T,".SAD",4)? FMT_SAD + : !stricmpn(T,".$",2)? FMT_HOBETA + : 0; + + /* Try loading by extension, ignore generic raw images for now */ + if(Format&&(Format!=FMT_DSK)&&(Format!=FMT_MGT)&&(J=LoadFDI(D,FileName,Format))) + return(J); + + /* Try loading by magic number... */ + + /* Starts with "FDI" */ + if(LoadFDI(D,FileName,FMT_FDI)) return(FMT_FDI); + + /* Starts with "SINCLAIR" */ + if(LoadFDI(D,FileName,FMT_SCL)) return(FMT_SCL); + + /* Starts with "Aley's disk backup" */ + if(LoadFDI(D,FileName,FMT_SAD)) return(FMT_SAD); + + /* Starts with "MV - CPC" or "EXTENDED CPC DSK File" */ + if(LoadFDI(D,FileName,FMT_CPCDSK)) return(FMT_CPCDSK); + + /* Starts with 0xE9 or 0xEB, with some other constraints */ + if(LoadFDI(D,FileName,FMT_MSXDSK)) return(FMT_MSXDSK); + + /* Try loading as a generic raw disk image */ + return(LoadFDI(D,FileName,FMT_DSK)); + } + + /* Open file and find its size */ + if(!(F=fopen(FileName,"rb"))) return(0); +#ifdef ZLIB + for(J=0;(I=fread(Buf,1,sizeof(Buf),F));J+=I); +#else + if(fseek(F,0,SEEK_END)<0) { fclose(F);return(0); } + if((J=ftell(F))<=0) { fclose(F);return(0); } +#endif + rewind(F); + + switch(Format) + { + case FMT_FDI: /* If .FDI format... */ + /* Allocate memory and read file */ + if(!(P=(byte *)malloc(J))) { fclose(F);return(0); } + if(fread(P,1,J,F)!=J) { free(P);fclose(F);return(0); } + /* Verify .FDI format tag */ + if(memcmp(P,"FDI",3)) { free(P);fclose(F);return(0); } + /* Eject current disk image */ + EjectFDI(D); + /* Read disk dimensions */ + D->Sides = FDI_SIDES(P); + D->Tracks = FDI_TRACKS(P); + D->Sectors = 0; + D->SecSize = 0; + /* Check number of sectors and sector size */ + for(J=FDI_SIDES(P)*FDI_TRACKS(P),DDir=FDI_DIR(P);J;--J) + { + /* Get number of sectors */ + I=FDI_SECTORS(DDir); + /* Check that all tracks have the same number of sectors */ + if(!D->Sectors) D->Sectors=I; else if(D->Sectors!=I) break; + /* Check that all sectors have the same size */ + for(DDir+=7;I;--I,DDir+=7) + if(!D->SecSize) D->SecSize=FDI_SECSIZE(DDir); + else if(D->SecSize!=FDI_SECSIZE(DDir)) break; + /* Drop out if the sector size is not uniform */ + if(I) break; + } + /* If no uniform sectors or sector size, set them to zeros */ + if(J) D->Sectors=D->SecSize=0; + break; + + case FMT_DSK: /* If generic raw disk image... */ + case FMT_MGT: /* ZX Spectrum .MGT is similar to .DSK */ + /* Try a few standard geometries first */ + I = J==IMAGE_SIZE(FMT_MSXDSK)? FMT_MSXDSK /* 737280 bytes */ + : J==IMAGE_SIZE(FMT_ADMDSK)? FMT_ADMDSK /* 163840 bytes */ + : J==IMAGE_SIZE(FMT_SAMDSK)? FMT_SAMDSK /* 819200 bytes */ + : J==IMAGE_SIZE(FMT_TRD)? FMT_TRD /* 655360 bytes */ + : J==IMAGE_SIZE(FMT_DDP)? FMT_DDP /* 262144 bytes */ + : J==IMAGE_SIZE(FMT_SF7000)? FMT_SF7000 /* 163840 bytes (!) */ + : J==IMAGE_SIZE(FMT_MGT)? FMT_MGT /* 819200 bytes (!) */ + : 0; + /* If a standard geometry found... */ + if(I) + { + /* Create a new disk image */ + P = FormatFDI(D,Format=I); + if(!P) { fclose(F);return(0); } + /* Read disk image file (ignore short reads!) */ + fread(P,1,IMAGE_SIZE(I),F); + /* Done */ + P = D->Data; + break; + } + /* Try finding matching geometry */ + for(K=1,P=0;!P&&(K<=2);K<<=1) + for(I=40;!P&&(I<=80);I<<=1) + for(N=8;!P&&(N<=16);++N) + for(L=256;!P&&(L<=512);L<<=1) + if(J==K*I*N*L) + { + /* Create a new disk image */ + P = NewFDI(D,K,I,N,L); + if(!P) { fclose(F);return(0); } + /* Read disk image file (ignore short reads!) */ + fread(P,1,J,F); + /* Done */ + P = D->Data; + } + break; + + case FMT_MSXDSK: /* If MSX .DSK format... */ + /* Read header */ + if(fread(Buf,1,32,F)!=32) { fclose(F);return(0); } + /* Check magic number */ + if((Buf[0]!=0xE9)&&(Buf[0]!=0xEB)) { fclose(F);return(0); } + /* Check media descriptor */ + if(Buf[21]<0xF8) { fclose(F);return(0); } + /* Compute disk geometry */ + K = Buf[26]+((int)Buf[27]<<8); /* Heads */ + N = Buf[24]+((int)Buf[25]<<8); /* Sectors */ + L = Buf[11]+((int)Buf[12]<<8); /* SecSize */ + I = Buf[19]+((int)Buf[20]<<8); /* Total S.*/ + I = K&&N? I/K/N:0; /* Tracks */ + /* Number of heads CAN BE WRONG */ + K = I&&N&&L? J/I/N/L:0; + /* Create a new disk image */ + P = NewFDI(D,K,I,N,L); + if(!P) { fclose(F);return(0); } + /* Make sure we do not read too much data */ + I = K*I*N*L; + J = J>I? I:J; + /* Read disk image file (ignore short reads!) */ + rewind(F); + fread(P,1,J,F); + /* Done */ + P = D->Data; + break; + + case FMT_CPCDSK: /* If Amstrad CPC .DSK format... */ + /* Read header (first track is next) */ + if(fread(Buf,1,256,F)!=256) { fclose(F);return(0); } + /* Check magic string */ + if(memcmp(Buf,"MV - CPC",8)&&memcmp(Buf,"EXTENDED CPC DSK File",21)) + { fclose(F);return(0); } + /* Compute disk geometry */ + I = Buf[48]; /* Tracks */ + K = Buf[49]; /* Heads */ + N = Formats[Format].Sectors; /* Maximal number of sectors */ + L = Buf[50]+((int)Buf[51]<<8); /* Track size + 0x100 */ + /* Extended CPC .DSK format lists sizes by track */ + if(!L) + for(J=0;JVerbose && (L!=J)) + printf("LoadFDI(): Adjusted %d-byte CPC disk sectors to %d bytes.\n",L,J); + L = J; + /* Check geometry */ + if(!K||!N||!L||!I) { fclose(F);return(0); } + /* Create a new disk image */ + if(!NewFDI(D,K,I,N,L)) { fclose(F);return(0); } + /* Sectors-per-track and bytes-per-sector may vary */ + D->Sectors = 0; + D->SecSize = 0; + /* We are rewriting .FDI directory and data */ + DDir = FDI_DIR(D->Data); + P = FDI_DATA(D->Data); + /* Skip to the first track info block */ + fseek(F,0x100,SEEK_SET); + /* Read tracks */ + for(I*=K;I;--I) + { + /* Read track header */ + if(fread(Buf,1,0x18,F)!=0x18) break; + /* Check magic string */ + if(memcmp(Buf,"Track-Info\r\n",12)) break; + /* Compute track geometry */ + N = Buf[21]; /* Sectors */ + L = Buf[20]; /* SecSize */ + J = P-FDI_DATA(D->Data); /* Data offset */ + /* Create .FDI track entry */ + DDir[0] = J&0xFF; + DDir[1] = (J>>8)&0xFF; + DDir[2] = (J>>16)&0xFF; + DDir[3] = (J>>24)&0xFF; + DDir[4] = 0; + DDir[5] = 0; + DDir[6] = N; + /* Read sector headers */ + for(DDir+=7,J=N,K=0;J&&(fread(Buf,8,1,F)==8);DDir+=7,--J,K+=SecSizes[L]) + { + /* Create .FDI sector entry */ + DDir[0] = Buf[0]; + DDir[1] = Buf[1]; + DDir[2] = Buf[2]; + DDir[3] = Buf[3]; +// DDir[4] = (1<>8; + } + /* Seek to the track data */ + if(fseek(F,0x100-0x18-8*N,SEEK_CUR)<0) break; + /* Read track data */ + if(fread(P,1,K,F)!=K) break; else P+=K; + } + /* Done */ + P = D->Data; + break; + + case FMT_SAD: /* If Sam Coupe .SAD format... */ + /* Read header */ + if(fread(Buf,1,22,F)!=22) { fclose(F);return(0); } + /* Check magic string */ + if(memcmp(Buf,"Aley's disk backup",18)) { fclose(F);return(0); } + /* Compute disk geometry */ + K = Buf[18]; /* Heads */ + I = Buf[19]; /* Tracks */ + N = Buf[20]; /* Sectors */ + L = Buf[21]*64; /* Sector size */ + /* Check geometry */ + if(!K||!N||!L||!I) { fclose(F);return(0); } + /* Create a new disk image */ + P = NewFDI(D,K,I,N,L); + if(!P) { fclose(F);return(0); } + /* Make sure we do not read too much data */ + I = K*I*N*L; + J = J-22; + J = J>I? I:J; + /* Read disk image file (ignore short reads!) */ + fread(P,1,J,F); + /* Done */ + P = D->Data; + break; + + case FMT_IMG: /* If .IMG format... */ + /* Create a new disk image */ + P = FormatFDI(D,Format); + if(!P) { fclose(F);return(0); } + /* Read disk image file track-by-track */ + K = Formats[Format].Tracks; + L = Formats[Format].Sectors*Formats[Format].SecSize; + I = Formats[Format].Tracks*Formats[Format].Sides; + for(J=0;J=K? L:0),1,L,F)!=L) break; + /* Done */ + P = D->Data; + break; + + case FMT_SCL: /* If .SCL format... */ + /* @@@ NEED TO CHECK CHECKSUM AT THE END */ + /* Read header */ + if(fread(Buf,1,9,F)!=9) { fclose(F);return(0); } + /* Verify .SCL format tag and the number of files */ + if(memcmp(Buf,"SINCLAIR",8)||(Buf[8]>128)) { fclose(F);return(0); } + /* Create a new disk image */ + P = FormatFDI(D,Format); + if(!P) { fclose(F);return(0); } + /* Compute the number of free sectors */ + I = D->Sides*D->Tracks*D->Sectors; + /* Build directory, until we run out of disk space */ + for(J=0,K=D->Sectors,DDir=P;(JSectors; + DDir[15] = K/D->Sectors; + /* Next entry */ + K += DDir[13]; + DDir += 16; + } + /* Skip over remaining directory entries */ + if(JSectors*D->SecSize-J*16); + memcpy(P+0x08E2,TRDDiskInfo,sizeof(TRDDiskInfo)); + strncpy((char *)P+0x08F5,"SPECCY",8); + P[0x8E1] = K%D->Sectors; /* First free sector */ + P[0x8E2] = K/D->Sectors; /* Track it belongs to */ + P[0x8E3] = 0x16 + (D->Tracks>40? 0:1) + (D->Sides>1? 0:2); + P[0x8E4] = J; /* Number of files */ + P[0x8E5] = (I-K)&0xFF; /* Number of free sectors */ + P[0x8E6] = (I-K)>>8; + /* Read data */ + for(DDir=P;J;--J,DDir+=16) + { + /* Determine data offset and size */ + I = (DDir[15]*D->Sectors+DDir[14])*D->SecSize; + N = DDir[13]*D->SecSize; + /* Read .SCL data (ignore short reads!) */ + fread(P+I,1,N,F); + } + /* Done */ + P = D->Data; + break; + + case FMT_HOBETA: /* If .$* format... */ + /* Create a new disk image */ + P = FormatFDI(D,Format); + if(!P) { fclose(F);return(0); } + /* Read header */ + if(fread(P,1,17,F)!=17) { fclose(F);return(0); } + /* Determine data offset and size */ + I = D->Sectors*D->SecSize; + N = P[13]+((int)P[14]<<8); + /* Build disk information */ + memset(P+16,0,I-16); + memcpy(P+0x08E2,TRDDiskInfo,sizeof(TRDDiskInfo)); + strncpy((char *)P+0x08F5,"SPECCY",8); + K = D->Sectors+N; + J = D->Sectors*D->Tracks*D->Sides-K; + P[0x8E1] = K%D->Sectors; /* First free sector */ + P[0x8E2] = K/D->Sectors; /* Track it belongs to */ + P[0x8E3] = 0x16 + (D->Tracks>40? 0:1) + (D->Sides>1? 0:2); + P[0x8E4] = 1; /* Number of files */ + P[0x8E5] = J&0xFF; /* Number of free sectors */ + P[0x8E6] = J>>8; + N = N*D->SecSize; /* N is now in bytes */ + /* Read data (ignore short reads!) */ + fread(P+I,1,N,F); + /* Compute and check checksum */ + for(L=I=0;I<15;++I) L+=P[I]; + L = ((L*257+105)&0xFFFF)-P[15]-((int)P[16]<<8); + if(L) { /* @@@ DO SOMETHING BAD! */ } + /* Place file at track #1 sector #0, limit its size to 255 sectors */ + P[13] = P[14]? 255:P[13]; + P[14] = 0; + P[15] = 1; + P[16] = 0; + /* Done */ + P = D->Data; + break; + + case FMT_SF7000: /* If SF-7000 .SF format... */ + case FMT_SAMDSK: /* If Sam Coupe .DSK format... */ + case FMT_ADMDSK: /* If Coleco Adam .DSK format... */ + case FMT_DDP: /* If Coleco Adam .DDP format... */ + case FMT_TRD: /* If ZX Spectrum .TRD format... */ + /* Must have exact size, unless it is a .TRD */ + if((Format!=FMT_TRD) && (J!=IMAGE_SIZE(Format))) { fclose(F);return(0); } + /* Create a new disk image */ + P = FormatFDI(D,Format); + if(!P) { fclose(F);return(0); } + /* Read disk image file (ignore short reads!) */ + fread(P,1,IMAGE_SIZE(Format),F); + /* Done */ + P = D->Data; + break; + + default: + /* Format not recognized */ + return(0); + } + + if(D->Verbose) + printf( + "LoadFDI(): Loaded '%s', %d sides x %d tracks x %d sectors x %d bytes\n", + FileName,D->Sides,D->Tracks,D->Sectors,D->SecSize + ); + + /* Done */ + fclose(F); + D->Data = P; + D->Format = Format; + return(Format); +} + +#ifdef ZLIB +#undef fopen +#undef fclose +#undef fread +#undef fwrite +#undef fseek +#undef ftell +#undef rewind +#endif + +/** SaveDSKData() ********************************************/ +/** Save uniform disk data, truncating or adding zeros as **/ +/** needed. Returns FDI_SAVE_OK on success, FDI_SAVE_PADDED **/ +/** if any sectors were padded, FDI_SAVE_TRUNCATED if any **/ +/** sectors were truncated, FDI_SAVE_FAILED if failed. **/ +/*************************************************************/ +static int SaveDSKData(FDIDisk *D,FILE *F,int Sides,int Tracks,int Sectors,int SecSize) +{ + int J,I,K,Result; + + Result = FDI_SAVE_OK; + + /* Scan through all tracks, sides, sectors */ + for(J=0;JSecSizeSecSize:SecSize; + /* Write sector */ + if(!P||!L||(fwrite(P,1,L,F)!=L)) return(FDI_SAVE_FAILED); + /* Pad sector to SecSize, if needed */ + if((SecSize>L)&&fseek(F,SecSize-L,SEEK_CUR)) return(FDI_SAVE_FAILED); + /* Update result */ + L = SecSize>L? FDI_SAVE_PADDED:SecSizeSecSizeSecSize:SecSize; + /* Write sector */ + if(!P||!L||(fwrite(P,1,L,F)!=L)) return(FDI_SAVE_FAILED); + /* Pad sector to SecSize, if needed */ + if((SecSize>L)&&fseek(F,SecSize-L,SEEK_CUR)) return(FDI_SAVE_FAILED); + /* Update result */ + L = SecSize>L? FDI_SAVE_PADDED:SecSizeData) return(0); + + /* Use original format if requested */ + if(!Format) Format=D->Format; + + /* Open file for writing */ + if(!(F=fopen(FileName,"wb"))) return(0); + + /* Assume success */ + Result = FDI_SAVE_OK; + + /* Depending on the format... */ + switch(Format) + { + case FMT_FDI: + /* This is the native format in which data is stored in memory */ + if(fwrite(D->Data,1,D->DataSize,F)!=D->DataSize) + { fclose(F);unlink(FileName);return(0); } + break; + + case FMT_IMG: + /* Check the number of tracks and sides */ + if((FDI_TRACKS(D->Data)!=Formats[Format].Tracks)||(FDI_SIDES(D->Data)!=Formats[Format].Sides)) + { fclose(F);unlink(FileName);return(0); } + /* Write out the data, in sides/tracks/sectors order */ + Result = SaveIMGData(D,F,Formats[Format].Sides,Formats[Format].Tracks,Formats[Format].Sectors,Formats[Format].SecSize); + if(!Result) { fclose(F);unlink(FileName);return(0); } + break; + + case FMT_SF7000: + case FMT_SAMDSK: + case FMT_ADMDSK: + case FMT_DDP: + case FMT_TRD: + /* Check the number of tracks and sides */ + if((FDI_TRACKS(D->Data)!=Formats[Format].Tracks)||(FDI_SIDES(D->Data)!=Formats[Format].Sides)) + { fclose(F);unlink(FileName);return(0); } + /* Write out the data, in tracks/sides/sectors order */ + Result = SaveDSKData(D,F,Formats[Format].Sides,Formats[Format].Tracks,Formats[Format].Sectors,Formats[Format].SecSize); + if(!Result) { fclose(F);unlink(FileName);return(0); } + break; + + case FMT_MSXDSK: + case FMT_DSK: + case FMT_MGT: + /* Must have uniform tracks */ + if(!D->Sectors || !D->SecSize) { fclose(F);unlink(FileName);return(0); } + /* Write out the data, in tracks/sides/sectors order */ + Result = SaveDSKData(D,F,FDI_SIDES(D->Data),FDI_TRACKS(D->Data),D->Sectors,D->SecSize); + if(!Result) { fclose(F);unlink(FileName);return(0); } + break; + + case FMT_SAD: + /* Must have uniform tracks with "even" sector size */ + if(!D->Sectors || !D->SecSize || (D->SecSize&0x3F)) + { fclose(F);unlink(FileName);return(0); } + /* Fill header */ + memset(S,0,sizeof(S)); + strcpy((char *)S,"Aley's disk backup"); + S[18] = FDI_SIDES(D->Data); + S[19] = FDI_TRACKS(D->Data); + S[20] = D->Sectors; + S[21] = D->SecSize>>6; + /* Write header */ + if(fwrite(S,1,22,F)!=22) { fclose(F);unlink(FileName);return(0); } + /* Write out the data, in tracks/sides/sectors order */ + Result = SaveDSKData(D,F,S[18],S[19],S[20],S[21]*64); + if(!Result) { fclose(F);unlink(FileName);return(0); } + break; + + case FMT_SCL: + /* Get data pointer */ + T=FDI_DATA(D->Data); + /* Check tracks, sides, sectors, and the TR-DOS magic number */ + if((FDI_SIDES(D->Data)!=Formats[Format].Sides) + ||(FDI_TRACKS(D->Data)!=Formats[Format].Tracks) + ||(D->Sectors!=Formats[Format].Sectors) + ||(D->SecSize!=Formats[Format].SecSize) + ||(T[0x8E3]!=0x16) + ) { fclose(F);unlink(FileName);return(0); } + /* Write header */ + strcpy((char *)S,"SINCLAIR"); + S[8]=T[0x8E4]; + if(fwrite(S,1,9,F)!=9) { fclose(F);unlink(FileName);return(0); } + for(C=I=0;I<9;++I) C+=S[I]; + /* Write directory entries */ + for(J=0,P=T;JSectors+P[14])*D->SecSize; + I = P[13]*D->SecSize; + /* Write data */ + if(fwrite(T+K,1,I,F)!=I) + { fclose(F);unlink(FileName);return(0); } + /* Compute checksum */ + for(L=K,I+=K;L>8)&0xFF; + S[2] = (C>>16)&0xFF; + S[3] = (C>>24)&0xFF; + if(fwrite(S,1,4,F)!=4) { fclose(F);unlink(FileName);return(0); } + /* Done */ + break; + + case FMT_HOBETA: + /* Get data pointer */ + T=FDI_DATA(D->Data); + /* Check tracks, sides, sectors, and the TR-DOS magic number */ + if((FDI_SIDES(D->Data)!=Formats[Format].Sides) + ||(FDI_TRACKS(D->Data)!=Formats[Format].Tracks) + ||(D->Sectors!=Formats[Format].Sectors) + ||(D->SecSize!=Formats[Format].SecSize) + ||(T[0x8E3]!=0x16) + ) { fclose(F);unlink(FileName);return(0); } + /* Look for the first file */ + for(J=0,P=T;(J=T[0x8E4]) { fclose(F);unlink(FileName);return(0); } + /* Copy header */ + memcpy(S,P,14); + /* Get single file address and size */ + I = P[13]*D->SecSize; + P = T+(P[14]+D->Sectors*P[15])*D->SecSize; + /* Compute checksum and build header */ + for(C=J=0;J<14;++J) C+=P[J]; + S[14] = 0; + C = (C+S[14])*257+105; + S[15] = C&0xFF; + S[16] = (C>>8)&0xFF; + /* Write header */ + if(fwrite(S,1,17,F)!=17) { fclose(F);unlink(FileName);return(0); } + /* Write file data */ + if(fwrite(P,1,I,F)!=I) { fclose(F);unlink(FileName);return(0); } + /* Done */ + break; + + default: + /* Can't save this format for now */ + fclose(F); + unlink(FileName); + return(0); + } + + /* Done */ + fclose(F); + return(Result); +} + +/** SeekFDI() ************************************************/ +/** Seek to given side/track/sector. Returns sector address **/ +/** on success or 0 on failure. **/ +/*************************************************************/ +byte *SeekFDI(FDIDisk *D,int Side,int Track,int SideID,int TrackID,int SectorID) +{ + byte *P,*T; + int J,Deleted; + + /* Have to have disk mounted */ + if(!D||!D->Data) return(0); + + /* May need to search for deleted sectors */ + Deleted = (SectorID>=0) && (SectorID&SEEK_DELETED)? 0x80:0x00; + if(Deleted) SectorID&=~SEEK_DELETED; + + switch(D->Format) + { + case FMT_TRD: + case FMT_DSK: + case FMT_SCL: + case FMT_FDI: + case FMT_MGT: + case FMT_IMG: + case FMT_DDP: + case FMT_SAD: + case FMT_CPCDSK: + case FMT_SAMDSK: + case FMT_ADMDSK: + case FMT_MSXDSK: + case FMT_SF7000: + /* Track directory */ + P = FDI_DIR(D->Data); + /* Find current track entry */ + for(J=Track*D->Sides+Side%D->Sides;J;--J) P+=(FDI_SECTORS(P)+1)*7; + /* Find sector entry */ + for(J=FDI_SECTORS(P),T=P+7;J;--J,T+=7) + if((T[0]==TrackID)||(TrackID<0)) + if((T[1]==SideID)||(SideID<0)) + if(((T[2]==SectorID)&&((T[4]&0x80)==Deleted))||(SectorID<0)) + break; + /* Fall out if not found */ + if(!J) return(0); + /* FDI stores a header for each sector */ + D->Header[0] = T[0]; + D->Header[1] = T[1]; + D->Header[2] = T[2]; + D->Header[3] = T[3]<=3? T[3]:3; + D->Header[4] = T[4]; + D->Header[5] = 0x00; + /* FDI has variable sector numbers and sizes */ + D->Sectors = FDI_SECTORS(P); + D->SecSize = FDI_SECSIZE(T); + return(FDI_SECTOR(D->Data,P,T)); + } + + /* Unknown format */ + return(0); +} + +/** LinearFDI() **********************************************/ +/** Seek to given sector by its linear number. Returns **/ +/** sector address on success or 0 on failure. **/ +/*************************************************************/ +byte *LinearFDI(FDIDisk *D,int SectorN) +{ + if(!D->Sectors || !D->Sides || (SectorN<0)) return(0); + else + { + int Sector = SectorN % D->Sectors; + int Track = SectorN / D->Sectors / D->Sides; + int Side = (SectorN / D->Sectors) % D->Sides; + return(SeekFDI(D,Side,Track,Side,Track,Sector+1)); + } +} + +/** FormatFDI() ***********************************************/ +/** Allocate memory and create new standard disk image for a **/ +/** given format. Returns disk data pointer on success, 0 on **/ +/** failure. **/ +/**************************************************************/ +byte *FormatFDI(FDIDisk *D,int Format) +{ + if((Format<0) || (Format>=sizeof(Formats)/sizeof(Formats[0]))) return(0); + + return(NewFDI(D, + Formats[Format].Sides, + Formats[Format].Tracks, + Formats[Format].Sectors, + Formats[Format].SecSize + )); +} + diff --git a/components/msx/fmsx/src/EMULib/FDIDisk.h b/components/msx/fmsx/src/EMULib/FDIDisk.h new file mode 100644 index 0000000..83073ab --- /dev/null +++ b/components/msx/fmsx/src/EMULib/FDIDisk.h @@ -0,0 +1,128 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** FDIDisk.h **/ +/** **/ +/** This file declares routines to load, save, and access **/ +/** disk images in various formats. The internal format is **/ +/** always .FDI. See FDIDisk.c for the actual code. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2007-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef FDIDISK_H +#define FDIDISK_H +#ifdef __cplusplus +extern "C" { +#endif + /* SaveFDI() result: */ +#define FDI_SAVE_FAILED 0 /* Failed saving disk image */ +#define FDI_SAVE_TRUNCATED 1 /* Truncated data while saving */ +#define FDI_SAVE_PADDED 2 /* Padded data while saving */ +#define FDI_SAVE_OK 3 /* Succeeded saving disk image */ + + /* Supported disk image formats: */ +#define FMT_AUTO 0 /* Determine format automatically */ +#define FMT_IMG 1 /* ZX Spectrum disk */ +#define FMT_MGT 2 /* ZX Spectrum disk, same as .DSK */ +#define FMT_TRD 3 /* ZX Spectrum TRDOS disk */ +#define FMT_FDI 4 /* Generic FDI image */ +#define FMT_SCL 5 /* ZX Spectrum TRDOS disk */ +#define FMT_HOBETA 6 /* ZX Spectrum HoBeta disk */ +#define FMT_MSXDSK 7 /* MSX disk */ +#define FMT_CPCDSK 8 /* CPC disk */ +#define FMT_SF7000 9 /* Sega SF-7000 disk */ +#define FMT_SAMDSK 10 /* Sam Coupe disk */ +#define FMT_ADMDSK 11 /* Coleco Adam disk */ +#define FMT_DDP 12 /* Coleco Adam tape */ +#define FMT_SAD 13 /* Sam Coupe disk */ +#define FMT_DSK 14 /* Generic raw disk image */ + +#define SEEK_DELETED (0x40000000) + +#define DataFDI(D) ((D)->Data+(D)->Data[10]+((int)((D)->Data[11])<<8)) + +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif + +/** FDIDisk **************************************************/ +/** This structure contains all disk image information and **/ +/** also the result of the last SeekFDI() call. **/ +/*************************************************************/ +typedef struct +{ + byte Format; /* Original disk format (FMT_*) */ + int Sides; /* Sides per disk */ + int Tracks; /* Tracks per side */ + int Sectors; /* Sectors per track */ + int SecSize; /* Bytes per sector */ + + byte *Data; /* Disk data */ + int DataSize; /* Disk data size */ + + byte Header[6]; /* Current header, result of SeekFDI() */ + byte Verbose; /* 1: Print debugging messages */ +} FDIDisk; + +/** InitFDI() ************************************************/ +/** Clear all data structure fields. **/ +/*************************************************************/ +void InitFDI(FDIDisk *D); + +/** EjectFDI() ***********************************************/ +/** Eject disk image. Free all allocated memory. **/ +/*************************************************************/ +void EjectFDI(FDIDisk *D); + +/** NewFDI() *************************************************/ +/** Allocate memory and create new .FDI disk image of given **/ +/** dimensions. Returns disk data pointer on success, 0 on **/ +/** failure. **/ +/*************************************************************/ +byte *NewFDI(FDIDisk *D,int Sides,int Tracks,int Sectors,int SecSize); + +/** FormatFDI() ***********************************************/ +/** Allocate memory and create new standard disk image for a **/ +/** given format. Returns disk data pointer on success, 0 on **/ +/** failure. **/ +/**************************************************************/ +byte *FormatFDI(FDIDisk *D,int Format); + +/** LoadFDI() ************************************************/ +/** Load a disk image from a given file, in a given format **/ +/** (see FMT_* #defines). Guess format from the file name **/ +/** when Format=FMT_AUTO. Returns format ID on success or **/ +/** 0 on failure. When FileName=0, ejects the disk freeing **/ +/** memory and returns 0. **/ +/*************************************************************/ +int LoadFDI(FDIDisk *D,const char *FileName,int Format); + +/** SaveFDI() ************************************************/ +/** Save a disk image to a given file, in a given format **/ +/** (see FMT_* #defines). Use the original format when **/ +/** when Format=FMT_AUTO. Returns FDI_SAVE_OK on success, **/ +/** FDI_SAVE_PADDED if any sectors were padded, **/ +/** FDI_SAVE_TRUNCATED if any sectors were truncated, **/ +/** FDI_SAVE_FAILED (0) if failed. **/ +/*************************************************************/ +int SaveFDI(FDIDisk *D,const char *FileName,int Format); + +/** SeekFDI() ************************************************/ +/** Seek to given side/track/sector. Returns sector address **/ +/** on success or 0 on failure. **/ +/*************************************************************/ +byte *SeekFDI(FDIDisk *D,int Side,int Track,int SideID,int TrackID,int SectorID); + +/** LinearFDI() **********************************************/ +/** Seek to given sector by its linear number. Returns **/ +/** sector address on success or 0 on failure. **/ +/*************************************************************/ +byte *LinearFDI(FDIDisk *D,int SectorN); + +#ifdef __cplusplus +} +#endif +#endif /* FDIDISK_H */ diff --git a/components/msx/fmsx/src/EMULib/Floppy.c b/components/msx/fmsx/src/EMULib/Floppy.c new file mode 100644 index 0000000..a40db59 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Floppy.c @@ -0,0 +1,531 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Floppy.c **/ +/** **/ +/** This file implements functions to operate on 720kB **/ +/** floppy disk images. See Floppy.h for declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2004-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "Floppy.h" +#include +#include +#include +#include +#include + +#if defined(UIQ) || defined(S60) || defined(UNIX) || defined(MAEMO) || defined(MEEGO) || defined(ANDROID) +#include +#else +#include +#endif + +#ifdef ZLIB +#include +#endif + +#define DSK_RESERVED_SECS 0 +#define DSK_FATS_PER_DISK 2 +#define DSK_SECS_PER_BOOT 1 +#define DSK_SECS_PER_FAT 3 +#define DSK_SECS_PER_DIR 7 +#define DSK_SECS_PER_CLTR 2 +#define DSK_CLUSTER_SIZE (DSK_SECTOR_SIZE*DSK_SECS_PER_CLTR) +#define DSK_DIR_SIZE 112 +#define DSK_DISK_SIZE (DSK_SECS_PER_DISK*DSK_SECTOR_SIZE) + +#define DSK_SECS_PER_DISK (\ + DSK_SIDS_PER_DISK \ +* DSK_TRKS_PER_SIDE \ +* DSK_SECS_PER_TRCK) + +#define DSK_FAT_SIZE ((\ + DSK_SECS_PER_DISK \ +- DSK_SECS_PER_BOOT \ +- DSK_RESERVED_SECS \ +- DSK_FATS_PER_DISK*DSK_SECS_PER_FAT \ +- DSK_SECS_PER_DIR)/2) + +#define DIRENTRY(Dsk,ID) (\ + (Dsk) \ ++ DSK_SECTOR_SIZE*(DSK_RESERVED_SECS+DSK_SECS_PER_BOOT) \ ++ DSK_SECTOR_SIZE*DSK_SECS_PER_FAT*DSK_FATS_PER_DISK \ ++ ((ID)*32) \ +) + +/** FindFreeCluster() ****************************************/ +/** Finds first free cluster starting from the given one. **/ +/** Returns cluster number on success or 0 on failure. **/ +/*************************************************************/ +static int FindFreeCluster(const byte *Dsk,int Start) +{ + const byte *FE; + int J; + + /* Search for the first zeroed entry */ + for(Start=Start>=2? Start:2;Start>1); + /* Read entry contents */ + J = Start&0x001? (FE[1]>>4)+((int)FE[2]<<4) + : FE[0]+((int)(FE[1]&0x0F)<<8); + if(!J) return(Start); + } + + /* No free clusters found */ + return(0); +} + +/** DSKCreate() **********************************************/ +/** Create disk image in Dsk or allocate memory when Dsk=0. **/ +/** Returns pointer to the disk image or 0 on failure. **/ +/*************************************************************/ +byte *DSKCreate(byte *Dsk,const char *Label) +{ + byte *FAT,*DIR,*DAT; + + /* Allocate memory if needed */ + if(!Dsk) + { + Dsk=(byte *)malloc(DSK_DISK_SIZE); + if(!Dsk) return(0); + } + + /* FAT and directory pointers */ + FAT = Dsk + DSK_SECTOR_SIZE*(DSK_RESERVED_SECS+1); + DIR = FAT + DSK_SECTOR_SIZE*DSK_SECS_PER_FAT*DSK_FATS_PER_DISK; + DAT = DIR + DSK_DIR_SIZE*32; + + /* Clear disk image */ + memset(Dsk,0x00,DSK_DISK_SIZE); + + /* Boot sector parameters */ + Dsk[0x00] = 0xC9; + Dsk[0x01] = 0xC9; + Dsk[0x02] = 0xC9; + memset(Dsk+3,0x00,8); + if(Label) strncpy((char *)(Dsk+3),Label,8); + Dsk[0x0B] = DSK_SECTOR_SIZE&0xFF; + Dsk[0x0C] = (DSK_SECTOR_SIZE>>8)&0xFF; + Dsk[0x0D] = DSK_SECS_PER_CLTR; + Dsk[0x0E] = DSK_SECS_PER_BOOT; + Dsk[0x10] = DSK_FATS_PER_DISK; + Dsk[0x11] = DSK_DIR_SIZE&0xFF; + Dsk[0x12] = (DSK_DIR_SIZE>>8)&0xFF; + Dsk[0x13] = DSK_SECS_PER_DISK&0xFF; + Dsk[0x14] = (DSK_SECS_PER_DISK>>8)&0xFF; + Dsk[0x15] = 0xF9; /* Media ID */ + Dsk[0x16] = DSK_SECS_PER_FAT&0xFF; + Dsk[0x17] = (DSK_SECS_PER_FAT>>8)&0xFF; + Dsk[0x18] = 9; /* Sectors per track */ + Dsk[0x19] = 0; + Dsk[0x1A] = 2; /* Heads per disk */ + Dsk[0x1B] = 0; + Dsk[0x1C] = DSK_RESERVED_SECS&0xFF; + Dsk[0x1D] = (DSK_RESERVED_SECS>>8)&0xFF; + Dsk[0x1E] = 0xC9; + + /* First two FAT entries are not used */ + FAT[0] = 0xF9; + FAT[1] = 0xFF; + FAT[2] = 0xFF; + + /* Done */ + return(Dsk); +} + +/** DSKFile() ************************************************/ +/** Create file with a given name, return file ID, or 0 on **/ +/** failure. **/ +/*************************************************************/ +int DSKFile(byte *Dsk,const char *FileName) +{ + byte *P; + int I,J; + + /* See if file exists */ + for(J=1;J<=DSK_DIR_SIZE;J++) + if(DSKFileName(Dsk,J)&&!memcmp(FileName,DSKFileName(Dsk,J),11)) + return(0); + + /* Find empty spot */ + for(J=1;J<=DSK_DIR_SIZE;J++) + if(!DSKFileName(Dsk,J)) + { + I=FindFreeCluster(Dsk,2); + P=DIRENTRY(Dsk,J-1); + memset(P,0x00,32); + memcpy(P,FileName,11); + P[0x0B] = 0x00; /* Attributes */ + P[0x0C] = 0x00; /* Reserved */ + P[0x1A] = I&0xFF; /* First cluster */ + P[0x1B] = (I>>8)&0x0F; + P[0x1C] = 0x00; /* File size */ + P[0x1D] = 0x00; + P[0x1E] = 0x00; + P[0x1F] = 0x00; + return(J); + } + + /* No free entries */ + return(0); +} + +/** DSKFileName() ********************************************/ +/** Return name of a file with a given ID or 0 on failure. **/ +/*************************************************************/ +const char *DSKFileName(const byte *Dsk,int ID) +{ + const char *Name; + + /* Can't have ID that is out of bounds */ + if((ID<1)||(ID>DSK_DIR_SIZE)) return(0); + /* Return file name */ + Name=(const char *)DIRENTRY(Dsk,ID-1); + return(!Name[0]||(Name[0]==(char)0xE5)? 0:Name); +} + +/** DSKFileSize() ********************************************/ +/** Return side of a file with a given ID or 0 on failure. **/ +/*************************************************************/ +int DSKFileSize(const byte *Dsk,int ID) +{ + const byte *Name; + + /* Can't have ID that is out of bounds */ + if((ID<1)||(ID>DSK_DIR_SIZE)) return(0); + /* Return file size */ + Name=DIRENTRY(Dsk,ID-1); + return(!Name[0]||(Name[0]==0xE5)? 0 + : (Name[0x1C]+Name[0x1D]*256) + + (Name[0x1E]+Name[0x1F]*256)*256 + ); +} + +/** DSKRead()/DSKWrite() *************************************/ +/** Read or write a given number of bytes to a given file. **/ +/** Both functions return the number of bytes written or **/ +/** read from the file, or 0 on failure. **/ +/*************************************************************/ +int DSKWrite(byte *Dsk,int ID,const byte *Buf,int Size) +{ + byte *DE,*FE,*FE2,*P; + int I,J,Written; + + /* Can't have ID that is out of bounds */ + if((ID<1)||(ID>DSK_DIR_SIZE)) return(0); + + /* Get directory entry */ + DE=DIRENTRY(Dsk,ID-1); + if(!DE[0]||(DE[0]==0xE5)) return(0); + + /* Delete file contents, leave name intact */ + J=DE[0]; + DSKDelete(Dsk,ID); + DE[0]=J; + + /* Find first free cluster and store it */ + J=FindFreeCluster(Dsk,2); + if(!J) { DSKDelete(Dsk,ID);return(0); } + DE[0x1A] = J&0xFF; + DE[0x1B] = (J>>8)&0x0F; + + for(Written=0;(J!=0xFFF)&&(Written>1); + FE2 = FE + + DSK_SECTOR_SIZE*DSK_SECS_PER_FAT; + + /* Chain clusters */ + if(J&0x001) { FE2[1]=FE[1]=(FE[1]&0x0F)|((I&0x0F)<<4);FE2[2]=FE[2]=I>>4; } + else { FE2[1]=FE[1]=(FE[1]&0xF0)|(I>>8);FE2[0]=FE[0]=I&0xFF; } + } + + /* Store new file size */ + DE[0x1C] = Written&0xFF; + DE[0x1D] = (Written>>8)&0xFF; + DE[0x1E] = (Written>>16)&0xFF; + DE[0x1F] = (Written>>24)&0xFF; + + /* Done */ + return(Written); +} + +int DSKRead(const byte *Dsk,int ID,byte *Buf,int Size) +{ + const byte *P; + int I,J,Read; + + /* Can't have ID that is out of bounds */ + if((ID<1)||(ID>DSK_DIR_SIZE)) return(0); + + /* Get directory entry */ + P=DIRENTRY(Dsk,ID-1); + if(!P[0]||(P[0]==0xE5)) return(0); + + /* Find first free cluster and store it */ + J=P[0x1A]+((int)(P[0x1B]&0x0F)<<8); + + /* Update size to read */ + if(DSKFileSize(Dsk,ID)1)&&(J<0xFF1);) + { + P = Dsk + + DSK_SECTOR_SIZE*(DSK_RESERVED_SECS+DSK_SECS_PER_BOOT) + + DSK_SECTOR_SIZE*DSK_SECS_PER_FAT*DSK_FATS_PER_DISK + + DSK_SECTOR_SIZE*DSK_SECS_PER_DIR; + I = Size-Read>1); + J = J&0x001? (P[1]>>4)+((int)P[2]<<4):P[0]+((int)(P[1]&0x0F)<<8); + } + + /* Done */ + return(Read); +} + +/** DSKDelete() **********************************************/ +/** Delete file with a given ID. Returns ID on success or 0 **/ +/** on failure. **/ +/*************************************************************/ +int DSKDelete(byte *Dsk,int ID) +{ + byte *DE,*FE,*FE2; + int J; + + /* Can't have ID that is out of bounds */ + if((ID<1)||(ID>DSK_DIR_SIZE)) return(0); + + /* Get directory entry */ + DE=DIRENTRY(Dsk,ID-1); + if(!DE[0]||(DE[0]==0xE5)) return(0); + + /* First cluster */ + J=DE[0x1A]+((int)(DE[0x1B]&0x0F)<<8); + + /* Free clusters */ + while((J>1)&&(J<0xFF1)) + { + /* FAT entry address */ + FE = Dsk + + DSK_SECTOR_SIZE*(DSK_RESERVED_SECS+DSK_SECS_PER_BOOT) + + 3*(J>>1); + FE2 = FE + + DSK_SECTOR_SIZE*DSK_SECS_PER_FAT; + + /* Delete entry */ + if(J&0x001) + { + J=(FE[1]>>4)+((int)FE[2]<<4); + if((J<0xFF1)||(J>0xFF7)) { FE2[1]=FE[1]&=0x0F;FE2[2]=FE[2]=0x00; } + } + else + { + J=FE[0]+((int)(FE[1]&0x0F)<<8); + if((J<0xFF1)||(J>0xFF7)) { FE2[1]=FE[1]&=0xF0;FE2[0]=FE[0]=0x00; } + } + } + + /* Mark file as deleted */ + DE[0]=0xE5; + /* Done */ + return(ID); +} + +/** DSKLoad()/DSKSave() **************************************/ +/** Load or save disk contents from/to a disk image or a **/ +/** directory. DSKLoad() will allocate space if Dsk=0. Both **/ +/** functions return pointer to disk contents on success or **/ +/** 0 on failure. **/ +/*************************************************************/ +byte *DSKLoad(const char *Name,byte *Dsk,const char *Label) +{ + byte *Dsk1,*Buf; + char *Path,FN[32]; + struct stat FS; + struct dirent *DE; + FILE *F; + DIR *D; + int J,I; + + /* Create disk image */ + Dsk1=DSKCreate(Dsk,Label); + if(!Dsk1) return(0); + + /* If is a directory... */ + if(!stat(Name,&FS)&&S_ISDIR(FS.st_mode)) + { + /* Open directory */ + D=opendir(Name); + if(!D) { if(!Dsk) free(Dsk1);return(0); } + + /* Scan, read, store files */ + while((DE=readdir(D))) + if((Path=malloc(strlen(Name)+strlen(DE->d_name)+5))) + { + /* Compose full input file name */ + strcpy(Path,Name); + I=strlen(Path); + if(Path[I-1]!='/') Path[I++]='/'; + strcpy(Path+I,DE->d_name); + + /* Compose 8.3 file name */ + for(J=0;(J<8)&&DE->d_name[J]&&(DE->d_name[J]!='.');J++) + FN[J]=toupper(DE->d_name[J]); + for(I=J;I<8;I++) FN[I]=' '; + for(;DE->d_name[J]&&(DE->d_name[J]!='.');J++); + if(DE->d_name[J]) J++; + for(;(I<11)&&DE->d_name[J];I++,J++) + FN[I]=toupper(DE->d_name[J]); + for(;I<11;I++) FN[I]=' '; + FN[I]='\0'; + + /* Open input file */ + if(!stat(Path,&FS)&&S_ISREG(FS.st_mode)&&FS.st_size) + if((F=fopen(Path,"rb"))) + { + /* Allocate input buffer */ + if((Buf=malloc(FS.st_size))) + { + /* Read file into the buffer */ + if(fread(Buf,1,FS.st_size,F)==FS.st_size) + { + /* Create and write floppy file */ + if((I=DSKFile(Dsk1,FN))) + if(DSKWrite(Dsk1,I,Buf,FS.st_size)!=FS.st_size) + DSKDelete(Dsk1,I); + } + /* Done with the input buffer */ + free(Buf); + } + /* Done with the input file */ + fclose(F); + } + + /* Done with the full input file name */ + free(Path); + } + + /* Done processing directory */ + closedir(D); + return(Dsk1); + } + +#ifdef ZLIB +#define fopen(N,M) (FILE *)gzopen(N,M) +#define fclose(F) gzclose((gzFile)(F)) +#define fread(B,L,N,F) gzread((gzFile)(F),B,(L)*(N)) +#endif + + /* Assume to be a disk image file */ + F=fopen(Name,"rb"); + if(!F) { if(!Dsk) free(Dsk1);return(0); } + + /* Read data */ + if(fread(Dsk1,1,DSK_DISK_SIZE,F)!=DSK_DISK_SIZE) + { if(!Dsk) free(Dsk1);fclose(F);return(0); } + + /* Done */ + fclose(F); + return(Dsk1); + +#ifdef ZLIB +#undef fopen +#undef fclose +#undef fread +#endif +} + +const byte *DSKSave(const char *Name,const byte *Dsk) +{ + const char *T; + char *Path,*P; + struct stat FS; + FILE *F; + int J,I,K; + + /* If is a directory... */ + if(!stat(Name,&FS)&&S_ISDIR(FS.st_mode)) + { + /* Compose path name */ + Path=malloc(strlen(Name)+20); + if(!Path) return(0); + strcpy(Path,Name); + I=strlen(Path); + if(Path[I-1]!='/') Path[I++]='/'; + + /* Scan, read, dump files */ + for(J=1;J<=DSK_DIR_SIZE;J++) + if(DSKFileSize(Dsk,J)) + { + /* Compose file name */ + T=DSKFileName(Dsk,J); + P=Path+I; + for(K=0;(K<8)&&T[K]&&(T[K]!=' ');K++) *P++=T[K]; + if(T[8]!=' ') + for(*P++='.',K=8;(K<11)&&T[K]&&(T[K]!=' ');K++) *P++=T[K]; + *P='\0'; + + /* Read and dump file */ + if((P=malloc(DSKFileSize(Dsk,J)))) + { + if((K=DSKRead(Dsk,J,(byte *)P,DSKFileSize(Dsk,J)))) + if((F=fopen(Path,"wb"))) { fwrite(P,1,K,F);fclose(F); } + free(P); + } + } + + /* Done processing directory */ + return(Dsk); + } + +#ifdef ZLIB +#define fopen(N,M) (FILE *)gzopen(N,M) +#define fclose(F) gzclose((gzFile)F) +#define fwrite(B,L,N,F) gzwrite((gzFile)F,(byte *)B,(L)*(N)) +#endif + + /* Assume to be a disk image file */ + F=fopen(Name,"wb"); + if(!F) return(0); + + /* Write data */ + if(fwrite(Dsk,1,DSK_DISK_SIZE,F)!=DSK_DISK_SIZE) + { fclose(F);return(0); } + + /* Done */ + fclose(F); + return(Dsk); + +#ifdef ZLIB +#undef fopen +#undef fclose +#undef fwrite +#endif +} diff --git a/components/msx/fmsx/src/EMULib/Floppy.h b/components/msx/fmsx/src/EMULib/Floppy.h new file mode 100644 index 0000000..5d3c6d4 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Floppy.h @@ -0,0 +1,71 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Floppy.h **/ +/** **/ +/** This file declares functions to operate on 720kB **/ +/** floppy disk images. See Floppy.c for implementation. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2004-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef FLOPPY_H +#define FLOPPY_H + +#define DSK_SIDS_PER_DISK 2 +#define DSK_TRKS_PER_SIDE 80 +#define DSK_SECS_PER_TRCK 9 +#define DSK_SECTOR_SIZE 512 + +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif + +/** DSKCreate() **********************************************/ +/** Create disk image in Dsk or allocate memory when Dsk=0. **/ +/** Returns pointer to the disk image or 0 on failure. **/ +/*************************************************************/ +byte *DSKCreate(byte *Dsk,const char *Label); + +/** DSKFile() ************************************************/ +/** Create file with a given name, return file ID or 0 on **/ +/** failure. **/ +/*************************************************************/ +int DSKFile(byte *Dsk,const char *FileName); + +/** DSKFileName() ********************************************/ +/** Return name of a file with a given ID or 0 on failure. **/ +/*************************************************************/ +const char *DSKFileName(const byte *Dsk,int ID); + +/** DSKFileSize() ********************************************/ +/** Return side of a file with a given ID or 0 on failure. **/ +/*************************************************************/ +int DSKFileSize(const byte *Dsk,int ID); + +/** DSKRead()/DSKWrite() *************************************/ +/** Read or write a given number of bytes to a given file. **/ +/** Both functions return the number of bytes written or **/ +/** read from the file, or 0 on failure. **/ +/*************************************************************/ +int DSKWrite(byte *Dsk,int ID,const byte *Buf,int Size); +int DSKRead(const byte *Dsk,int ID,byte *Buf,int Size); + +/** DSKDelete() **********************************************/ +/** Delete file with a given ID. Returns ID on success or 0 **/ +/** on failure. **/ +/*************************************************************/ +int DSKDelete(byte *Dsk,int ID); + +/** DSKLoad()/DSKSave() **************************************/ +/** Load or save disk contents from/to a disk image or a **/ +/** directory. DSKLoad() will allocate space if Dsk=0. Both **/ +/** functions return pointer to disk contents on success or **/ +/** 0 on failure. **/ +/*************************************************************/ +byte *DSKLoad(const char *Name,byte *Dsk,const char *Label); +const byte *DSKSave(const char *Name,const byte *Dsk); + +#endif /* FLOPPY_H */ diff --git a/components/msx/fmsx/src/EMULib/Hunt.c b/components/msx/fmsx/src/EMULib/Hunt.c new file mode 100644 index 0000000..5698a7e --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Hunt.c @@ -0,0 +1,234 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Hunt.c **/ +/** **/ +/** This file implements mechanism for searching possible **/ +/** cheats inside running game data. Also see Hunt.h. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2013-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "Hunt.h" +#include + +#if defined(VGBA) +#include "ARM.h" +#define MEMREAD32(A) QRdARM(A) +#define MEMREAD16(A) WRdARM(A) +#define MEMREAD8(A) BRdARM(A) +#elif defined(INES) +#include "M6502.h" +#define MEMREAD32(A) (Rd6502(A)+((int)Rd6502(A+1)<<8)+((int)Rd6502(A+2)<<16)+((int)Rd6502(A+3)<<24)) +#define MEMREAD16(A) (Rd6502(A)+((int)Rd6502(A+1)<<8)) +#define MEMREAD8(A) Rd6502(A) +#elif defined(VGB) || defined(MG) || defined(COLEM) || defined(SPECCY) || defined(FMSX) +#include "Z80.h" +#define MEMREAD32(A) (RdZ80(A)+((int)RdZ80(A+1)<<8)+((int)RdZ80(A+2)<<16)+((int)RdZ80(A+3)<<24)) +#define MEMREAD16(A) (RdZ80(A)+((int)RdZ80(A+1)<<8)) +#define MEMREAD8(A) RdZ80(A) +#else +#define MEMREAD32(A) (0) +#define MEMREAD16(A) (0) +#define MEMREAD8(A) (0) +#endif + +// static HUNTEntry Buf[HUNT_BUFSIZE]; +HUNTEntry *Buf = NULL; +static int Count; + +/** InitHUNT() ***********************************************/ +/** Initialize cheat search, clearing all data. **/ +/*************************************************************/ +void InitHUNT(void) { Count=0; } + +/** TotalHUNT() **********************************************/ +/** Get total number of currently watched locations. **/ +/*************************************************************/ +int TotalHUNT(void) { return(Count); } + +/** GetHUNT() ************************************************/ +/** Get Nth memory location. Returns 0 for invalid N. **/ +/*************************************************************/ +HUNTEntry *GetHUNT(int N) +{ return((N>=0)&&(N=0x10000)||(NewValue>=0x10000)) + Flags = (Flags&~HUNT_MASK_SIZE)|HUNT_32BIT; + else if((Value>=0x100)||(NewValue>=0x100)) + Flags = (Flags&~HUNT_MASK_SIZE)|HUNT_16BIT; + + /* Compute mask for given value size and truncate value */ + M = Flags&HUNT_32BIT? 0xFFFFFFFF:Flags&HUNT_16BIT? 0xFFFF:0x00FF; + +#ifdef VGBA + /* ARM aligns data to the size boundary */ + if(M>0xFFFF) Addr&=~3; else if(M>0xFF) Addr&=~1; +#endif + + /* Scan memory for given value */ + for(Size+=Addr,I=0;(Addr0xFFFF? MEMREAD32(Addr):M>0xFF? MEMREAD16(Addr):MEMREAD8(Addr))&M; + + if((J==Value)||(J==((Value-1)&M))) + { + Buf[Count].Addr = Addr; + Buf[Count].Flags = Flags; + Buf[Count].Value = J; + Buf[Count].Orig = J==Value? NewValue:((NewValue-1)&M); + Buf[Count].Count = 0; + ++Count; + ++I; + } + +#ifdef VGBA + /* ARM aligns data to the size boundary */ + if(M>0xFFFF) Addr+=3; else if(M>0xFF) Addr+=1; +#endif + } + + /* Return the number of matches found */ + return(I); +} + +/** ScanHUNT() ***********************************************/ +/** Scan memory for changed values and update search data. **/ +/** Returns number of memory locations updated. **/ +/*************************************************************/ +int ScanHUNT(void) +{ + unsigned int K,L; + int J,I; + + /* Scan active search entries */ + for(J=I=0;J0xFFFF? MEMREAD32(Buf[J].Addr):L>0xFF? MEMREAD16(Buf[J].Addr):MEMREAD8(Buf[J].Addr))&L; + + /* Check for expected changes */ + switch(Buf[J].Flags&HUNT_MASK_CHANGE) + { + case HUNT_PLUSONE: L = K==((Buf[J].Value+1)&L);break; + case HUNT_PLUSMANY: L = K>Buf[J].Value;break; + case HUNT_MINUSONE: L = K==((Buf[J].Value-1)&L);break; + case HUNT_MINUSMANY: L = KFlags&HUNT_16BIT? '2':'0', + (HE->Addr&0x000FFFFF)|((HE->Addr&0x0F000000)>>4), + HE->Orig + ); + return(Buf); + + case HUNT_GBA_CB: + /* 2AAAAAAA XXXX - 16bit RAM Write */ + /* 3AAAAAAA 00XX - 8bit RAM Write */ + sprintf(Buf,"%c%07X %04X", + HE->Flags&HUNT_16BIT? '2':'3', + HE->Addr&0x0FFFFFFF, + HE->Orig + ); + return(Buf); + +/** GameBoy **************************************************/ +/** GameBoy has GameShark and GameGenie, but only GameShark **/ +/** can modify RAM. **/ +/*************************************************************/ + case HUNT_GB_GS: + /* 00DDAAAA - 8bit RAM Write, No Bank */ + sprintf(Buf,"00%02X%02X%02X",(HE->Orig)&0xFF,HE->Addr&0x00FF,(HE->Addr&0xFF00)>>8); + if(HE->Flags&HUNT_16BIT) + sprintf(Buf+8,";00%02X%02X%02X",HE->Orig>>8,(HE->Addr+1)&0x00FF,((HE->Addr+1)&0xFF00)>>8); + return(Buf); + +/** NES ******************************************************/ +/** NES has both Pro Action Replay and GameGenie, but only **/ +/** Pro Action Replay can modify RAM. **/ +/*************************************************************/ + case HUNT_NES_AR: + /* 00AAAADD - 8bit RAM Write */ + sprintf(Buf,"00%04X%02X",HE->Addr&0xFFFF,HE->Orig&0xFF); + if(HE->Flags&HUNT_16BIT) + sprintf(Buf+8,";00%04X%02X",(HE->Addr+1)&0xFFFF,HE->Orig>>8); + return(Buf); + +/** Sega MasterSystem and GameGear ***************************/ +/** MasterSystem has Pro Action Replay, while GameGear has **/ +/** GameGenie. Only Pro Action Replay can modify RAM. **/ +/*************************************************************/ + case HUNT_SMS_AR: + /* 00AA-AADD - 8bit RAM Write */ + sprintf(Buf,"00%02X-%02X%02X",(HE->Addr&0xFF00)>>8,HE->Addr&0x00FF,HE->Orig&0xFF); + if(HE->Flags&HUNT_16BIT) + sprintf(Buf+9,";00%02X-%02X%02X",((HE->Addr+1)&0xFF00)>>8,(HE->Addr+1)&0x00FF,HE->Orig>>8); + return(Buf); + +/** ColecoVision, MSX, ZX Spectrum ***************************/ +/** There was no official cheating hardware for these **/ +/** systems, so we come up with our own "hardware". **/ +/*************************************************************/ + case HUNT_MSX: + case HUNT_ZXS: + case HUNT_COLECO: + /* AAAA-DD[DD] - 8bit[16bit] RAM Write */ + if(HE->Flags&HUNT_16BIT) + sprintf(Buf,"%04X-%04X",HE->Addr,HE->Orig&0xFFFF); + else + sprintf(Buf,"%04X-%02X",HE->Addr,HE->Orig&0x00FF); + return(Buf); + } + + /* Invalid cheat type */ + return(0); +} diff --git a/components/msx/fmsx/src/EMULib/Hunt.h b/components/msx/fmsx/src/EMULib/Hunt.h new file mode 100644 index 0000000..1babc29 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Hunt.h @@ -0,0 +1,86 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Hunt.h **/ +/** **/ +/** This file declares functions to search for possible **/ +/** cheats inside running game data. Also see Hunt.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2013-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef HUNT_H +#define HUNT_H + +#define HUNT_BUFSIZE 1024 + +/** Flags used in AddHUNT() **********************************/ +#define HUNT_MASK_ID 0x00FF +#define HUNT_MASK_CHANGE 0x0700 +#define HUNT_CONSTANT 0x0000 +#define HUNT_PLUSONE 0x0100 +#define HUNT_PLUSMANY 0x0200 +#define HUNT_MINUSONE 0x0300 +#define HUNT_MINUSMANY 0x0400 +#define HUNT_MASK_SIZE 0x1800 +#define HUNT_8BIT 0x0000 +#define HUNT_16BIT 0x0800 +#define HUNT_32BIT 0x1000 + +/** Types used in HUNT2Cheat() *******************************/ +#define HUNT_GBA_GS 0 /* GBA GameShark Advance */ +#define HUNT_GBA_CB 1 /* GBA CodeBreaker Advance */ +#define HUNT_SMS_AR 2 /* SMS Pro Action Replay */ +#define HUNT_NES_AR 3 /* NES Pro Action Replay */ +#define HUNT_GB_GS 4 /* GB GameShark */ +#define HUNT_COLECO 5 /* ColecoVision cheats */ +#define HUNT_MSX 6 /* MSX cheats */ +#define HUNT_ZXS 7 /* ZX Spectrum cheats */ + +/** HUNTEntry ************************************************/ +/** Search entry containing data on one memory location. **/ +/*************************************************************/ +typedef struct +{ + unsigned int Addr; /* Memory location address */ + unsigned int Orig; /* Original value at the address */ + unsigned int Value; /* Current value at the address */ + unsigned short Flags; /* Options supplied in AddHUNT() */ + unsigned short Count; /* Number of detected changes */ +} HUNTEntry; + +/** InitHUNT() ***********************************************/ +/** Initialize cheat search, clearing all data. **/ +/*************************************************************/ +void InitHUNT(void); + +/** AddHUNT() ************************************************/ +/** Add a new value to search for, with the address range **/ +/** to search in. Returns number of memory locations found. **/ +/*************************************************************/ +int AddHUNT(unsigned int Addr,unsigned int Size,unsigned int Value,unsigned int NewValue,unsigned int Flags); + +/** ScanHUNT() ***********************************************/ +/** Scan memory for changed values and update search data. **/ +/** Returns number of memory locations updated. **/ +/*************************************************************/ +int ScanHUNT(void); + +/** TotalHUNT() **********************************************/ +/** Get total number of currently watched locations. **/ +/*************************************************************/ +int TotalHUNT(void); + +/** GetHUNT() ************************************************/ +/** Get Nth memory location. Returns 0 for invalid N. **/ +/*************************************************************/ +HUNTEntry *GetHUNT(int N); + +/** HUNT2Cheat() *********************************************/ +/** Create cheat code from Nth hunt entry. Returns 0 if the **/ +/** entry is invalid. **/ +/*************************************************************/ +const char *HUNT2Cheat(int N,unsigned int Type); + +#endif /* HUNT_H */ diff --git a/components/msx/fmsx/src/EMULib/I8255.c b/components/msx/fmsx/src/EMULib/I8255.c new file mode 100644 index 0000000..8c4e2e8 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/I8255.c @@ -0,0 +1,88 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** I8255.c **/ +/** **/ +/** This file contains emulation for the i8255 parallel **/ +/** port interface (PPI) chip from Intel. See I8255.h for **/ +/** declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2001-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "I8255.h" + +/** Reset8255 ************************************************/ +/** Reset the chip. Set all data to 0x00. Set all ports to **/ +/** "input" mode. **/ +/*************************************************************/ +void Reset8255(register I8255 *D) +{ + /* Initialize all registers and ports */ + D->R[0]=D->Rout[0]=D->Rin[0]=0x00; + D->R[1]=D->Rout[1]=D->Rin[1]=0x00; + D->R[2]=D->Rout[2]=D->Rin[2]=0x00; + D->R[3]=0x9B; +} + +/** Write8255 ************************************************/ +/** Write value V into i8255 register A. Returns 0 when A **/ +/** is out of range, 1 otherwise. **/ +/*************************************************************/ +byte Write8255(register I8255 *D,register byte A,register byte V) +{ + switch(A) + { + case 0: + case 1: + case 2: + /* Data registers */ + D->R[A]=V; + break; + case 3: + /* Control register */ + if(V&0x80) D->R[A]=V; + else + { + A=1<<((V&0x0E)>>1); + if(V&0x01) D->R[2]|=A; else D->R[2]&=~A; + } + break; + default: + /* Invalid register */ + return(0); + } + + /* Set output ports */ + V=D->R[3]; + D->Rout[0] = V&0x10? 0x00:D->R[0]; + D->Rout[1] = V&0x02? 0x00:D->R[1]; + D->Rout[2] = ((V&0x01? 0x00:D->R[2])&0x0F) + | ((V&0x08? 0x00:D->R[2])&0xF0); + + /* Done */ + return(1); +} + +/** Read8255 *************************************************/ +/** Read value from an i8255 register A. Returns 0 when A **/ +/** is out of range. **/ +/*************************************************************/ +byte Read8255(register I8255 *D,register byte A) +{ + switch(A) + { + case 0: return(D->R[3]&0x10? D->Rin[0]:D->R[0]); + case 1: return(D->R[3]&0x02? D->Rin[1]:D->R[1]); + case 2: return + ( + ((D->R[3]&0x01? D->Rin[2]:D->R[2])&0x0F)| + ((D->R[3]&0x08? D->Rin[2]:D->R[2])&0xF0) + ); + case 3: return(D->R[3]); + } + + /* Invalid address */ + return(0x00); +} diff --git a/components/msx/fmsx/src/EMULib/I8255.h b/components/msx/fmsx/src/EMULib/I8255.h new file mode 100644 index 0000000..7d3957b --- /dev/null +++ b/components/msx/fmsx/src/EMULib/I8255.h @@ -0,0 +1,56 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** I8255.h **/ +/** **/ +/** This file contains emulation for the i8255 parallel **/ +/** port interface (PPI) chip from Intel. See I8255.h for **/ +/** the actual code. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2001-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef I8255_H +#define I8255_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif + +/** I8255 ****************************************************/ +/** This data structure stores i8255 state and port values. **/ +/*************************************************************/ +typedef struct +{ + byte R[4]; /* Registers */ + byte Rout[3]; /* Output ports */ + byte Rin[3]; /* Input ports */ +} I8255; + +/** Reset8255 ************************************************/ +/** Reset the i8255 chip. Set all data to 0x00. Set all **/ +/** ports to "input" mode. **/ +/*************************************************************/ +void Reset8255(register I8255 *D); + +/** Write8255 ************************************************/ +/** Write value V into i8255 register A. Returns 0 when A **/ +/** is out of range, 1 otherwise. **/ +/*************************************************************/ +byte Write8255(register I8255 *D,register byte A,register byte V); + +/** Read8255 *************************************************/ +/** Read value from an i8255 register A. Returns 0 when A **/ +/** is out of range. **/ +/*************************************************************/ +byte Read8255(register I8255 *D,register byte A); + +#ifdef __cplusplus +} +#endif +#endif /* I8255_H */ diff --git a/components/msx/fmsx/src/EMULib/IPS.c b/components/msx/fmsx/src/EMULib/IPS.c new file mode 100644 index 0000000..c1cb663 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/IPS.c @@ -0,0 +1,131 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** IPS.c **/ +/** **/ +/** This file contains support for the .IPS patch file **/ +/** format. See IPS.h for declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "IPS.h" + +#include +#include + +#if defined(ANDROID) +#include "MemFS.h" +#define fopen mopen +#define fclose mclose +#define fread mread +#define fseek mseek +#elif defined(ZLIB) +#include +#define fopen(F,M) (FILE *)gzopen(F,M) +#define fclose(F) gzclose((gzFile)(F)) +#define fread(B,N,L,F) gzread((gzFile)(F),B,(L)*(N)) +#define fseek(F,N,W) (gzseek((gzFile)(F),N,W)<0? -1:0) +#endif + +/** MeasureIPS() *********************************************/ +/** Find total data size assumed by a given .IPS file. **/ +/*************************************************************/ +unsigned int MeasureIPS(const char *FileName) +{ + return(ApplyIPS(FileName,0,0)); +} + +/** ApplyIPS() ***********************************************/ +/** Loads patches from an .IPS file and applies them to the **/ +/** given data buffer. Returns number of patches applied. **/ +/*************************************************************/ +unsigned int ApplyIPS(const char *FileName,unsigned char *Data,unsigned int Size) +{ + unsigned char Buf[16]; + unsigned int Result,Count,J,N; + FILE *F; + + F = fopen(FileName,"rb"); + if(!F) return(0); + + /* Verify file header */ + if(fread(Buf,1,5,F)!=5) { fclose(F);return(0); } + if(memcmp(Buf,"PATCH",5)) { fclose(F);return(0); } + + for(Result=0,Count=1;fread(Buf,1,5,F)==5;++Count) + { + J = Buf[2]+((unsigned int)Buf[1]<<8)+((unsigned int)Buf[0]<<16); + N = Buf[4]+((unsigned int)Buf[3]<<8); + + /* Apparently, these may signal the end of .IPS file */ + if((J==0xFFFFFF) || !memcmp(Buf,"EOF",3)) break; + + /* If patching with a block of data... */ + if(N) + { + /* Copying data */ + + if(!Data) + { + /* Just measuring patch size */ + if(ResultSize) + { + printf("IPS: Failed applying COPY patch #%d to 0x%X..0x%X of 0x%X bytes.\n",Count,J,J+N-1,Size); + if(fseek(F,N,SEEK_CUR)<0) break; + } + else if(fread(Data+J,1,N,F)==N) + { +// printf("IPS: Applied COPY patch #%d to 0x%X..0x%X.\n",Count,J,J+N-1); + ++Result; + } + else + { + printf("IPS: Failed reading COPY patch #%d from the file.\n",Count); + break; + } + } + else + { + /* Filling with a value */ + + if(fread(Buf,1,3,F)!=3) + { + if(Data) printf("IPS: Failed reading FILL patch #%d from the file.\n",Count); + break; + } + + /* Compute fill length */ + N = ((unsigned int)Buf[0]<<8)+Buf[1]; + + if(!Data) + { + if(ResultSize)) + { + printf("IPS: Failed applying FILL patch #%d (0x%02X) to 0x%X..0x%X of 0x%X bytes.\n",Count,Buf[1],J,J+N-1,Size); + } + else + { +// printf("IPS: Applied FILL patch #%d (0x%02X) to 0x%X..0x%X.\n",Count,Buf[1],J,J+N-1); + memset(Data+J,Buf[2],N); + ++Result; + } + } + } + + fclose(F); + return(Result); +} + +#if defined(ZLIB) || defined(ANDROID) +#undef fopen +#undef fclose +#undef fread +#undef fseek +#endif diff --git a/components/msx/fmsx/src/EMULib/IPS.h b/components/msx/fmsx/src/EMULib/IPS.h new file mode 100644 index 0000000..4647617 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/IPS.h @@ -0,0 +1,27 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** IPS.h **/ +/** **/ +/** This file contains declarations for the .IPS patch file **/ +/** support. See IPS.c for implementation. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef IPS_H +#define IPS_H + +/** ApplyIPS() ***********************************************/ +/** Loads patches from an .IPS file and applies them to the **/ +/** given data buffer. Returns number of patches applied. **/ +/*************************************************************/ +unsigned int ApplyIPS(const char *FileName,unsigned char *Data,unsigned int Size); + +/** MeasureIPS() *********************************************/ +/** Find total data size assumed by a given .IPS file. **/ +/*************************************************************/ +unsigned int MeasureIPS(const char *FileName); + +#endif /* IPS_H */ diff --git a/components/msx/fmsx/src/EMULib/Image.c b/components/msx/fmsx/src/EMULib/Image.c new file mode 100644 index 0000000..9b7141b --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Image.c @@ -0,0 +1,1294 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Image.c **/ +/** **/ +/** This file contains non-essential functions that operate **/ +/** on images. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#if !defined(BPP32) && !defined(BPP24) && !defined(BPP16) && !defined(BPP8) +#include "ImageMux.h" +#else + +#include "EMULib.h" +#include + +#if defined(ARM_CPU) +#include "LibARM.h" +#endif + +#ifdef LIBPNG +#include +#include +#include +#endif + +#ifdef ANDROID +#define puts LOGI +#define printf LOGI +#endif + +#if defined(BPP16) || defined(BPP24) || defined(BPP32) +/** Merge2() *************************************************/ +/** Merges pixels A and B with bias X. **/ +/*************************************************************/ +static __inline pixel Merge2(pixel A,pixel B,unsigned int X) +{ +#if defined(ARM_CPU) + return(MERGE2(A,B,X)); +#else + unsigned int DA,DB; + + // Do not interpolate if values are the same + if(A==B) return A; + + // Reduce 16 bit fraction to 5 bits + DB = (X>>11)&0x001F; + DA = 0x20 - DB; + +#if defined(BPP24) || defined(BPP32) + X = (DA*(A&GMASK)+DB*(B&GMASK))>>5; + DA*= A&(RMASK|BMASK); + DB*= B&(RMASK|BMASK); + DA = (DA+DB)>>5; + return((DA&(RMASK|BMASK))|(X&GMASK)); +#else + DA*= (A&(RMASK|BMASK)) | ((A&GMASK)<<16); + DB*= (B&(RMASK|BMASK)) | ((B&GMASK)<<16); + DA = (DA+DB)>>5; + return((DA&(RMASK|BMASK))|((DA>>16)&GMASK)); +#endif +#endif /* ARM_CPU */ +} + +/** Merge4() *************************************************/ +/** Merges pixels A,B,C,D with biases X,Y. **/ +/*************************************************************/ +static __inline pixel Merge4(pixel A,pixel B,pixel C,pixel D,unsigned int X,unsigned int Y) +{ +#if defined(ARM_CPU) + return(MERGE4(A,B,C,D,X,Y)); +#else + unsigned int DA,DB,DC,DD; + + X = (X>>11)&0x001F; + Y = (Y>>11)&0x001F; + DD = (X*Y)>>5; + DA = 0x20 + DD - X - Y; + DB = X - DD; + DC = Y - DD; + +#if defined(BPP24) || defined(BPP32) + X = (DA*(A&GMASK)+DB*(B&GMASK)+DC*(C&GMASK)+DD*(D&GMASK))>>5; + DA*= A&(RMASK|BMASK); + DB*= B&(RMASK|BMASK); + DC*= C&(RMASK|BMASK); + DD*= D&(RMASK|BMASK); + DD = (DA+DB+DC+DD)>>5; + return((DD&(RMASK|BMASK))|(X&GMASK)); +#else + DA*= (A&(RMASK|BMASK)) | ((A&GMASK)<<16); + DB*= (B&(RMASK|BMASK)) | ((B&GMASK)<<16); + DC*= (C&(RMASK|BMASK)) | ((C&GMASK)<<16); + DD*= (D&(RMASK|BMASK)) | ((D&GMASK)<<16); + DD = (DA+DB+DC+DD)>>5; + return((DD&(RMASK|BMASK))|((DD>>16)&GMASK)); +#endif +#endif /* ARM_CPU */ +} +#endif /* BPP16||BPP24||BPP32 */ + +/** GetColor() ***********************************************/ +/** Return pixel corresponding to the given value. **/ +/** This only works for non-palletized modes. **/ +/*************************************************************/ +pixel GetColor(unsigned char R,unsigned char G,unsigned char B) +{ + return(PIXEL(R,G,B)); +} + +/** ClearImage() *********************************************/ +/** Clear image with a given color. **/ +/*************************************************************/ +void ClearImage(Image *Img,pixel Color) +{ + pixel *P; + int X,Y; + + for(P=(pixel *)Img->Data,Y=Img->H;Y;--Y,P+=Img->L) + for(X=0;XW;++X) P[X]=Color; +} + +/** IMGCopy() ************************************************/ +/** Copy one image into another. **/ +/*************************************************************/ +void IMGCopy(Image *Dst,int DX,int DY,const Image *Src,int SX,int SY,int W,int H,int TColor) +{ + const pixel *S; + pixel *D; + int X; + + if(DX<0) { W+=DX;SX-=DX;DX=0; } + if(DY<0) { H+=DY;SY-=DY;DY=0; } + if(SX<0) { W+=SX;DX-=SX;SX=0; } else if(SX+W>Src->W) W=Src->W-SX; + if(SY<0) { H+=SY;DY-=SY;SY=0; } else if(SY+H>Src->H) H=Src->H-SY; + if(DX+W>Dst->W) W=Dst->W-DX; + if(DY+H>Dst->H) H=Dst->H-DY; + if((W<=0)||(H<=0)) return; + + S = (pixel *)Src->Data+Src->L*SY+SX; + D = (pixel *)Dst->Data+Dst->L*DY+DX; + +#if defined(ARM_CPU) && (defined(BPP16) || defined(BPP24) || defined(BPP32)) + /* ARM optimizations require W aligned to 16 pixels, */ + /* coordinates and strides aligned to 2 pixels... */ + if(!(W&15)&&!(SX&1)&&!(DX&1)&&!(Src->L&1)&&!(Dst->L&1)&&(TColor<0)) + { + for(;H;--H,S+=Src->L,D+=Dst->L) C256T256(D,S,W); + return; + } +#endif /* ARM_CPU && (BPP16 || BPP24 || BPP32) */ + + if(TColor<0) + for(;H;--H,S+=Src->L,D+=Dst->L) + memcpy(D,S,W*sizeof(pixel)); + else + for(;H;--H,S+=Src->L,D+=Dst->L) + for(X=0;XImg->W) W=Img->W-X; + if(Y<0) { H+=Y;Y=0; } else if(Y+H>Img->H) H=Img->H-Y; + + if((W>0)&&(H>0)) + { + for(P=(pixel *)Img->Data+Img->L*Y+X,J=0;JL;H;--H,P+=Img->L) P[0]=P[W-1]=Color; + for(J=0;JImg->W) W=Img->W-X; + if(Y<0) { H+=Y;Y=0; } else if(Y+H>Img->H) H=Img->H-Y; + + if((W>0)&&(H>0)) + for(P=(pixel *)Img->Data+Img->L*Y+X;H;--H,P+=Img->L) + for(X=0;XImg->W-8? Img->W-8:X; + Y = Y<0? 0:Y>Img->H-8? Img->H-8:Y; + + for(K=X;*S;S++) + switch(*S) + { + case '\n': + K=X;Y+=8; + if(Y>Img->H-8) Y=0; + break; + default: + P=(pixel *)Img->Data+Img->L*Y+K; + for(C=CurFont+(*S<<3),J=8;J;P+=Img->L,++C,--J) + for(I=0;I<8;++I) P[I]=*C&(0x80>>I)? FG:BG; + K+=8; + if(X>Img->W-8) + { + K=0;Y+=8; + if(Y>Img->H-8) Y=0; + } + + break; + } +} +***/ + +/** ScaleImage() *********************************************/ +/** Copy Src into Dst, scaling as needed **/ +/*************************************************************/ +void ScaleImage(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + register pixel *DP,*SP,*S; + register unsigned int DX,DY; + + /* If not scaling image, use plain copy */ + if((Dst->W==W)&&(Dst->H==H)) { IMGCopy(Dst,0,0,Src,X,Y,W,H,-1);return; } + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Src->W? Src->W:X; + Y = Y<0? 0:Y>Src->H? Src->H:Y; + W = X+W>Src->W? Src->W-X:W; + H = Y+H>Src->H? Src->H-Y:H; + if(!W||!H) return; + SP = (pixel *)Src->Data+Y*Src->L+X; + DP = (pixel *)Dst->Data; + W = W<<16; + H = H<<16; + DX = (W+Dst->W-1)/Dst->W; + DY = (H+Dst->H-1)/Dst->H; + + for(Y=0;Y>16)*Src->L; + for(X=0;X>16]; + DP+=Dst->L-Dst->W; + } +} + +/** TelevizeImage() ******************************************/ +/** Create televizion effect on the image. **/ +/*************************************************************/ +void TelevizeImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(ARM_CPU) + /* ARM optimizations require W aligned to 16 pixels, */ + /* coordinates and strides aligned to 2 pixels... */ + if(!(W&15)&&!(X&1)&&!(Img->L&1)) + { + for(Y=H;Y>0;--Y,P+=Img->L) + if(Y&1) TELEVIZE1(P,W); else TELEVIZE0(P,W); + return; + } +#endif /* ARM_CPU */ + +#if defined(BPP32) || defined(BPP24) + for(Y=H;Y>0;--Y,P+=Img->L) + if(Y&1) for(X=0;X>4)&0x000F0F0F; + else for(X=0;X>4)&0x000F0F0F; +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + if(Y&1) for(X=0;X>3)&0x18E3; + else for(X=0;X>3)&0x18E3; +#else + /* Other platforms use 15bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + if(Y&1) for(X=0;X>3)&0x0C63;//0x0421;//0x0C63; + else for(X=0;X>3)&0x0C63;//0x0421;//0x0C63; +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** LcdizeImage() ********************************************/ +/** Create LCD effect on the image. **/ +/*************************************************************/ +void LcdizeImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(ARM_CPU) + /* ARM optimizations require W aligned to 16 pixels, */ + /* coordinates and strides aligned to 2 pixels... */ + if(!(W&15)&&!(X&1)&&!(Img->L&1)) + { + for(Y=H;Y>0;--Y,P+=Img->L) LCDIZE(P,W); + return; + } +#endif /* ARM_CPU */ + +#if defined(BPP32) || defined(BPP24) + for(Y=H,W&=~1;Y>0;--Y,P+=Img->L) + for(X=0;X>4)&0x000F0F0F;++X;P[X]+=(~P[X]>>4)&0x000F0F0F; } +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H,W&=~1;Y>0;--Y,P+=Img->L) + for(X=0;X>3)&0x18E3;++X;P[X]+=(~P[X]>>3)&0x18E3; } +#else + /* Other platforms use 15bit color */ + for(Y=H,W&=~1;Y>0;--Y,P+=Img->L) + for(X=0;X>3)&0x0C63;++X;P[X]+=(~P[X]>>3)&0x0C63; } +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** RasterizeImage() *****************************************/ +/** Create raster effect on the image. **/ +/*************************************************************/ +void RasterizeImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(ARM_CPU) + /* ARM optimizations require W aligned to 16 pixels, */ + /* coordinates and strides aligned to 2 pixels... */ + if(!(W&15)&&!(X&1)&&!(Img->L&1)) + { + for(Y=H;Y>0;--Y,P+=Img->L) + if(Y&1) TELEVIZE0(P,W); else LCDIZE(P,W); + return; + } +#endif /* ARM_CPU */ + +#if defined(BPP32) || defined(BPP24) + for(Y=H,W&=~1;Y>0;--Y,P+=Img->L) + if(Y&1) for(X=0;X>4)&0x000F0F0F; + else for(X=0;X>4)&0x000F0F0F;++X;P[X]+=(~P[X]>>4)&0x000F0F0F; } +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H,W&=~1;Y>0;--Y,P+=Img->L) + if(Y&1) for(X=0;X>3)&0x18E3; + else for(X=0;X>3)&0x18E3;++X;P[X]+=(~P[X]>>3)&0x18E3; } +#else + /* Other platforms use 15bit color */ + for(Y=H,W&=~1;Y>0;--Y,P+=Img->L) + if(Y&1) for(X=0;X>3)&0x0C63; + else for(X=0;X>3)&0x0C63;++X;P[X]+=(~P[X]>>3)&0x0C63; } +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** CMYizeImage() ********************************************/ +/** Apply vertical CMY stripes to the image. **/ +/*************************************************************/ +void CMYizeImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P,C; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(BPP32) || defined(BPP24) + for(Y=H,X=0;Y>0;--Y,P+=Img->L-W+X) + { + for(X=W;X>=3;X-=3,P+=3) + { + C=P[0];P[0]=C-((C>>4)&0x0000000F)+((~C>>4)&0x000F0F00); + C=P[1];P[1]=C-((C>>4)&0x000F0000)+((~C>>4)&0x00000F0F); + C=P[2];P[2]=C-((C>>4)&0x00000F00)+((~C>>4)&0x000F000F); + } + if(X>=1) { C=P[0];P[0]=C-((C>>4)&0x0000000F)+((~C>>4)&0x000F0F00); } + if(X>=2) { C=P[1];P[1]=C-((C>>4)&0x000F0000)+((~C>>4)&0x00000F0F); } + } +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H,X=0;Y>0;--Y,P+=Img->L-W+X) + { + for(X=W;X>=3;X-=3,P+=3) + { + C=P[0];P[0]=C-((C>>3)&0x0003)+((~C>>3)&0x18E0); + C=P[1];P[1]=C-((C>>3)&0x1800)+((~C>>3)&0x00E3); + C=P[2];P[2]=C-((C>>3)&0x00E0)+((~C>>3)&0x1803); + } + if(X>=1) { C=P[0];P[0]=C-((C>>3)&0x0003)+((~C>>3)&0x18E0); } + if(X>=2) { C=P[1];P[1]=C-((C>>3)&0x1800)+((~C>>3)&0x00E3); } + } +#else + /* Other platforms use 15bit color */ + for(Y=H,X=0;Y>0;--Y,P+=Img->L-W+X) + { + for(X=W;X>=3;X-=3,P+=3) + { + C=P[0];P[0]=C-((C>>3)&0x0003)+((~C>>3)&0x0C60); + C=P[1];P[1]=C-((C>>3)&0x0C00)+((~C>>3)&0x0063); + C=P[2];P[2]=C-((C>>3)&0x0060)+((~C>>3)&0x0C03); + } + if(X>=1) { C=P[0];P[0]=C-((C>>3)&0x0003)+((~C>>3)&0x0C60); } + if(X>=2) { C=P[1];P[1]=C-((C>>3)&0x0C00)+((~C>>3)&0x0063); } + } +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** RGBizeImage() ********************************************/ +/** Apply vertical RGB stripes to the image. **/ +/*************************************************************/ +void RGBizeImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P,C; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(BPP32) || defined(BPP24) + for(Y=H,X=0;Y>0;--Y,P+=Img->L-W+X) + { + for(X=W;X>=3;X-=3,P+=3) + { + C=P[0];P[0]=C-((C>>4)&0x00000F0F)+((~C>>4)&0x000F0000); + C=P[1];P[1]=C-((C>>4)&0x000F000F)+((~C>>4)&0x00000F00); + C=P[2];P[2]=C-((C>>4)&0x000F0F00)+((~C>>4)&0x0000000F); + } + if(X>=1) { C=P[0];P[0]=C-((C>>4)&0x00000F0F)+((~C>>4)&0x000F0000); } + if(X>=2) { C=P[1];P[1]=C-((C>>4)&0x000F000F)+((~C>>4)&0x00000F00); } + } +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H,X=0;Y>0;--Y,P+=Img->L-W+X) + { + for(X=W;X>=3;X-=3,P+=3) + { + C=P[0];P[0]=C-((C>>3)&0x00E3)+((~C>>3)&0x1800); + C=P[1];P[1]=C-((C>>3)&0x1803)+((~C>>3)&0x00E0); + C=P[2];P[2]=C-((C>>3)&0x18E0)+((~C>>3)&0x0003); + } + if(X>=1) { C=P[0];P[0]=C-((C>>3)&0x00E3)+((~C>>3)&0x1800); } + if(X>=2) { C=P[1];P[1]=C-((C>>3)&0x1803)+((~C>>3)&0x00E0); } + } +#else + /* Other platforms use 15bit color */ + for(Y=H,X=0;Y>0;--Y,P+=Img->L-W+X) + { + for(X=W;X>=3;X-=3,P+=3) + { + C=P[0];P[0]=C-((C>>3)&0x0063)+((~C>>3)&0x0C00); + C=P[1];P[1]=C-((C>>3)&0x0C03)+((~C>>3)&0x0060); + C=P[2];P[2]=C-((C>>3)&0x0C60)+((~C>>3)&0x0003); + } + if(X>=1) { C=P[0];P[0]=C-((C>>3)&0x0063)+((~C>>3)&0x0C00); } + if(X>=2) { C=P[1];P[1]=C-((C>>3)&0x0C03)+((~C>>3)&0x0060); } + } +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** MonoImage() **********************************************/ +/** Turn image into monochrome. **/ +/*************************************************************/ +void MonoImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P,C; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(BPP32) || defined(BPP24) + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0xFF00))>>16) + ((77*(C&0xFF0000))>>24); + C = C<256? C:255; + P[X] = C|(C<<8)|(C<<16); + } +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0x07C0))>>14) + ((77*(C&0xF800))>>19); + C = C<32? C:31; + P[X] = C|(C<<6)|(C<<11); + } +#else + /* Other platforms use 15bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0x03E0))>>13) + ((77*(C&0x7C00))>>18); + C = C<32? C:31; + P[X] = C|(C<<5)|(C<<10); + } +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** SepiaImage() *********************************************/ +/** Turn image into sepia tones. **/ +/*************************************************************/ +void SepiaImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P,C; + register int R,G,B; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(BPP32) || defined(BPP24) + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8)&0xFF; + B = (C>>16)&0xFF; + C = (17826*R + 34996*G + 8585*B)>>16; + C = C&0x00000100? 0x000000FF:C; + C |= ((22872*R + 44958*G + 11010*B)>>16)<<8; + C = C&0x00010000? ((C|0x0000FF00)&0x0000FFFF):C; + C |= ((25756*R + 50397*G + 12386*B)>>16)<<16; + P[X] = C&0x01000000? ((C|0x00FF0000)&0x00FFFFFF):C; + } +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>5; + B = (C&0xF800)>>10; + C = (17826*R + 34996*G + 8585*B)>>17; + C = C&0x0020? 0x001F:C; + C |= ((22872*R + 44958*G + 11010*B)>>16)<<5; + C = C&0x0800? ((C|0x07E0)&0x07FF):C; + R = C + (((25756*R + 50397*G + 12386*B)>>17)<<11); + P[X] = R&0x10000? ((R|0xF800)&0xFFFF):R; + } +#else + /* Other platforms use 15bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>5; + B = (C&0x7C00)>>10; + C = (17826*R + 34996*G + 8585*B)>>16; + C = C&0x0020? 0x001F:C; + C |= ((22872*R + 44958*G + 11010*B)>>16)<<5; + C = C&0x0400? ((C|0x03E0)&0x03FF):C; + C |= ((25756*R + 50397*G + 12386*B)>>16)<<10; + P[X] = C&0x8000? ((C|0x7C00)&0x7FFF):C; + } +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** GreenImage() *********************************************/ +/** Simulate green CRT phosphor. **/ +/*************************************************************/ +void GreenImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P,C; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(BPP32) || defined(BPP24) + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0xFF00))>>16) + ((77*(C&0xFF0000))>>24); + C = C<256? C:255; + P[X] = ((92*C)>>8)|(C<<8)|(((51*C)>>8)<<16); + } +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0x07C0))>>14) + ((77*(C&0xF800))>>19); + C = C<32? C:31; + P[X] = ((92*C)>>8)|(C<<6)|(((51*C)>>8)<<11); + } +#else + /* Other platforms use 15bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0x03E0))>>13) + ((77*(C&0x7C00))>>18); + C = C<32? C:31; + P[X] = ((92*C)>>8)|(C<<5)|(((51*C)>>8)<<10); + } +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** AmberImage() *********************************************/ +/** Simulate amber CRT phosphor. **/ +/*************************************************************/ +void AmberImage(Image *Img,int X,int Y,int W,int H) +{ +#if defined(BPP32) || defined(BPP24) || defined(BPP16) + register pixel *P,C; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Img->W? Img->W:X; + Y = Y<0? 0:Y>Img->H? Img->H:Y; + W = X+W>Img->W? Img->W-X:W; + H = Y+H>Img->H? Img->H-Y:H; + if(!W||!H) return; + P = (pixel *)Img->Data+Y*Img->L+X; + +#if defined(BPP32) || defined(BPP24) + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0xFF00))>>16) + ((77*(C&0xFF0000))>>24); + C = C<256? C:255; + P[X] = ((51*C)>>8)|((215*C)&0xFF00)|(C<<16); + } +#elif defined(UNIX) || defined(ANDROID) || defined(ARM_CPU) || defined(NXC2600) + /* Unix/X11, ARM, and MIPS platforms all use 16bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0x07C0))>>14) + ((77*(C&0xF800))>>19); + C = C<32? C:31; + P[X] = ((51*C)>>8)|(((215*C)>>8)<<6)|(C<<11); + } +#else + /* Other platforms use 15bit color */ + for(Y=H;Y>0;--Y,P+=Img->L) + for(X=0;X>8) + ((161*(C&0x03E0))>>13) + ((77*(C&0x7C00))>>18); + C = C<32? C:31; + P[X] = ((51*C)>>8)|(((215*C)>>8)<<5)|(C<<10); + } +#endif /* !UNIX && !ANDROID && !ARM_CPU && !NXC2600 */ +#endif /* BPP32 || BPP24 || BPP16 */ +} + +/** InterpolateImage() ***************************************/ +/** Scale image Src into Dst using linear interpolation. **/ +/*************************************************************/ +void InterpolateImage(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ +#ifdef BPP8 + /* Things look real nasty in BPP8, better just scale */ + ScaleImage(Dst,Src,X,Y,W,H); +#else + register unsigned int DX,DY; + register const pixel *S,*SP; + register pixel *DP,*D; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Src->W? Src->W:X; + Y = Y<0? 0:Y>Src->H? Src->H:Y; + W = X+W>Src->W-3? Src->W-3-X:W; + H = Y+H>Src->H-3? Src->H-3-Y:H; + // Source image must be at least 4x4 pixels + if((W<=0)||(H<=0)) return; + S = (pixel *)Src->Data+Y*Src->L+Src->L+X+1; + + // Convert to 16:16 fixed point values + W = (W-2)<<16; + DX = (W-0x10001+Dst->W)/Dst->W; + H = (H-2)<<16; + DY = (H-0x10001+Dst->H)/Dst->H; + + for(Y=0x10000,DP=D=(pixel *)Dst->Data;YL)) + { + unsigned int A,B,C,D,Y1; + + // Compute new source pointer + SP = S+(Y>>16)*Src->L; + + // Get fractional part of Y + Y1 = Y&0xFFFF; + + for(X=0x10000;X>16,X+=DX) + { + // Get next 2x2 pixels + SP+= X>>16; + A = SP[0]; + B = SP[1]; + C = SP[Src->L]; + D = SP[Src->L+1]; + + // If all 2x2 pixels are the same, do not interpolate + *DP++ = (A==B && C==D)? (A==C? A:Merge2(A,C,Y1)) + : (A==C && B==D)? Merge2(A,B,X&0xFFFF) + : Merge4(A,B,C,D,X&0xFFFF,Y1); + } + } +#endif /* !BPP8 */ +} + +/** SoftenImage() ********************************************/ +/** Uses softening algorithm to interpolate image Src into **/ +/** a bigger image Dst. **/ +/*************************************************************/ +void SoftenImage(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ +#ifdef BPP8 + /* Things look real nasty in BPP8, better just scale */ + ScaleImage(Dst,Src,X,Y,W,H); +#else + register unsigned int DX,DY; + register const pixel *S,*SP; + register pixel *DP,*D; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Src->W? Src->W:X; + Y = Y<0? 0:Y>Src->H? Src->H:Y; + W = X+W>Src->W-3? Src->W-3-X:W; + H = Y+H>Src->H-3? Src->H-3-Y:H; + // Source image must be at least 4x4 pixels + if((W<=0)||(H<=0)) return; + S = (pixel *)Src->Data+Y*Src->L+Src->L+X+1; + + // Convert to 16:16 fixed point values + W = (W-2)<<16; + DX = (W-0x10001+Dst->W)/Dst->W; + H = (H-2)<<16; + DY = (H-0x10001+Dst->H)/Dst->H; + + for(Y=0x10000,DP=D=(pixel *)Dst->Data;YL)) + { + unsigned int A,B,C,D,X1,X2,Y1,Y2,F1,F2; + + // Compute new source pointer + SP = S+(Y>>16)*Src->L; + // Get fractional part of Y + Y1 = Y&0xFFFF; + Y2 = 0x10000-Y1; + + for(X=0x10000;X>16,X+=DX,*DP++=X2) + { + // SP points to [A]: + // + // E F + // G[A]B I + // H C D J + // K L + + // Get next 2x2 core pixels + SP+= X>>16; + A = SP[0]; + B = SP[1]; + C = SP[Src->L]; + D = SP[Src->L+1]; + + // If all 4x4 pixels are the same... + if(A==B && C==D && A==C) + { + // Do not interpolate + X2 = A; + } + // If diagonal line "\"... + else if(A==D && B!=C) + { + X1 = X&0xFFFF; + F1 = (X1>>1) + (0x10000>>2); + F2 = (Y1>>1) + (0x10000>>2); + + if(Y1<=F1 && A==SP[Src->L+2] && A!=SP[-Src->L]) + X2 = Merge2(A,B,F1-Y1); + else if(Y1>=F1 && A==SP[-1] && A!=SP[(Src->L<<1)+1]) + X2 = Merge2(A,C,Y1-F1); + else if(X1>=F2 && A==SP[-Src->L] && A!=SP[Src->L+2]) + X2 = Merge2(A,B,X1-F2); + else if(X1<=F2 && A==SP[(Src->L<<1)+1] && A!=SP[-1]) + X2 = Merge2(A,C,F2-X1); + else if(Y1>=X1) + X2 = Merge2(A,C,Y1-X1); + else + X2 = Merge2(A,B,X1-Y1); + } + // If diagonal line "/"... + else if(B==C && A!=D) + { + X1 = X&0xFFFF; + X2 = 0x10000-X1; + F1 = (X1>>1) + (0x10000>>2); + F2 = (Y1>>1) + (0x10000>>2); + + if(Y2>=F1 && B==SP[Src->L-1] && B!=SP[-Src->L+1]) + X2 = Merge2(B,A,Y2-F1); + else if(Y2<=F1 && B==SP[2] && B!=SP[Src->L<<1]) + X2 = Merge2(B,D,F1-Y2); + else if(X2>=F2 && B==SP[-Src->L+1] && B!=SP[Src->L-1]) + X2 = Merge2(B,A,X2-F2); + else if(X2<=F2 && B==SP[Src->L<<1] && B!=SP[2]) + X2 = Merge2(B,D,F2-X2); + else if(Y2>=X1) + X2 = Merge2(B,A,Y2-X1); + else + X2 = Merge2(B,D,X1-Y2); + } + // No diagonal lines detected... + else + { + X2 = Merge4(A,B,C,D,X&0xFFFF,Y1); + } + } + } +#endif /* !BPP8 */ +} + +/** SoftenEPX() **********************************************/ +/** Uses EPX softening algorithm to interpolate image Src **/ +/** into a bigger image Dst. **/ +/*************************************************************/ +void SoftenEPX(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ +#ifdef BPP8 + /* Things look real nasty in BPP8, better just scale */ + ScaleImage(Dst,Src,X,Y,W,H); +#else + register unsigned int DX,DY; + register const pixel *S,*SP; + register pixel *DP,*D; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Src->W? Src->W:X; + Y = Y<0? 0:Y>Src->H? Src->H:Y; + W = X+W>Src->W-2? Src->W-2-X:W; + H = Y+H>Src->H-2? Src->H-2-Y:H; + // Source image must be at least 3x3 pixels + if((W<=0)||(H<=0)) return; + S = (pixel *)Src->Data+Y*Src->L+Src->L+X+1; + + // Convert to 16:16 fixed point values + W = (W-2)<<16; + DX = (W-0x10001+Dst->W)/Dst->W; + H = (H-2)<<16; + DY = (H-0x10001+Dst->H)/Dst->H; + + for(Y=0x10000,DP=D=(pixel *)Dst->Data;YL)) + { + unsigned int A,B,C,D,P,NW,NE,SW,SE,X1,Y1; + + // Compute new source pointer + SP = S+(Y>>16)*Src->L; + Y1 = Y&0xFFFF; + + for(X=0x10000;X>16,X+=DX,*DP++=P) + { + // SP points to [P]: + // + // A + // C[P]B + // D + + // Get next 3x3 core pixels + SP+= X>>16; + A = SP[-Src->L]; + B = SP[1]; + C = SP[-1]; + D = SP[Src->L]; + P = SP[0]; + + // Compute 2x2 output pixels + NW = (C==A) && (C!=D) && (A!=B)? A:P; + NE = (A==B) && (A!=C) && (B!=D)? B:P; + SW = (D==C) && (D!=B) && (C!=A)? C:P; + SE = (B==D) && (B!=A) && (D!=C)? D:P; + X1 = X&0xFFFF; + + // Merge pixels, with thresholds + if(X1<=0x4000) + P = Y1<=0x4000? NW : Y1>=0xC000? SW : Merge2(NW,SW,Y1); + else if(X1>=0xC000) + P = Y1<=0x4000? NE : Y1>=0xC000? SE : Merge2(NE,SE,Y1); + else + P = Merge4(NW,NE,SW,SE,X1,Y1); + } + } +#endif /* !BPP8 */ +} + +/** SoftenSCALE2X() ******************************************/ +/** Uses SCALE2X softening algorithm to interpolate image **/ +/** Src into a bigger image Dst. **/ +/*************************************************************/ +void SoftenSCALE2X(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ +#ifdef BPP8 + /* Things look real nasty in BPP8, better just scale */ + ScaleImage(Dst,Src,X,Y,W,H); +#else + register unsigned int DX,DY; + register const pixel *S,*SP; + register pixel *DP,*D; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Src->W? Src->W:X; + Y = Y<0? 0:Y>Src->H? Src->H:Y; + W = X+W>Src->W-2? Src->W-2-X:W; + H = Y+H>Src->H-2? Src->H-2-Y:H; + // Source image must be at least 3x3 pixels + if((W<=0)||(H<=0)) return; + S = (pixel *)Src->Data+Y*Src->L+Src->L+X+1; + + // Convert to 16:16 fixed point values + W = (W-2)<<16; + DX = (W-0x10001+Dst->W)/Dst->W; + H = (H-2)<<16; + DY = (H-0x10001+Dst->H)/Dst->H; + + for(Y=0x10000,DP=D=(pixel *)Dst->Data;YL)) + { + unsigned int A,B,C,D,P,NW,NE,SW,SE,X1,Y1; + + // Compute new source pointer + SP = S+(Y>>16)*Src->L; + Y1 = Y&0xFFFF; + + for(X=0x10000;X>16,X+=DX,*DP++=P) + { + // SP points to [P]: + // + // A + // C[P]B + // D + + // Get next 3x3 core pixels + SP+= X>>16; + A = SP[-Src->L]; + B = SP[1]; + C = SP[-1]; + D = SP[Src->L]; + P = SP[0]; + + if((A!=D) && (B!=C)) + { + // Compute 2x2 output pixels + NW = A==C? C:P; + NE = A==B? B:P; + SW = C==D? C:P; + SE = B==D? B:P; + X1 = X&0xFFFF; + + // Merge pixels, with thresholds + if(X1<=0x4000) + P = Y1<=0x4000? NW : Y1>=0xC000? SW : Merge2(NW,SW,Y1); + else if(X1>=0xC000) + P = Y1<=0x4000? NE : Y1>=0xC000? SE : Merge2(NE,SE,Y1); + else + P = Merge4(NW,NE,SW,SE,X1,Y1); + } + } + } +#endif /* !BPP8 */ +} + +/** SoftenEAGLE() ********************************************/ +/** Uses EAGLE softening algorithm to interpolate image Src **/ +/** into a bigger image Dst. **/ +/*************************************************************/ +void SoftenEAGLE(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ +#ifdef BPP8 + /* Things look real nasty in BPP8, better just scale */ + ScaleImage(Dst,Src,X,Y,W,H); +#else + register unsigned int DX,DY; + register const pixel *S,*SP; + register pixel *DP,*D; + + /* Check arguments, compute start pointer */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Src->W? Src->W:X; + Y = Y<0? 0:Y>Src->H? Src->H:Y; + W = X+W>Src->W-2? Src->W-2-X:W; + H = Y+H>Src->H-2? Src->H-2-Y:H; + // Source image must be at least 3x3 pixels + if((W<=0)||(H<=0)) return; + S = (pixel *)Src->Data+Y*Src->L+Src->L+X+1; + + // Convert to 16:16 fixed point values + W = (W-2)<<16; + DX = (W-0x10001+Dst->W)/Dst->W; + H = (H-2)<<16; + DY = (H-0x10001+Dst->H)/Dst->H; + + for(Y=0x10000,DP=D=(pixel *)Dst->Data;YL)) + { + unsigned int A,B,C,D,P,NW,NE,SW,SE,X1,Y1; + + // Compute new source pointer + SP = S+(Y>>16)*Src->L; + Y1 = Y&0xFFFF; + + for(X=0x10000;X>16,X+=DX,*DP++=P) + { + // SP points to [P]: + // + // A + // C[P]B + // D + + // Get next 3x3 core pixels + SP+= X>>16; + A = SP[-Src->L]; + B = SP[1]; + C = SP[-1]; + D = SP[Src->L]; + P = SP[0]; + + // Compute 2x2 output pixels + NW = (A==C) && (A==SP[-Src->L-1])? A:P; + NE = (B==A) && (B==SP[-Src->L+1])? B:P; + SW = (C==D) && (C==SP[Src->L-1])? C:P; + SE = (D==B) && (D==SP[Src->L+1])? D:P; + X1 = X&0xFFFF; + + // Merge pixels, with thresholds + if(X1<=0x4000) + P = Y1<=0x4000? NW : Y1>=0xC000? SW : Merge2(NW,SW,Y1); + else if(X1>=0xC000) + P = Y1<=0x4000? NE : Y1>=0xC000? SE : Merge2(NE,SE,Y1); + else + P = Merge4(NW,NE,SW,SE,X1,Y1); + } + } +#endif /* !BPP8 */ +} + +#if defined(ARM_CPU) && (defined(BPP16) || defined(BPP24) || defined(BPP32)) +unsigned int ARMScaleImage(Image *Dst,Image *Src,int X,int Y,int W,int H,int Attrs) +{ + void (*F)(pixel *D,const pixel *S,int L); + pixel *S,*D; + int DW,DY,J,I; + + /* Check arguments */ + if(W<0) { X+=W;W=-W; } + if(H<0) { Y+=H;H=-H; } + X = X<0? 0:X>Src->W? Src->W:X; + Y = Y<0? 0:Y>Src->H? Src->H:Y; + W = X+W>Src->W? Src->W-X:W; + H = Y+H>Src->H? Src->H-Y:H; + if(!W||!H) return(0); + + /* ARM optimizations require W and X aligned to */ + /* 16 pixels and strides aligned to 2 pixels... */ + if((W&15)||(X&1)||(Src->L&1)||(Dst->L&1)) + { + ScaleImage(Dst,Src,X,Y,W,H); + return((Dst->H<<16)|Dst->W); + } + + /* Determine optimal handler depending on DstW/SrcW ratio */ + F = 0; + I = Dst->W; + DW = (I<<8)/W; + DY = Attrs&ARMSI_HFILL; + if((DW>1536)&&((J=W<<3)<=I)&&((H<<3)<=Dst->H)) F=C256T2048; + else if((DW>1280)&&((J=(W<<2)+(W<<1))<=I)&&((H<<2)+(H<<1)<=Dst->H)) F=C256T1536; + else if((DW>1024)&&((J=(W<<2)+W)<=I)&&((H<<2)+H<=Dst->H)) F=C256T1280; + else if((DW>768)&&((J=W<<2)<=I)&&((H<<2)<=Dst->H)) F=C256T1024; + else if((DW>512)&&((J=(W<<1)+W)<=I)&&((H<<1)+H<=Dst->H)) F=C256T768; + else if((DW>480)&&((J=W<<1)<=I)&&((H<<1)<=Dst->H)) F=C256T512; + else if((DW>448)&&((J=(480*W)>>8)<=I)) F=C256T480; + else if((DW>416)&&((J=(448*W)>>8)<=I)) F=C256T448; + else if(DY&&(DW>352)&&((J=(416*W)>>8)<=I)) F=C256T416; + else if(DY&&(DW>320)&&((J=(352*W)>>8)<=I)) F=C256T352; + else if(DY&&((J=I)==320)&&(W==240)) F=C240T320; + else if(DY&&(DW>256)&&((J=(320*W)>>8)<=I)) F=C256T320; + else if((DW>240)&&((J=W)<=I)) F=C256T256; + else if((DW>224)&&((J=(240*W)>>8)<=I)) F=C256T240; + else if((DW>208)&&((J=(224*W)>>8)<=I)) F=C256T224; + else if((DW>176)&&((J=(208*W)>>8)<=I)) F=C256T208; + else if((DW>160)&&((J=(176*W)>>8)<=I)) F=C256T176; + else if((DW>120)&&((J=(160*W)>>8)<=I)) F=C256T160; + else if((J=(120*W)>>8)<=I) F=C256T120; + + /* If handler found... */ + if(F) + { + /* Compute source and destination pointers (align destination to 32bit) */ + DW = J; + J = Attrs&ARMSI_VFILL? Dst->H:((H*(W>H? W/H:1))*DW/W); + J = J>Dst->H? Dst->H:J; + D = (pixel *)Dst->Data+((Dst->H-J)>>1)*Dst->L+(((Dst->W-DW)>>1)&~1); + S = (pixel *)Src->Data+Y*Src->L+X; + DY = ((H<<16)+J-1)/J; + +/* Show selected video resolution */ +//printf("%dx%d into %dx%d => %dx%d\n",W,H,Dst->W,Dst->H,DW,J); + + /* Scale the first line */ + (*F)(D,S,W); + + /* Scale line by line, copy repeating lines */ + for(H=J-1,D+=Dst->L,Y=0;H;--H,D+=Dst->L,Y=X) + { + X=Y+DY; + if((Y^X)>>16) (*F)(D,S+(X>>16)*Src->L,W); else C256T256(D,D-Dst->L,DW); + } + + /* Done, return destination width/height */ + return((J<<16)|DW); + } + + /* ARMScaleImage() does not support this platform */ + ScaleImage(Dst,Src,X,Y,W,H); + return((Dst->H<<16)|Dst->W); +} +#endif /* ARM_CPU && (BPP16||BPP24||BPP32) */ + +#ifdef LIBPNG +int LoadPNG(Image *Img,const char *FileName) +{ + png_structp PNGRead; + png_infop PNGInfo; + png_infop PNGEnd; + png_bytepp Rows; + png_bytep Src; + png_uint_32 W,H; + int J,D,X,Y; + char Buf[16]; + pixel *Dst; + FILE *F; + + /* Open file */ + F=fopen(FileName,"rb"); + if(!F) return(0); + + /* Read and verify header */ +// J=fread(Buf,1,8,F); +//printf("Checking PNG tag %s (%d)\n",Buf,J);fflush(stdout); +// if(!J||!png_sig_cmp(Buf,0,J)) { fclose(F);return(0); } + + PNGRead = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); + PNGInfo = PNGRead? png_create_info_struct(PNGRead):0; + PNGEnd = PNGInfo? png_create_info_struct(PNGRead):0; + + if(!PNGRead||!PNGInfo||!PNGEnd) + { + png_destroy_read_struct(PNGRead? &PNGRead:0,PNGInfo? &PNGInfo:0,PNGEnd? &PNGEnd:0); + fclose(F); + return(0); + } + + if(setjmp(png_jmpbuf(PNGRead))) + { + png_destroy_read_struct(&PNGRead,&PNGInfo,&PNGEnd); + fclose(F); + return(0); + } + + /* Read PNG file into memory */ + png_init_io(PNGRead,F); + png_read_png(PNGRead,PNGInfo,PNG_TRANSFORM_EXPAND,0); + + /* Get metadata */ + Rows = png_get_rows(PNGRead,PNGInfo); + W = png_get_image_width(PNGRead,PNGInfo); + H = png_get_image_height(PNGRead,PNGInfo); + D = png_get_bit_depth(PNGRead,PNGInfo); + W = W>Img->W? Img->W:W; + H = H>Img->H? Img->H:H; + + /* Copy image */ + for(Y=0,Dst=Img->Data;YL-W) + for(X=0,Src=Rows[Y];X value. **/ +/** This only works for non-palletized modes. **/ +/*************************************************************/ +pixel GetColor(unsigned char R,unsigned char G,unsigned char B) +{ + if(VideoImg) + switch(VideoImg->D) + { + case 8: return(GetColor_8(R,G,B)); + case 16: return(GetColor_16(R,G,B)); + case 24: + case 32: return(GetColor_32(R,G,B)); + } + + return(0); +} + +/** ClearImage() *********************************************/ +/** Clear image with a given color. **/ +/*************************************************************/ +void ClearImage(Image *Img,pixel Color) +{ + switch(Img->D) + { + case 8: ClearImage_8(Img,Color);break; + case 16: ClearImage_16(Img,Color);break; + case 24: + case 32: ClearImage_32(Img,Color);break; + } +} + +/** IMGCopy() ************************************************/ +/** Copy one image into another. **/ +/*************************************************************/ +void IMGCopy(Image *Dst,int DX,int DY,const Image *Src,int SX,int SY,int W,int H,int TColor) +{ + if(Src->D!=Dst->D) return; + + switch(Dst->D) + { + case 8: IMGCopy_8(Dst,DX,DY,Src,SX,SY,W,H,TColor);break; + case 16: IMGCopy_16(Dst,DX,DY,Src,SX,SY,W,H,TColor);break; + case 24: + case 32: IMGCopy_32(Dst,DX,DY,Src,SX,SY,W,H,TColor);break; + } +} + +/** IMGDrawRect()/IMGFillRect() ******************************/ +/** Draw filled/unfilled rectangle in a given image. **/ +/*************************************************************/ +void IMGDrawRect(Image *Img,int X,int Y,int W,int H,pixel Color) +{ + switch(Img->D) + { + case 8: IMGDrawRect_8(Img,X,Y,W,H,Color);break; + case 16: IMGDrawRect_16(Img,X,Y,W,H,Color);break; + case 24: + case 32: IMGDrawRect_32(Img,X,Y,W,H,Color);break; + } +} + +void IMGFillRect(Image *Img,int X,int Y,int W,int H,pixel Color) +{ + switch(Img->D) + { + case 8: IMGFillRect_8(Img,X,Y,W,H,Color);break; + case 16: IMGFillRect_16(Img,X,Y,W,H,Color);break; + case 24: + case 32: IMGFillRect_32(Img,X,Y,W,H,Color);break; + } +} + +/** SoftenImage() ********************************************/ +/** Uses softening algorithm to interpolate image Src into **/ +/** a bigger image Dst. **/ +/*************************************************************/ +void SoftenImage(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + if(Src->D!=Dst->D) return; + + switch(Dst->D) + { + case 8: SoftenImage_8(Dst,Src,X,Y,W,H);break; + case 16: SoftenImage_16(Dst,Src,X,Y,W,H);break; + case 24: + case 32: SoftenImage_32(Dst,Src,X,Y,W,H);break; + } +} + +/** SoftenEPX() **********************************************/ +/** Uses softening algorithm to interpolate image Src into **/ +/** a bigger image Dst. **/ +/*************************************************************/ +void SoftenEPX(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + if(Src->D!=Dst->D) return; + + switch(Dst->D) + { + case 8: SoftenEPX_8(Dst,Src,X,Y,W,H);break; + case 16: SoftenEPX_16(Dst,Src,X,Y,W,H);break; + case 24: + case 32: SoftenEPX_32(Dst,Src,X,Y,W,H);break; + } +} + +/** SoftenEAGLE() ********************************************/ +/** Uses softening algorithm to interpolate image Src into **/ +/** a bigger image Dst. **/ +/*************************************************************/ +void SoftenEAGLE(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + if(Src->D!=Dst->D) return; + + switch(Dst->D) + { + case 8: SoftenEAGLE_8(Dst,Src,X,Y,W,H);break; + case 16: SoftenEAGLE_16(Dst,Src,X,Y,W,H);break; + case 24: + case 32: SoftenEAGLE_32(Dst,Src,X,Y,W,H);break; + } +} + +/** SoftenSCALE2X() ******************************************/ +/** Uses softening algorithm to interpolate image Src into **/ +/** a bigger image Dst. **/ +/*************************************************************/ +void SoftenSCALE2X(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + if(Src->D!=Dst->D) return; + + switch(Dst->D) + { + case 8: SoftenSCALE2X_8(Dst,Src,X,Y,W,H);break; + case 16: SoftenSCALE2X_16(Dst,Src,X,Y,W,H);break; + case 24: + case 32: SoftenSCALE2X_32(Dst,Src,X,Y,W,H);break; + } +} + +/** ScaleImage() *********************************************/ +/** Copy Src into Dst, scaling as needed **/ +/*************************************************************/ +void ScaleImage(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + if(Src->D!=Dst->D) return; + + switch(Dst->D) + { + case 8: ScaleImage_8(Dst,Src,X,Y,W,H);break; + case 16: ScaleImage_16(Dst,Src,X,Y,W,H);break; + case 24: + case 32: ScaleImage_32(Dst,Src,X,Y,W,H);break; + } +} + +/** InterpolateImage() ***************************************/ +/** Scale Src into Dst using simple interpolation. **/ +/*************************************************************/ +void InterpolateImage(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + if(Src->D!=Dst->D) return; + + switch(Dst->D) + { + case 8: InterpolateImage_8(Dst,Src,X,Y,W,H);break; + case 16: InterpolateImage_16(Dst,Src,X,Y,W,H);break; + case 24: + case 32: InterpolateImage_32(Dst,Src,X,Y,W,H);break; + } +} + +/** TelevizeImage() ******************************************/ +/** Create televizion effect on the image. **/ +/*************************************************************/ +void TelevizeImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: TelevizeImage_8(Img,X,Y,W,H);break; + case 16: TelevizeImage_16(Img,X,Y,W,H);break; + case 24: + case 32: TelevizeImage_32(Img,X,Y,W,H);break; + } +} + +/** LcdizeImage() ********************************************/ +/** Create LCD effect on the image. **/ +/*************************************************************/ +void LcdizeImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: LcdizeImage_8(Img,X,Y,W,H);break; + case 16: LcdizeImage_16(Img,X,Y,W,H);break; + case 24: + case 32: LcdizeImage_32(Img,X,Y,W,H);break; + } +} + +/** RasterizeImage() *****************************************/ +/** Create raster effect on the image. **/ +/*************************************************************/ +void RasterizeImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: RasterizeImage_8(Img,X,Y,W,H);break; + case 16: RasterizeImage_16(Img,X,Y,W,H);break; + case 24: + case 32: RasterizeImage_32(Img,X,Y,W,H);break; + } +} + +/** CMYizeImage() ********************************************/ +/** Apply vertical CMY stripes to the image. **/ +/*************************************************************/ +void CMYizeImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: CMYizeImage_8(Img,X,Y,W,H);break; + case 16: CMYizeImage_16(Img,X,Y,W,H);break; + case 24: + case 32: CMYizeImage_32(Img,X,Y,W,H);break; + } +} + +/** RGBizeImage() ********************************************/ +/** Apply vertical RGB stripes to the image. **/ +/*************************************************************/ +void RGBizeImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: RGBizeImage_8(Img,X,Y,W,H);break; + case 16: RGBizeImage_16(Img,X,Y,W,H);break; + case 24: + case 32: RGBizeImage_32(Img,X,Y,W,H);break; + } +} + +/** MonoImage() **********************************************/ +/** Turn image into monochrome. **/ +/*************************************************************/ +void MonoImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: MonoImage_8(Img,X,Y,W,H);break; + case 16: MonoImage_16(Img,X,Y,W,H);break; + case 24: + case 32: MonoImage_32(Img,X,Y,W,H);break; + } +} + +/** SepiaImage() *********************************************/ +/** Turn image into sepia tones. **/ +/*************************************************************/ +void SepiaImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: SepiaImage_8(Img,X,Y,W,H);break; + case 16: SepiaImage_16(Img,X,Y,W,H);break; + case 24: + case 32: SepiaImage_32(Img,X,Y,W,H);break; + } +} + +/** GreenImage() *********************************************/ +/** Simulate green CRT phosphor. **/ +/*************************************************************/ +void GreenImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: GreenImage_8(Img,X,Y,W,H);break; + case 16: GreenImage_16(Img,X,Y,W,H);break; + case 24: + case 32: GreenImage_32(Img,X,Y,W,H);break; + } +} + +/** AmberImage() *********************************************/ +/** Simulate amber CRT phosphor. **/ +/*************************************************************/ +void AmberImage(Image *Img,int X,int Y,int W,int H) +{ + switch(Img->D) + { + case 8: AmberImage_8(Img,X,Y,W,H);break; + case 16: AmberImage_16(Img,X,Y,W,H);break; + case 24: + case 32: AmberImage_32(Img,X,Y,W,H);break; + } +} + +#ifdef ARM_CPU +/** ARMScaleImage() ******************************************/ +/** Copy Src into Dst using ARM-optimized assembly code. **/ +/** Returns 0 if this function is not supported or there is **/ +/** an alignment problem. Returns destination height and **/ +/** width on success in format. **/ +/*************************************************************/ +unsigned int ARMScaleImage(Image *Dst,Image *Src,int X,int Y,int W,int H,int Attrs) +{ + if(Src->D!=Dst->D) return(0); + + switch(Dst->D) + { + case 16: return(ARMScaleImage_16(Dst,Src,X,Y,W,H,Attrs)); + case 24: + case 32: return(ARMScaleImage_32(Dst,Src,X,Y,W,H,Attrs)); + } + + /* ARMScaleImage() does not support this image depth */ + ScaleImage(Dst,Src,X,Y,W,H); + return((Dst->H<<16)|Dst->W); +} +#endif /* ARM_CPU */ + +#endif /* IMAGEMUX_H */ diff --git a/components/msx/fmsx/src/EMULib/MCF.c b/components/msx/fmsx/src/EMULib/MCF.c new file mode 100644 index 0000000..f8741d0 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/MCF.c @@ -0,0 +1,72 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** MCF.c **/ +/** **/ +/** This file contains support for the .MCF cheat file **/ +/** format. See MCF.h for declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2017 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "MCF.h" + +#include +#include + +/** LoadFileMCF() ********************************************/ +/** Load cheats from .MCF file. Returns number of loaded **/ +/** cheat entries or 0 on failure. **/ +/*************************************************************/ +int LoadFileMCF(const char *Name,MCFEntry *Cheats,int MaxCheats) +{ + char Buf[256],Note[256]; + unsigned int Arg0,Addr,Data,Arg3; + int J; + FILE *F; + + /* Open .MCF text file with cheats */ + F = fopen(Name,"rb"); + if(!F) return(0); + + /* Load cheats from file */ + for(J=0;!feof(F)&&(J0xFFFF? 4:Data>0xFF? 2:1; + strncpy(Cheats[J].Note,Note,sizeof(Cheats[J].Note)); + Cheats[J].Note[sizeof(Cheats[J].Note)-1] = '\0'; + ++J; + } + + /* Done with the file */ + fclose(F); + + /* Done */ + return(J); +} + +/** SaveFileMCF() ********************************************/ +/** Save cheats to .MCF file. Returns number of loaded **/ +/** cheat entries or 0 on failure. **/ +/*************************************************************/ +int SaveFileMCF(const char *Name,const MCFEntry *Cheats,int CheatCount) +{ + FILE *F; + int J; + + /* Open .MCF text file with cheats */ + F = fopen(Name,"wb"); + if(!F) return(0); + + /* Save cheats */ + for(J=0;J +#include + +/** Dummy NET*() Functions ***********************************/ +/** These dummy functions are for systems where networking **/ +/** is not implemented. **/ +/*************************************************************/ +#if !defined(WINDOWS) && !defined(UNIX) && !defined(ANDROID) && !defined(MAEMO) && !defined(MEEGO) && !defined(S60) && !defined(UIQ) +const char *NETMyName(char *Buffer,int MaxChars) { return(0); } +int NETConnect(const char *Server,unsigned short Port) { return(NET_OFF); } +int NETConnectAsync(const char *Server,unsigned short Port) { return(0); } +int NETSend(const char *Out,int N) { return(0); } +int NETRecv(char *In,int N) { return(0); } +int NETBlock(int Switch) { return(NET_OFF); } +int NETConnected(void) { return(NET_OFF); } +void NETClose(void) { } +#endif + +/** NETPlay() ************************************************/ +/** Connect/disconnect NetPlay interface, toggle or query **/ +/** connection status (use NET_* as argument). Returns the **/ +/** NetPlay connection status. **/ +/*************************************************************/ +int NETPlay(int Switch) +{ + char S[256],T[256],*P; + int J; + + // Toggle connection if requested + if(Switch==NET_TOGGLE) Switch=NETConnected()? NET_OFF:NET_ON; + + switch(Switch) + { + case NET_OFF: + // Disconnect NetPlay + NETClose(); + break; + case NET_ON: + case NET_SERVER: + // If already connected, drop out + if(NETConnected()==Switch) break; + // If connecting in client mode, ask for hostname + if((Switch==NET_SERVER)||CONInput( + -1,-1,NET_FGCOLOR,NET_BGCOLOR, + "Enter Hostname",S,sizeof(S) + )) + { + // Compose title + T[0]='~'; + if(!NETMyName(T+1,sizeof(T)-1)) strcpy(T+1,"Network Play"); + // Show "Connecting" message + CONMsg(-1,-1,-1,-1,NET_FGCOLOR,NET_OKCOLOR,T," \0 Connecting... \0 \0"); + ShowVideo(); + // Convert string to lower case, remove spaces + if(Switch==NET_SERVER) + { + for(J=0,P=S;*P;++P) + if(*P>' ') S[J++]=(*P>='A')&&(*P<='Z')? *P+'a'-'A':*P; + S[J]='\0'; + } + // Connect second player, report problems if present + if(!NETConnect(Switch==NET_SERVER? 0:S,NET_PORT)) + CONMsg( + -1,-1,-1,-1,NET_FGCOLOR,NET_ERRCOLOR, + T+1," \0 Failed! \0 \0" + ); + } + break; + } + + // Always return connection status + return(NETConnected()); +} + +/** NETExchange() ********************************************/ +/** Exchanges given number of bytes with the other side and **/ +/** returns 1 on success, 0 on failure. **/ +/*************************************************************/ +int NETExchange(char *In,const char *Out,int N) +{ + /* Send data */ + if(NETSend(Out,N)!=N) return(0); + /* Receive data */ + if(NETRecv(In,N)!=N) return(0); + /* Success */ + return(1); +} + +/** NETJoystick() ********************************************/ +/** When NetPlay disconnected, returns GetJoystick(). Else, **/ +/** exchanges joystick states over the network and presents **/ +/** server as player #1, client as player #2. **/ +/*************************************************************/ +unsigned int NETJoystick(void) +{ + unsigned int J,I; + + /* Get local joystick state */ + J = GetJoystick(); + + /* When ALT pressed, swap players back */ + if(J&BTN_ALT) J=(J&BTN_MODES)|((J>>16)&BTN_ALL)|((J&BTN_ALL)<<16); + + /* If exchanged joystick states over the network... */ + /* Assume server to be player #1, client to be player #2 */ + if(NETExchange((char *)&I,(const char *)&J,sizeof(J))) + J = NETConnected()==NET_SERVER? + ((J&(BTN_ALL|BTN_MODES))|((I&BTN_ALL)<<16)) + : ((I&(BTN_ALL|BTN_MODES))|((J&BTN_ALL)<<16)); + + /* Done */ + return(J); +} diff --git a/components/msx/fmsx/src/EMULib/NetPlay.h b/components/msx/fmsx/src/EMULib/NetPlay.h new file mode 100644 index 0000000..283500d --- /dev/null +++ b/components/msx/fmsx/src/EMULib/NetPlay.h @@ -0,0 +1,114 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** NetPlay.h **/ +/** **/ +/** This file contains declarations for routines that let **/ +/** people play over the network. See NetPlay.c for the **/ +/** platform-independent implementation part. Platform **/ +/** specific implementation for platform XXX goes into **/ +/** NetXXX.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef NETPLAY_H +#define NETPLAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** NetPlay **************************************************/ +/** NET_PORT is used by NetPlay to exchange joypad states **/ +/** with the other player. NET_* values are arguments to **/ +/** NETPlay(). **/ +/*************************************************************/ +#define NET_PORT 6666 + +#define NET_OFF 0 +#define NET_ON 1 +#define NET_CLIENT 1 +#define NET_SERVER 2 +#define NET_TOGGLE 3 +#define NET_QUERY 4 + +#define NET_FGCOLOR PIXEL(255,255,255) +#define NET_BGCOLOR PIXEL(80,40,0) +#define NET_OKCOLOR PIXEL(0,80,0) +#define NET_ERRCOLOR PIXEL(200,0,0) + +/** NETPlay() ************************************************/ +/** Connect/disconnect NetPlay interface, toggle or query **/ +/** connection status (use NET_* as argument). Returns the **/ +/** NetPlay connection status, like NETConnected(). **/ +/*************************************************************/ +int NETPlay(int Switch); + +/** NETExchange() ********************************************/ +/** Exchanges given number of bytes with the other side and **/ +/** returns 1 on success, 0 on failure. **/ +/*************************************************************/ +int NETExchange(char *In,const char *Out,int N); + +/** NETJoystick() ********************************************/ +/** When NetPlay disconnected, returns GetJoystick(). Else, **/ +/** exchanges joystick states over the network and presents **/ +/** server as player #1, client as player #2. **/ +/*************************************************************/ +unsigned int NETJoystick(void); + +/** NETConnected() *******************************************/ +/** Returns NET_SERVER, NET_CLIENT, or NET_OFF. **/ +/*************************************** PLATFORM DEPENDENT **/ +int NETConnected(void); + +/** NETConnect() *********************************************/ +/** Connects to Server:Port. When Host=0, becomes a server, **/ +/** waits at for an incoming request, and connects. Returns **/ +/** the NetPlay connection status, like NETConnected(). **/ +/*************************************** PLATFORM DEPENDENT **/ +int NETConnect(const char *Server,unsigned short Port); + +/** UDPConnect() *********************************************/ +/** Connects to Server:Port. When Server=0, becomes server, **/ +/** waits at for an incoming request, and connects. Returns **/ +/** the NetPlay connection status, like NETConnected(). **/ +/*************************************** PLATFORM DEPENDENT **/ +int UDPConnect(const char *Server,unsigned short Port); + +/** NETClose() ***********************************************/ +/** Closes connection open with NETConnect(). **/ +/*************************************** PLATFORM DEPENDENT **/ +void NETClose(void); + +/** NETConnectAsync() ****************************************/ +/** Asynchronous version of NETConnect(). **/ +/*************************************** PLATFORM DEPENDENT **/ +int NETConnectAsync(const char *Server,unsigned short Port); + +/** NETBlock() ***********************************************/ +/** Makes NETSend()/NETRecv() blocking or not blocking. **/ +/*************************************** PLATFORM DEPENDENT **/ +int NETBlock(int Switch); + +/** NETSend() ************************************************/ +/** Send N bytes. Returns number of bytes sent or 0. **/ +/*************************************** PLATFORM DEPENDENT **/ +int NETSend(const char *Out,int N); + +/** NETRecv() ************************************************/ +/** Receive N bytes. Returns number of bytes received or 0. **/ +/*************************************** PLATFORM DEPENDENT **/ +int NETRecv(char *In,int N); + +/** NETMyName() **********************************************/ +/** Returns local hostname/address or 0 on failure. **/ +/*************************************** PLATFORM DEPENDENT **/ +const char *NETMyName(char *Buffer,int MaxChars); + +#ifdef __cplusplus +} +#endif +#endif /* NETPLAY_H */ diff --git a/components/msx/fmsx/src/EMULib/Record.c b/components/msx/fmsx/src/EMULib/Record.c new file mode 100644 index 0000000..8715ad5 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Record.c @@ -0,0 +1,653 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Record.c **/ +/** **/ +/** This file contains routines for gameplay recording and **/ +/** replay. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2013-2021 **/ +/** The contents of this file are property of Marat **/ +/** Fayzullin and should only be used as agreed with **/ +/** him. The file is confidential. Absolutely no **/ +/** distribution allowed. **/ +/*************************************************************/ +#include "Record.h" +#include "Console.h" +#include +#include +#include +#include + +#ifndef DEFINE_ONCE +#define DEFINE_ONCE + +#define READ_INT(Buf) \ + ((Buf)[0]+((int)(Buf)[1]<<8)+((int)(Buf)[2]<<16)+((int)(Buf)[3]<<24)) + +#define WRITE_INT(Buf,V) { \ + (Buf)[0] = ((V)&0xFF); \ + (Buf)[1] = (((V)>>8)&0xFF); \ + (Buf)[2] = (((V)>>16)&0xFF); \ + (Buf)[3] = (((V)>>24)&0xFF); \ +} + +// static RPLState RPLData[RPL_BUFSIZE] = {{0}}; +RPLState *RPLData = NULL; +static unsigned int StateSize = 0; +static unsigned int TimeLeft; +static int RPLRCount = -1; +static int RPLWCount = -1; +static int RPLUCount = -1; +static int RPtr1,RPtr2; +static int WPtr1,WPtr2; + +static unsigned int (*SaveState)(unsigned char *,unsigned int) = 0; +static unsigned int (*LoadState)(unsigned char *,unsigned int) = 0; + +/** RPLInit() ************************************************/ +/** Initialize record/relay subsystem. **/ +/*************************************************************/ +void RPLInit(unsigned int (*SaveHandler)(unsigned char *,unsigned int),unsigned int (*LoadHandler)(unsigned char *,unsigned int),unsigned int MaxSize) +{ + RPLTrash(); + SaveState = SaveHandler; + LoadState = LoadHandler; + StateSize = MaxSize; +} + +/** RPLTrash() ***********************************************/ +/** Free all record/replay resources. **/ +/*************************************************************/ +void RPLTrash(void) +{ + /* Free recording buffers */ + RPLRecord(RPL_RESET); + /* Disable both recording and playback */ + RPLRecord(RPL_OFF); + RPLPlay(RPL_OFF); +} + +/** RPLRecord() **********************************************/ +/** Record emulation state and joystick input for replaying **/ +/** it back later. **/ +/*************************************************************/ +int RPLRecord(unsigned int Cmd) { return(RPLRecordKeys(Cmd,0,0)); } + +/** RPLRecordKeys() ******************************************/ +/** Record emulation state, keys, and joystick input for **/ +/** replaying them back later. **/ +/*************************************************************/ +int RPLRecordKeys(unsigned int Cmd,const unsigned char *Keys,unsigned int KeySize) +{ + int J; + + /* Insure that recording is initialized */ + if(!SaveState || !LoadState || !StateSize) return(0); + + /* Toggle recording as necessary */ + if(Cmd==RPL_TOGGLE) Cmd=RPLWCount<0? RPL_ON:RPL_OFF; + + /* Reset recording as necessary */ + if(Cmd==RPL_RESET) { RPLWCount=-1;Cmd=RPL_ON; } + + /* Act on recording command */ + switch(Cmd) + { + case RPL_ON: + /* If recording already on, do nothing */ + if(RPLWCount>=0) return(1); + /* Clear current records */ + for(J=0;J=0); + } + + /* If not recording or replaying, return immediately */ + if((RPLWCount<0) || (RPLRCount>=0)) return(0); + + /* If not creating a new state record yet... */ + if((++RPLWCountsizeof(RPLData[0].KeyState[0])) + KeySize = sizeof(RPLData[0].KeyState[0]); + /* Detect changes in keyboard state */ + J = memcmp(Keys,RPLData[WPtr1].KeyState[WPtr2],KeySize); + } + + /* If same joystick and keyboard states, just increment counter */ + if(!J && (RPLData[WPtr1].JoyState[WPtr2]==Cmd)) + { + ++RPLData[WPtr1].Count[WPtr2]; + return(1); + } + + /* If empty input record or created new record... */ + if(!RPLData[WPtr1].Count[WPtr2] || (++WPtr2=0) return(1); + /* Look for the oldest valid state to replay */ + for(RPtr1=(WPtr1+1)&(RPL_BUFSIZE-1);RPtr1!=WPtr1;RPtr1=(RPtr1+1)&(RPL_BUFSIZE-1)) + if(RPLData[RPtr1].State && RPLData[RPtr1].StateSize && RPLData[RPtr1].Count[0]) + { + /* State found, replay from that state */ + RPLRCount = 0; + RPtr2 = -1; + TimeLeft = RPLCount(); + return(1); + } + /* State not found */ + return(0); + + case RPL_NEXT: + /* This will play the next record */ + if(RPLRCount<0) return(RPL_ENDED); + break; + + case RPL_OFF: + /* If terminating playback while recording... */ + if((RPLRCount>=0) && (RPLWCount>=0)) + { + /* Set recording to the spot where playback ends */ + WPtr1 = RPtr1; + WPtr2 = RPtr2; + + /* Truncate last input record */ + RPLData[WPtr1].Count[WPtr2]-=RPLRCount; + + /* Terminate truncated input record */ + if(WPtr2=0); + } + + /* If finished repeating previous joystick state... */ + if(!RPLRCount) + { + /* Stop at the current recording position */ + if((RPtr1==WPtr1) && (RPtr2==WPtr2)) { RPLPlay(RPL_OFF);return(RPL_ENDED); } + + /* Go to the next input record inside a state slot */ + if((RPtr2<0) || (++RPtr2>=RPL_RECSIZE) || !RPLData[RPtr1].Count[RPtr2]) + { + /* Go to the next state slot */ + RPtr1 = RPtr2>=0? ((RPtr1+1)&(RPL_BUFSIZE-1)) : RPtr1; + + /* Exit when done with replay */ + if(!RPLData[RPtr1].Count[0]) { RPLPlay(RPL_OFF);return(RPL_ENDED); } + + /* Load next emulation state, if present */ + if(RPLData[RPtr1].State && RPLData[RPtr1].StateSize) + if(!LoadState(RPLData[RPtr1].State,RPLData[RPtr1].StateSize)) + { RPLPlay(RPL_OFF);return(RPL_ENDED); } + + /* Go to the first record */ + RPtr2 = 0; + } + + /* New joystick state will be valid for this many frames */ + RPLRCount = RPLData[RPtr1].Count[RPtr2]; + if(!RPLRCount) { RPLPlay(RPL_OFF);return(RPL_ENDED); } + } + + /* Load keyboard state if supplied */ + if(Keys) + { + if(KeySize>sizeof(RPLData[0].KeyState[0])) + KeySize = sizeof(RPLData[0].KeyState[0]); + memcpy(Keys,RPLData[RPtr1].KeyState[RPtr2],KeySize); + } + + /* Return joystick state */ + --RPLRCount; + if(TimeLeft) --TimeLeft; + return(RPLData[RPtr1].JoyState[RPtr2]); +} + +/** RPLCount() ***********************************************/ +/** Compute the number of remaining replay records. **/ +/*************************************************************/ +unsigned int RPLCount(void) +{ + unsigned int Count; + int I,J; + + /* Playback must be on */ + if((RPLRCount<0) || (RPtr1<0)) return(0); + + /* Start with the current count */ + Count = RPLRCount; + + /* Go through all states, iterating over available records */ + for(J=RPtr1 ; (J!=WPtr1) && RPLData[J].Count[0] ; J=(J+1)&(RPL_BUFSIZE-1)) + for(I=0 ; (I=0) + for(I=0;I<=RPtr2;++I) + Count-=RPLData[RPtr1].Count[I]; + + /* Done */ + return(Count); +} + +/** SaveRPL() ************************************************/ +/** Save gameplay recording into given file. **/ +/*************************************************************/ +int SaveRPL(const char *FileName) +{ + static unsigned char Header[16] = "RPL\032\001\0\0\0\0\0\0\0\0\0\0\0"; + unsigned char Buf[16]; + FILE *F; + int J,K; + + /* Look for the oldest valid state to replay */ + for(J=(WPtr1+1)&(RPL_BUFSIZE-1);J!=WPtr1;J=(J+1)&(RPL_BUFSIZE-1)) + if(RPLData[J].State && RPLData[J].StateSize && RPLData[J].Count[0]) break; + + /* If state not found, drop out */ + if(J==WPtr1) return(0); + + /* Open file */ + F = fopen(FileName,"wb"); + if(!F) return(0); + + /* Fill and write header */ + WRITE_INT(Header+5,RPLData[J].StateSize); + if(fwrite(Header,1,sizeof(Header),F)!=sizeof(Header)) + { fclose(F);unlink(FileName);return(0); } + + /* Write initial state */ + if(fwrite(RPLData[J].State,1,RPLData[J].StateSize,F)!=RPLData[J].StateSize) + { fclose(F);unlink(FileName);return(0); } + + /* Write input records */ + for(;J!=WPtr1;J=(J+1)&(RPL_BUFSIZE-1)) + for(K=0;(K=0)&&(J0) --RPLUCount; + else if(Buttons&BTN_UP) RPLUCount=4; + else if(Buttons&~BTN_UP) RPLPlay(RPL_OFF); + /* Swallowing UP button */ + return(Buttons&~BTN_UP); + } + + /* Wait for a button to be pressed, then act on it */ + switch(WaitJoystick(BTN_ALL)) + { + case BTN_LEFT: + /* Go to the previous state as needed */ + J = (RPtr1-1)&(RPL_BUFSIZE-1); + RPtr1 = (J!=WPtr1)&&RPLData[J].Count[0]? J:RPtr1; + RPtr2 = -1; + RPLRCount = 0; + TimeLeft = RPLCount(); + RPLUCount = 4; + break; + + case BTN_RIGHT: + /* Go to the previous state as needed */ + J = (RPtr1+1)&(RPL_BUFSIZE-1); + RPtr1 = (RPtr1!=WPtr1)&&((J!=WPtr1)||(WPtr2>0))&&RPLData[J].Count[0]? J:RPtr1; + RPtr2 = -1; + RPLRCount = 0; + TimeLeft = RPLCount(); + RPLUCount = 4; + break; + + case BTN_UP: + /* Continue replay */ + RPLUCount = -1; + break; + + default: + /* Stop replay */ + RPLPlay(RPL_OFF); + RPLUCount = -1; + break; + } + + /* Make sure all buttons are up */ + while(VideoImg&&(GetJoystick()&~(BTN_LEFT|BTN_RIGHT))); + + /* Swallowing all buttons */ + return(0); +} + +#endif /* DEFINE_ONCE */ + +#if defined(BPP8) || defined(BPP16) || defined(BPP24) || defined(BPP32) +#define CLR_FG PIXEL(255,255,255) +#define CLR_BG PIXEL(0,0,0) + +/** RPLShow() ************************************************/ +/** Draw replay icon when active. **/ +/*************************************************************/ +void RPLShow(Image *Img,int X,int Y) +{ + pixel *P; + int J,I; + + /* Draw nothing when record/replay is inactive */ + if((RPLWCount<0) && (RPLRCount<0)) return; + + /* Starting address to draw the arrow */ + P = (pixel *)Img->Data + Img->L*Y + X; + + /* If replay active... */ + if(RPLRCount>=0) + { + /* If replay is playing... */ + if(RPLUCount<0) + { + ShadowPrintXY(Img,"^ TO PAUSE",(Img->W-8*10)>>1,Img->H-Y-8,CLR_FG,CLR_BG); + + for(J=1;J<=RPL_SIGNSIZE/2;++J,P+=Img->L) + { + P[0] = CLR_BG; + for(I=1;IL) + { + P[0] = CLR_BG; + for(I=1;IW-8*11)>>1,Img->H-Y-8,CLR_FG,CLR_BG); + ShadowPrintXY(Img,"<",X,(Img->H-8)>>1,CLR_FG,CLR_BG); + ShadowPrintXY(Img,">",Img->W-X-8,(Img->H-8)>>1,CLR_FG,CLR_BG); + + for(J=0;J<7;++J) P[J]=CLR_BG; + for(J=1,P+=Img->L;JL) + { + P[1] = P[2] = P[4] = P[5] = CLR_FG; + P[0] = P[3] = P[6] = CLR_BG; + } + for(J=0;J<7;++J) P[J]=CLR_BG; + } + + if(TimeLeft>0) + { + char S[16]; + J = TimeLeft*100/60; + sprintf(S,"%d:%02d",J/100,J%100); + ShadowPrintXY(Img,S,X+RPL_SIGNSIZE/2+2,Y+(RPL_SIGNSIZE-8)/2-1,CLR_FG,CLR_BG); +// PrintXY(Img,S,X+RPL_SIGNSIZE/2+2,Y+(RPL_SIGNSIZE-8)/2,CLR_FG,-1); + } + } + else + { + /* Recording is active */ + for(J=0;J<=RPL_SIGNSIZE/2;++J,P+=Img->L) + { + I = RPL_SIGNSIZE/2-J; + P[I] = CLR_BG; + while(I=0;--J,P+=Img->L) + { + I = RPL_SIGNSIZE/2-J; + P[I] = CLR_BG; + while(ID) + { + case 8: RPLShow_8(Img,X,Y);break; + case 16: RPLShow_16(Img,X,Y);break; + case 24: + case 32: RPLShow_32(Img,X,Y);break; + } +} + +#endif /* !BPP8 && !BPP16 && !BPP24 && !BPP32 */ diff --git a/components/msx/fmsx/src/EMULib/Record.h b/components/msx/fmsx/src/EMULib/Record.h new file mode 100644 index 0000000..c4faaa4 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Record.h @@ -0,0 +1,109 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Record.h **/ +/** **/ +/** This file contains routines for gameplay recording and **/ +/** replay. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2013-2021 **/ +/** The contents of this file are property of Marat **/ +/** Fayzullin and should only be used as agreed with **/ +/** him. The file is confidential. Absolutely no **/ +/** distribution allowed. **/ +/*************************************************************/ +#ifndef RECORD_H +#define RECORD_H + +#include "EMULib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** RPLPlay()/RPLRecord() arguments **************************/ +#define RPL_OFF 0xFFFFFFFF /* Disable */ +#define RPL_ON 0xFFFFFFFE /* Enable */ +#define RPL_TOGGLE 0xFFFFFFFD /* Toggle */ +#define RPL_RESET 0xFFFFFFFC /* Reset and enable */ +#define RPL_NEXT 0xFFFFFFFB /* Play the next record */ +#define RPL_QUERY 0xFFFFFFFA /* Query state */ + +/** RPLPlay(RPL_NEXT) results ********************************/ +#define RPL_ENDED 0xFFFFFFFF /* Finished or stopped */ + +#define RPL_RECSIZE (RPL_STEP+1) +#define RPL_BUFSIZE 64 +#define RPL_STEP 10 +#define RPL_SIGNSIZE 12 + +typedef struct +{ + unsigned char *State; + unsigned int StateSize; + unsigned int JoyState[RPL_RECSIZE]; + unsigned int Count[RPL_RECSIZE]; + unsigned char KeyState[RPL_RECSIZE][16]; +} RPLState; + +/** RPLInit() ************************************************/ +/** Initialize record/relay subsystem. **/ +/*************************************************************/ +void RPLInit(unsigned int (*SaveHandler)(unsigned char *,unsigned int),unsigned int (*LoadHandler)(unsigned char *,unsigned int),unsigned int MaxSize); + +/** RPLTrash() ***********************************************/ +/** Free all record/replay resources. **/ +/*************************************************************/ +void RPLTrash(void); + +/** RPLRecord() **********************************************/ +/** Record emulation state and joystick input for replaying **/ +/** it back later. **/ +/*************************************************************/ +int RPLRecord(unsigned int JoyState); + +/** RPLRecordKeys() ******************************************/ +/** Record emulation state, keys, and joystick input for **/ +/** replaying them back later. **/ +/*************************************************************/ +int RPLRecordKeys(unsigned int JoyState,const unsigned char *Keys,unsigned int KeySize); + +/** RPLPlay() ************************************************/ +/** Replay gameplay saved with RPLRecord(). **/ +/*************************************************************/ +unsigned int RPLPlay(int Cmd); + +/** RPLPlayKeys() ********************************************/ +/** Replay gameplay saved with RPLRecordKeys(). **/ +/*************************************************************/ +unsigned int RPLPlayKeys(int Cmd,unsigned char *Keys,unsigned int KeySize); + +/** RPLCount() ***********************************************/ +/** Compute the number of remaining replay records. **/ +/*************************************************************/ +unsigned int RPLCount(void); + +/** RPLShow() ************************************************/ +/** Draw replay icon when active. **/ +/*************************************************************/ +void RPLShow(Image *Img,int X,int Y); + +/** SaveRPL() ************************************************/ +/** Save gameplay recording into given file. **/ +/*************************************************************/ +int SaveRPL(const char *FileName); + +/** LoadRPL() ************************************************/ +/** Load gameplay recording from given file. **/ +/*************************************************************/ +int LoadRPL(const char *FileName); + +/** RPLControls() ********************************************/ +/** Let user browse through replay states with directional **/ +/** buttons: LEFT:REW, RIGHT:FWD, DOWN:STOP, UP:CONTINUE. **/ +/*************************************************************/ +unsigned int RPLControls(unsigned int Buttons); + +#ifdef __cplusplus +} +#endif +#endif /* RECORD_H */ diff --git a/components/msx/fmsx/src/EMULib/Rules.Unix b/components/msx/fmsx/src/EMULib/Rules.Unix new file mode 100644 index 0000000..4e9a61b --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Rules.Unix @@ -0,0 +1,12 @@ +### +### Include this file in the first line of your Makefile when compiling +### EMULib application for FreeBSD, NetBSD, OpenBSD, Linux, or any other +### generic Unix flavor. +### + +include ../../EMULib/Rules.gcc + +CFLAGS += -I$(EMULIB)/Unix -I/usr/X11R6/include +DEFINES+= -DUNIX -DMITSHM -DBPS16 -DPULSE_AUDIO +LIBS += -lX11 -lXext -lpulse-simple +OBJECTS+= $(EMUUNIX) diff --git a/components/msx/fmsx/src/EMULib/Rules.gcc b/components/msx/fmsx/src/EMULib/Rules.gcc new file mode 100644 index 0000000..63103cb --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Rules.gcc @@ -0,0 +1,118 @@ +BASEDIR = ../.. +EMULIB = $(BASEDIR)/EMULib +LIBARM = $(BASEDIR)/ARM +LIBZ80 = $(BASEDIR)/Z80 +LIB6502 = $(BASEDIR)/M6502 +LIB68K = $(BASEDIR)/M68000 +LIBGBZ = $(BASEDIR)/GBZ80 +PRIVATE = $(BASEDIR)/Private + +CXX = g++ +CC = gcc +CPP = cpp +MOC = moc +SDT2GAS = perl $(EMULIB)/Tools/sdt2gas.pl +CFLAGS = -O2 -pthread -I. -I.. -I$(EMULIB) +DEFINES = -DZLIB +LIBS = -lz -lpthread + +# EMULib sound, console, and other utility functions +OBJECTS = $(EMULIB)/EMULib.o $(EMULIB)/Sound.o \ + $(EMULIB)/Image.o $(EMULIB)/Console.o \ + $(EMULIB)/Record.o $(EMULIB)/NetPlay.o \ + $(EMULIB)/Touch.o + +# Platform-specific EMULib files +EMUUNIX = $(EMULIB)/Unix/LibUnix.o $(EMULIB)/Unix/SndUnix.o \ + $(EMULIB)/Unix/NetUnix.o +EMUMAEMO= $(EMULIB)/Maemo/LibMaemo.o $(EMULIB)/Unix/GConf.o \ + $(EMULIB)/Unix/NetUnix.o $(EMULIB)/LibARM-BPP16.o +EMUMEEGO= $(EMULIB)/Meego/LibMeego.o $(EMULIB)/Unix/GConf.o \ + $(EMULIB)/Unix/NetUnix.o $(EMULIB)/LibARM-BPP16.o +EMUSTMP = $(EMULIB)/STMP3700/LibSTMP3700.o $(EMULIB)/Unix/SndALSA.o \ + $(EMULIB)/LibARM-BPP16.o $(EMULIB)/LibARM-BPP32.o +EMUNXC = $(EMULIB)/NXC2600/LibNXC2600.o $(EMULIB)/Unix/SndSDL.o + +# SHA1 computation library +SHA1 = $(EMULIB)/SHA1.o + +# CRC32 computation library +CRC32 = $(EMULIB)/CRC32.o + +# .IPS file patch library +IPS = $(EMULIB)/IPS.o + +# .MCF file cheat library +MCF = $(EMULIB)/MCF.o + +# .INI file access library +INIFILE = $(EMULIB)/INIFile.o + +# Record/replay library +RECORD = $(EMULIB)/Record.o + +# Cheat code hunting library +HUNT = $(EMULIB)/Hunt.o + +# 720kB floppy utility library +FLOPPY = $(EMULIB)/Floppy.o + +# Disk image utility library +FDIDISK = $(EMULIB)/FDIDisk.o + +# CPU emulation libraries +ARM = $(LIBARM)/ARM.o $(LIBARM)/ConDebug.o +Z80 = $(LIBZ80)/Z80.o $(LIBZ80)/ConDebug.o +GBZ80 = $(LIBGBZ)/Z80.o $(LIBGBZ)/ConDebug.o +M6502 = $(LIB6502)/M6502.o $(LIB6502)/ConDebug.o +M68000 = $(LIB68K)/M68000.o $(LIB68K)/ConDebug.o + +# Optimized CPU emulation +ARMONARM = $(ARM) \ + $(LIBARM)/ARMonARM/OptARM16.o \ + $(LIBARM)/ARMonARM/OptARM32.o +M6502ONARM = $(M6502) $(LIB6502)/M6502-ARM.o + +# TMS9918/9928/9929 VDP emulation +TMS9918 = $(EMULIB)/TMS9918.o $(EMULIB)/DRV9918.o + +# Disk controller emulation libraries +WD1793 = $(EMULIB)/WD1793.o +UPD765 = $(EMULIB)/uPD765.o +WD2793 = $(WD1793) +I8272 = $(UPD765) + +# EEPROM chip emulation libraries +C93XX = $(EMULIB)/C93XX.o +C24XX = $(EMULIB)/C24XX.o + +# Intel 8255 PPI chip +I8255 = $(EMULIB)/I8255.o + +# Sound chip emulation libraries +AY8910 = $(EMULIB)/AY8910.o +SN76489 = $(EMULIB)/SN76489.o +SAA1099 = $(EMULIB)/SAA1099.o +YM2413 = $(EMULIB)/YM2413.o +SCC = $(EMULIB)/SCC.o +GBSND = $(PRIVATE)/GBSND.o +NESSND = $(PRIVATE)/NESSND.o + +# Cartridge parsing utilities +NESCART = $(EMULIB)/NESCarts.o +GBCART = $(EMULIB)/GBCarts.o +GBACART = $(EMULIB)/GBACarts.o +DSCART = $(EMULIB)/DSCarts.o + +%.o: %.c + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< +%.o: %.cc + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< +%.o: %.cpp + $(CXX) $(CFLAGS) $(DEFINES) -c -o $@ $< +%.o: %.s + $(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< +%.s: %.asm + $(SDT2GAS) $< >$@ +%.moc: %.h + $(MOC) $(DEFINES) $< >$@ diff --git a/components/msx/fmsx/src/EMULib/SCC.c b/components/msx/fmsx/src/EMULib/SCC.c new file mode 100644 index 0000000..d11166f --- /dev/null +++ b/components/msx/fmsx/src/EMULib/SCC.c @@ -0,0 +1,187 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** SCC.c **/ +/** **/ +/** This file contains emulation for the SCC sound chip **/ +/** produced by Konami. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +#include "SCC.h" +#include "Sound.h" +#include + +/** ResetSCC() ***********************************************/ +/** Reset the sound chip and use sound channels from the **/ +/** one given in First. **/ +/*************************************************************/ +void ResetSCC(register SCC *D,int First) +{ + int J; + + /* Reset registers */ + memset(D->R,0x00,sizeof(D->R)); + + /* Set instruments, frequencies, volumes */ + for(J=0;JFreq[J]=D->Volume[J]=0; + } + + D->First = First; + D->Sync = SCC_ASYNC; + D->Changed = 0x00; + D->WChanged = 0x00; +} + +/** ReadSCC() ************************************************/ +/** Call this function to read contents of the generic SCC **/ +/** sound chip registers. **/ +/*************************************************************/ +byte ReadSCC(register SCC *D,register byte R) +{ + return(R<0x80? D->R[R]:0xFF); +} + +/** ReadSCCP() ***********************************************/ +/** Call this function to read contents of the newer SCC+ **/ +/** sound chip registers. **/ +/*************************************************************/ +byte ReadSCCP(register SCC *D,register byte R) +{ + return(R<0xA0? D->R[R]:0xFF); +} + +/** WriteSCC() ***********************************************/ +/** Call this function to output a value V into the generic **/ +/** SCC sound chip. **/ +/*************************************************************/ +void WriteSCC(register SCC *D,register byte R,register byte V) +{ + /* Prevent rollover */ + if(R>=0xE0) return; + /* Generic SCC has one waveform less than SCC+ */ + if(R>=0x80) { WriteSCCP(D,R+0x20,V);return; } + /* The last waveform applies to both channels 3 and 4 */ + if(R>=0x60) { WriteSCCP(D,R,V);WriteSCCP(D,R+0x20,V);return; } + /* Other waveforms are the same */ + WriteSCCP(D,R,V); +} + +/** WriteSCCP() **********************************************/ +/** Call this function to output a value V into the newer **/ +/** SCC+ sound chip. **/ +/*************************************************************/ +void WriteSCCP(register SCC *D,register byte R,register byte V) +{ + register int J; + register byte I; + + /* Exit if no change */ + if(V==D->R[R]) return; + + if((R&0xE0)==0xA0) + { + /* Emulate melodic features */ + + /* Save active channel mask in I */ + I=D->R[0xAF]; + /* Write to a register */ + R&=0xEF; + D->R[R]=D->R[R+0x10]=V; + + /* Melodic register number 0..15 */ + R&=0x0F; + switch(R) + { + case 0: case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: case 9: + /* Exit if the channel is silenced */ + if(!(I&(1<<(R>>1)))) return; + /* Go to the first register of the pair */ + R=(R&0xFE)+0xA0; + /* Compute frequency */ + J=((int)(D->R[R+1]&0x0F)<<8)+D->R[R]; + /* Compute channel number */ + R=(R&0x0F)>>1; + /* Assign frequency */ + D->Freq[R]=J? SCC_BASE/J:0; + /* Compute changed channels mask */ + D->Changed|=1<Volume[R]=255*(V&0x0F)/15; + /* Compute changed channels mask */ + D->Changed|=(1<Changed|=R; + /* Update frequencies */ + for(I=0;R&&(I>=1,V>>=1) + if(R&1) + { + if(!(V&1)) D->Freq[I]=0; + else + { + J=I*2+0xA0; + J=((int)(D->R[J+1]&0x0F)<<8)+D->R[J]; + D->Freq[I]=J? SCC_BASE/J:0; + } + } + break; + + default: + /* Wrong register, do nothing */ + return; + } + } + else + { + /* Emulate wave table features */ + + /* Write data to SCC */ + D->R[R]=V; + /* Wrong register, do nothing */ + if(R>=0xA0) return; + /* Mark channel waveform as changed */ + D->WChanged|=1<<(R>>5); + } + + /* For asynchronous mode, make Sound() calls right away */ + if(!D->Sync&&(D->Changed||D->WChanged)) SyncSCC(D,SCC_FLUSH); +} + +/** SyncSCC() ************************************************/ +/** Flush all accumulated changes by issuing Sound() calls **/ +/** and set the synchronization on/off. The second argument **/ +/** should be SCC_SYNC/SCC_ASYNC to set/reset sync, or **/ +/** SCC_FLUSH to leave sync mode as it is. **/ +/*************************************************************/ +void SyncSCC(register SCC *D,register byte Sync) +{ + register int J,I; + + if(Sync!=SCC_FLUSH) D->Sync=Sync; + + /* Modify waveforms */ + for(J=0,I=D->WChanged;I&&(J>=1) + if(I&1) SetWave(J+D->First,(signed char *)(D->R+(J<<5)),32,0); + + /* Modify frequencies and volumes */ + for(J=0,I=D->Changed;I&&(J>=1) + if(I&1) Sound(J+D->First,D->Freq[J],D->Volume[J]); + + D->Changed=D->WChanged=0x00; +} diff --git a/components/msx/fmsx/src/EMULib/SCC.h b/components/msx/fmsx/src/EMULib/SCC.h new file mode 100644 index 0000000..f360919 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/SCC.h @@ -0,0 +1,82 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** SCC.h **/ +/** **/ +/** This file contains definitions and declarations for **/ +/** routines in SCC.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef SCC_H +#define SCC_H + +#define SCC_BASE 111861 /* Base frequency for SCC */ +#define SCC_CHANNELS 5 /* 5 melodic channels */ + +#define SCC_ASYNC 0 /* Asynchronous emulation */ +#define SCC_SYNC 1 /* Synchronous emulation mode */ +#define SCC_FLUSH 2 /* Flush buffers only */ + +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif + +/** SCC ******************************************************/ +/** This data structure stores SCC state. **/ +/*************************************************************/ +#pragma pack(4) +typedef struct +{ + byte R[256]; /* SCC register contents */ + int Freq[SCC_CHANNELS]; /* Frequencies (0 for off) */ + int Volume[SCC_CHANNELS]; /* Volumes (0..255) */ + int First; /* First used Sound() channel */ + byte Changed; /* Bitmap of changed channels */ + byte WChanged; /* Bitmap of changed waveforms */ + byte Sync; /* SCC_SYNC/SCC_ASYNC */ +} SCC; +#pragma pack() + +/** ResetSCC() ***********************************************/ +/** Reset the sound chip and use sound channels from the **/ +/** one given in First. **/ +/*************************************************************/ +void ResetSCC(register SCC *D,int First); + +/** ReadSCC() ************************************************/ +/** Call this function to read contents of the generic SCC **/ +/** sound chip registers. **/ +/*************************************************************/ +byte ReadSCC(register SCC *D,register byte R); + +/** ReadSCCP() ***********************************************/ +/** Call this function to read contents of the newer SCC+ **/ +/** sound chip registers. **/ +/*************************************************************/ +byte ReadSCCP(register SCC *D,register byte R); + +/** WriteSCC() ***********************************************/ +/** Call this function to output a value V into the generic **/ +/** SCC sound chip. **/ +/*************************************************************/ +void WriteSCC(register SCC *D,register byte R,register byte V); + +/** WriteSCCP() **********************************************/ +/** Call this function to output a value V into the newer **/ +/** SCC+ sound chip. **/ +/*************************************************************/ +void WriteSCCP(register SCC *D,register byte R,register byte V); + +/** SyncSCC() ************************************************/ +/** Flush all accumulated changes by issuing Sound() calls **/ +/** and set the synchronization on/off. The second argument **/ +/** should be SCC_SYNC/SCC_ASYNC to set/reset sync, or **/ +/** SCC_FLUSH to leave sync mode as it is. **/ +/*************************************************************/ +void SyncSCC(register SCC *D,register byte Sync); + +#endif /* SCC_H */ diff --git a/components/msx/fmsx/src/EMULib/SHA1.c b/components/msx/fmsx/src/EMULib/SHA1.c new file mode 100644 index 0000000..a0dc27e --- /dev/null +++ b/components/msx/fmsx/src/EMULib/SHA1.c @@ -0,0 +1,194 @@ +#include "SHA1.h" + +#define ROTATE(Data,Shift) \ + ((((Data)<<(Shift))|((Data)>>(32-(Shift))))&0xFFFFFFFF) + +static void ProcessSHA1(SHA1 *State) +{ + static const unsigned int K[] = { 0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6 }; + unsigned int A,B,C,D,E,T,W[80]; + int J; + + if(State->PtrBuf)) return; + + for(J=0;J<64;J+=4) + W[J>>2] = + (((unsigned int)State->Buf[J])<<24) + | (((unsigned int)State->Buf[J+1])<<16) + | (((unsigned int)State->Buf[J+2])<<8) + | State->Buf[J+3]; + + for(J=16;J<80;++J) + { + T = W[J-3]^W[J-8]^W[J-14]^W[J-16]; + W[J] = ROTATE(T,1); + } + + A = State->Msg[0]; + B = State->Msg[1]; + C = State->Msg[2]; + D = State->Msg[3]; + E = State->Msg[4]; + + for(J=0;J<20;++J) + { + T = (ROTATE(A,5) + ((B&C)|(~B&D)) + E + W[J] + K[0]) & 0xFFFFFFFF; + E = D; + D = C; + C = ROTATE(B,30); + B = A; + A = T; + } + + for(J=20;J<40;++J) + { + T = (ROTATE(A,5) + (B^C^D) + E + W[J] + K[1]) & 0xFFFFFFFF; + E = D; + D = C; + C = ROTATE(B,30); + B = A; + A = T; + } + + for(J=40;J<60;++J) + { + T = (ROTATE(A,5) + ((B&C)|(B&D)|(C&D)) + E + W[J] + K[2]) & 0xFFFFFFFF; + E = D; + D = C; + C = ROTATE(B,30); + B = A; + A = T; + } + + for(J=60;J<80;++J) + { + T = (ROTATE(A,5) + (B^C^D) + E + W[J] + K[3]) & 0xFFFFFFFF; + E = D; + D = C; + C = ROTATE(B,30); + B = A; + A = T; + } + + + State->Msg[0] = (State->Msg[0] + A) & 0xFFFFFFFF; + State->Msg[1] = (State->Msg[1] + B) & 0xFFFFFFFF; + State->Msg[2] = (State->Msg[2] + C) & 0xFFFFFFFF; + State->Msg[3] = (State->Msg[3] + D) & 0xFFFFFFFF; + State->Msg[4] = (State->Msg[4] + E) & 0xFFFFFFFF; + + State->Ptr = 0; +} + +void ResetSHA1(SHA1 *State) +{ + State->LenL = 0; + State->LenH = 0; + State->Ptr = 0; + State->Done = 0; + State->Error = 0; + + State->Msg[0] = 0x67452301; + State->Msg[1] = 0xEFCDAB89; + State->Msg[2] = 0x98BADCFE; + State->Msg[3] = 0x10325476; + State->Msg[4] = 0xC3D2E1F0; +} + +int ComputeSHA1(SHA1 *State) +{ + if(State->Error) return(0); + if(State->Done) return(1); + + // + // Padding digest to required number of bits + // + + // Make sure buffer is not full + ProcessSHA1(State); + + // Add 0x80 + State->Buf[State->Ptr++] = 0x80; + + // If not enough space for message length... + if(State->Ptr>sizeof(State->Buf)-8) + { + // Pad with zeros + while(State->PtrBuf)) + State->Buf[State->Ptr++] = 0; + ProcessSHA1(State); + } + + // Pad with more zeros, leaving 8 octets at the end + while(State->PtrBuf)-8) + State->Buf[State->Ptr++] = 0; + + // Store the message length as the last 8 octets + State->Buf[State->Ptr++] = (State->LenH>>24) & 0xFF; + State->Buf[State->Ptr++] = (State->LenH>>16) & 0xFF; + State->Buf[State->Ptr++] = (State->LenH>>8) & 0xFF; + State->Buf[State->Ptr++] = (State->LenH) & 0xFF; + State->Buf[State->Ptr++] = (State->LenL>>24) & 0xFF; + State->Buf[State->Ptr++] = (State->LenL>>16) & 0xFF; + State->Buf[State->Ptr++] = (State->LenL>>8) & 0xFF; + State->Buf[State->Ptr++] = (State->LenL) & 0xFF; + + ProcessSHA1(State); + + // Done + State->Done = 1; + return(1); +} + +int InputSHA1(SHA1 *State,const unsigned char *Data,unsigned int Size) +{ + unsigned int J; + + if(State->Done || State->Error) + { + State->Error = 1; + return(0); + } + + // Make sure buffer is not full + ProcessSHA1(State); + + // No data, nothing to process + if(!Size) return(1); + + // Push input data into the buffer + for(J=0 ; !State->Error && (JBuf[State->Ptr++] = Data[J]; + State->LenL = (State->LenL+8) & 0xFFFFFFFF; + + if(!State->LenL) + { + State->LenH = (State->LenH+1)&0xFFFFFFFF; + if(!State->LenH) + { + State->Error = 1; + return(0); + } + } + + if(State->Ptr>=sizeof(State->Buf)) ProcessSHA1(State); + } + + return(1); +} + +const char *OutputSHA1(SHA1 *State,char *Output,unsigned int Size) +{ + const char *Hex = "0123456789abcdef"; + unsigned int J; + + if(!State->Done || State->Error || (Size<41)) return(0); + + for(J=0 ; J<40 ; ++J) + Output[J] = Hex[(State->Msg[J>>3]>>((~J&7)<<2))&15]; + + Output[J] = '\0'; + return(Output); +} + diff --git a/components/msx/fmsx/src/EMULib/SHA1.h b/components/msx/fmsx/src/EMULib/SHA1.h new file mode 100644 index 0000000..c826b3a --- /dev/null +++ b/components/msx/fmsx/src/EMULib/SHA1.h @@ -0,0 +1,19 @@ +#ifndef SHA1_H +#define SHA1_H + +typedef struct +{ + unsigned int Done; + unsigned int Error; + unsigned int LenL,LenH; + unsigned int Ptr; + unsigned char Buf[64]; + unsigned int Msg[5]; +} SHA1; + +void ResetSHA1(SHA1 *State); +int ComputeSHA1(SHA1 *State); +int InputSHA1(SHA1 *State,const unsigned char *Data,unsigned int Size); +const char *OutputSHA1(SHA1 *State,char *Output,unsigned int Size); + +#endif /* SHA1_H */ diff --git a/components/msx/fmsx/src/EMULib/Sound.c b/components/msx/fmsx/src/EMULib/Sound.c new file mode 100644 index 0000000..fb785dc --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Sound.c @@ -0,0 +1,905 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Sound.c **/ +/** **/ +/** This file file implements core part of the sound API **/ +/** and functions needed to log soundtrack into a MIDI **/ +/** file. See Sound.h for declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "Sound.h" + +#include +#include + +#if defined(UNIX) || defined(MAEMO) || defined(STMP3700) || defined(NXC2600) || defined(ANDROID) +#include +#endif + +#if defined(ANDROID) +/* On Android, may need to open files for writing at an */ +/* alternative location, if the requested location is */ +/* not available. OpenRealFile() WILL NOT USE ZLIB. */ +#define fopen OpenRealFile +#endif + +typedef unsigned char byte; +typedef unsigned short word; + +struct SndDriverStruct SndDriver = +{ + (void (*)(int,int))0, + (void (*)(int,int))0, + (void (*)(int,int))0, + (void (*)(int,int,int))0, + (void (*)(int,const signed char *,int,int))0, + (const signed char *(*)(int))0 +}; + +static const struct { byte Note;word Wheel; } Freqs[4096] = +{ +#include "MIDIFreq.h" +}; + +static const int Programs[] = +{ + 80, /* SND_MELODIC/SND_RECTANGLE */ + 80, /* SND_TRIANGLE */ + 122, /* SND_NOISE */ + 122, /* SND_PERIODIC */ + 80, /* SND_WAVE */ +}; + +static struct +{ + int Type; + int Note; + int Pitch; + int Level; + int Power; +} MidiCH[MIDI_CHANNELS] = +{ + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 }, + { -1,-1,-1,-1,256 } +}; + +static struct +{ + int Type; /* Channel type (SND_*) */ + int Freq; /* Channel frequency (Hz) */ + int Volume; /* Channel volume (0..255) */ + + const signed char *Data; /* Wave data (-128..127 each) */ + int Length; /* Wave length in Data */ + int Rate; /* Wave playback rate (or 0Hz) */ + int Pos; /* Wave current position in Data */ + + int Count; /* Phase counter */ +} WaveCH[SND_CHANNELS] = +{ + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 }, + { SND_MELODIC,0,0,0,0,0,0,0 } +}; + +/** RenderAudio() Variables *******************************************/ +static int SndRate = 0; /* Sound rate (0=Off) */ +static int NoiseGen = 0x10000; /* Noise generator seed */ +static int NoiseOut = 16; /* NoiseGen bit used for output */ +static int NoiseXor = 14; /* NoiseGen bit used for XORing */ +int MasterSwitch = 0xFFFF; /* Switches to turn channels on/off */ +int MasterVolume = 192; /* Master volume */ + +/** MIDI Logging Variables ********************************************/ +static const char *LogName = 0; /* MIDI logging file name */ +static int Logging = MIDI_OFF; /* MIDI logging state (MIDI_*) */ +static int TickCount = 0; /* MIDI ticks since WriteDelta() */ +static int LastMsg = -1; /* Last MIDI message */ +static int DrumOn = 0; /* 1: MIDI drums are ON */ +static FILE *MIDIOut = 0; /* MIDI logging file handle */ + +static void MIDISound(int Channel,int Freq,int Volume); +static void MIDISetSound(int Channel,int Type); +static void MIDIDrum(int Type,int Force); +static void MIDIMessage(byte D0,byte D1,byte D2); +static void NoteOn(byte Channel,byte Note,byte Level); +static void NoteOff(byte Channel); +static void WriteDelta(void); +static void WriteTempo(int Freq); + +/** SHIFT() **************************************************/ +/** Make MIDI channel#10 last, as it is normally used for **/ +/** percussion instruments only and doesn't sound nice. **/ +/*************************************************************/ +#define SHIFT(Ch) (Ch==15? 9:Ch>8? Ch+1:Ch) + +/** GetSndRate() *********************************************/ +/** Get current sampling rate used for synthesis. **/ +/*************************************************************/ +unsigned int GetSndRate(void) { return(SndRate); } + +/** Sound() **************************************************/ +/** Generate sound of given frequency (Hz) and volume **/ +/** (0..255) via given channel. Setting Freq=0 or Volume=0 **/ +/** turns sound off. **/ +/*************************************************************/ +void Sound(int Channel,int Freq,int Volume) +{ + /* All parameters have to be valid */ + if((Channel<0)||(Channel>=SND_CHANNELS)) return; + Freq = Freq<0? 0:Freq; + Volume = Volume<0? 0:Volume>255? 255:Volume; + + /* Modify channel parameters */ + WaveCH[Channel].Volume = Volume; + WaveCH[Channel].Freq = Freq; + + /* When disabling sound, reset waveform */ + if(!Freq||!Volume) + { + WaveCH[Channel].Pos = 0; + WaveCH[Channel].Count = 0; + } + + /* Call sound driver if present */ + if(SndDriver.Sound) (*SndDriver.Sound)(Channel,Freq,Volume); + + /* Log sound to MIDI file */ + MIDISound(Channel,Freq,Volume); +} + +/** Drum() ***************************************************/ +/** Hit a drum of given type with given force (0..255). **/ +/** MIDI drums can be used by ORing their numbers with **/ +/** SND_MIDI. **/ +/*************************************************************/ +void Drum(int Type,int Force) +{ + /* Drum force has to be valid */ + Force = Force<0? 0:Force>255? 255:Force; + + /* Call sound driver if present */ + if(SndDriver.Drum) (*SndDriver.Drum)(Type,Force); + + /* Log drum to MIDI file */ + MIDIDrum(Type,Force); +} + +/** SetSound() ***********************************************/ +/** Set sound type at a given channel. MIDI instruments can **/ +/** be set directly by ORing their numbers with SND_MIDI. **/ +/*************************************************************/ +void SetSound(int Channel,int Type) +{ + /* Channel has to be valid */ + if((Channel<0)||(Channel>=SND_CHANNELS)) return; + + /* Set wave channel type */ + WaveCH[Channel].Type = Type; + + /* Call sound driver if present */ + if(SndDriver.SetSound) (*SndDriver.SetSound)(Channel,Type); + + /* Log instrument change to MIDI file */ + MIDISetSound(Channel,Type); +} + +/** SetChannels() ********************************************/ +/** Set master volume (0..255) and switch channels on/off. **/ +/** Each channel N has corresponding bit 2^N in Switch. Set **/ +/** or reset this bit to turn the channel on or off. **/ +/*************************************************************/ +void SetChannels(int Volume,int Switch) +{ + /* Volume has to be valid */ + Volume = Volume<0? 0:Volume>255? 255:Volume; + + /* Call sound driver if present */ + if(SndDriver.SetChannels) (*SndDriver.SetChannels)(Volume,Switch); + + /* Modify wave master settings */ + MasterVolume = Volume; + MasterSwitch = Switch&((1<=SND_CHANNELS)||(Length<=0)) return; + + /* Set wave channel parameters */ + WaveCH[Channel].Type = SND_WAVE; + WaveCH[Channel].Length = Length; + WaveCH[Channel].Rate = Rate; + WaveCH[Channel].Pos = Length? WaveCH[Channel].Pos%Length:0; + WaveCH[Channel].Count = 0; + WaveCH[Channel].Data = Data; + + /* Call sound driver if present */ + if(SndDriver.SetWave) (*SndDriver.SetWave)(Channel,Data,Length,Rate); + + /* Log instrument change to MIDI file */ + MIDISetSound(Channel,Rate? -1:SND_WAVE); + + /* Compute overall waveform power for MIDI */ + if(Rate) I=0; + else + { + for(J=I=0;J0? Data[J]:-Data[J]; + I = (I<<1)/Length; + I = I>256? 256:I; + } + + /* Will use power value when computing MIDI volume */ + MidiCH[Channel].Power = I; +} + +/** GetWave() ************************************************/ +/** Get current read position for the buffer set with the **/ +/** SetWave() call. Returns 0 if no buffer has been set, or **/ +/** if there is no playrate set (i.e. wave is instrument). **/ +/*************************************************************/ +const signed char *GetWave(int Channel) +{ + /* Channel has to be valid */ + if((Channel<0)||(Channel>=SND_CHANNELS)) return(0); + + /* If driver present, call it */ + if(SndDriver.GetWave) return((*SndDriver.GetWave)(Channel)); + + /* Return current read position */ + return( + WaveCH[Channel].Rate&&(WaveCH[Channel].Type==SND_WAVE)? + WaveCH[Channel].Data+WaveCH[Channel].Pos:0 + ); +} + +/** InitMIDI() ***********************************************/ +/** Initialize soundtrack logging into MIDI file FileName. **/ +/** Repeated calls to InitMIDI() will close current MIDI **/ +/** file and continue logging into a new one. **/ +/*************************************************************/ +void InitMIDI(const char *FileName) +{ + int WasLogging; + + /* Must pass a name! */ + if(!FileName) return; + + /* Memorize logging status */ + WasLogging=Logging; + + /* If MIDI logging in progress, close current file */ + if(MIDIOut) TrashMIDI(); + + /* Set log file name and ticks/second parameter, no logging yet */ + LogName = FileName; + Logging = MIDI_OFF; + LastMsg = -1; + TickCount = 0; + MIDIOut = 0; + DrumOn = 0; + + /* If was logging, restart */ + if(WasLogging) MIDILogging(MIDI_ON); +} + +/** TrashMIDI() **********************************************/ +/** Finish logging soundtrack and close the MIDI file. **/ +/*************************************************************/ +void TrashMIDI(void) +{ + long Length; + int J; + + /* If not logging, drop out */ + if(!MIDIOut) return; + /* Turn sound off */ + for(J=0;J>24)&0xFF,MIDIOut); + fputc((Length>>16)&0xFF,MIDIOut); + fputc((Length>>8)&0xFF,MIDIOut); + fputc(Length&0xFF,MIDIOut); + + /* Done logging */ + fclose(MIDIOut); + Logging = MIDI_OFF; + LastMsg = -1; + TickCount = 0; + MIDIOut = 0; +} + +/** MIDILogging() ********************************************/ +/** Turn soundtrack logging on/off and return its current **/ +/** status. Possible values of Switch are MIDI_OFF (turn **/ +/** logging off), MIDI_ON (turn logging on), MIDI_TOGGLE **/ +/** (toggle logging), and MIDI_QUERY (just return current **/ +/** state of logging). **/ +/*************************************************************/ +int MIDILogging(int Switch) +{ + static const char MThd[] = "MThd\0\0\0\006\0\0\0\1"; + /* ID DataLen Fmt Trks */ + static const char MTrk[] = "MTrk\0\0\0\0"; + /* ID TrkLen */ + int J,I; + + /* Toggle logging if requested */ + if(Switch==MIDI_TOGGLE) Switch=!Logging; + + if((Switch==MIDI_ON)||(Switch==MIDI_OFF)) + if(Switch^Logging) + { + /* When turning logging off, silence all channels */ + if(!Switch&&MIDIOut) + for(J=0;J>8)&0xFF,MIDIOut); + fputc(MIDI_DIVISIONS&0xFF,MIDIOut); + if(fwrite(MTrk,1,8,MIDIOut)!=8) + { fclose(MIDIOut);MIDIOut=0;return(MIDI_OFF); } + + /* Write out the tempo */ + WriteTempo(MIDI_DIVISIONS); + } + + /* Turn logging off on failure to open MIDIOut */ + if(!MIDIOut) Switch=MIDI_OFF; + + /* Assign new switch value */ + Logging=Switch; + + /* If switching logging on... */ + if(Switch) + { + /* Start logging without a pause */ + TickCount=0; + + /* Write instrument changes */ + for(J=0;J=0)&&(MidiCH[J].Type&0x10000)) + { + I=MidiCH[J].Type&~0x10000; + MidiCH[J].Type=-1; + MIDISetSound(J,I); + } + } + } + + /* Return current logging status */ + return(Logging); +} + +/** MIDITicks() **********************************************/ +/** Log N 1ms MIDI ticks. **/ +/*************************************************************/ +void MIDITicks(int N) +{ + if(Logging&&MIDIOut&&(N>0)) TickCount+=N; +} + +/** MIDISound() **********************************************/ +/** Set sound frequency (Hz) and volume (0..255) for a **/ +/** given channel. **/ +/*************************************************************/ +void MIDISound(int Channel,int Freq,int Volume) +{ + int MIDIVolume,MIDINote,MIDIWheel; + + /* If logging off, file closed, or invalid channel, drop out */ + if(!Logging||!MIDIOut||(Channel>=MIDI_CHANNELS-1)||(Channel<0)) return; + /* Frequency must be in range */ + if((FreqMIDI_MAXFREQ)) Freq=0; + /* Instrument number must be valid */ + if(MidiCH[Channel].Type<0) Freq=0; + + /* MIDI sound volume values are in 0..127 range */ + /* SND_TRIANGLE has 1/2 volume of SND_MELODIC */ + /* SND_WAVE may have different effective volume */ + Volume = + MidiCH[Channel].Type==SND_TRIANGLE? ((Volume+3)>>2) + : MidiCH[Channel].Type==SND_WAVE? (((Volume*MidiCH[Channel].Power)+511)>>9) + : ((Volume+1)>>1); + + /* Volume must be in range */ + if(Volume<0) Volume=0; else if(Volume>127) Volume=127; + + if(!Volume||!Freq) NoteOff(Channel); + else + { + /* Compute MIDI note parameters */ + MIDIVolume = Volume; + MIDINote = Freqs[Freq/3].Note; + MIDIWheel = Freqs[Freq/3].Wheel; + + /* Play new note */ + NoteOn(Channel,MIDINote,MIDIVolume); + + /* Change pitch */ + if(MidiCH[Channel].Pitch!=MIDIWheel) + { + MIDIMessage(0xE0+SHIFT(Channel),MIDIWheel&0x7F,(MIDIWheel>>7)&0x7F); + MidiCH[Channel].Pitch=MIDIWheel; + } + } +} + +/** MIDISetSound() *******************************************/ +/** Set sound type for a given channel. **/ +/*************************************************************/ +void MIDISetSound(int Channel,int Type) +{ + /* Channel must be valid */ + if((Channel>=MIDI_CHANNELS-1)||(Channel<0)) return; + + /* If instrument changed... */ + if(MidiCH[Channel].Type!=Type) + { + /* If logging off or file closed, drop out */ + if(!Logging||!MIDIOut) MidiCH[Channel].Type=Type|0x10000; + else + { + MidiCH[Channel].Type=Type; + if(Type<0) NoteOff(Channel); + else + { + Type=Type&SND_MIDI? (Type&0x7F):Programs[Type%5]; + MIDIMessage(0xC0+SHIFT(Channel),Type,255); + } + } + } +} + +/** MIDIDrum() ***********************************************/ +/** Hit a drum of a given type with given force. **/ +/*************************************************************/ +void MIDIDrum(int Type,int Force) +{ + /* If logging off or invalid channel, drop out */ + if(!Logging||!MIDIOut) return; + /* The only non-MIDI drum is a click ("Low Wood Block") */ + Type=Type&DRM_MIDI? (Type&0x7F):77; + /* Release previous drum */ + if(DrumOn) MIDIMessage(0x89,DrumOn,127); + /* Hit next drum */ + if(Type) MIDIMessage(0x99,Type,(Force&0xFF)>>1); + DrumOn=Type; +} + +/** MIDIMessage() ********************************************/ +/** Write out a MIDI message. **/ +/*************************************************************/ +void MIDIMessage(byte D0,byte D1,byte D2) +{ + /* Write number of ticks that passed */ + WriteDelta(); + + /* Write out the command */ + if(D0!=LastMsg) { LastMsg=D0;fputc(D0,MIDIOut); } + + /* Write out the arguments */ + if(D1<128) + { + fputc(D1,MIDIOut); + if(D2<128) fputc(D2,MIDIOut); + } +} + +/** NoteOn() *************************************************/ +/** Turn on a note on a given channel. **/ +/*************************************************************/ +void NoteOn(byte Channel,byte Note,byte Level) +{ + Note = Note>0x7F? 0x7F:Note; + Level = Level>0x7F? 0x7F:Level; + + if((MidiCH[Channel].Note!=Note)||(MidiCH[Channel].Level!=Level)) + { + if(MidiCH[Channel].Note>=0) NoteOff(Channel); + MIDIMessage(0x90+SHIFT(Channel),Note,Level); + MidiCH[Channel].Note=Note; + MidiCH[Channel].Level=Level; + } +} + +/** NoteOff() ************************************************/ +/** Turn off a note on a given channel. **/ +/*************************************************************/ +void NoteOff(byte Channel) +{ + if(MidiCH[Channel].Note>=0) + { + MIDIMessage(0x80+SHIFT(Channel),MidiCH[Channel].Note,127); + MidiCH[Channel].Note=-1; + } +} + +/** WriteDelta() *********************************************/ +/** Write number of ticks since the last MIDI command and **/ +/** reset the counter. **/ +/*************************************************************/ +void WriteDelta(void) +{ + if(TickCount<128) fputc(TickCount,MIDIOut); + else + { + if(TickCount<128*128) + { + fputc((TickCount>>7)|0x80,MIDIOut); + fputc(TickCount&0x7F,MIDIOut); + } + else + { + fputc(((TickCount>>14)&0x7F)|0x80,MIDIOut); + fputc(((TickCount>>7)&0x7F)|0x80,MIDIOut); + fputc(TickCount&0x7F,MIDIOut); + } + } + + TickCount=0; +} + +/** WriteTempo() *********************************************/ +/** Write out soundtrack tempo (Hz). **/ +/*************************************************************/ +void WriteTempo(int Freq) +{ + int J; + + J=500000*MIDI_DIVISIONS*2/Freq; + WriteDelta(); + fputc(0xFF,MIDIOut); + fputc(0x51,MIDIOut); + fputc(0x03,MIDIOut); + fputc((J>>16)&0xFF,MIDIOut); + fputc((J>>8)&0xFF,MIDIOut); + fputc(J&0xFF,MIDIOut); +} + +/** InitSound() **********************************************/ +/** Initialize RenderSound() with given parameters. **/ +/*************************************************************/ +unsigned int InitSound(unsigned int Rate,unsigned int Latency) +{ + int I; + + /* Shut down current sound */ + TrashSound(); + + /* Initialize internal variables (keeping MasterVolume/MasterSwitch!) */ + SndRate = 0; + + /* Reset sound parameters */ + for(I=0;I0? + (SndRate<<15)/WaveCH[J].Freq/WaveCH[J].Rate + : (SndRate<<15)/WaveCH[J].Freq/WaveCH[J].Length; + /* Do not allow high frequencies (GBC Frogger) */ + if(K<0x8000) break; + L1 = WaveCH[J].Pos%WaveCH[J].Length; + L2 = WaveCH[J].Count; + A1 = WaveCH[J].Data[L1]*V; +#if !defined(WAVE_INTERPOLATION) + /* Add waveform to the buffer */ + for(I=0;I=K) + { + L1 = (L1+L2/K)%WaveCH[J].Length; + A1 = WaveCH[J].Data[L1]*V; + L2 = L2%K; + } + /* Output waveform */ + Wave[I]+=A1; + /* Next waveform step */ + L2+=0x8000; + } +#else /* WAVE_INTERPOLATION */ + /* If expecting interpolation... */ + if(L2>15)+1; + N = ((K-(L2&0x7FFF))>>15)+1; + } + /* Add waveform to the buffer */ + for(I=0;I>15)+1; + } + } +#endif /* WAVE_INTERPOLATION */ + /* End counting */ + WaveCH[J].Pos = L1; + WaveCH[J].Count = L2; + break; + + case SND_NOISE: /* White Noise */ + /* For high frequencies, recompute volume */ + if(WaveCH[J].Freq>NoiseOut)&1? 127:-128)*V; + L1+=K; + if(L1&0xFFFF0000) + { + /* XOR NoiseOut and NoiseXOR bits and feed them back */ + NoiseGen= + (((NoiseGen>>NoiseOut)^(NoiseGen>>NoiseXor))&1) + | ((NoiseGen<<1)&((2<=SndRate/2) break; + K=0x10000*WaveCH[J].Freq/SndRate; + L1=WaveCH[J].Count; +#if !defined(SLOW_MELODIC_AUDIO) + for(I=0;IJ? J:Samples-K; + + /* Convert samples */ + for(I=0;I>8; + D = D>32767? 32767:D<-32768? -32768:D; +#if defined(BPU16) + Buf[I] = D+32768; +#elif defined(BPS16) + Buf[I] = D; +#elif defined(BPU8) + Buf[I] = (D>>8)+128; +#else + Buf[I] = D>>8; +#endif + } + + /* Play samples */ + I = WriteAudio(Buf,J); + } + + /* Return number of samples played */ + return(K); +} + +/** RenderAndPlayAudio() *************************************/ +/** Render and play a given number of samples. Returns the **/ +/** number of samples actually played. **/ +/*************************************************************/ +unsigned int RenderAndPlayAudio(unsigned int Samples) +{ + int Buf[256]; + unsigned int J,I; + + /* Exit if wave sound not initialized */ + if(SndRate<8192) return(0); + + J = GetFreeAudio(); + Samples = Samples +#include + +#define XKEYS 12 /* Number of keys in a row */ +#define YKEYS 6 /* Number of key rows */ + +#define MAX_PENJOY_WIDTH 320 /* Max pen joystick width */ + +#if defined(ANDROID) +static int KEYSTEP = 39; /* Key step in pixels */ +static int KEYSIZE = 31; /* Key size in pixels */ +static int CHRSIZE = 16; /* Key label size */ +#elif defined(MEEGO) +static int KEYSTEP = 46; /* Key step in pixels */ +static int KEYSIZE = 38; /* Key size in pixels */ +static int CHRSIZE = 16; /* Key label size */ +#elif defined(MAEMO) +static int KEYSTEP = 34; /* Key step in pixels */ +static int KEYSIZE = 30; /* Key size in pixels */ +static int CHRSIZE = 16; /* Key label size */ +#else +static int KEYSTEP = 14; /* Key step in pixels */ +static int KEYSIZE = 12; /* Key size in pixels */ +static int CHRSIZE = 8; /* Key label size */ +#endif + +/* Currently selected virtual keyboard key */ +static int KBDXPos = 0; +static int KBDYPos = 0; + +/* Horizontal offsets of virtual keyboard lines */ +#if defined(ANDROID) || defined(MEEGO) +static const int KBDOffsets[YKEYS] = { 0,0,0,0,32,16 }; +#elif defined(MAEMO) +static const int KBDOffsets[YKEYS] = { 0,0,0,0,20,10 }; +#else +static const int KBDOffsets[YKEYS] = { 0,0,0,0,8,4 }; +#endif + +/* Characters printed on virtual keyboard keys */ +static const char *KBDLines[YKEYS+1] = +{ + "\33\20\21\22\23\24\25\26\27\16\17\32", + "1234567890-=", + "\11QWERTYUIOP\10", + "^ASDFGHJKL;\15", + "ZXCVBNM,./", + "[] \\'", + 0 +}; + +static const char *PenCues[32] = +{ + "LEFT","RIGHT","UP","DOWN","A","B","L","R", + "START","SELECT","EXIT","X","Y",0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +static int CueSizes[32] = +{ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +/* Characters returned from virtual keyboard */ +static const unsigned char KBDKeys[YKEYS][XKEYS] = +{ + { + 0x1B,CON_F1,CON_F2,CON_F3,CON_F4,CON_F5, + CON_F6,CON_F7,CON_F8,CON_INSERT,CON_DELETE,CON_STOP + }, + { '1','2','3','4','5','6','7','8','9','0','-','=' }, + { CON_TAB,'Q','W','E','R','T','Y','U','I','O','P',CON_BS }, + { '^','A','S','D','F','G','H','J','K','L',';',CON_ENTER }, + { 'Z','X','C','V','B','N','M',',','.','/',0,0 }, + { '[',']',' ',' ',' ',' ',' ','\\','\'',0,0,0 } +}; + +static unsigned int FFWDButtons = 0; +static unsigned int MENUButtons = 0; + +static int JoyCuesSetup = 0; +#if defined(ANDROID) || defined(MEEGO) +static int OldCueImgW = 0; +#endif + +static const struct { int W,H,X,Y; } DefButtons[] = +{ + { 256,256, 8, -48-16-256, }, + { 128,128, -128-8,48+8, }, + { 128,128, -128-8,48+8+128+8, }, + { 128,48, 0, 0, }, + { 128,48, -128, 0, }, + { 128,48, 0, -48, }, + { 128,48, -128, -48, }, + { 128,48, -256-8,-48, }, +}; + +static struct +{ + int Bit; + Image Img; + int W,H,X,Y; + unsigned int Flags; +} Buttons[] = +{ + { 4, {0}, 0,0,0,0,0 }, // FIRE-A + { 5, {0}, 0,0,0,0,0 }, // FIRE-B + { 6, {0}, 0,0,0,0,0 }, // FIRE-L + { 7, {0}, 0,0,0,0,0 }, // FIRE-R + { 9, {0}, 0,0,0,0,0 }, // SELECT + { 8, {0}, 0,0,0,0,0 }, // START + { 11, {0}, 0,0,0,0,0 }, // FIRE-X + { -1, {0}, 0,0,0,0,0 }, // Arrows (have to be last) + { -2, {0}, 0,0,0,0,0 } +}; + +static int abs(int X) { return(X>=0? X:-X); } + +/** GetKbdWidth()/GetKbdHeight() *****************************/ +/** Return virtual keyboard dimensions. **/ +/*************************************************************/ +unsigned int GetKbdWidth() { return(KEYSTEP*XKEYS+8); } +unsigned int GetKbdHeight() { return(KEYSTEP*YKEYS+8+CHRSIZE); } + +/** GenericFullJoystick() ************************************/ +/** Treat whole screen as one big directional pad. Result **/ +/** compatible with GetJoystick() (arrows only though). **/ +/*************************************************************/ +unsigned int GenericFullJoystick(int X,int Y,int W,int H) +{ + /* Just consider the whole screen as one big directional pad */ + return( + (X<(W>>1)-(W>>3)? BTN_LEFT : X>(W>>1)+(W>>3)? BTN_RIGHT : 0) + | (Y<(H>>1)-(H>>3)? BTN_UP : Y>(H>>1)+(H>>3)? BTN_DOWN : 0) + ); +} + +/** GenericPenJoystick() *************************************/ +/** Get simulated joystick buttons from touch screen UI. **/ +/** Result compatible with GetJoystick(). **/ +/*************************************************************/ +unsigned int GenericPenJoystick(int X,int Y,int W,int H) +{ + unsigned int J; + int W3; + + /* Simulate joystick when pen touches the screen at X,Y */ + J = 0; + + /* Don't accept touches outside of the window frame */ + if((X<0)||(Y<0)||(X>=W)||(Y>=H)) return(0); + W3 = W>MAX_PENJOY_WIDTH*3? MAX_PENJOY_WIDTH:W/3; + + /* Top 1/16 of the widget: FIREL and FIRER */ + if(Y<(H>>3)) + { if(X=W-W3) J|=BTN_FIRER; } + + /* Bottom 1/16 of the widget: SELECT/EXIT and START */ + if(!J&&(Y>=(H-(H>>3)))) + { if(X=W-W3) J|=BTN_START; } + + /* Right upper corner of the screen is the fire buttons */ + if(!J&&(X>=W-(W3>>1))&&(Y>=(H>>3))&&(Y<(H>>3)+W3+(W3>>3))) + { + /* Fire buttons overlap */ + if(Y<=(H>>3)+(W3>>1)+(W3>>3)) J|=BTN_FIREA; + if(Y>=(H>>3)+(W3>>1)) J|=BTN_FIREB; + } + + /* Left 1/3 of the screen is the directional pad */ + if(!J&&(X=H-(H>>3)-W3)) + { + Y-=H-W3-(H>>3); + W3/=3; + if(X=(W3<<1)) J|=BTN_RIGHT; + if(Y=(W3<<1)) J|=BTN_DOWN; + } + + /* Apply dynamically assigned FFWD and MENU buttons */ + J |= J&FFWDButtons? BTN_FFWD:0; + J |= J&MENUButtons? BTN_MENU:0; + + /* Done, return simulated "joystick" state */ + return(J); +} + +/** GenericPenDialpad() **************************************/ +/** Get simulated dialpad buttons from touch screen UI. **/ +/*************************************************************/ +unsigned char GenericPenDialpad(int X,int Y,int W,int H) +{ + int W3,H2; + + /* Dialpad is the middle 1/3 of the screen */ + W3 = W>MAX_PENJOY_WIDTH*3? MAX_PENJOY_WIDTH:W/3; + H2 = H>W3? ((H-W3)>>1):0; + W3 = (W-W3)>>1; + return( + (Y>=H2)&&(Y=W3)&&(X>2))+1 : 0 + ); +} + +/** GenericPenKeyboard() *************************************/ +/** Get virtual on-screen keyboard buttons. **/ +/*************************************************************/ +unsigned char GenericPenKeyboard(int X,int Y,int W,int H) +{ + int J; + + /* Pen coordinates relative to keyboard's top left corner */ + X -= W-KEYSTEP*XKEYS-8; + Y -= H-KEYSTEP*YKEYS-8; + + /* Pen must be inside the keyboard */ + if((X<0)||(Y<0)) return(0); + + /* Keyboard row index */ + Y/= KEYSTEP; + if(Y>=YKEYS) return(0); + + /* Adjust for row position on screen */ + for(J=0;J<=Y;++J) X-=KBDOffsets[J]; + if(X<0) return(0); + + /* Keyboard column index */ + X/= KEYSTEP; + if(X>=XKEYS) return(0); + + /* Memorize last pressed key */ + KBDXPos = X; + KBDYPos = Y; + + /* Return key */ + return(KBDKeys[Y][X]); +} + +/** GenericDialKeyboard() ************************************/ +/** Process dialpad input to the virtual keyboard. Returns **/ +/** virtual keyboard key if selected, or 0 if not. **/ +/*************************************************************/ +unsigned char GenericDialKeyboard(unsigned char Key) +{ + /* Interpret input key */ + switch(Key) + { + case CON_LEFT: + KBDXPos = (KBDXPos>0? KBDXPos:strlen(KBDLines[KBDYPos]))-1; + break; + case CON_RIGHT: + KBDXPos = KBDXPos0? KBDYPos-1:YKEYS-1; + KBDXPos = KBDXPos8? (ChrSize&~7):8; + /* Make sure keys do not overlap */ + if(KeySize+4>KeyStep) KeySize=KeyStep-4; + /* Make sure key labels fit into keys */ + if(CHRSIZE+2>KeySize) KeySize=CHRSIZE+2; + /* Now grow key step again, if needed */ + if(KeySize+4>KeyStep) KeyStep=KeySize+4; + /* Assign new virtual key dimensions */ + KEYSIZE = KeySize; + KEYSTEP = KeyStep; + + /* Recompute cue lengths */ + for(JoyCuesSetup=1,J=0;J-2;++J) + { + /* Free the existing image */ + FreeImage(&Buttons[J].Img); + + /* If source image supplied... */ + if(Src) + { + /* Compute button's top-left corner position in the Src */ + X0 = DefButtons[J].X + (DefButtons[J].X<0? Src->W:0); + Y0 = DefButtons[J].Y + (DefButtons[J].Y<0? Src->H:0); + + /* Crop the button out of the Src image */ + CropImage(&Buttons[J].Img,Src,X0,Y0,DefButtons[J].W,DefButtons[J].H); + } + + /* Reset button size and position to the defaults */ + Buttons[J].X = DefButtons[J].X; + Buttons[J].Y = DefButtons[J].Y; + Buttons[J].W = DefButtons[J].W; + Buttons[J].H = DefButtons[J].H; + + /* Button now visible and enabled */ + Buttons[J].Flags = 0; + } + + /* Return number of buttons initialized */ + return(J); +} + +/** SetFinButton() *******************************************/ +/** Set finger joystick button(s) to given location. When **/ +/** Img=0, create wireframe buttons. When Mask=0, set the **/ +/** directional buttons image and location. When Mask ORed **/ +/** with BTN_INVISIBLE, create invisible buttons. Returns **/ +/** the number of virtual buttons set or 0 for none. **/ +/*************************************************************/ +int SetFinButton(unsigned int Mask,const Image *Img,int X,int Y,int W,int H) +{ + unsigned int Flags; + int I,J,Result; + + /* Special Mask bits: make button invisible or disable it */ + Flags = Mask&BTN_INVISIBLE; + Mask = Mask&~BTN_INVISIBLE; + + /* No image for invisible buttons */ + if(Flags&BTN_INVISIBLE) Img=0; + + /* When Mask=0, we are assigning the arrow buttons */ + if(!Mask) Mask=0x80000000; + + for(J=Result=0;Mask;++J,Mask>>=1) + if(Mask&1) + for(I=0;Buttons[I].Bit>-2;++I) + if((Buttons[I].Bit==J) || ((Buttons[I].Bit==-1)&&(J==31))) + { + /* When Img=0, make a wireframe or invisible button */ + if(Img) CropImage(&Buttons[I].Img,Img,0,0,W,H); + else FreeImage(&Buttons[I].Img); + + Buttons[I].Flags = Flags; + Buttons[I].X = X; + Buttons[I].Y = Y; + Buttons[I].W = W; + Buttons[I].H = H; + ++Result; + } + + /* Return number of modified buttons */ + return(Result); +} + +/** GenericFinJoystick() *************************************/ +/** Return the BTN_* bits corresponding to position X,Y of **/ +/** the finger joystick shown in Dst. **/ +/*************************************************************/ +unsigned int GenericFinJoystick(int X,int Y,int W,int H,unsigned int CurState) +{ + unsigned int Result,I,J; + int X0,Y0,AX,AY,W0,H0; + + /* For every known button... */ + for(J=Result=0;Buttons[J].Bit>-2;++J) + { + /* Compute finger position relative to the button */ + X0 = X - Buttons[J].X - (Buttons[J].X<0? W:0); + Y0 = Y - Buttons[J].Y - (Buttons[J].Y<0? H:0); + + /* If button has been pressed... */ + if( + ((X0>=0) && (Y0>=0) && (X0=0) + { + /* Compute button mask from bit number */ + I = 1<>1; + H0 = Buttons[J].H>>1; + X0 -= W0; + Y0 -= H0; + AX = abs(X0); + AY = abs(Y0); + + /* If finger is outside the joypad, keep pressing previous arrows */ + if((AX>W0) || (AY>H0)) + Result |= CurState & ((X0<0? BTN_LEFT:BTN_RIGHT)|(Y0<0? BTN_UP:BTN_DOWN)); + + /* Joypad's center is inactive at 1/16 of the radius */ + else if((AX>=(W0>>3)) || (AY>=(H0>>3))) + { + /* This is joypad's edge at 1/4 of the radius */ + W0 = W0>>1; + H0 = H0>>1; + + Result |= + ((X0<0) && ((AX>AY) || (AX>W0))? BTN_LEFT:0) + | ((Y0<0) && ((AY>AX) || (AY>H0))? BTN_UP:0) + | ((X0>0) && ((AX>AY) || (AX>W0))? BTN_RIGHT:0) + | ((Y0>0) && ((AY>AX) || (AY>H0))? BTN_DOWN:0); + } + } + } + } + + /* Done */ + return(Result); +} + +#endif /* DEFINE_ONCE */ + +#if !defined(BPP32) && !defined(BPP24) && !defined(BPP16) && !defined(BPP8) +/* When pixel size not defined, compile in the universal multiplexer */ +#include "TouchMux.h" +#else + +#if defined(ANDROID) || defined(MEEGO) +#define CLR_NORMALF PIXEL(64,255,64) /* Normal key foreground */ +#else +#define CLR_NORMALF PIXEL(0,0,0) /* Normal key foreground */ +#define CLR_NORMALB PIXEL(255,255,255) /* Normal key background */ +#endif +#define CLR_ACTIVEF PIXEL(255,255,255) /* Active key foreground */ +#define CLR_ACTIVEB PIXEL(255,64,64) /* Active key background */ +#define CLR_FPS PIXEL(255,128,255) /* Framerate counter color */ +#define CLR_CUES PIXEL(127,127,127) /* Touch joypad cues color */ + +/** PrintXY2() ***********************************************/ +/** Print a string in given color on transparent background.**/ +/*************************************************************/ +static void PrintXY2(Image *Img,const char *S,int X,int Y,pixel FG) +{ + const unsigned char *C; + pixel *P; + int I,J,K,N; + + X = X<0? 0:X>Img->W-CHRSIZE? Img->W-CHRSIZE:X; + Y = Y<0? 0:Y>Img->H-CHRSIZE? Img->H-CHRSIZE:Y; + + for(K=X;*S;S++) + switch(*S) + { + case '\n': + K=X;Y+=CHRSIZE; + if(Y>Img->H-CHRSIZE) Y=0; + break; + default: + P=(pixel *)Img->Data+Img->L*Y+K; + for(C=CONGetFont()+(*S<<3),J=8;J;P+=Img->L*(CHRSIZE/8),++C,--J) + for(I=0,N=(int)*C<<24;N&&(IImg->W-CHRSIZE) + { + K=0;Y+=CHRSIZE; + if(Y>Img->H-CHRSIZE) Y=0; + } + break; + } +} + +/** DrawVLine()/DrawHLine() **********************************/ +/** Draw dotted lines used to show cues for PenJoystick(). **/ +/*************************************************************/ +static void DrawVLine(Image *Img,int X,int Y1,int Y2,pixel Color) +{ + pixel *P; + int J; + + if((X<0)||(X>=Img->W)) return; + + Y1 = Y1<0? 0:Y1>=Img->H? Img->H-1:Y1; + Y2 = Y2<0? 0:Y2>=Img->H? Img->H-1:Y2; + if(Y1>Y2) { J=Y1;Y1=Y2;Y2=J; } + + P = (pixel *)Img->Data+Img->L*Y1+X; + for(J=Y1;J<=Y2;J+=4) { *P=Color;P+=Img->L<<2; } +} + +static void DrawHLine(Image *Img,int X1,int X2,int Y,pixel Color) +{ + pixel *P; + int J; + + if((Y<0)||(Y>=Img->H)) return; + + X1 = X1<0? 0:X1>=Img->W? Img->W-1:X1; + X2 = X2<0? 0:X2>=Img->W? Img->W-1:X2; + if(X1>X2) { J=X1;X1=X2;X2=J; } + + P = (pixel *)Img->Data+Img->L*Y+X1; + for(J=X1;J<=X2;J+=4) { *P=Color;P+=4; } +} + +/** DrawDialpad() ********************************************/ +/** Draw virtual dialpad in a given image. **/ +/*************************************************************/ +void DrawDialpad(Image *Img,int Color) +{ + int W,H,H2,W9,W3; + + /* Use default color, if requested */ + if(Color<0) Color=CLR_CUES; + + W = Img->W; + H = Img->H; + W3 = W>MAX_PENJOY_WIDTH*3? MAX_PENJOY_WIDTH:W/3; + H2 = H>W3? ((H-W3)>>1):0; + W9 = W3/3; + W3 = (W-W3)>>1; + + DrawHLine(Img,W3,W-W3,H2,Color); + DrawHLine(Img,W3,W-W3,(H2>>1)+(H>>2),Color); + DrawHLine(Img,W3,W-W3,H>>1,Color); + DrawHLine(Img,W3,W-W3,(H>>1)-(H2>>1)+(H>>2),Color); + DrawHLine(Img,W3,W-W3,H-H2-1,Color); + DrawVLine(Img,W3,H2,H-H2-1,Color); + DrawVLine(Img,W3+W9,H2,H-H2-1,Color); + DrawVLine(Img,W-W3-W9,H2,H-H2-1,Color); + DrawVLine(Img,W-W3,H2,H-H2-1,Color); + PrintXY2(Img,"1",W3+2,H2+2,Color); + PrintXY2(Img,"2",W3+W9+2,H2+2,Color); + PrintXY2(Img,"3",W-W3-W9+2,H2+2,Color); + PrintXY2(Img,"4",W3+2,(H2>>1)+(H>>2)+2,Color); + PrintXY2(Img,"5",W3+W9+2,(H2>>1)+(H>>2)+2,Color); + PrintXY2(Img,"6",W-W3-W9+2,(H2>>1)+(H>>2)+2,Color); + PrintXY2(Img,"7",W3+2,(H>>1)+2,Color); + PrintXY2(Img,"8",W3+W9+2,(H>>1)+2,Color); + PrintXY2(Img,"9",W-W3-W9+2,(H>>1)+2,Color); + PrintXY2(Img,"*",W3+2,(H>>1)-(H2>>1)+(H>>2)+2,Color); + PrintXY2(Img,"0",W3+W9+2,(H>>1)-(H2>>1)+(H>>2)+2,Color); + PrintXY2(Img,"#",W-W3-W9+2,(H>>1)-(H2>>1)+(H>>2)+2,Color); +} + +/** DrawPenCues() ********************************************/ +/** Overlay dotted cue lines for using PenJoystick() onto a **/ +/** given image. Show dialpad cues if requested. **/ +/*************************************************************/ +void DrawPenCues(Image *Img,int ShowDialpad,int Color) +{ + int W,H,W9,W3; + + /* Use default color, if requested */ + if(Color<0) Color=CLR_CUES; + + /* Set up pen keyboard for the first time */ + if(!JoyCuesSetup) SetPenKeyboard(KEYSTEP,KEYSIZE,CHRSIZE); + + W = Img->W; + H = Img->H; + W3 = Img->W>MAX_PENJOY_WIDTH*3? MAX_PENJOY_WIDTH:W/3; + W9 = W3/3; + + /* Vertical edges */ + DrawVLine(Img,W3,0,H>>3,Color); + DrawVLine(Img,W3,H-W3-(H>>3),H-1,Color); + DrawVLine(Img,W-W3,0,H>>3,Color); + DrawVLine(Img,W-W3,H-(H>>3),H,Color); + + /* Corner buttons */ + DrawHLine(Img,0,W3,H>>3,Color); + DrawHLine(Img,W-W3,W-1,H>>3,Color); + DrawHLine(Img,0,W3,H-(H>>3),Color); + DrawHLine(Img,W-W3,W-1,H-(H>>3),Color); + + /* Fire buttons (with overlap) */ + DrawHLine(Img,W-(W3>>1),W-1,(H>>3)+(W3>>1),Color); + DrawHLine(Img,W-(W3>>1),W-1,(H>>3)+(W3>>1)+(W3>>3),Color); + DrawHLine(Img,W-(W3>>1),W-1,(H>>3)+W3+(W3>>3),Color); + DrawVLine(Img,W-(W3>>1),H>>3,(H>>3)+W3+(W3>>3),Color); + + /* Directional buttons */ + DrawVLine(Img,W9,H-W3-(H>>3),H-(H>>3),Color); + DrawVLine(Img,W3-W9,H-W3-(H>>3),H-(H>>3),Color); + DrawHLine(Img,0,W3,H-W3-(H>>3),Color); + DrawHLine(Img,0,W3,H-(W9<<1)-(H>>3),Color); + DrawHLine(Img,0,W3,H-W9-(H>>3),Color); + + /* Button labels */ + if(PenCues[4]) PrintXY2(Img,PenCues[4],W-CueSizes[4]-2,(H>>3)+2,Color); + if(PenCues[5]) PrintXY2(Img,PenCues[5],W-CueSizes[5]-2,(H>>3)+(W3>>1)+(W3>>3)+2,Color); + if(PenCues[6]) PrintXY2(Img,PenCues[6],2,2,Color); + if(PenCues[7]) PrintXY2(Img,PenCues[7],W-CueSizes[7]-2,2,Color); + if(PenCues[8]) PrintXY2(Img,PenCues[8],W-CueSizes[8]-2,H-(H>>3)+2,Color); + if(PenCues[9]) PrintXY2(Img,PenCues[9],2,H-(H>>3)+2,Color); + + /* Arrow labels */ + if(PenCues[0]&&(CueSizes[0]<=W9)) PrintXY2(Img,PenCues[0],2,H-(W9<<1)-(H>>3)+2,Color); + if(PenCues[1]&&(CueSizes[1]<=W9)) PrintXY2(Img,PenCues[1],(W9<<1)+2,H-(W9<<1)-(H>>3)+2,Color); + if(PenCues[2]&&(CueSizes[2]<=W9)) PrintXY2(Img,PenCues[2],W9+2,H-W3-(H>>3)+2,Color); + if(PenCues[3]&&(CueSizes[3]<=W9)) PrintXY2(Img,PenCues[3],W9+2,H-W9-(H>>3)+2,Color); + + /* If requested, show on-screen dialpad */ + if(ShowDialpad) DrawDialpad(Img,Color); +} + +/** DrawKeyboard() *******************************************/ +/** Draw virtual keyboard in a given image. Key modifiers **/ +/** and the key code passed in CurKey are highlighted. **/ +/*************************************************************/ +void DrawKeyboard(Image *Img,unsigned int CurKey) +{ + int X,Y,J,I,K,L; + char S[2]; + pixel *P; + + /* Keyboard in the right-bottom corner by default */ + X = Img->W-GetKbdWidth(); + Y = Img->H-GetKbdHeight(); + if((X<0)||(Y<0)) return; + + /* Draw modifiers */ + if(CurKey&CON_MODES) + { + J=X; + if(CurKey&CON_SHIFT) { PrintXY2(Img,"SHIFT",J,Y,CLR_ACTIVEB);J+=6*CHRSIZE; } + if(CurKey&CON_CONTROL) { PrintXY2(Img,"CTRL",J,Y,CLR_ACTIVEB);J+=5*CHRSIZE; } + if(CurKey&CON_ALT) { PrintXY2(Img,"ALT",J,Y,CLR_ACTIVEB);J+=4*CHRSIZE; } + } + + /* Draw keyboard under modifiers */ + Y += CHRSIZE; + + /* Draw keys */ + for(I=J=0,S[1]='\0';KBDLines[I];++I,Y+=KEYSTEP,X+=KBDOffsets[I]-J*KEYSTEP) + for(J=0;KBDLines[I][J];++J,X+=KEYSTEP) + { + /* Draw key frame */ + P = (pixel *)Img->Data + + Img->L*(Y+(KEYSTEP-KEYSIZE)/2) + + X+(KEYSTEP-KEYSIZE)/2; + + /* Highlight current key */ + if(KBDKeys[I][J]==(CurKey&CON_KEYCODE)) + { +#ifdef CLR_ACTIVEB + for(K=1;KL;KL) + for(L=0;LL;KL) + { +#ifdef CLR_NORMALB + for(L=1;LW,DH? DH:Dst->H); + + /* Draw controls */ + for(J=Result=0;Buttons[J].Bit>-2;++J) + { + X0 = Buttons[J].X + (Buttons[J].X<0? Dst->W:0) - DX; + Y0 = Buttons[J].Y + (Buttons[J].Y<0? Dst->H:0) - DY; + X1 = X0 + Buttons[J].W; + Y1 = Y0 + Buttons[J].H; + NeedLabel = Buttons[J].W && Buttons[J].H; + NeedFrame = 0; + + /* If need to draw something... */ + if(!(Buttons[J].Flags&BTN_INVISIBLE) && NeedLabel) + { + /* If "dirty" rectangle given... */ + if(DW && DH) + { + /* Image obscures "dirty" rectangle: will draw frame and label */ + if((X0=0) && (Y1>=0)) + { NeedFrame=NeedLabel=1;++Result; } + } + else + { + /* Draw frame if no image data */ + if(!Buttons[J].Img.Data) NeedFrame=1; + else IMGCopy(Dst,X0,Y0,&Buttons[J].Img,0,0,Buttons[J].W,Buttons[J].H,PIXEL(255,0,255)); + /* Draw label */ + NeedLabel=1; + } + } + + /* If need to draw a frame... */ + if(NeedFrame) + { + /* Draw button frame */ + DrawVLine(&Dirty,X0,Y0,Y1-1,TextColor); + DrawVLine(&Dirty,X1-1,Y0,Y1-1,TextColor); + DrawHLine(&Dirty,X0,X1-1,Y0,TextColor); + DrawHLine(&Dirty,X0,X1-1,Y1-1,TextColor); + + /* Draw arrow pad */ + if(Buttons[J].Bit<0) + { + int I; + I = (X1-X0)/3; + DrawVLine(&Dirty,X0+I,Y0,Y1-1,TextColor); + DrawVLine(&Dirty,X1-I,Y0,Y1-1,TextColor); + I = (Y1-Y0)/3; + DrawHLine(&Dirty,X0,X1-1,Y0+I,TextColor); + DrawHLine(&Dirty,X0,X1-1,Y1-I,TextColor); + } + } + + /* If need to draw label and the label exists... */ + if(NeedLabel && (Buttons[J].Bit>=0) && PenCues[Buttons[J].Bit]) + PrintXY2( + Dst, + PenCues[Buttons[J].Bit], + ((X0+X1-CueSizes[Buttons[J].Bit])>>1)+DX, + ((Y0+Y1-CHRSIZE)>>1)+DY, + TextColor + ); + } + + /* Return number of buttons overlapping "safe" rectangle */ + return(Result); +} + +/** RenderVideo() ********************************************/ +/** Draw video buffer to a given image. Return 0 on failure **/ +/** or destination rectangle size inside OutImg on success. **/ +/*************************************************************/ +unsigned int RenderVideo(Image *OutImg,Image *CueImg,int Effects,int PenKey,int FrameRate) +{ + unsigned int DW,DH,SW,SH,X,Y,W,H,J; + Image TmpImg; + + /* Safety check */ + if(!VideoImg || !VideoImg->Data) return(0); + +#ifdef USE_GLES2 + // When using GLES2 shaders, do not apply software effects + if(Effects&EFF_GLES) Effects&=~(EFF_RASTER_ALL|EFF_MASK_ALL); +#endif + + if(Effects&EFF_DIRECT) + { + W = VideoImg->W; + H = VideoImg->H; + X = 0; + Y = 0; + } + else + { + W = VideoW; + H = VideoH; + X = VideoX; + Y = VideoY; + } + + /* Determine destination image dimensions */ + if(!OutImg) + { + /* If no destination image given, we assume VideoImg */ + CropImage(&TmpImg,VideoImg,X,Y,W,H); + CueImg = CueImg? CueImg:&TmpImg; + OutImg = &TmpImg; + DW = W; + DH = H; + } + else if(!(Effects&EFF_SCALE)) { DW=W;DH=H; } + else if(Effects&EFF_STRETCH) { DW=OutImg->W;DH=OutImg->H; } + else + { + DW = W/H; + DH = H/W; + SW = (Effects&EFF_4X3? 4*H/3:W)*(DH>0? DH:1); + SH = H*(DW>0? DW:1); + DW = SW*OutImg->H/SH; + DH = SH*OutImg->W/SW; + if(DW>OutImg->W) DW=OutImg->W; else DH=OutImg->H; + } + + /* Get current interpolation setting */ + J = Effects&EFF_SOFTEN_ALL; + + /* If destination image has not been given... */ + if(OutImg==&TmpImg) + { + /* We do not copy or scale */ + } + /* EFF_SOFTEN_ALL: Soften image using pixel interpolation */ + else if(J && (J!=EFF_NEAREST)) + { + CropImage(&TmpImg,OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH); + switch(J) + { + case EFF_HQ4X: + case EFF_2XSAI: SoftenImage(&TmpImg,VideoImg,X,Y,W,H);break; + case EFF_EPX: SoftenEPX(&TmpImg,VideoImg,X,Y,W,H);break; + case EFF_EAGLE: SoftenEAGLE(&TmpImg,VideoImg,X,Y,W,H);break; + case EFF_SCALE2X: SoftenSCALE2X(&TmpImg,VideoImg,X,Y,W,H);break; + case EFF_LINEAR: InterpolateImage(&TmpImg,VideoImg,X,Y,W,H);break; + case EFF_NEAREST: + default: ScaleImage(&TmpImg,VideoImg,X,Y,W,H);break; + } + } + /* EFF_SCALE: Scale image to the screen size */ + else if(Effects&EFF_SCALE) + { + /* EFF_STRETCH: Stretch image to fill the whole screen */ + if(Effects&EFF_STRETCH) + ScaleImage(OutImg,VideoImg,X,Y,W,H); + else + { +#if defined(ARM_CPU) + /* Scale image to fill the screen, using ARM assembler */ + DW = ARMScaleImage(OutImg,VideoImg,X,Y,W,H,0); + /* Update destination image dimensions to optimized values */ + DH = DW>>16; + DW&= 0xFFFF; +#else + /* Scale image to fill the screen, using C code */ + CropImage(&TmpImg,OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH); + ScaleImage(&TmpImg,VideoImg,X,Y,W,H); +#endif + } + } + /* DEFAULT: Not scaling, stretching, or softening image */ + else + { + /* Center image at the screen */ + IMGCopy(OutImg,(OutImg->W-W)>>1,(OutImg->H-H)>>1,VideoImg,X,Y,W,H,-1); + DW = W; + DH = H; + } + + /* EFF_MASK_ALL: Apply color component masks */ + switch(Effects&EFF_MASK_ALL) + { + case EFF_CMYMASK: CMYizeImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);break; + case EFF_RGBMASK: RGBizeImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);break; + case EFF_MONO: MonoImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);break; + case EFF_GREEN: GreenImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);break; + case EFF_AMBER: AmberImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);break; + case EFF_SEPIA: SepiaImage(OutImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH);break; + } + + /* EFF_RASTER_ALL: Apply "scanlines" effect */ + if((J=Effects&EFF_RASTER_ALL)) + { + /* Make sure width is a multiple of 8/16 pixels for optimization */ +#if !defined(ARM_CPU) + Y = J==EFF_TVLINES? DW:(DW&~1); +#elif defined(BPP32) || defined(BPP24) + Y = DW&~7; +#else + Y = DW&~15; +#endif + + switch(J) + { + case EFF_TVLINES: TelevizeImage(OutImg,(OutImg->W-Y)>>1,(OutImg->H-DH)>>1,Y,DH);break; + case EFF_LCDLINES: LcdizeImage(OutImg,(OutImg->W-Y)>>1,(OutImg->H-DH)>>1,Y,DH);break; + case EFF_RASTER: RasterizeImage(OutImg,(OutImg->W-Y)>>1,(OutImg->H-DH)>>1,Y,DH);break; + } + } + + /* If drawing any touch input cues... */ + if(Effects&(EFF_VKBD|EFF_PENCUES)) + { + /* If no image supplied for the input cues... */ + if(!CueImg) + { + /* In landscape mode, draw input cues on top of OutImg */ + if(OutImg->H<=OutImg->W) CueImg=OutImg; + else + { + /* In portrait mode, draw input cues below OutImg */ + CropImage(&TmpImg,OutImg,0,DH,OutImg->W,OutImg->H-DH); + CueImg = &TmpImg; + } + } + +#if defined(ANDROID) || defined(MEEGO) + /* If cue image width changed... */ + if(OldCueImgW!=CueImg->W) + { + /* Adjust virtual keyboard and cues size to fit screen */ + if(CueImg->W>=1024) SetPenKeyboard(72,64,16); + else if(CueImg->W>=768) SetPenKeyboard(60,52,16); + else if(CueImg->W>=640) SetPenKeyboard(46,38,16); + else if(CueImg->W>=480) SetPenKeyboard(39,31,16); + else if(CueImg->W>=320) SetPenKeyboard(24,20,16); + else SetPenKeyboard(16,14,8); + /* New cue image width now in effect */ + OldCueImgW = CueImg->W; + } + + /* Draw virtual joystick */ + if(Effects&EFF_PENCUES) + DrawFinJoystick(CueImg,(OutImg->W-DW)>>1,(OutImg->H-DH)>>1,DW,DH,CLR_CUES); +#else + /* Draw virtual joystick */ + if(Effects&EFF_PENCUES) DrawPenCues(CueImg,Effects&EFF_DIALCUES,CLR_CUES); +#endif + + /* Draw virtual keyboard */ + if(Effects&EFF_VKBD) DrawKeyboard(CueImg,PenKey); + } + + /* Show framerate if requested */ + if((Effects&EFF_SHOWFPS)&&(FrameRate>0)) + { + char S[16]; + sprintf(S,"%dfps",FrameRate); + PrintXY2(OutImg,S,((OutImg->W-DW)>>1)+8,((OutImg->H-DH)>>1)+8,CLR_FPS); + } + + /* Done with the screen */ + return((DW>1)&&(DH>1)? ((DW&0xFFFF)|(DH<<16)):0); +} + +#undef CLR_NORMALF +#undef CLR_NORMALB +#undef CLR_ACTIVEF +#undef CLR_ACTIVEB +#undef CLR_FPS +#undef CLR_CUES + +#endif /* BPP32||BPP24||BPP16||BPP8 */ diff --git a/components/msx/fmsx/src/EMULib/Touch.h b/components/msx/fmsx/src/EMULib/Touch.h new file mode 100644 index 0000000..3806478 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Touch.h @@ -0,0 +1,143 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** Touch.h **/ +/** **/ +/** This file declares functions that simulate joystick and **/ +/** dialpad with the touch screen. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2008-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef TOUCH_H +#define TOUCH_H + +#include "EMULib.h" + +/* SetFinButton() special mask bits **************************/ +#define BTN_INVISIBLE 0x80000000 + +/* GenericPenDialpad() result ********************************/ +#define DIAL_NONE 0 /* No dialpad buttons pressed */ +#define DIAL_1 1 +#define DIAL_2 2 +#define DIAL_3 3 +#define DIAL_4 4 +#define DIAL_5 5 +#define DIAL_6 6 +#define DIAL_7 7 +#define DIAL_8 8 +#define DIAL_9 9 +#define DIAL_STAR 10 +#define DIAL_0 11 +#define DIAL_POUND 12 + +#ifdef __cplusplus +extern "C" { +#endif + +/** RenderVideo() ********************************************/ +/** Draw video buffer to a given image. Return 0 on failure **/ +/** or destination rectangle size inside OutImg on success. **/ +/*************************************************************/ +unsigned int RenderVideo(Image *OutImg,Image *CueImg,int Effects,int PenKey,int FrameRate); + +/** GenericFullJoystick() ************************************/ +/** Treat whole screen as one big directional pad. Result **/ +/** compatible with GetJoystick() (arrows only though). **/ +/*************************************************************/ +unsigned int GenericFullJoystick(int X,int Y,int W,int H); + +/** GenericPenJoystick() *************************************/ +/** Get simulated joystick buttons from touch screen UI. **/ +/** Result compatible with GetJoystick(). **/ +/*************************************************************/ +unsigned int GenericPenJoystick(int X,int Y,int W,int H); + +/** GenericPenDialpad() **************************************/ +/** Get simulated dialpad buttons from touch screen UI. **/ +/*************************************************************/ +unsigned char GenericPenDialpad(int X,int Y,int W,int H); + +/** GenericPenKeyboard() *************************************/ +/** Get virtual on-screen keyboard buttons. **/ +/*************************************************************/ +unsigned char GenericPenKeyboard(int X,int Y,int W,int H); + +/** GenericDialKeyboard() ************************************/ +/** Process dialpad input to the virtual keyboard. Returns **/ +/** virtual keyboard key if selected, or 0 if not. **/ +/*************************************************************/ +unsigned char GenericDialKeyboard(unsigned char Key); + +/** GenericFinJoystick() *************************************/ +/** Return the BTN_* bits corresponding to position X,Y of **/ +/** the finger joystick shown in Dst. **/ +/*************************************************************/ +unsigned int GenericFinJoystick(int X,int Y,int W,int H,unsigned int CurState); + +/** SetPenCues() *********************************************/ +/** Set pen cues for given buttons to a given string. **/ +/*************************************************************/ +void SetPenCues(unsigned int Buttons,const char *CueText); + +/** SetPenKeyboard() *****************************************/ +/** Set pen keyboard dimensions. **/ +/*************************************************************/ +void SetPenKeyboard(unsigned int KeyStep,unsigned int KeySize,unsigned int ChrSize); + +/** InitFinJoystick() ****************************************/ +/** Initialize finger joystick images by cropping them from **/ +/** the given source image. Returns number of buttons set **/ +/** successfully (i.e. found inside the Src bounds). **/ +/*************************************************************/ +int InitFinJoystick(const Image *Src); + +/** SetFinButton() *******************************************/ +/** Set finger joystick button(s) to given location. When **/ +/** Img=0, create wireframe buttons. When Mask=0, set the **/ +/** directional buttons image and location. When Mask ORed **/ +/** with BTN_INVISIBLE, create invisible buttons. Returns **/ +/** the number of virtual buttons set or 0 for none. **/ +/*************************************************************/ +int SetFinButton(unsigned int Mask,const Image *Src,int X,int Y,int W,int H); + +/** DrawPenCues() ********************************************/ +/** Overlay dotted cue lines for using PenJoystick() onto a **/ +/** given image. Show dialpad cues if requested. **/ +/*************************************************************/ +void DrawPenCues(Image *Img,int ShowDialpad,int Color); + +/** DrawDialpad() ********************************************/ +/** Draw virtual dialpad in a given image. **/ +/*************************************************************/ +void DrawDialpad(Image *Img,int Color); + +/** DrawKeyboard() *******************************************/ +/** Draw virtual keyboard in a given image. Key modifiers **/ +/** and the key code passed in CurKey are highlighted. **/ +/*************************************************************/ +void DrawKeyboard(Image *Img,unsigned int CurKey); + +/** DrawFinJoystick() ****************************************/ +/** Draw finger joystick into given destination image. When **/ +/** DW=0 or DWH=0, the whole image will be updated. When **/ +/** DWxDH+DX+DY represent a dirty rectangle inside Dst, the **/ +/** function will only update joystick buttons overlapping **/ +/** this rectangle, representing them with dotted lines. **/ +/** Returns the number of buttons overlapping the dirty **/ +/** rectangle. **/ +/*************************************************************/ +int DrawFinJoystick(Image *Dst,int DX,int DY,int DW,int DH,int TextColor); + +/** GetKbdWidth()/GetKbdHeight() *****************************/ +/** Return virtual keyboard dimensions. **/ +/*************************************************************/ +unsigned int GetKbdWidth(); +unsigned int GetKbdHeight(); + +#ifdef __cplusplus +} +#endif +#endif /* TOUCH_H */ diff --git a/components/msx/fmsx/src/EMULib/TouchMux.h b/components/msx/fmsx/src/EMULib/TouchMux.h new file mode 100644 index 0000000..35d0dba --- /dev/null +++ b/components/msx/fmsx/src/EMULib/TouchMux.h @@ -0,0 +1,200 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** TouchMux.h **/ +/** **/ +/** This file wraps Touch.c routines for multiple display **/ +/** depths (BPP8,BPP16,BPP32). It is used automatically **/ +/** when none of BPP* values are defined. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2008-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef TOUCHMUX_H +#define TOUCHMUX_H + +#include "EMULib.h" +#include "Touch.h" + +#undef BPP8 +#undef BPP16 +#undef BPP24 +#undef BPP32 +#undef PIXEL + +#define BPP8 +#define pixel unsigned char +#define PIXEL(R,G,B) (pixel)(((7*(R)/255)<<5)|((7*(G)/255)<<2)|(3*(B)/255)) +#define DrawHLine DrawHLine_8 +#define DrawVLine DrawVLine_8 +#define DrawPenCues DrawPenCues_8 +#define DrawDialpad DrawDialpad_8 +#define DrawKeyboard DrawKeyboard_8 +#define DrawFinJoystick DrawFinJoystick_8 +#define RenderVideo RenderVideo_8 +#define PrintXY PrintXY_8 +#define PrintXY2 PrintXY2_8 +#include "Touch.c" +#undef pixel +#undef PIXEL +#undef DrawHLine +#undef DrawVLine +#undef DrawPenCues +#undef DrawDialpad +#undef DrawKeyboard +#undef DrawFinJoystick +#undef RenderVideo +#undef PrintXY +#undef PrintXY2 +#undef BPP8 + +#define BPP16 +#define pixel unsigned short +#if defined(UNIX) || defined(ANDROID) || defined(S60) || defined(UIQ) || defined(NXC2600) || defined(STMP3700) +/* Symbian and Unix use true 16BPP color */ +#define PIXEL(R,G,B) (pixel)(((31*(R)/255)<<11)|((63*(G)/255)<<5)|(31*(B)/255)) +#else +/* Other platforms use 15BPP color */ +#define PIXEL(R,G,B) (pixel)(((31*(R)/255)<<10)|((31*(G)/255)<<5)|(31*(B)/255)) +#endif +#define DrawHLine DrawHLine_16 +#define DrawVLine DrawVLine_16 +#define DrawPenCues DrawPenCues_16 +#define DrawDialpad DrawDialpad_16 +#define DrawKeyboard DrawKeyboard_16 +#define DrawFinJoystick DrawFinJoystick_16 +#define RenderVideo RenderVideo_16 +#define PrintXY PrintXY_16 +#define PrintXY2 PrintXY2_16 +#include "Touch.c" +#undef pixel +#undef PIXEL +#undef DrawHLine +#undef DrawVLine +#undef DrawPenCues +#undef DrawDialpad +#undef DrawKeyboard +#undef DrawFinJoystick +#undef RenderVideo +#undef PrintXY +#undef PrintXY2 +#undef BPP16 + +#define BPP32 +#define pixel unsigned int +#if defined(ANDROID) +#define PIXEL(R,G,B) (pixel)(((int)R<<16)|((int)G<<8)|B|0xFF000000) +#else +#define PIXEL(R,G,B) (pixel)(((int)R<<16)|((int)G<<8)|B) +#endif +#define DrawHLine DrawHLine_32 +#define DrawVLine DrawVLine_32 +#define DrawPenCues DrawPenCues_32 +#define DrawDialpad DrawDialpad_32 +#define DrawKeyboard DrawKeyboard_32 +#define DrawFinJoystick DrawFinJoystick_32 +#define RenderVideo RenderVideo_32 +#define PrintXY PrintXY_32 +#define PrintXY2 PrintXY2_32 +#include "Touch.c" +#undef pixel +#undef PIXEL +#undef DrawHLine +#undef DrawVLine +#undef DrawPenCues +#undef DrawDialpad +#undef DrawKeyboard +#undef DrawFinJoystick +#undef RenderVideo +#undef PrintXY +#undef PrintXY2 +#undef BPP32 + +/** DrawPenCues() ********************************************/ +/** Overlay dotted cue lines for using PenJoystick() onto a **/ +/** given image. Show dialpad cues if requested. **/ +/*************************************************************/ +void DrawPenCues(Image *Img,int ShowDialpad,int Color) +{ + switch(Img->D) + { + case 8: DrawPenCues_8(Img,ShowDialpad,Color);break; + case 16: DrawPenCues_16(Img,ShowDialpad,Color);break; + case 24: + case 32: DrawPenCues_32(Img,ShowDialpad,Color);break; + } +} + +/** DrawDialpad() ********************************************/ +/** Draw virtual dialpad in a given image. **/ +/*************************************************************/ +void DrawDialpad(Image *Img,int Color) +{ + switch(Img->D) + { + case 8: DrawDialpad_8(Img,Color);break; + case 16: DrawDialpad_16(Img,Color);break; + case 24: + case 32: DrawDialpad_32(Img,Color);break; + } +} + +/** DrawKeyboard() *******************************************/ +/** Draw virtual keyboard in a given image. Key modifiers **/ +/** and the key code passed in CurKey are highlighted. **/ +/*************************************************************/ +void DrawKeyboard(Image *Img,unsigned int CurKey) +{ + switch(Img->D) + { + case 8: DrawKeyboard_8(Img,CurKey);break; + case 16: DrawKeyboard_16(Img,CurKey);break; + case 24: + case 32: DrawKeyboard_32(Img,CurKey);break; + } +} + +/** DrawFinJoystick() ****************************************/ +/** Draw finger joystick into given destination image. If **/ +/** the destination if too small, this function returns 0, **/ +/** it returns 1 otherwise. **/ +/*************************************************************/ +int DrawFinJoystick(Image *Dst,int DX,int DY,int DW,int DH,int TextColor) +{ + switch(Dst->D) + { + case 8: return(DrawFinJoystick_8(Dst,DX,DY,DW,DH,TextColor)); + case 16: return(DrawFinJoystick_16(Dst,DX,DY,DW,DH,TextColor)); + case 24: + case 32: return(DrawFinJoystick_32(Dst,DX,DY,DW,DH,TextColor)); + } + + /* Wrong depth */ + return(0); +} + +/** RenderVideo() ********************************************/ +/** Draw video buffer to a given image. Return 0 on failure **/ +/** or destination rectangle size inside OutImg on success. **/ +/*************************************************************/ +unsigned int RenderVideo(Image *OutImg,Image *CueImg,int Effects,int PenKey,int FrameRate) +{ + int D = OutImg? OutImg->D:VideoImg? VideoImg->D:0; + + /* Check that the depths are the same */ + if(CueImg && (D!=CueImg->D)) return(0); + + switch(D) + { + case 8: return(RenderVideo_8(OutImg,CueImg,Effects,PenKey,FrameRate)); + case 16: return(RenderVideo_16(OutImg,CueImg,Effects,PenKey,FrameRate)); + case 24: + case 32: return(RenderVideo_32(OutImg,CueImg,Effects,PenKey,FrameRate)); + } + + /* Wrong depth */ + return(0); +} + +#endif /* TOUCHMUX_H */ diff --git a/components/msx/fmsx/src/EMULib/Unix/LibUnix.c b/components/msx/fmsx/src/EMULib/Unix/LibUnix.c new file mode 100644 index 0000000..d3f1548 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Unix/LibUnix.c @@ -0,0 +1,886 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** LibUnix.c **/ +/** **/ +/** This file contains Unix-dependent implementation **/ +/** parts of the emulation library. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "EMULib.h" +#include "Sound.h" +#include "Console.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef MITSHM +#include +#include +#endif + +#define FPS_COLOR PIXEL(255,0,255) + +extern int MasterSwitch; /* Switches to turn channels on/off */ +extern int MasterVolume; /* Master volume */ + +static volatile int TimerReady = 0; /* 1: Sync timer ready */ +static volatile unsigned int JoyState = 0; /* Joystick state */ +static volatile unsigned int LastKey = 0; /* Last key prsd */ +static volatile unsigned int KeyModes = 0; /* SHIFT/CTRL/ALT */ + +static int Effects = EFF_SCALE|EFF_SAVECPU; /* EFF_* bits */ +static int TimerON = 0; /* 1: sync timer is running */ +static Display *Dsp = 0; /* X11 display */ +static Screen *Scr = 0; /* X11 screen */ +static Window Wnd = 0; /* X11 window */ +static Colormap CMap; /* X11 color map */ +static Image OutImg; /* Scaled output image buffer */ +static const char *AppTitle; /* Current window title */ +static int XSize,YSize; /* Current window dimensions */ + +static int FrameCount; /* Frame counter for EFF_SHOWFPS */ +static int FrameRate; /* Last frame rate value */ +static struct timeval TimeStamp; /* Last timestamp */ + +/** TimerHandler() *******************************************/ +/** The main timer handler used by SetSyncTimer(). **/ +/*************************************************************/ +static void TimerHandler(int Arg) +{ + /* Mark sync timer as "ready" */ + TimerReady=1; + /* Repeat signal next time */ + signal(Arg,TimerHandler); +} + +/** InitUnix() ***********************************************/ +/** Initialize Unix/X11 resources and set initial window **/ +/** title and dimensions. **/ +/*************************************************************/ +int InitUnix(const char *Title,int Width,int Height) +{ + /* Initialize variables */ + AppTitle = Title; + XSize = Width; + YSize = Height; + TimerON = 0; + TimerReady = 0; + JoyState = 0; + LastKey = 0; + KeyModes = 0; + Wnd = 0; + Dsp = 0; + Scr = 0; + CMap = 0; + FrameCount = 0; + FrameRate = 0; + + /* Get initial timestamp */ + gettimeofday(&TimeStamp,0); + + /* No output image yet */ + OutImg.XImg = 0; +#ifdef MITSHM + OutImg.SHMInfo.shmaddr = 0; +#endif + + /* Open X11 display */ + if(!(Dsp=XOpenDisplay(0))) return(0); + + /* Get default screen and color map */ + Scr = DefaultScreenOfDisplay(Dsp); + CMap = DefaultColormapOfScreen(Scr); + + /* Done */ + return(1); +} + +/** TrashUnix() **********************************************/ +/** Free resources allocated in InitUnix() **/ +/*************************************************************/ +void TrashUnix(void) +{ + /* Remove sync timer */ + SetSyncTimer(0); + /* Shut down audio */ + TrashAudio(); + /* Free output image buffer */ + FreeImage(&OutImg); + + /* If X11 display open... */ + if(Dsp) + { + /* Close the window */ + if(Wnd) { XDestroyWindow(Dsp,Wnd);Wnd=0; } + /* Done with display */ + XCloseDisplay(Dsp); + /* Display now closed */ + Dsp=0; + } +} + +/** ShowVideo() **********************************************/ +/** Show "active" image at the actual screen or window. **/ +/*************************************************************/ +int ShowVideo(void) +{ + Image *Output; + int SX,SY,SW,SH,J; + + /* Must have active video image, X11 display */ + if(!Dsp||!VideoImg||!VideoImg->Data) return(0); + + /* If no window yet... */ + if(!Wnd) + { + /* Create new window */ + Wnd=X11Window(AppTitle? AppTitle:"EMULib",Effects&EFF_4X3? 4*YSize/3:XSize,YSize); + if(!Wnd) return(0); + } + + /* Allocate image buffer if none */ + if(!OutImg.Data&&!NewImage(&OutImg,Effects&EFF_4X3? 4*YSize/3:XSize,YSize)) return(0); + + /* Wait for all X11 requests to complete, to avoid flicker */ + XSync(Dsp,False); + + /* If not scaling or post-processing image, avoid extra work */ + if(!(Effects&(EFF_RASTER_ALL|EFF_MASK_ALL|EFF_SOFTEN_ALL|EFF_VIGNETTE|EFF_SCALE|EFF_4X3))) + { +#ifdef MITSHM + if(VideoImg->Attrs&EFF_MITSHM) + XShmPutImage(Dsp,Wnd,DefaultGCOfScreen(Scr),VideoImg->XImg,VideoX,VideoY,(XSize-VideoW)>>1,(YSize-VideoH)>>1,VideoW,VideoH,False); + else +#endif + XPutImage(Dsp,Wnd,DefaultGCOfScreen(Scr),VideoImg->XImg,VideoX,VideoY,(XSize-VideoW)>>1,(YSize-VideoH)>>1,VideoW,VideoH); + return(1); + } + + /* By default, we will be showing OutImg */ + Output = &OutImg; + SX = 0; + SY = 0; + SW = OutImg.W; + SH = OutImg.H; + + /* Interpolate image if required */ + J = Effects&EFF_SOFTEN_ALL; + if((J==EFF_2XSAI) || (J==EFF_HQ4X)) + SoftenImage(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH); + else if(J==EFF_LINEAR) + InterpolateImage(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH); + else if(J==EFF_EPX) + SoftenEPX(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH); + else if(J==EFF_EAGLE) + SoftenEAGLE(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH); + else if(J==EFF_SCALE2X) + SoftenSCALE2X(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH); + else if(J || (J==EFF_NEAREST)) + ScaleImage(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH); + else if((OutImg.W==VideoW)&&(OutImg.H==VideoH)) + { + if(Effects&(EFF_RASTER_ALL|EFF_MASK_ALL)) + IMGCopy(&OutImg,0,0,VideoImg,VideoX,VideoY,VideoW,VideoH,-1); + else + { + /* Use VideoImg directly */ + Output = VideoImg; + SX = VideoX; + SY = VideoY; + } + } + else if(Effects&(EFF_SCALE|EFF_4X3)) + { + /* Scale VideoImg to OutImg */ + ScaleImage(&OutImg,VideoImg,VideoX,VideoY,VideoW,VideoH); + } + else if((OutImg.W<=VideoW)&&(OutImg.H<=VideoH)) + { + if(Effects&(EFF_RASTER_ALL|EFF_MASK_ALL)) + IMGCopy(&OutImg,0,0,VideoImg,VideoX+((VideoW-OutImg.W)>>1),VideoY+((VideoH-OutImg.H)>>1),OutImg.W,OutImg.H,-1); + else + { + /* Use VideoImg directly */ + Output = VideoImg; + SX = VideoX+((VideoW-OutImg.W)>>1); + SY = VideoY+((VideoH-OutImg.H)>>1); + } + } + else + { + /* Use rectangle at the center of OutImg */ + SX = (OutImg.W-VideoW)>>1; + SY = (OutImg.H-VideoH)>>1; + SW = VideoW; + SH = VideoH; + /* Center VideoImg in OutImg */ + IMGCopy(&OutImg,SX,SY,VideoImg,VideoX,VideoY,VideoW,VideoH,-1); + } + + /* Apply color mask to the pixels */ + switch(Effects&EFF_MASK_ALL) + { + case EFF_CMYMASK: CMYizeImage(&OutImg,SX,SY,SW,SH);break; + case EFF_RGBMASK: RGBizeImage(&OutImg,SX,SY,SW,SH);break; + case EFF_MONO: MonoImage(&OutImg,SX,SY,SW,SH);break; + case EFF_GREEN: GreenImage(&OutImg,SX,SY,SW,SH);break; + case EFF_AMBER: AmberImage(&OutImg,SX,SY,SW,SH);break; + case EFF_SEPIA: SepiaImage(&OutImg,SX,SY,SW,SH);break; + } + + /* Apply scanlines or raster */ + switch(Effects&EFF_RASTER_ALL) + { + case EFF_TVLINES: TelevizeImage(&OutImg,SX,SY,SW,SH);break; + case EFF_LCDLINES: LcdizeImage(&OutImg,SX,SY,SW,SH);break; + case EFF_RASTER: RasterizeImage(&OutImg,SX,SY,SW,SH);break; + } + + /* Show framerate if requested */ + if((Effects&EFF_SHOWFPS)&&(FrameRate>0)) + { + char S[16]; + sprintf(S,"%dfps",FrameRate); + PrintXY( + &OutImg,S, + ((OutImg.W-VideoW)>>1)+8,((OutImg.H-VideoH)>>1)+8, + FPS_COLOR,-1 + ); + } + + /* Wait for sync timer if requested */ + if(Effects&EFF_SYNC) WaitSyncTimer(); + + /* Copy image to the window, either using SHM or not */ +#ifdef MITSHM + if(VideoImg->Attrs&EFF_MITSHM) + XShmPutImage(Dsp,Wnd,DefaultGCOfScreen(Scr),Output->XImg,SX,SY,0,0,OutImg.W,OutImg.H,False); + else +#endif + XPutImage(Dsp,Wnd,DefaultGCOfScreen(Scr),Output->XImg,SX,SY,0,0,OutImg.W,OutImg.H); + + /* Done */ + return(1); +} + +/** GetJoystick() ********************************************/ +/** Get the state of joypad buttons (1="pressed"). Refer to **/ +/** the BTN_* #defines for the button mappings. **/ +/*************************************************************/ +unsigned int GetJoystick(void) +{ + /* Count framerate */ + if((Effects&EFF_SHOWFPS)&&(++FrameCount>=300)) + { + struct timeval NewTS; + int Time; + + gettimeofday(&NewTS,0); + Time = (NewTS.tv_sec-TimeStamp.tv_sec)*1000 + + (NewTS.tv_usec-TimeStamp.tv_usec)/1000; + FrameRate = 1000*FrameCount/(Time>0? Time:1); + TimeStamp = NewTS; + FrameCount = 0; + FrameRate = FrameRate>999? 999:FrameRate; + } + + /* Process any pending events */ + ProcessEvents(0); + + /* Return current joystick state */ + return(JoyState|KeyModes); +} + +/** GetMouse() ***********************************************/ +/** Get mouse position and button states in the following **/ +/** format: RMB.LMB.Y[29-16].X[15-0]. **/ +/*************************************************************/ +unsigned int GetMouse(void) +{ + unsigned int Mask; + int X,Y,J; + Window W; + + /* Need to have a display and a window */ + if(!Dsp||!Wnd) return(0); + + /* Query mouse pointer */ + if(!XQueryPointer(Dsp,Wnd,&W,&W,&J,&J,&X,&Y,&Mask)) return(0); + + /* If scaling video... */ + if(Effects&(EFF_SOFTEN_ALL|EFF_SCALE|EFF_RASTER_ALL)) + { + /* Scale mouse position */ + X = VideoW*(X<0? 0:X>=XSize? XSize-1:X)/XSize; + Y = VideoH*(Y<0? 0:Y>=YSize? YSize-1:Y)/YSize; + } + else + { + /* Translate mouse position */ + X-= ((XSize-VideoW)>>1); + Y-= ((YSize-VideoH)>>1); + X = X<0? 0:X>=XSize? XSize-1:X; + Y = Y<0? 0:Y>=YSize? YSize-1:Y; + } + + /* Return mouse position and buttons */ + return( + (X&MSE_XPOS) + | ((Y<<16)&MSE_YPOS) + | (Mask&Button1Mask? MSE_LEFT:0) + | (Mask&Button3Mask? MSE_RIGHT:0) + ); +} + +/** GetKey() *************************************************/ +/** Get currently pressed key or 0 if none pressed. Returns **/ +/** CON_* definitions for arrows and special keys. **/ +/*************************************************************/ +unsigned int GetKey(void) +{ + unsigned int J; + + ProcessEvents(0); + J=LastKey; + LastKey=0; + return(J); +} + +/** WaitKey() ************************************************/ +/** Wait for a key to be pressed. Returns CON_* definitions **/ +/** for arrows and special keys. **/ +/*************************************************************/ +unsigned int WaitKey(void) +{ + unsigned int J; + + /* Swallow current keypress */ + GetKey(); + /* Wait in 100ms increments for a new keypress */ + while(!(J=GetKey())&&VideoImg) usleep(100000); + /* Return key code */ + return(J); +} + +/** WaitKeyOrMouse() *****************************************/ +/** Wait for a key or a mouse button to be pressed. Returns **/ +/** the same result as GetMouse(). If no mouse buttons **/ +/** reported to be pressed, do GetKey() to fetch a key. **/ +/*************************************************************/ +unsigned int WaitKeyOrMouse(void) +{ + unsigned int J; + + /* Swallow current keypress */ + GetKey(); + /* Make sure mouse keys are not pressed */ + while(GetMouse()&MSE_BUTTONS) usleep(100000); + /* Wait in 100ms increments for a key or mouse click */ + while(!(J=GetKey())&&!(GetMouse()&MSE_BUTTONS)&&VideoImg) usleep(100000); + /* Place key back into the buffer and return mouse state */ + LastKey=J; + return(GetMouse()); +} + +/** WaitSyncTimer() ******************************************/ +/** Wait for the timer to become ready. Returns number of **/ +/** times timer has been triggered after the last call to **/ +/** WaitSyncTimer(). **/ +/*************************************************************/ +int WaitSyncTimer(void) +{ + int J; + + /* Wait in 1ms increments until timer becomes ready */ + while(!TimerReady&&TimerON&&VideoImg) usleep(1000); + /* Warn of missed timer events */ + if((TimerReady>1)&&(Effects&EFF_VERBOSE)) + printf("WaitSyncTimer(): Missed %d timer events.\n",TimerReady-1); + /* Reset timer */ + J=TimerReady; + TimerReady=0; + return(J); +} + +/** SyncTimerReady() *****************************************/ +/** Return 1 if sync timer ready, 0 otherwise. **/ +/*************************************************************/ +int SyncTimerReady(void) +{ + /* Return whether timer is ready or not */ + return(TimerReady||!TimerON||!VideoImg); +} + +/** SetSyncTimer() *******************************************/ +/** Set synchronization timer to a given frequency in Hz. **/ +/*************************************************************/ +int SetSyncTimer(int Hz) +{ + struct itimerval TimerValue; + + /* Compute and set timer period */ + TimerValue.it_interval.tv_sec = + TimerValue.it_value.tv_sec = 0; + TimerValue.it_interval.tv_usec = + TimerValue.it_value.tv_usec = Hz? 1000000L/Hz:0; + + /* Set timer */ + if(setitimer(ITIMER_REAL,&TimerValue,NULL)) return(0); + + /* Set timer signal */ + signal(SIGALRM,Hz? TimerHandler:SIG_DFL); + + /* Done */ + TimerON=Hz; + return(1); +} + +/** ChangeDir() **********************************************/ +/** This function is a wrapper for chdir(). **/ +/*************************************************************/ +int ChangeDir(const char *Name) { return(chdir(Name)); } + +/** MicroSleep() *********************************************/ +/** Wait for a given number of microseconds. **/ +/*************************************************************/ +void MicroSleep(unsigned int uS) { usleep(uS); } + +/** NewImage() ***********************************************/ +/** Create a new image of the given size. Returns pointer **/ +/** to the image data on success, 0 on failure. **/ +/*************************************************************/ +pixel *NewImage(Image *Img,int Width,int Height) +{ + XVisualInfo VInfo; + int Depth,J,I; + + /* Set data fields to ther defaults */ + Img->Data = 0; + Img->W = 0; + Img->H = 0; + Img->L = 0; + Img->D = 0; + Img->Attrs = 0; + Img->Cropped = 0; + + /* Need to initalize library first */ + if(!Dsp) return(0); + + /* Image depth we are going to use */ + Depth = Effects&EFF_VARBPP? DefaultDepthOfScreen(Scr):(sizeof(pixel)<<3); + + /* Get appropriate Visual for this depth */ + I=XScreenNumberOfScreen(Scr); + for(J=7;J>=0;J--) + if(XMatchVisualInfo(Dsp,I,Depth,J,&VInfo)) break; + if(J<0) return(0); + +#ifdef MITSHM + if(Effects&EFF_MITSHM) + { + /* Create shared XImage */ + Img->XImg = XShmCreateImage(Dsp,VInfo.visual,Depth,ZPixmap,0,&Img->SHMInfo,Width,Height); + if(!Img->XImg) return(0); + + /* Get ID for shared segment */ + Img->SHMInfo.shmid = shmget(IPC_PRIVATE,Img->XImg->bytes_per_line*Img->XImg->height,IPC_CREAT|0777); + if(Img->SHMInfo.shmid==-1) { XDestroyImage(Img->XImg);return(0); } + + /* Attach to shared segment by ID */ + Img->XImg->data = Img->SHMInfo.shmaddr = shmat(Img->SHMInfo.shmid,0,0); + if(!Img->XImg->data) + { + shmctl(Img->SHMInfo.shmid,IPC_RMID,0); + XDestroyImage(Img->XImg); + return(0); + } + + /* Can write into shared segment */ + Img->SHMInfo.readOnly = False; + + /* Attach segment to X display and make sure it is done */ + J=XShmAttach(Dsp,&Img->SHMInfo); + XSync(Dsp,False); + + /* We do not need an ID any longer */ + shmctl(Img->SHMInfo.shmid,IPC_RMID,0); + + /* If attachment failed, break out */ + if(!J) + { + shmdt(Img->SHMInfo.shmaddr); + XDestroyImage(Img->XImg); + return(0); + } + } + else +#endif + { + /* Create normal XImage */ + Img->XImg = XCreateImage(Dsp,VInfo.visual,Depth,ZPixmap,0,0,Width,Height,Depth,0); + if(!Img->XImg) return(0); + + /* Allocate data */ + Img->XImg->data = (char *)malloc(Img->XImg->bytes_per_line*Img->XImg->height); + if(!Img->XImg->data) { XDestroyImage(Img->XImg);return(0); } + } + + /* Done */ + Depth = Depth==24? 32:Depth; + Img->Data = (pixel *)Img->XImg->data; + Img->W = Img->XImg->width; + Img->H = Img->XImg->height; + Img->L = Img->XImg->bytes_per_line/(Depth>>3); + Img->D = Depth; + Img->Attrs = Effects&(EFF_MITSHM|EFF_VARBPP); + return(Img->Data); +} + +/** FreeImage() **********************************************/ +/** Free previously allocated image. **/ +/*************************************************************/ +void FreeImage(Image *Img) +{ + /* Need to initalize library first */ + if(!Dsp||!Img->Data) return; + +#ifdef MITSHM + /* Detach shared memory segment */ + if((Img->Attrs&EFF_MITSHM)&&Img->SHMInfo.shmaddr) + { XShmDetach(Dsp,&Img->SHMInfo);shmdt(Img->SHMInfo.shmaddr); } + Img->SHMInfo.shmaddr = 0; +#endif + + /* Get rid of the image */ + if(Img->XImg) { XDestroyImage(Img->XImg);Img->XImg=0; } + + /* Image freed */ + Img->Data = 0; + Img->W = 0; + Img->H = 0; + Img->L = 0; +} + +/** CropImage() **********************************************/ +/** Create a subimage Dst of the image Src. Returns Dst. **/ +/*************************************************************/ +Image *CropImage(Image *Dst,const Image *Src,int X,int Y,int W,int H) +{ + Dst->Data = (pixel *)((char *)Src->Data+(Src->L*Y+X)*(Src->D>>3)); + Dst->Cropped = 1; + Dst->W = W; + Dst->H = H; + Dst->L = Src->L; + Dst->D = Src->D; + Dst->XImg = 0; + Dst->Attrs = 0; + return(Dst); +} + +/** SetVideo() ***********************************************/ +/** Set part of the image as "active" for display. **/ +/*************************************************************/ +void SetVideo(Image *Img,int X,int Y,int W,int H) +{ + /* Save current dimensions */ + int OldW = VideoW; + int OldH = VideoH; + + /* Call default SetVideo() function */ + GenericSetVideo(Img,X,Y,W,H); + + /* If video exists, modify its size */ + if(Dsp&&VideoW&&VideoH&&((VideoW!=OldW)||(VideoH!=OldH))) + { + int DW,DH,SW,SH; + + /* Make sure window dimensions stay at ~1:1 ratio */ + DW = W/H>1? W/(W/H):W; + DH = H/W>1? H/(H/W):H; + SW = VideoW/VideoH>1? VideoW/(VideoW/VideoH):VideoW; + SH = VideoH/VideoW>1? VideoH/(VideoH/VideoW):VideoH; + XSize = XSize*DW/SW; + YSize = YSize*DH/SH; + XSize = Effects&EFF_4X3? 4*YSize/3:XSize; + + if(Wnd) XResizeWindow(Dsp,Wnd,XSize,YSize); + FreeImage(&OutImg); + } +} + +/** SetEffects() *********************************************/ +/** Set visual effects applied to video in ShowVideo(). **/ +/*************************************************************/ +void SetEffects(unsigned int NewEffects) +{ + /* Set new effects */ + Effects=NewEffects; +} + +/** ProcessEvents() ******************************************/ +/** Process X11 event messages. **/ +/*************************************************************/ +int ProcessEvents(int Wait) +{ + XEvent E; + unsigned int I; + int J,Count; + + /* Need to have display and a window */ + if(!Dsp||!Wnd) return(0); + + do + { + /* Check for keypresses/keyreleases */ + for(Count=0;XCheckWindowEvent(Dsp,Wnd,KeyPressMask|KeyReleaseMask,&E);++Count) + { + /* Get key code */ + J=XLookupKeysym((XKeyEvent *)&E,0); + + /* If key pressed... */ + if(E.type==KeyPress) + { + /* Process ASCII keys */ + if((J>=' ')&&(J<0x7F)) LastKey=toupper(J); + + /* Special keys pressed... */ + switch(J) + { + case XK_Left: JoyState|=BTN_LEFT;LastKey=CON_LEFT;break; + case XK_Right: JoyState|=BTN_RIGHT;LastKey=CON_RIGHT;break; + case XK_Up: JoyState|=BTN_UP;LastKey=CON_UP;break; + case XK_Down: JoyState|=BTN_DOWN;LastKey=CON_DOWN;break; + case XK_Shift_L: + case XK_Shift_R: KeyModes|=CON_SHIFT;break; + case XK_Alt_L: + case XK_Alt_R: KeyModes|=CON_ALT;break; + case XK_Control_L: + case XK_Control_R: KeyModes|=CON_CONTROL;break; + case XK_Escape: JoyState|=BTN_EXIT;LastKey=CON_EXIT;break; + case XK_Tab: JoyState|=BTN_SELECT;break; + case XK_Return: JoyState|=BTN_START;LastKey=CON_OK;break; + case XK_BackSpace: LastKey=8;break; + + case XK_Caps_Lock: + if(!XkbGetIndicatorState(Dsp,XkbUseCoreKbd,&I)) + KeyModes = (KeyModes&~CON_CAPS)|(I&1? CON_CAPS:0); + break; + + case 'q': case 'e': case 't': case 'u': case 'o': + JoyState|=BTN_FIREL;break; + case 'w': case 'r': case 'y': case 'i': case 'p': + JoyState|=BTN_FIRER;break; + case 'a': case 's': case 'd': case 'f': case 'g': + case 'h': case 'j': case 'k': case 'l': case ' ': + JoyState|=BTN_FIREA;break; + case 'z': case 'x': case 'c': case 'v': case 'b': + case 'n': case 'm': + JoyState|=BTN_FIREB;break; + + case XK_Page_Up: + if(KeyModes&CON_ALT) + { + /* Volume up */ + SetChannels(MasterVolume<247? MasterVolume+8:255,MasterSwitch); + /* Key swallowed */ + J=0; + } + break; + + case XK_Page_Down: + if(KeyModes&CON_ALT) + { + /* Volume down */ + SetChannels(MasterVolume>8? MasterVolume-8:0,MasterSwitch); + /* Key swallowed */ + J=0; + } + break; + } + + /* Call user handler */ + if(J&&KeyHandler) (*KeyHandler)(J|KeyModes); + } + + /* If key released... */ + if(E.type==KeyRelease) + { + /* Special keys released... */ + switch(J) + { + case XK_Left: JoyState&=~BTN_LEFT;break; + case XK_Right: JoyState&=~BTN_RIGHT;break; + case XK_Up: JoyState&=~BTN_UP;break; + case XK_Down: JoyState&=~BTN_DOWN;break; + case XK_Shift_L: + case XK_Shift_R: KeyModes&=~CON_SHIFT;break; + case XK_Alt_L: + case XK_Alt_R: KeyModes&=~CON_ALT;break; + case XK_Control_L: + case XK_Control_R: KeyModes&=~CON_CONTROL;break; + case XK_Escape: JoyState&=~BTN_EXIT;break; + case XK_Tab: JoyState&=~BTN_SELECT;break; + case XK_Return: JoyState&=~BTN_START;break; + + case XK_Caps_Lock: + if(!XkbGetIndicatorState(Dsp,XkbUseCoreKbd,&I)) + KeyModes = (KeyModes&~CON_CAPS)|(I&1? CON_CAPS:0); + break; + + case 'q': case 'e': case 't': case 'u': case 'o': + JoyState&=~BTN_FIREL;break; + case 'w': case 'r': case 'y': case 'i': case 'p': + JoyState&=~BTN_FIRER;break; + case 'a': case 's': case 'd': case 'f': case 'g': + case 'h': case 'j': case 'k': case 'l': case ' ': + JoyState&=~BTN_FIREA;break; + case 'z': case 'x': case 'c': case 'v': case 'b': + case 'n': case 'm': + JoyState&=~BTN_FIREB;break; + } + + /* Call user handler */ + if(J&&KeyHandler) (*KeyHandler)(J|CON_RELEASE|KeyModes); + } + } + + /* Check for focus change events */ + for(E.type=0;XCheckWindowEvent(Dsp,Wnd,FocusChangeMask,&E);++Count); + /* If saving CPU and focus is out... */ + if((Effects&EFF_SAVECPU)&&(E.type==FocusOut)) + { + /* Pause audio */ + J=MasterSwitch; + SetChannels(MasterVolume,0); + PauseAudio(1); + /* Wait for focus-in event */ + do + while(!XCheckWindowEvent(Dsp,Wnd,FocusChangeMask,&E)&&VideoImg) sleep(1); + while((E.type!=FocusIn)&&VideoImg); + /* Resume audio */ + PauseAudio(0); + SetChannels(MasterVolume,J); + } + + /* If window has been resized, remove current output buffer */ + for(E.type=0;XCheckWindowEvent(Dsp,Wnd,StructureNotifyMask,&E);++Count) + if((E.type==ConfigureNotify)&&!E.xconfigure.send_event) + if((XSize!=E.xconfigure.width)||(YSize!=E.xconfigure.height)) + { + FreeImage(&OutImg); + XSize=E.xconfigure.width; + YSize=E.xconfigure.height; + } + + /* X11 does not let us wait for event, so we sleep for 100ms instead */ + if(!Wait||Count||!VideoImg) break; else usleep(100000); + } + while(1); + + /* Returning 0 when application quits */ + return(!!VideoImg); +} + +/** X11GetColor **********************************************/ +/** Get pixel for the current screen depth based on the RGB **/ +/** values. **/ +/*************************************************************/ +unsigned int X11GetColor(unsigned char R,unsigned char G,unsigned char B) +{ + int J; + + /* If using constant BPP, just return a pixel */ + if(!Dsp||!(Effects&EFF_VARBPP)) return(PIXEL(R,G,B)); + + /* If variable BPP, compute pixel based on the screen depth */ + J=DefaultDepthOfScreen(Scr); + return( + J<=8? (((7*(R)/255)<<5)|((7*(G)/255)<<2)|(3*(B)/255)) + : J<=16? (((31*(R)/255)<<11)|((63*(G)/255)<<5)|(31*(B)/255)) + : J<=32? (((R)<<16)|((G)<<8)|B) + : 0 + ); +} + +/** X11Window() **********************************************/ +/** Open a window of a given size with a given title. **/ +/*************************************************************/ +Window X11Window(const char *Title,int Width,int Height) +{ + XSetWindowAttributes Attrs; + XClassHint ClassHint; + XSizeHints Hints; + XWMHints WMHints; + Window Wnd; + char *P; + int Q; + + /* Need to initalize library first */ + if(!Dsp) return(0); + + /* Set necessary attributes */ + Attrs.event_mask = + FocusChangeMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask; + Attrs.background_pixel=BlackPixelOfScreen(Scr); + Attrs.backing_store=Always; + + /* Create a window */ + Wnd=XCreateWindow + ( + Dsp,RootWindowOfScreen(Scr),0,0,Width,Height,0, + CopyFromParent,CopyFromParent,CopyFromParent, + CWBackPixel|CWEventMask|CWBackingStore,&Attrs + ); + if(!Wnd) return(0); + + /* Set application class hint */ + if(ARGC&&ARGV) + { + P=strrchr(ARGV[0],'/'); + ClassHint.res_name = P? P+1:ARGV[0]; + ClassHint.res_class = P? P+1:ARGV[0]; + XSetClassHint(Dsp,Wnd,&ClassHint); + XSetCommand(Dsp,Wnd,ARGV,ARGC); + } + + /* Set hints */ + Q=sizeof(long); + Hints.flags = PSize|PMinSize|PMaxSize|PResizeInc; + Hints.min_width = ((Width/4)/Q)*Q; + Hints.max_width = ((Width*4)/Q)*Q; + Hints.base_width = (Width/Q)*Q; + Hints.width_inc = Q; + Hints.min_height = ((Height/4)/Q)*Q; + Hints.max_height = ((Height*4)/Q)*Q; + Hints.base_height = (Height/Q)*Q; + Hints.height_inc = Q; + WMHints.input = True; + WMHints.flags = InputHint; + + if(ARGC&&ARGV) + { + WMHints.window_group=Wnd; + WMHints.flags|=WindowGroupHint; + } + + /* Set hints, title, size */ + XSetWMHints(Dsp,Wnd,&WMHints); + XSetWMNormalHints(Dsp,Wnd,&Hints); + XStoreName(Dsp,Wnd,Title); + + /* Do additional housekeeping and return */ + XMapRaised(Dsp,Wnd); + XClearWindow(Dsp,Wnd); + + /* Done */ + return(Wnd); +} diff --git a/components/msx/fmsx/src/EMULib/Unix/LibUnix.h b/components/msx/fmsx/src/EMULib/Unix/LibUnix.h new file mode 100644 index 0000000..1f64a3a --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Unix/LibUnix.h @@ -0,0 +1,106 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** LibUnix.h **/ +/** **/ +/** This file contains Unix-dependent definitions and **/ +/** declarations for the emulation library. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef LIBUNIX_H +#define LIBUNIX_H + +#include +#include +#include +#include + +#ifdef MITSHM +#include +#endif + +/* X11 defines "Status" to be "int" but this may shadow some */ +/* of our application variables! */ +#undef Status + +#ifdef __cplusplus +extern "C" { +#endif + +#define SND_CHANNELS 16 /* Number of sound channels */ +#define SND_BITS 8 +#define SND_BUFSIZE (1<>16)&0xFF)+((P>>8)&0xFF)+(P&0xFF))/3) +#define RMASK 0xFF0000 +#define GMASK 0x00FF00 +#define BMASK 0x0000FF + +#elif defined(BPP16) +#define PIXEL(R,G,B) (pixel)(((31*(R)/255)<<11)|((63*(G)/255)<<5)|(31*(B)/255)) +#define PIXEL2MONO(P) (522*(((P)&31)+(((P)>>5)&63)+(((P)>>11)&31))>>8) +#define RMASK 0xF800 +#define GMASK 0x07E0 +#define BMASK 0x001F + +#elif defined(BPP8) +#define PIXEL(R,G,B) (pixel)(((7*(R)/255)<<5)|((7*(G)/255)<<2)|(3*(B)/255)) +#define PIXEL2MONO(P) (3264*((((P)<<1)&7)+(((P)>>2)&7)+(((P)>>5)&7))>>8) +#define RMASK 0xE0 +#define GMASK 0x1C +#define BMASK 0x03 +#endif + +int ARGC; +char **ARGV; + +/** InitUnix() ***********************************************/ +/** Initialize Unix/X11 resources and set initial window. **/ +/** title and dimensions. **/ +/*************************************************************/ +int InitUnix(const char *Title,int Width,int Height); + +/** TrashUnix() **********************************************/ +/** Free resources allocated in InitUnix() **/ +/*************************************************************/ +void TrashUnix(void); + +/** InitAudio() **********************************************/ +/** Initialize sound. Returns rate (Hz) on success, else 0. **/ +/** Rate=0 to skip initialization (will be silent). **/ +/*************************************************************/ +unsigned int InitAudio(unsigned int Rate,unsigned int Latency); + +/** TrashAudio() *********************************************/ +/** Free resources allocated by InitAudio(). **/ +/*************************************************************/ +void TrashAudio(void); + +/** PauseAudio() *********************************************/ +/** Pause/resume audio playback. **/ +/*************************************************************/ +int PauseAudio(int Switch); + +/** X11Window() **********************************************/ +/** Open a window of a given size with a given title. **/ +/*************************************************************/ +Window X11Window(const char *Title,int Width,int Height); + +/** X11GetColor **********************************************/ +/** Get pixel for the current screen depth based on the RGB **/ +/** values. **/ +/*************************************************************/ +unsigned int X11GetColor(unsigned char R,unsigned char G,unsigned char B); + +#ifdef __cplusplus +} +#endif +#endif /* LIBUNIX_H */ diff --git a/components/msx/fmsx/src/EMULib/Unix/NetUnix.c b/components/msx/fmsx/src/EMULib/Unix/NetUnix.c new file mode 100644 index 0000000..d4b1d63 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Unix/NetUnix.c @@ -0,0 +1,441 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** NetUnix.c **/ +/** **/ +/** This file contains standard communication routines for **/ +/** Unix using TCP/IP via sockets. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1997-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "EMULib.h" +#include "NetPlay.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ANDROID +#define puts LOGI +#define printf LOGI +#endif + +#ifndef SOL_TCP +#define SOL_TCP IPPROTO_TCP +#endif + +static volatile int IsServer = 0; +static volatile int Socket = -1; +static volatile int Blocking = 1; +static volatile int UseUDP = 0; +static volatile pthread_t Thr = 0; +static struct sockaddr_in PeerAddr; + +/** ThrHandler() *********************************************/ +/** This is the thread function responsible for asyncronous **/ +/** connection process. **/ +/*************************************************************/ +static void *ThrHandler(void *Arg) +{ + void **Args = (void **)Arg; + char *Server = (char *)Args[0]; + unsigned int Port = (unsigned int)Args[1]; + + /* Try connecting */ + NETConnect(Server,Port); + + /* No longer need strdup()ed server name */ + if(Server) free(Server); + + /* Thread done */ + Thr = 0; + return(0); +} + +/** NETConnected() *******************************************/ +/** Returns NET_SERVER, NET_CLIENT, or NET_OFF. **/ +/*************************************************************/ +int NETConnected(void) +{ return(Socket<0? NET_OFF:IsServer? NET_SERVER:NET_CLIENT); } + +/** NETMyName() **********************************************/ +/** Returns local hostname/address or 0 on failure. **/ +/*************************************************************/ +const char *NETMyName(char *Buffer,int MaxChars) +{ + struct hostent *Host; + + /* Have to have enough characters */ + if(MaxChars<16) return(0); + /* Get local hostname */ + gethostname(Buffer,MaxChars); + /* Look up address */ + if(!(Host=gethostbyname(Buffer))) return(0); + /* Must have an address */ + if(!Host->h_addr_list||!Host->h_addr_list[0]) return(0); + /* Copy address */ + sprintf(Buffer,"%d.%d.%d.%d", + Host->h_addr_list[0][0], + Host->h_addr_list[0][1], + Host->h_addr_list[0][2], + Host->h_addr_list[0][3] + ); + return(Buffer); +} + +/** NETConnectAsync() ****************************************/ +/** Asynchronous version of NETConnect(). **/ +/*************************************************************/ +int NETConnectAsync(const char *Server,unsigned short Port) +{ + static void *Args[2]; + + /* Thread already exists, nothing to do */ + if(Thr) return(1); + + /* Close existing network connection */ + NETClose(); + + /* Set up arguments */ + Args[0] = (void *)(Server? strdup(Server):0); + Args[1] = (void *)(unsigned int)Port; + if(!Args[0]&&Server) return(0); + + /* Create connection thread */ + return(!!pthread_create((pthread_t *)&Thr,0,ThrHandler,Args)); +} + +/** UDPConnect() *********************************************/ +/** Connects to Server:Port. When Server=0, becomes server, **/ +/** waits at for an incoming request, and connects. Returns **/ +/** the NetPlay connection status, like NETConnected(). **/ +/*************************************************************/ +int UDPConnect(const char *Server,unsigned short Port) +{ + struct sockaddr_in MyAddr; + struct hostent *Host; + int SSocket,J; + pthread_t T; + + /* Store current thread ID (or 0 if called synchronously) */ + T = Thr; + + /* Close existing network connection */ + if(!T) NETClose(); + + /* Create UDP socket */ + if((SSocket=socket(AF_INET,SOCK_DGRAM,0))<0) return(NET_OFF); + + /* Set fields of the address structure */ + memset(&MyAddr,0,sizeof(MyAddr)); + MyAddr.sin_family = AF_INET; + MyAddr.sin_port = htons(Port); + MyAddr.sin_addr.s_addr = htonl(INADDR_ANY); + memcpy(&PeerAddr,&MyAddr,sizeof(PeerAddr)); + + /* If the server name is given, we first try to */ + /* connect as a client. */ + if(Server) + { + printf("UDP-CLIENT: Looking up '%s'...\n",Server); + + /* Look up server address */ + if(!(Host=gethostbyname(Server))) { close(SSocket);return(NET_OFF); } + + /* Set server address */ + memcpy(&PeerAddr.sin_addr,Host->h_addr,Host->h_length); + + /* Bind socket */ + if(bind(SSocket,(struct sockaddr *)&MyAddr,sizeof(MyAddr))<0) + { close(SSocket);return(NET_OFF); } + + printf("UDP-CLIENT: Client established.\n"); + } + else + { + printf("UDP-SERVER: Becoming server...\n"); + + /* Accepting messages from any address */ + PeerAddr.sin_addr.s_addr = htonl(INADDR_ANY); + + /* Bind socket */ + if(bind(SSocket,(struct sockaddr *)&MyAddr,sizeof(MyAddr))<0) + { close(SSocket);return(NET_OFF); } + + printf("UDP-SERVER: Server established.\n"); + } + + /* Make communication socket blocking/non-blocking */ + J=!Blocking; + if(ioctl(SSocket,FIONBIO,&J)<0) { close(SSocket);return(NET_OFF); } + + /* Success */ + IsServer = !Server; + UseUDP = 1; + Socket = SSocket; + + return(Server? NET_SERVER:NET_CLIENT); +} + +/** NETConnect() *********************************************/ +/** Connects to Server:Port. When Server=0, becomes server, **/ +/** waits at for an incoming request, and connects. Returns **/ +/** the NetPlay connection status, like NETConnected(). **/ +/*************************************************************/ +int NETConnect(const char *Server,unsigned short Port) +{ + struct sockaddr_in Addr; + struct hostent *Host; + struct timeval TV; + int LSocket,SSocket; + socklen_t AddrLength; + unsigned long J; + pthread_t T; + fd_set FDs; + + /* Store current thread ID (or 0 if called synchronously) */ + T = Thr; + + /* Close existing network connection */ + if(!T) NETClose(); + + /* Clear the address structure */ + memset(&Addr,0,sizeof(Addr)); + + /* If the server name is given, we first try to */ + /* connect as a client. */ + if(Server) + { + printf("NET-CLIENT: Connecting to '%s'...\n",Server); + + /* Look up server address */ + if(!(Host=gethostbyname(Server))) return(NET_OFF); + + printf("NET-CLIENT: Got server's IP address...\n"); + + /* Set fields of the address structure */ + memcpy(&Addr.sin_addr,Host->h_addr,Host->h_length); + Addr.sin_family = AF_INET; + Addr.sin_port = htons(Port); + + /* Create a socket */ + if((SSocket=socket(AF_INET,SOCK_STREAM,0))<0) return(NET_OFF); + + printf("NET-CLIENT: Created socket...\n"); + + /* Connecting... */ + if(connect(SSocket,(struct sockaddr *)&Addr,sizeof(Addr))>=0) + { + printf("NET-CLIENT: Connected to the server...\n"); + + /* Make communication socket blocking/non-blocking */ + J=!Blocking; + if(ioctl(SSocket,FIONBIO,&J)<0) + { close(SSocket);return(NET_OFF); } + + /* Disable Nagle algorithm */ + J=1; + setsockopt(SSocket,SOL_TCP,TCP_NODELAY,&J,sizeof(J)); + + /* Succesfully connected as client */ + IsServer = 0; + UseUDP = 0; + Socket = SSocket; + + printf("NET-CLIENT: Connected to '%s'.\n",Server); + + return(NET_CLIENT); + } + + printf("NET-CLIENT: Failed connecting to '%s'.\n",Server); + + /* Failed to connect as a client */ + close(SSocket); + +#ifdef ANDROID + /* Do not try becoming server! */ + return(NET_OFF); +#endif + } + + /* Connection as client either failed or hasn't */ + /* been attempted at all. Becoming a server and */ + /* waiting for connection request. */ + + printf("NET-SERVER: Becoming server...\n"); + + /* Set fields of the address structure */ + Addr.sin_addr.s_addr = htonl(INADDR_ANY); + Addr.sin_family = AF_INET; + Addr.sin_port = htons(Port); + + /* Create a listening socket */ + if((LSocket=socket(AF_INET,SOCK_STREAM,0))<0) return(NET_OFF); + + printf("NET-SERVER: Created socket...\n"); + + /* Bind listening socket */ + if(bind(LSocket,(struct sockaddr *)&Addr,sizeof(Addr))<0) + { close(LSocket);return(NET_OFF); } + + printf("NET-SERVER: Bound socket...\n"); + + /* Listen for one client */ + if(listen(LSocket,1)<0) + { close(LSocket);return(NET_OFF); } + + /* We will need address length */ + AddrLength=sizeof(Addr); + + printf("NET-SERVER: Accepting calls...\n"); + + /* No sockets yet */ + FD_ZERO(&FDs); + +#ifdef ANDROID + /* Accepting calls... */ + for(SSocket=-1;(SSocket<0)&&VideoImg&&(T==Thr);) + { + /* Prepare data for select() */ + FD_SET(LSocket,&FDs); + TV.tv_sec = 1; + TV.tv_usec = 0; + /* Listen and accept connection */ + if(select(LSocket+1,&FDs,0,0,&TV)>0) + SSocket=accept(LSocket,(struct sockaddr *)&Addr,&AddrLength); + } +#else + /* Accepting calls... */ + for(SSocket=-1,GetKey();(SSocket<0)&&VideoImg&&!GetKey()&&(T==Thr);) + { + /* Prepare data for select() */ + FD_SET(LSocket,&FDs); + TV.tv_sec = 0; + TV.tv_usec = 100000; + /* Make sure UI is still running */ + ProcessEvents(0); + /* Listen and accept connection */ + if(select(LSocket+1,&FDs,0,0,&TV)>0) + SSocket=accept(LSocket,(struct sockaddr *)&Addr,&AddrLength); + } +#endif + + printf("NET-SERVER: Client %s...\n",SSocket>=0? "connected":"failed to connect"); + + /* Done listening */ + close(LSocket); + + /* Client failed to connect */ + if(SSocket<0) return(NET_OFF); + + /* Make communication socket blocking/non-blocking */ + J=!Blocking; + if(ioctl(SSocket,FIONBIO,&J)<0) + { close(SSocket);return(NET_OFF); } + + /* Disable Nagle algorithm */ + J=1; + setsockopt(SSocket,SOL_TCP,TCP_NODELAY,&J,sizeof(J)); + + /* Client connected succesfully */ + IsServer = 1; + UseUDP = 0; + Socket = SSocket; + + printf("NET-SERVER: Client connected.\n"); + + return(NET_SERVER); +} + +/** NETClose() ***********************************************/ +/** Closes connection open with NETConnect(). **/ +/*************************************************************/ +void NETClose(void) +{ + pthread_t T = Thr; + + /* If there is a connection thread running, stop it */ + if(T) { Thr=0;pthread_join(T,0); } + + if(Socket>=0) close(Socket); + Socket = -1; + IsServer = 0; +} + +/** NETBlock() ***********************************************/ +/** Makes NETSend()/NETRecv() blocking or not blocking. **/ +/*************************************************************/ +int NETBlock(int Switch) +{ + unsigned long J; + + /* Toggle blocking if requested */ + if(Switch==NET_TOGGLE) Switch=!Blocking; + + /* If switching blocking... */ + if((Switch==NET_OFF)||(Switch==NET_ON)) + { + J=!Switch; + if((Socket<0)||(ioctl(Socket,FIONBIO,&J)>=0)) Blocking=Switch; + } + + /* Return blocking state */ + return(Blocking); +} + +/** NETSend() ************************************************/ +/** Send N bytes. Returns number of bytes sent or 0. **/ +/*************************************************************/ +int NETSend(const char *Out,int N) +{ + int J,I; + + /* Have to have a socket and an address */ + if((Socket<0)||(UseUDP&&(PeerAddr.sin_addr.s_addr==INADDR_ANY))) return(0); + + /* Send data */ + for(I=J=N;(J>=0)&&I;) + { + J = UseUDP? sendto(Socket,Out,I,0,(struct sockaddr *)&PeerAddr,sizeof(PeerAddr)):send(Socket,Out,I,0); + if(J>0) { Out+=J;I-=J; } + } + + /* Return number of bytes sent */ + return(N-I); +} + +/** NETRecv() ************************************************/ +/** Receive N bytes. Returns number of bytes received or 0. **/ +/*************************************************************/ +int NETRecv(char *In,int N) +{ + socklen_t AddrLen; + int J,I; + + /* Have to have a socket */ + if(Socket<0) return(0); + + /* This is needed for recvfrom() */ + AddrLen = sizeof(PeerAddr); + + /* Receive data */ + for(I=J=N;(J>=0)&&I;) + { + J = UseUDP? recvfrom(Socket,In,I,0,(struct sockaddr *)&PeerAddr,&AddrLen):recv(Socket,In,I,0); + if(J>0) { In+=J;I-=J; } + } + + /* Return number of bytes received */ + return(N-I); +} diff --git a/components/msx/fmsx/src/EMULib/Unix/SndUnix.c b/components/msx/fmsx/src/EMULib/Unix/SndUnix.c new file mode 100644 index 0000000..7fff526 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/Unix/SndUnix.c @@ -0,0 +1,378 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** SndUnix.c **/ +/** **/ +/** This file contains Unix-dependent sound implementation **/ +/** for the emulation library. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "EMULib.h" +#include "Sound.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PULSE_AUDIO) + +#include + +#elif defined(ESD_AUDIO) + +#include "esd.h" + +#elif defined(SUN_AUDIO) + +#include +#include +#include + +static const unsigned char ULAW[256] = +{ + 31, 31, 31, 32, 32, 32, 32, 33, + 33, 33, 33, 34, 34, 34, 34, 35, + 35, 35, 35, 36, 36, 36, 36, 37, + 37, 37, 37, 38, 38, 38, 38, 39, + 39, 39, 39, 40, 40, 40, 40, 41, + 41, 41, 41, 42, 42, 42, 42, 43, + 43, 43, 43, 44, 44, 44, 44, 45, + 45, 45, 45, 46, 46, 46, 46, 47, + 47, 47, 47, 48, 48, 49, 49, 50, + 50, 51, 51, 52, 52, 53, 53, 54, + 54, 55, 55, 56, 56, 57, 57, 58, + 58, 59, 59, 60, 60, 61, 61, 62, + 62, 63, 63, 64, 65, 66, 67, 68, + 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 81, 83, 85, 87, 89, + 91, 93, 95, 99, 103, 107, 111, 119, + 255, 247, 239, 235, 231, 227, 223, 221, + 219, 217, 215, 213, 211, 209, 207, 206, + 205, 204, 203, 202, 201, 200, 199, 198, + 219, 217, 215, 213, 211, 209, 207, 206, + 205, 204, 203, 202, 201, 200, 199, 198, + 197, 196, 195, 194, 193, 192, 191, 191, + 190, 190, 189, 189, 188, 188, 187, 187, + 186, 186, 185, 185, 184, 184, 183, 183, + 182, 182, 181, 181, 180, 180, 179, 179, + 178, 178, 177, 177, 176, 176, 175, 175, + 175, 175, 174, 174, 174, 174, 173, 173, + 173, 173, 172, 172, 172, 172, 171, 171, + 171, 171, 170, 170, 170, 170, 169, 169, + 169, 169, 168, 168, 168, 168, 167, 167, + 167, 167, 166, 166, 166, 166, 165, 165, + 165, 165, 164, 164, 164, 164, 163, 163 +}; + +#else /* !SUN_AUDIO && !ESD_AUDIO && !PULSE_AUDIO */ + +#ifdef __FreeBSD__ +#include +#endif + +#ifdef __NetBSD__ +#include +#endif + +#ifdef __linux__ +#include +#endif + +#endif /* !SUN_AUDIO && !ESD_AUDIO && !PULSE_AUDIO */ + +#if defined(SUN_AUDIO) +#define AUDIO_CONV(A) (ULAW[0xFF&(128+(A))]) +#elif defined(BPS16) +#define AUDIO_CONV(A) (A) +#else +#define AUDIO_CONV(A) (128+(A)) +#endif + +#if defined(PULSE_AUDIO) +#define SOUNDFD_INVALID (0) +static pa_simple *SoundFD = 0; +#else +#define SOUNDFD_INVALID (-1) +static int SoundFD = -1; +#endif + +static int SndRate = 0; /* Audio sampling rate */ +static int SndSize = 0; /* SndData[] size */ +static sample *SndData = 0; /* Audio buffers */ +static int RPtr = 0; /* Read pointer into Bufs */ +static int WPtr = 0; /* Write pointer into Bufs */ +static pthread_t Thr = 0; /* Audio thread */ +static volatile int AudioPaused = 0; /* 1: Audio paused */ + +/** ThrHandler() *********************************************/ +/** This is the thread function responsible for sending **/ +/** buffers to the audio device. **/ +/*************************************************************/ +static void *ThrHandler(void *Arg) +{ + int J; + + /* Spin until audio has been trashed */ + for(RPtr=WPtr=0;SndRate&&SndData&&(SoundFD!=SOUNDFD_INVALID);) + { +#if defined(PULSE_AUDIO) + pa_simple_write(SoundFD,SndData+RPtr,SND_BUFSIZE*sizeof(sample),0); +#elif defined(SUN_AUDIO) + /* Flush output first, don't care about return status. After this + ** write next buffer of audio data. This method produces a horrible + ** click on each buffer :( Any ideas, how to fix this? + */ + J = SND_BUFSIZE*sizeof(sample); + ioctl(SoundFD,AUDIO_DRAIN); + if(write(SoundFD,SndData+RPtr,J)!=J) { /* Something went wrong */ } +#else + /* We'll block here until next DMA buffer becomes free. It happens + ** once per SND_BUFSIZE/SndRate seconds. + */ + J = SND_BUFSIZE*sizeof(sample); + if(write(SoundFD,SndData+RPtr,J)!=J) { /* Something went wrong */ } +#endif + + /* Advance buffer pointer, clearing the buffer */ + for(J=0;J=SndSize) RPtr=0; + } + + Thr = 0; + return(0); +} + +/** InitAudio() **********************************************/ +/** Initialize sound. Returns rate (Hz) on success, else 0. **/ +/** Rate=0 to skip initialization (will be silent). **/ +/*************************************************************/ +unsigned int InitAudio(unsigned int Rate,unsigned int Latency) +{ + int J; + + /* Shut down audio, just to be sure */ + TrashAudio(); + SndRate = 0; + SoundFD = SOUNDFD_INVALID; + SndSize = 0; + SndData = 0; + RPtr = 0; + WPtr = 0; + Thr = 0; + AudioPaused = 0; + + /* Have to have at least 8kHz sampling rate and 1ms buffer */ + if((Rate<8000)||!Latency) return(0); + + /* Compute number of sound buffers */ + SndSize=(Rate*Latency/1000+SND_BUFSIZE-1)/SND_BUFSIZE; + +#if defined(PULSE_AUDIO) + + { + /* Configure PulseAudio sound */ + pa_sample_spec PASpec; + PASpec.format = sizeof(sample)>1? PA_SAMPLE_S16LE:PA_SAMPLE_U8; + PASpec.rate = Rate; + PASpec.channels = 1; + /* Try opening PulseAudio */ + if(!(SoundFD=pa_simple_new(0,"EMULib",PA_STREAM_PLAYBACK,0,"playback",&PASpec,0,0,0))) + { SoundFD=SOUNDFD_INVALID;return(0); } + } + +#elif defined(ESD_AUDIO) + + /* ESD options for playing wave audio */ + J=ESD_MONO|ESD_STREAM|ESD_PLAY|(sizeof(sample)>1? ESD_BITS16:ESD_BITS8); + /* Open ESD socket, fall back to /dev/dsp is no ESD */ + if((SoundFD=esd_play_stream_fallback(J,Rate,0,0))<0) return(0); + +#elif defined(SUN_AUDIO) + + /* Open Sun's audio device */ + if((SoundFD=open("/dev/audio",O_WRONLY|O_NONBLOCK))<0) return(0); + + /* + ** Sun's specific initialization should be here... + ** We assume, that it's set to 8000Hz u-law mono right now. + */ + +#else /* !SUN_AUDIO */ + + { + int I,K; + + /* Open /dev/dsp audio device */ + if((SoundFD=open("/dev/dsp",O_WRONLY))<0) return(0); + + /* Set sound format */ + J=sizeof(sample)>1? AFMT_S16_NE:AFMT_U8; + I=ioctl(SoundFD,SNDCTL_DSP_SETFMT,&J)<0; + + /* Set mono sound */ + J=0; + I|=ioctl(SoundFD,SNDCTL_DSP_STEREO,&J)<0; + /* Set sampling rate */ + I|=ioctl(SoundFD,SNDCTL_DSP_SPEED,&Rate)<0; + + /* Set buffer length and number of buffers */ + J=K=SND_BITS|(SndSize<<16); + I|=ioctl(SoundFD,SNDCTL_DSP_SETFRAGMENT,&J)<0; + + /* Buffer length as n, not 2^n! */ + if((J&0xFFFF)<=16) J=(J&0xFFFF0000)|(1<<(J&0xFFFF)); + K=SND_BUFSIZE|(SndSize<<16); + + /* Check audio parameters */ + I|=(J!=K)&&(((J>>16)=0)&&(Switch<=1)&&(Switch!=AudioPaused)) + { + if(Switch) + { + /* Memorize audio parameters and kill audio */ + Rate = SndRate; + Latency = 1000*SndSize/SndRate; + TrashAudio(); + } + else + { + /* Start audio using memorized parameters */ + if(!InitAudio(Rate,Latency)) Switch=0; + } + + /* Audio switched */ + AudioPaused=Switch; + } + + /* Return current status */ + return(AudioPaused); +} + +/** GetFreeAudio() *******************************************/ +/** Get the amount of free samples in the audio buffer. **/ +/*************************************************************/ +unsigned int GetFreeAudio(void) +{ + return(!SndRate? 0:RPtr>=WPtr? RPtr-WPtr:RPtr-WPtr+SndSize); +} + +/** WriteAudio() *********************************************/ +/** Write up to a given number of samples to audio buffer. **/ +/** Returns the number of samples written. **/ +/*************************************************************/ +unsigned int WriteAudio(sample *Data,unsigned int Length) +{ + unsigned int J; + + /* Require audio to be initialized */ + if(!SndRate) return(0); + + /* Copy audio samples */ + for(J=0;(J=SndSize) WPtr=0; + } + + /* Return number of samples copied */ + return(J); +} + diff --git a/components/msx/fmsx/src/EMULib/WD1793.c b/components/msx/fmsx/src/EMULib/WD1793.c new file mode 100644 index 0000000..5a4399c --- /dev/null +++ b/components/msx/fmsx/src/EMULib/WD1793.c @@ -0,0 +1,375 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** WD1793.c **/ +/** **/ +/** This file contains emulation for the WD1793/2793 disk **/ +/** controller produced by Western Digital. See WD1793.h **/ +/** for declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2005-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "WD1793.h" +#include +#include + +/** Reset1793() **********************************************/ +/** Reset WD1793. When Disks=WD1793_INIT, also initialize **/ +/** disks. When Disks=WD1793_EJECT, eject inserted disks, **/ +/** freeing memory. **/ +/*************************************************************/ +void Reset1793(register WD1793 *D,FDIDisk *Disks,register byte Eject) +{ + int J; + + D->R[0] = 0x00; + D->R[1] = 0x00; + D->R[2] = 0x00; + D->R[3] = 0x00; + D->R[4] = S_RESET|S_HALT; + D->Drive = 0; + D->Side = 0; + D->LastS = 0; + D->IRQ = 0; + D->WRLength = 0; + D->RDLength = 0; + D->Wait = 0; + D->Cmd = 0xD0; + D->Rsrvd2 = 0; + + /* For all drives... */ + for(J=0;J<4;++J) + { + /* Reset drive-dependent state */ + D->Disk[J] = Disks? &Disks[J]:0; + D->Track[J] = 0; + D->Rsrvd1[J] = 0; + /* Initialize disk structure, if requested */ + if((Eject==WD1793_INIT)&&D->Disk[J]) InitFDI(D->Disk[J]); + /* Eject disk image, if requested */ + if((Eject==WD1793_EJECT)&&D->Disk[J]) EjectFDI(D->Disk[J]); + } +} + +/** Save1793() ***********************************************/ +/** Save WD1793 state to a given buffer of given maximal **/ +/** size. Returns number of bytes saved or 0 on failure. **/ +/*************************************************************/ +unsigned int Save1793(const register WD1793 *D,byte *Buf,unsigned int Size) +{ + unsigned int N = (const byte *)&(D->Ptr) - (const byte *)D; + if(N>Size) return(0); + memcpy(Buf,D,N); + return(N); +} + +/** Load1793() ***********************************************/ +/** Load WD1793 state from a given buffer of given maximal **/ +/** size. Returns number of bytes loaded or 0 on failure. **/ +/*************************************************************/ +unsigned int Load1793(register WD1793 *D,byte *Buf,unsigned int Size) +{ + unsigned int N = (const byte *)&(D->Ptr) - (const byte *)D; + if(N>Size) return(0); + memcpy(D,Buf,N); + return(N); +} + +/** Read1793() ***********************************************/ +/** Read value from a WD1793 register A. Returns read data **/ +/** on success or 0xFF on failure (bad register address). **/ +/*************************************************************/ +byte Read1793(register WD1793 *D,register byte A) +{ + switch(A) + { + case WD1793_STATUS: + A=D->R[0]; + /* If no disk present, set F_NOTREADY */ + if(!D->Disk[D->Drive]||!D->Disk[D->Drive]->Data) A|=F_NOTREADY; + if((D->Cmd<0x80)||(D->Cmd==0xD0)) + { + /* Keep flipping F_INDEX bit as the disk rotates (Sam Coupe) */ + D->R[0]=(D->R[0]^F_INDEX)&(F_INDEX|F_BUSY|F_NOTREADY|F_READONLY|F_TRACK0); + } + else + { + /* When reading status, clear all bits but F_BUSY and F_NOTREADY */ + D->R[0]&=F_BUSY|F_NOTREADY|F_READONLY|F_DRQ; + } + return(A); + case WD1793_TRACK: + case WD1793_SECTOR: + /* Return track/sector numbers */ + return(D->R[A]); + case WD1793_DATA: + /* When reading data, load value from disk */ + if(!D->RDLength) + { if(D->Verbose) printf("WD1793: EXTRA DATA READ\n"); } + else + { + /* Read data */ + D->R[A]=*D->Ptr++; + /* Decrement length */ + if(--D->RDLength) + { + /* Reset timeout watchdog */ + D->Wait=255; + /* Advance to the next sector as needed */ + if(!(D->RDLength&(D->Disk[D->Drive]->SecSize-1))) ++D->R[2]; + } + else + { + /* Read completed */ + if(D->Verbose) printf("WD1793: READ COMPLETED\n"); + D->R[0]&= ~(F_DRQ|F_BUSY); + D->IRQ = WD1793_IRQ; + } + } + return(D->R[A]); + case WD1793_READY: + /* After some idling, stop read/write operations */ + if(D->Wait) + if(!--D->Wait) + { + if(D->Verbose) printf("WD1793: COMMAND TIMED OUT\n"); + D->RDLength=D->WRLength=0; + D->R[0] = (D->R[0]&~(F_DRQ|F_BUSY))|F_LOSTDATA; + D->IRQ = WD1793_IRQ; + } + /* Done */ + return(D->IRQ); + } + + /* Bad register */ + return(0xFF); +} + +/** Write1793() **********************************************/ +/** Write value V into WD1793 register A. Returns DRQ/IRQ **/ +/** values. **/ +/*************************************************************/ +byte Write1793(register WD1793 *D,register byte A,register byte V) +{ + int J; + + switch(A) + { + case WD1793_COMMAND: + /* Reset interrupt request */ + D->IRQ=0; + /* If it is FORCE-IRQ command... */ + if((V&0xF0)==0xD0) + { + if(D->Verbose) printf("WD1793: FORCE-INTERRUPT (%02Xh)\n",V); + /* Reset any executing command */ + D->RDLength=D->WRLength=0; + D->Cmd=0xD0; + /* Either reset BUSY flag or reset all flags if BUSY=0 */ + if(D->R[0]&F_BUSY) D->R[0]&=~F_BUSY; + else D->R[0]=(D->Track[D->Drive]? 0:F_TRACK0)|F_INDEX; + /* Cause immediate interrupt if requested */ + if(V&C_IRQ) D->IRQ=WD1793_IRQ; + /* Done */ + return(D->IRQ); + } + /* If busy, drop out */ + if(D->R[0]&F_BUSY) break; + /* Reset status register */ + D->R[0]=0x00; + D->Cmd=V; + /* Depending on the command... */ + switch(V&0xF0) + { + case 0x00: /* RESTORE (seek track 0) */ + if(D->Verbose) printf("WD1793: RESTORE-TRACK-0 (%02Xh)\n",V); + D->Track[D->Drive]=0; + D->R[0] = F_INDEX|F_TRACK0|(V&C_LOADHEAD? F_HEADLOAD:0); + D->R[1] = 0; + D->IRQ = WD1793_IRQ; + break; + + case 0x10: /* SEEK */ + if(D->Verbose) printf("WD1793: SEEK-TRACK %d (%02Xh)\n",D->R[3],V); + /* Reset any executing command */ + D->RDLength=D->WRLength=0; + D->Track[D->Drive]=D->R[3]; + D->R[0] = F_INDEX + | (D->Track[D->Drive]? 0:F_TRACK0) + | (V&C_LOADHEAD? F_HEADLOAD:0); + D->R[1] = D->Track[D->Drive]; + D->IRQ = WD1793_IRQ; + break; + + case 0x20: /* STEP */ + case 0x30: /* STEP-AND-UPDATE */ + case 0x40: /* STEP-IN */ + case 0x50: /* STEP-IN-AND-UPDATE */ + case 0x60: /* STEP-OUT */ + case 0x70: /* STEP-OUT-AND-UPDATE */ + if(D->Verbose) printf("WD1793: STEP%s%s (%02Xh)\n", + V&0x40? (V&0x20? "-OUT":"-IN"):"", + V&0x10? "-AND-UPDATE":"", + V + ); + /* Either store or fetch step direction */ + if(V&0x40) D->LastS=V&0x20; else V=(V&~0x20)|D->LastS; + /* Step the head, update track register if requested */ + if(V&0x20) { if(D->Track[D->Drive]) --D->Track[D->Drive]; } + else ++D->Track[D->Drive]; + /* Update track register if requested */ + if(V&C_SETTRACK) D->R[1]=D->Track[D->Drive]; + /* Update status register */ + D->R[0] = F_INDEX|(D->Track[D->Drive]? 0:F_TRACK0); +// @@@ WHY USING J HERE? +// | (J&&(V&C_VERIFY)? 0:F_SEEKERR); + /* Generate IRQ */ + D->IRQ = WD1793_IRQ; + break; + + case 0x80: + case 0x90: /* READ-SECTORS */ + if(D->Verbose) printf("WD1793: READ-SECTOR%s %c:%d:%d:%d (%02Xh)\n",V&0x10? "S":"",D->Drive+'A',D->Side,D->R[1],D->R[2],V); + /* Seek to the requested sector */ + D->Ptr=SeekFDI( + D->Disk[D->Drive],D->Side,D->Track[D->Drive], + V&C_SIDECOMP? !!(V&C_SIDE):D->Side,D->R[1],D->R[2] + ); + /* If seek successful, set up reading operation */ + if(!D->Ptr) + { + if(D->Verbose) printf("WD1793: READ ERROR\n"); + D->R[0] = (D->R[0]&~F_ERRCODE)|F_NOTFOUND; + D->IRQ = WD1793_IRQ; + } + else + { + D->RDLength = D->Disk[D->Drive]->SecSize + * (V&0x10? (D->Disk[D->Drive]->Sectors-D->R[2]+1):1); + D->R[0] |= F_BUSY|F_DRQ; + D->IRQ = WD1793_DRQ; + D->Wait = 255; + } + break; + + case 0xA0: + case 0xB0: /* WRITE-SECTORS */ + if(D->Verbose) printf("WD1793: WRITE-SECTOR%s %c:%d:%d:%d (%02Xh)\n",V&0x10? "S":"",'A'+D->Drive,D->Side,D->R[1],D->R[2],V); + /* Seek to the requested sector */ + D->Ptr=SeekFDI( + D->Disk[D->Drive],D->Side,D->Track[D->Drive], + V&C_SIDECOMP? !!(V&C_SIDE):D->Side,D->R[1],D->R[2] + ); + /* If seek successful, set up writing operation */ + if(!D->Ptr) + { + if(D->Verbose) printf("WD1793: WRITE ERROR\n"); + D->R[0] = (D->R[0]&~F_ERRCODE)|F_NOTFOUND; + D->IRQ = WD1793_IRQ; + } + else + { + D->WRLength = D->Disk[D->Drive]->SecSize + * (V&0x10? (D->Disk[D->Drive]->Sectors-D->R[2]+1):1); + D->R[0] |= F_BUSY|F_DRQ; + D->IRQ = WD1793_DRQ; + D->Wait = 255; + } + break; + + case 0xC0: /* READ-ADDRESS */ + if(D->Verbose) printf("WD1793: READ-ADDRESS (%02Xh)\n",V); + /* Read first sector address from the track */ + if(!D->Disk[D->Drive]) D->Ptr=0; + else + for(J=0;J<256;++J) + { + D->Ptr=SeekFDI( + D->Disk[D->Drive], + D->Side,D->Track[D->Drive], + D->Side,D->Track[D->Drive],J + ); + if(D->Ptr) break; + } + /* If address found, initiate data transfer */ + if(!D->Ptr) + { + if(D->Verbose) printf("WD1793: READ-ADDRESS ERROR\n"); + D->R[0] |= F_NOTFOUND; + D->IRQ = WD1793_IRQ; + } + else + { + D->Ptr = D->Disk[D->Drive]->Header; + D->RDLength = 6; + D->R[0] |= F_BUSY|F_DRQ; + D->IRQ = WD1793_DRQ; + D->Wait = 255; + } + break; + + case 0xE0: /* READ-TRACK */ + if(D->Verbose) printf("WD1793: READ-TRACK %d (%02Xh) UNSUPPORTED!\n",D->R[1],V); + break; + + case 0xF0: /* WRITE-TRACK */ + if(D->Verbose) printf("WD1793: WRITE-TRACK %d (%02Xh) UNSUPPORTED!\n",D->R[1],V); + break; + + default: /* UNKNOWN */ + if(D->Verbose) printf("WD1793: UNSUPPORTED OPERATION %02Xh!\n",V); + break; + } + break; + + case WD1793_TRACK: + case WD1793_SECTOR: + if(!(D->R[0]&F_BUSY)) D->R[A]=V; + break; + + case WD1793_SYSTEM: +// @@@ Too verbose +// if(D->Verbose) printf("WD1793: Drive %c, %cD side %d\n",'A'+(V&S_DRIVE),V&S_DENSITY? 'D':'S',V&S_SIDE? 0:1); + /* Reset controller if S_RESET goes up */ + if((D->R[4]^V)&V&S_RESET) Reset1793(D,D->Disk[0],WD1793_KEEP); + /* Set disk #, side #, ignore the density (@@@) */ + D->Drive = V&S_DRIVE; + D->Side = !(V&S_SIDE); + /* Save last written value */ + D->R[4] = V; + break; + + case WD1793_DATA: + /* When writing data, store value to disk */ + if(!D->WRLength) + { if(D->Verbose) printf("WD1793: EXTRA DATA WRITE (%02Xh)\n",V); } + else + { + /* Write data */ + *D->Ptr++=V; + /* Decrement length */ + if(--D->WRLength) + { + /* Reset timeout watchdog */ + D->Wait=255; + /* Advance to the next sector as needed */ + if(!(D->WRLength&(D->Disk[D->Drive]->SecSize-1))) ++D->R[2]; + } + else + { + /* Write completed */ + if(D->Verbose) printf("WD1793: WRITE COMPLETED\n"); + D->R[0]&= ~(F_DRQ|F_BUSY); + D->IRQ = WD1793_IRQ; + } + } + /* Save last written value */ + D->R[A]=V; + break; + } + + /* Done */ + return(D->IRQ); +} diff --git a/components/msx/fmsx/src/EMULib/WD1793.h b/components/msx/fmsx/src/EMULib/WD1793.h new file mode 100644 index 0000000..b417708 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/WD1793.h @@ -0,0 +1,143 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** WD1793.h **/ +/** **/ +/** This file contains emulation for the WD1793/2793 disk **/ +/** controller produced by Western Digital. See WD1793.c **/ +/** for implementation. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2005-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef WD1793_H +#define WD1793_H + +#include "FDIDisk.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define WD1793_KEEP 0 +#define WD1793_INIT 1 +#define WD1793_EJECT 2 + +#define WD1793_COMMAND 0 +#define WD1793_STATUS 0 +#define WD1793_TRACK 1 +#define WD1793_SECTOR 2 +#define WD1793_DATA 3 +#define WD1793_SYSTEM 4 +#define WD1793_READY 4 + +#define WD1793_IRQ 0x80 +#define WD1793_DRQ 0x40 + + /* Common status bits: */ +#define F_BUSY 0x01 /* Controller is executing a command */ +#define F_READONLY 0x40 /* The disk is write-protected */ +#define F_NOTREADY 0x80 /* The drive is not ready */ + + /* Type-1 command status: */ +#define F_INDEX 0x02 /* Index mark detected */ +#define F_TRACK0 0x04 /* Head positioned at track #0 */ +#define F_CRCERR 0x08 /* CRC error in ID field */ +#define F_SEEKERR 0x10 /* Seek error, track not verified */ +#define F_HEADLOAD 0x20 /* Head loaded */ + + /* Type-2 and Type-3 command status: */ +#define F_DRQ 0x02 /* Data request pending */ +#define F_LOSTDATA 0x04 /* Data has been lost (missed DRQ) */ +#define F_ERRCODE 0x18 /* Error code bits: */ +#define F_BADDATA 0x08 /* 1 = bad data CRC */ +#define F_NOTFOUND 0x10 /* 2 = sector not found */ +#define F_BADID 0x18 /* 3 = bad ID field CRC */ +#define F_DELETED 0x20 /* Deleted data mark (when reading) */ +#define F_WRFAULT 0x20 /* Write fault (when writing) */ + +#define C_DELMARK 0x01 +#define C_SIDECOMP 0x02 +#define C_STEPRATE 0x03 +#define C_VERIFY 0x04 +#define C_WAIT15MS 0x04 +#define C_LOADHEAD 0x08 +#define C_SIDE 0x08 +#define C_IRQ 0x08 +#define C_SETTRACK 0x10 +#define C_MULTIREC 0x10 + +#define S_DRIVE 0x03 +#define S_RESET 0x04 +#define S_HALT 0x08 +#define S_SIDE 0x10 +#define S_DENSITY 0x20 + +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif + +#pragma pack(4) +typedef struct +{ + int Rsrvd1[4]; /* Reserved, do not touch */ + + byte R[5]; /* Registers */ + byte Drive; /* Current disk # */ + byte Side; /* Current side # */ + byte Track[4]; /* Current track # */ + byte LastS; /* Last STEP direction */ + byte IRQ; /* 0x80: IRQ pending, 0x40: DRQ pending */ + byte Wait; /* Expiration counter */ + byte Cmd; /* Last command */ + + int WRLength; /* Data left to write */ + int RDLength; /* Data left to read */ + int Rsrvd2; /* Reserved, do not touch */ + + byte Verbose; /* 1: Print debugging messages */ + + /*--- Save1793() will save state above this line ----*/ + + byte *Ptr; /* Pointer to data */ + FDIDisk *Disk[4]; /* Disk images */ +} WD1793; +#pragma pack() + +/** Reset1793() **********************************************/ +/** Reset WD1793. When Disks=WD1793_INIT, also initialize **/ +/** disks. When Disks=WD1793_EJECT, eject inserted disks, **/ +/** freeing memory. **/ +/*************************************************************/ +void Reset1793(register WD1793 *D,FDIDisk *Disks,register byte Eject); + +/** Read1793() ***********************************************/ +/** Read value from a WD1793 register A. Returns read data **/ +/** on success or 0xFF on failure (bad register address). **/ +/*************************************************************/ +byte Read1793(register WD1793 *D,register byte A); + +/** Write1793() **********************************************/ +/** Write value V into WD1793 register A. Returns DRQ/IRQ **/ +/** values. **/ +/*************************************************************/ +byte Write1793(register WD1793 *D,register byte A,register byte V); + +/** Save1793() ***********************************************/ +/** Save WD1793 state to a given buffer of given maximal **/ +/** size. Returns number of bytes saved or 0 on failure. **/ +/*************************************************************/ +unsigned int Save1793(const register WD1793 *D,byte *Buf,unsigned int Size); + +/** Load1793() ***********************************************/ +/** Load WD1793 state from a given buffer of given maximal **/ +/** size. Returns number of bytes loaded or 0 on failure. **/ +/*************************************************************/ +unsigned int Load1793(register WD1793 *D,byte *Buf,unsigned int Size); + +#ifdef __cplusplus +} +#endif +#endif /* WD1793_H */ diff --git a/components/msx/fmsx/src/EMULib/YM2413.c b/components/msx/fmsx/src/EMULib/YM2413.c new file mode 100644 index 0000000..f09d0c6 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/YM2413.c @@ -0,0 +1,290 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** YM2413.c **/ +/** **/ +/** This file contains emulation for the OPLL sound chip **/ +/** produced by Yamaha (also see OPL2, OPL3, OPL4 chips). **/ +/** See YM2413.h for declarations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +#include "YM2413.h" +#include "Sound.h" +#include + +/** Patches2413() ********************************************/ +/** MIDI instruments corresponding to the OPLL patches. **/ +/*************************************************************/ +static const byte Patches2413[16] = +{ + /*** OPLL ***/ /*** MIDI ***/ + 90, /* Original */ /* User (Polysynth) */ + 40, /* Violin */ /* Violin */ + 27, /* Guitar */ /* Electric Guitar (clean) */ + 1, /* Piano */ /* Bright Acoustic Piano */ + 73, /* Flute */ /* Flute */ + 71, /* Clarinet */ /* Clarinet */ + 68, /* Oboe */ /* Oboe */ + 56, /* Trumpet */ /* Trumpet */ + 20, /* Organ */ /* Reed Organ */ + 58, /* Horn */ /* Tuba */ + 50, /* Synthesizer */ /* Synth Strings 1 */ + 6, /* Harpsichord */ /* Harpsichord */ + 11, /* Vibraphone */ /* Vibraphone */ + 38, /* Synth Bass */ /* Synth Bass 1 */ + 34, /* Wood Bass */ /* Electric Bass (pick) */ + 33 /* Elec Guitar */ /* Electric Bass (finger) */ +}; + +/** Drums2413() **********************************************/ +/** MIDI instruments corresponding to the OPLL drums. **/ +/*************************************************************/ +static const byte Drums2413[5] = +{ + /*** OPLL ***/ /*** MIDI ***/ + 42, /* High Hat */ /* Closed Hi Hat */ + 49, /* Top Cymbal */ /* Crash Cymbal 1 */ + 47, /* Tom-Tom */ /* Low-Mid Tom */ + 40, /* Snare Drum */ /* Electric Snare */ + 36 /* Bass Drum */ /* Bass Drum 1 */ +}; + +/** Synth2413() **********************************************/ +/** Synthesizer parameters corresponding to OPLL patches. **/ +/*************************************************************/ +#if 0 +static const byte Synth2413[19*16] = +{ + 0x49,0x4c,0x4c,0x32,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x61,0x61,0x1e,0x17,0xf0,0x7f,0x00,0x17, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x13,0x41,0x16,0x0e,0xfd,0xf4,0x23,0x23, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x01,0x9a,0x04,0xf3,0xf3,0x13,0xf3, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x11,0x61,0x0e,0x07,0xfa,0x64,0x70,0x17, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x21,0x22,0x16,0x05,0xf0,0x71,0x00,0x18, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x21,0x61,0x1d,0x07,0x82,0x80,0x17,0x17, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x23,0x21,0x2d,0x16,0x90,0x90,0x00,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x21,0x21,0x1b,0x06,0x64,0x65,0x10,0x17, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x21,0x21,0x0b,0x1a,0x85,0xa0,0x70,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x23,0x01,0x83,0x10,0xff,0xb4,0x10,0xf4, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x97,0xc1,0x20,0x07,0xff,0xf4,0x22,0x22, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x61,0x00,0x0c,0x05,0xc2,0xf6,0x40,0x44, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x01,0x56,0x03,0x94,0xc2,0x03,0x12, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x21,0x01,0x89,0x03,0xf1,0xe4,0xf0,0x23, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; +#endif + +/** Reset2413() **********************************************/ +/** Reset the sound chip and use sound channels from the **/ +/** one given in First. **/ +/*************************************************************/ +void Reset2413(register YM2413 *D,int First) +{ + int J; + + /* All registers filled with 0x00 by default */ + memset(D->R,0x00,sizeof(D->R)); + + /* Set initial frequencies, volumes, and instruments */ + for(J=0;JFreq[J] = 0; + D->Volume[J] = 0; + D->R[J+0x30] = 0x0F; + } + + D->First = First; + D->Sync = YM2413_ASYNC; + D->Changed = (1<PChanged = (1<DChanged = 0x1F; + D->Latch = 0; +} + +/** WrCtrl2413() *********************************************/ +/** Write a value V to the OPLL Control Port. **/ +/*************************************************************/ +void WrCtrl2413(register YM2413 *D,register byte V) +{ + D->Latch=V&0x3F; +} + +/** WrData2413() *********************************************/ +/** Write a value V to the OPLL Data Port. **/ +/*************************************************************/ +void WrData2413(register YM2413 *D,register byte V) +{ + Write2413(D,D->Latch,V); +} + +/** Write2413() **********************************************/ +/** Call this function to output a value V into the sound **/ +/** chip. **/ +/*************************************************************/ +void Write2413(register YM2413 *D,register byte R,register byte V) +{ + register byte C,Oct; + register int Frq; + + /* OPLL registers are 0..63 */ + R&=0x3F; + + /* Lowest 4 bits are channel number */ + C=R&0x0F; + + switch(R>>4) + { + case 0: + switch(C) + { + case 0x0E: + if(V==D->R[R]) return; + /* Keep all drums off when drum mode is off */ + if(!(V&0x20)) V&=0xE0; + /* Mark all activated drums as changed */ + D->DChanged|=(V^D->R[R])&0x1F; + /* If drum mode was turned on... */ + if((V^D->R[R])&V&0x20) + { + /* Turn off melodic channels 6,7,8 */ + D->Freq[6]=D->Freq[7]=D->Freq[8]=0; + /* Mark channels 6,7,8 as changed */ + D->Changed|=0x1C0; + } + /* Done */ + break; + } + break; + + case 1: + if((C>8)||(V==D->R[R])) return; + if(!YM2413_DRUMS(D)||(C<6)) + if(D->R[R+0x10]&0x10) + { + /* Set channel frequency */ + Oct=D->R[R+0x10]; + Frq=((int)(Oct&0x01)<<8)+V; + Oct=(Oct&0x0E)>>1; + D->Freq[C]=(3125*Frq*(1<>15; + + /* Mark channel as changed */ + D->Changed|=1<8) return; + if(!YM2413_DRUMS(D)||(C<6)) + { + /* Depending on whether channel is on/off... */ + if(!(V&0x10)) D->Freq[C]=0; + else + { + /* Set channel frequency */ + Frq=((int)(V&0x01)<<8)+D->R[R-0x10]; + Oct=(V&0x0E)>>1; + D->Freq[C]=(3125*Frq*(1<>15; + } + + /* Mark channel as changed */ + D->Changed|=1<8)||(V==D->R[R])) return; + /* Register any patch changes */ + if((V^D->R[R])&0xF0) D->PChanged|=1<R[R])&0x0F) + { + /* Set channel volume */ + D->Volume[C]=255*(~V&0x0F)/15; + /* Mark channel as changed */ + D->Changed|=1<DChanged|=0x10&D->R[0x0E];break; + case 7: D->DChanged|=0x09&D->R[0x0E];break; + case 8: D->DChanged|=0x06&D->R[0x0E];break; + } + /* Done */ + break; + } + + /* Write value into the register */ + D->R[R]=V; + + /* For asynchronous mode, make Sound() calls right away */ + if(!D->Sync&&(D->Changed||D->PChanged||D->DChanged)) + Sync2413(D,YM2413_FLUSH); +} + +/** Sync2413() ***********************************************/ +/** Flush all accumulated changes by issuing Sound() calls **/ +/** and set the synchronization on/off. The second argument **/ +/** should be YM2413_SYNC/YM2413_ASYNC to set/reset sync, **/ +/** or YM2413_FLUSH to leave sync mode as it is. **/ +/*************************************************************/ +void Sync2413(register YM2413 *D,register byte Sync) +{ + register int J,I; + + /* Change sync mode as requested */ + if(Sync!=YM2413_FLUSH) D->Sync=Sync; + + /* Convert channel instrument changes into SetSound() calls */ + for(J=0,I=D->PChanged;I&&(J>=1) + if(I&1) SetSound(J+D->First,SND_MIDI|Patches2413[D->R[J+0x30]>>4]); + + /* Convert channel freq/volume changes into Sound() calls */ + for(J=0,I=D->Changed;I&&(J>=1) + if(I&1) Sound(J+D->First,D->Freq[J],D->Volume[J]); + + /* If there were any changes to the drums... */ + I=D->DChanged; + J=D->R[0x0E]; + if(I) + { + /* Turn drums on/off as requested */ + if(I&0x01) Drum(DRM_MIDI|Drums2413[0],J&0x01? 255*(D->R[0x37]>>4)/15:0); + if(I&0x02) Drum(DRM_MIDI|Drums2413[1],J&0x02? 255*(D->R[0x38]&0x0F)/15:0); + if(I&0x04) Drum(DRM_MIDI|Drums2413[2],J&0x04? 255*(D->R[0x38]>>4)/15:0); + if(I&0x08) Drum(DRM_MIDI|Drums2413[3],J&0x08? 255*(D->R[0x37]&0x0F)/15:0); + if(I&0x10) Drum(DRM_MIDI|Drums2413[4],J&0x10? 255*(D->R[0x36]&0x0F)/15:0); + } + + D->Changed=D->PChanged=D->DChanged=0x000; +} diff --git a/components/msx/fmsx/src/EMULib/YM2413.h b/components/msx/fmsx/src/EMULib/YM2413.h new file mode 100644 index 0000000..cbd9220 --- /dev/null +++ b/components/msx/fmsx/src/EMULib/YM2413.h @@ -0,0 +1,85 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** YM2413.h **/ +/** **/ +/** This file contains emulation for the OPLL sound chip **/ +/** produced by Yamaha (also see OPL2, OPL3, OPL4 chips). **/ +/** See YM2413.h for the code. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1996-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef YM2413_H +#define YM2413_H +#ifdef __cplusplus +extern "C" { +#endif + +#define YM2413_BASE 3125 /* Base frequency for OPLL */ +#define YM2413_CHANNELS 9 /* 9 melodic channels */ + +#define YM2413_ASYNC 0 /* Asynchronous emulation */ +#define YM2413_SYNC 1 /* Synchronous emulation mode */ +#define YM2413_FLUSH 2 /* Flush buffers only */ + +#define YM2413_DRUMS(D) ((D)->R[0x0E]&0x20) + +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif + +/** YM2413 ***************************************************/ +/** This data structure stores OPLL state. **/ +/*************************************************************/ +#pragma pack(4) +typedef struct +{ + byte R[64]; /* OPLL register contents */ + int Freq[YM2413_CHANNELS]; /* Frequencies (0 for off) */ + int Volume[YM2413_CHANNELS]; /* Volumes (0..255) */ + int First; /* First used Sound() channel */ + int Changed; /* Bitmap of changed channels */ + int PChanged; /* Bitmap of changed patches */ + int DChanged; /* Bitmap of changed drums */ + byte Sync; /* YM2413_SYNC/YM2413_ASYNC */ + byte Latch; /* Latch for the register num */ +} YM2413; +#pragma pack() + +/** Reset2413() **********************************************/ +/** Reset the sound chip and use sound channels from the **/ +/** one given in First. **/ +/*************************************************************/ +void Reset2413(register YM2413 *D,int First); + +/** WrCtrl2413() *********************************************/ +/** Write a value V to the OPLL Control Port. **/ +/*************************************************************/ +void WrCtrl2413(register YM2413 *D,register byte V); + +/** WrData2413() *********************************************/ +/** Write a value V to the OPLL Data Port. **/ +/*************************************************************/ +void WrData2413(register YM2413 *D,register byte V); + +/** Write2413() **********************************************/ +/** Call this function to output a value V into given OPLL **/ +/** register R. **/ +/*************************************************************/ +void Write2413(register YM2413 *D,register byte R,register byte V); + +/** Sync2413() ***********************************************/ +/** Flush all accumulated changes by issuing Sound() calls **/ +/** and set the synchronization on/off. The second argument **/ +/** should be YM2413_SYNC/YM2413_ASYNC to set/reset sync, **/ +/** or YM2413_FLUSH to leave sync mode as it is. **/ +/*************************************************************/ +void Sync2413(register YM2413 *D,register byte Sync); + +#ifdef __cplusplus +} +#endif +#endif /* YM2413_H */ diff --git a/components/msx/fmsx/src/Z80/Codes.h b/components/msx/fmsx/src/Z80/Codes.h new file mode 100644 index 0000000..1b81745 --- /dev/null +++ b/components/msx/fmsx/src/Z80/Codes.h @@ -0,0 +1,385 @@ +/** Z80: portable Z80 emulator *******************************/ +/** **/ +/** Codes.h **/ +/** **/ +/** This file contains implementation for the main table of **/ +/** Z80 commands. It is included from Z80.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +case JR_NZ: if(R->AF.B.l&Z_FLAG) R->PC.W++; else { R->ICount-=5;M_JR; } break; +case JR_NC: if(R->AF.B.l&C_FLAG) R->PC.W++; else { R->ICount-=5;M_JR; } break; +case JR_Z: if(R->AF.B.l&Z_FLAG) { R->ICount-=5;M_JR; } else R->PC.W++; break; +case JR_C: if(R->AF.B.l&C_FLAG) { R->ICount-=5;M_JR; } else R->PC.W++; break; + +case JP_NZ: if(R->AF.B.l&Z_FLAG) R->PC.W+=2; else { M_JP; } break; +case JP_NC: if(R->AF.B.l&C_FLAG) R->PC.W+=2; else { M_JP; } break; +case JP_PO: if(R->AF.B.l&P_FLAG) R->PC.W+=2; else { M_JP; } break; +case JP_P: if(R->AF.B.l&S_FLAG) R->PC.W+=2; else { M_JP; } break; +case JP_Z: if(R->AF.B.l&Z_FLAG) { M_JP; } else R->PC.W+=2; break; +case JP_C: if(R->AF.B.l&C_FLAG) { M_JP; } else R->PC.W+=2; break; +case JP_PE: if(R->AF.B.l&P_FLAG) { M_JP; } else R->PC.W+=2; break; +case JP_M: if(R->AF.B.l&S_FLAG) { M_JP; } else R->PC.W+=2; break; + +case RET_NZ: if(!(R->AF.B.l&Z_FLAG)) { R->ICount-=6;M_RET; } break; +case RET_NC: if(!(R->AF.B.l&C_FLAG)) { R->ICount-=6;M_RET; } break; +case RET_PO: if(!(R->AF.B.l&P_FLAG)) { R->ICount-=6;M_RET; } break; +case RET_P: if(!(R->AF.B.l&S_FLAG)) { R->ICount-=6;M_RET; } break; +case RET_Z: if(R->AF.B.l&Z_FLAG) { R->ICount-=6;M_RET; } break; +case RET_C: if(R->AF.B.l&C_FLAG) { R->ICount-=6;M_RET; } break; +case RET_PE: if(R->AF.B.l&P_FLAG) { R->ICount-=6;M_RET; } break; +case RET_M: if(R->AF.B.l&S_FLAG) { R->ICount-=6;M_RET; } break; + +case CALL_NZ: if(R->AF.B.l&Z_FLAG) R->PC.W+=2; else { R->ICount-=7;M_CALL; } break; +case CALL_NC: if(R->AF.B.l&C_FLAG) R->PC.W+=2; else { R->ICount-=7;M_CALL; } break; +case CALL_PO: if(R->AF.B.l&P_FLAG) R->PC.W+=2; else { R->ICount-=7;M_CALL; } break; +case CALL_P: if(R->AF.B.l&S_FLAG) R->PC.W+=2; else { R->ICount-=7;M_CALL; } break; +case CALL_Z: if(R->AF.B.l&Z_FLAG) { R->ICount-=7;M_CALL; } else R->PC.W+=2; break; +case CALL_C: if(R->AF.B.l&C_FLAG) { R->ICount-=7;M_CALL; } else R->PC.W+=2; break; +case CALL_PE: if(R->AF.B.l&P_FLAG) { R->ICount-=7;M_CALL; } else R->PC.W+=2; break; +case CALL_M: if(R->AF.B.l&S_FLAG) { R->ICount-=7;M_CALL; } else R->PC.W+=2; break; + +case ADD_B: M_ADD(R->BC.B.h);break; +case ADD_C: M_ADD(R->BC.B.l);break; +case ADD_D: M_ADD(R->DE.B.h);break; +case ADD_E: M_ADD(R->DE.B.l);break; +case ADD_H: M_ADD(R->HL.B.h);break; +case ADD_L: M_ADD(R->HL.B.l);break; +case ADD_A: M_ADD(R->AF.B.h);break; +case ADD_xHL: I=msx_RdZ80(R->HL.W);M_ADD(I);break; +case ADD_BYTE: I=OpZ80(R->PC.W++);M_ADD(I);break; + +case SUB_B: M_SUB(R->BC.B.h);break; +case SUB_C: M_SUB(R->BC.B.l);break; +case SUB_D: M_SUB(R->DE.B.h);break; +case SUB_E: M_SUB(R->DE.B.l);break; +case SUB_H: M_SUB(R->HL.B.h);break; +case SUB_L: M_SUB(R->HL.B.l);break; +case SUB_A: R->AF.B.h=0;R->AF.B.l=N_FLAG|Z_FLAG;break; +case SUB_xHL: I=msx_RdZ80(R->HL.W);M_SUB(I);break; +case SUB_BYTE: I=OpZ80(R->PC.W++);M_SUB(I);break; + +case AND_B: M_AND(R->BC.B.h);break; +case AND_C: M_AND(R->BC.B.l);break; +case AND_D: M_AND(R->DE.B.h);break; +case AND_E: M_AND(R->DE.B.l);break; +case AND_H: M_AND(R->HL.B.h);break; +case AND_L: M_AND(R->HL.B.l);break; +case AND_A: M_AND(R->AF.B.h);break; +case AND_xHL: I=msx_RdZ80(R->HL.W);M_AND(I);break; +case AND_BYTE: I=OpZ80(R->PC.W++);M_AND(I);break; + +case OR_B: M_OR(R->BC.B.h);break; +case OR_C: M_OR(R->BC.B.l);break; +case OR_D: M_OR(R->DE.B.h);break; +case OR_E: M_OR(R->DE.B.l);break; +case OR_H: M_OR(R->HL.B.h);break; +case OR_L: M_OR(R->HL.B.l);break; +case OR_A: M_OR(R->AF.B.h);break; +case OR_xHL: I=msx_RdZ80(R->HL.W);M_OR(I);break; +case OR_BYTE: I=OpZ80(R->PC.W++);M_OR(I);break; + +case ADC_B: M_ADC(R->BC.B.h);break; +case ADC_C: M_ADC(R->BC.B.l);break; +case ADC_D: M_ADC(R->DE.B.h);break; +case ADC_E: M_ADC(R->DE.B.l);break; +case ADC_H: M_ADC(R->HL.B.h);break; +case ADC_L: M_ADC(R->HL.B.l);break; +case ADC_A: M_ADC(R->AF.B.h);break; +case ADC_xHL: I=msx_RdZ80(R->HL.W);M_ADC(I);break; +case ADC_BYTE: I=OpZ80(R->PC.W++);M_ADC(I);break; + +case SBC_B: M_SBC(R->BC.B.h);break; +case SBC_C: M_SBC(R->BC.B.l);break; +case SBC_D: M_SBC(R->DE.B.h);break; +case SBC_E: M_SBC(R->DE.B.l);break; +case SBC_H: M_SBC(R->HL.B.h);break; +case SBC_L: M_SBC(R->HL.B.l);break; +case SBC_A: M_SBC(R->AF.B.h);break; +case SBC_xHL: I=msx_RdZ80(R->HL.W);M_SBC(I);break; +case SBC_BYTE: I=OpZ80(R->PC.W++);M_SBC(I);break; + +case XOR_B: M_XOR(R->BC.B.h);break; +case XOR_C: M_XOR(R->BC.B.l);break; +case XOR_D: M_XOR(R->DE.B.h);break; +case XOR_E: M_XOR(R->DE.B.l);break; +case XOR_H: M_XOR(R->HL.B.h);break; +case XOR_L: M_XOR(R->HL.B.l);break; +case XOR_A: R->AF.B.h=0;R->AF.B.l=P_FLAG|Z_FLAG;break; +case XOR_xHL: I=msx_RdZ80(R->HL.W);M_XOR(I);break; +case XOR_BYTE: I=OpZ80(R->PC.W++);M_XOR(I);break; + +case CP_B: M_CP(R->BC.B.h);break; +case CP_C: M_CP(R->BC.B.l);break; +case CP_D: M_CP(R->DE.B.h);break; +case CP_E: M_CP(R->DE.B.l);break; +case CP_H: M_CP(R->HL.B.h);break; +case CP_L: M_CP(R->HL.B.l);break; +case CP_A: R->AF.B.l=N_FLAG|Z_FLAG;break; +case CP_xHL: I=msx_RdZ80(R->HL.W);M_CP(I);break; +case CP_BYTE: I=OpZ80(R->PC.W++);M_CP(I);break; + +case LD_BC_WORD: M_LDWORD(BC);break; +case LD_DE_WORD: M_LDWORD(DE);break; +case LD_HL_WORD: M_LDWORD(HL);break; +case LD_SP_WORD: M_LDWORD(SP);break; + +case LD_PC_HL: R->PC.W=R->HL.W;JumpZ80(R->PC.W);break; +case LD_SP_HL: R->SP.W=R->HL.W;break; +case LD_A_xBC: R->AF.B.h=msx_RdZ80(R->BC.W);break; +case LD_A_xDE: R->AF.B.h=msx_RdZ80(R->DE.W);break; + +case ADD_HL_BC: M_ADDW(HL,BC);break; +case ADD_HL_DE: M_ADDW(HL,DE);break; +case ADD_HL_HL: M_ADDW(HL,HL);break; +case ADD_HL_SP: M_ADDW(HL,SP);break; + +case DEC_BC: R->BC.W--;break; +case DEC_DE: R->DE.W--;break; +case DEC_HL: R->HL.W--;break; +case DEC_SP: R->SP.W--;break; + +case INC_BC: R->BC.W++;break; +case INC_DE: R->DE.W++;break; +case INC_HL: R->HL.W++;break; +case INC_SP: R->SP.W++;break; + +case DEC_B: M_DEC(R->BC.B.h);break; +case DEC_C: M_DEC(R->BC.B.l);break; +case DEC_D: M_DEC(R->DE.B.h);break; +case DEC_E: M_DEC(R->DE.B.l);break; +case DEC_H: M_DEC(R->HL.B.h);break; +case DEC_L: M_DEC(R->HL.B.l);break; +case DEC_A: M_DEC(R->AF.B.h);break; +case DEC_xHL: I=msx_RdZ80(R->HL.W);M_DEC(I);msx_WrZ80(R->HL.W,I);break; + +case INC_B: M_INC(R->BC.B.h);break; +case INC_C: M_INC(R->BC.B.l);break; +case INC_D: M_INC(R->DE.B.h);break; +case INC_E: M_INC(R->DE.B.l);break; +case INC_H: M_INC(R->HL.B.h);break; +case INC_L: M_INC(R->HL.B.l);break; +case INC_A: M_INC(R->AF.B.h);break; +case INC_xHL: I=msx_RdZ80(R->HL.W);M_INC(I);msx_WrZ80(R->HL.W,I);break; + +case RLCA: + I=R->AF.B.h&0x80? C_FLAG:0; + R->AF.B.h=(R->AF.B.h<<1)|I; + R->AF.B.l=(R->AF.B.l&~(C_FLAG|N_FLAG|H_FLAG))|I; + break; +case RLA: + I=R->AF.B.h&0x80? C_FLAG:0; + R->AF.B.h=(R->AF.B.h<<1)|(R->AF.B.l&C_FLAG); + R->AF.B.l=(R->AF.B.l&~(C_FLAG|N_FLAG|H_FLAG))|I; + break; +case RRCA: + I=R->AF.B.h&0x01; + R->AF.B.h=(R->AF.B.h>>1)|(I? 0x80:0); + R->AF.B.l=(R->AF.B.l&~(C_FLAG|N_FLAG|H_FLAG))|I; + break; +case RRA: + I=R->AF.B.h&0x01; + R->AF.B.h=(R->AF.B.h>>1)|(R->AF.B.l&C_FLAG? 0x80:0); + R->AF.B.l=(R->AF.B.l&~(C_FLAG|N_FLAG|H_FLAG))|I; + break; + +case RST00: M_RST(0x0000);break; +case RST08: M_RST(0x0008);break; +case RST10: M_RST(0x0010);break; +case RST18: M_RST(0x0018);break; +case RST20: M_RST(0x0020);break; +case RST28: M_RST(0x0028);break; +case RST30: M_RST(0x0030);break; +case RST38: M_RST(0x0038);break; + +case PUSH_BC: M_PUSH(BC);break; +case PUSH_DE: M_PUSH(DE);break; +case PUSH_HL: M_PUSH(HL);break; +case PUSH_AF: M_PUSH(AF);break; + +case POP_BC: M_POP(BC);break; +case POP_DE: M_POP(DE);break; +case POP_HL: M_POP(HL);break; +case POP_AF: M_POP(AF);break; + +case DJNZ: if(--R->BC.B.h) { R->ICount-=5;M_JR; } else R->PC.W++;break; +case JP: M_JP;break; +case JR: M_JR;break; +case CALL: M_CALL;break; +case RET: M_RET;break; +case SCF: S(C_FLAG);R(N_FLAG|H_FLAG);break; +case CPL: R->AF.B.h=~R->AF.B.h;S(N_FLAG|H_FLAG);break; +case NOP: break; +case OUTA: I=OpZ80(R->PC.W++);msx_OutZ80(I|(R->AF.W&0xFF00),R->AF.B.h);break; +case INA: I=OpZ80(R->PC.W++);R->AF.B.h=msx_InZ80(I|(R->AF.W&0xFF00));break; + +case HALT: + R->PC.W--; + R->IFF|=IFF_HALT; + R->IBackup=0; + R->ICount=0; + break; + +case DI: + if(R->IFF&IFF_EI) R->ICount+=R->IBackup-1; + R->IFF&=~(IFF_1|IFF_2|IFF_EI); + break; + +case EI: + if(!(R->IFF&(IFF_1|IFF_EI))) + { + R->IFF|=IFF_2|IFF_EI; + R->IBackup=R->ICount; + R->ICount=1; + } + break; + +case CCF: + R->AF.B.l^=C_FLAG;R(N_FLAG|H_FLAG); + R->AF.B.l|=R->AF.B.l&C_FLAG? 0:H_FLAG; + break; + +case EXX: + J.W=R->BC.W;R->BC.W=R->BC1.W;R->BC1.W=J.W; + J.W=R->DE.W;R->DE.W=R->DE1.W;R->DE1.W=J.W; + J.W=R->HL.W;R->HL.W=R->HL1.W;R->HL1.W=J.W; + break; + +case EX_DE_HL: J.W=R->DE.W;R->DE.W=R->HL.W;R->HL.W=J.W;break; +case EX_AF_AF: J.W=R->AF.W;R->AF.W=R->AF1.W;R->AF1.W=J.W;break; + +case LD_B_B: R->BC.B.h=R->BC.B.h;break; +case LD_C_B: R->BC.B.l=R->BC.B.h;break; +case LD_D_B: R->DE.B.h=R->BC.B.h;break; +case LD_E_B: R->DE.B.l=R->BC.B.h;break; +case LD_H_B: R->HL.B.h=R->BC.B.h;break; +case LD_L_B: R->HL.B.l=R->BC.B.h;break; +case LD_A_B: R->AF.B.h=R->BC.B.h;break; +case LD_xHL_B: msx_WrZ80(R->HL.W,R->BC.B.h);break; + +case LD_B_C: R->BC.B.h=R->BC.B.l;break; +case LD_C_C: R->BC.B.l=R->BC.B.l;break; +case LD_D_C: R->DE.B.h=R->BC.B.l;break; +case LD_E_C: R->DE.B.l=R->BC.B.l;break; +case LD_H_C: R->HL.B.h=R->BC.B.l;break; +case LD_L_C: R->HL.B.l=R->BC.B.l;break; +case LD_A_C: R->AF.B.h=R->BC.B.l;break; +case LD_xHL_C: msx_WrZ80(R->HL.W,R->BC.B.l);break; + +case LD_B_D: R->BC.B.h=R->DE.B.h;break; +case LD_C_D: R->BC.B.l=R->DE.B.h;break; +case LD_D_D: R->DE.B.h=R->DE.B.h;break; +case LD_E_D: R->DE.B.l=R->DE.B.h;break; +case LD_H_D: R->HL.B.h=R->DE.B.h;break; +case LD_L_D: R->HL.B.l=R->DE.B.h;break; +case LD_A_D: R->AF.B.h=R->DE.B.h;break; +case LD_xHL_D: msx_WrZ80(R->HL.W,R->DE.B.h);break; + +case LD_B_E: R->BC.B.h=R->DE.B.l;break; +case LD_C_E: R->BC.B.l=R->DE.B.l;break; +case LD_D_E: R->DE.B.h=R->DE.B.l;break; +case LD_E_E: R->DE.B.l=R->DE.B.l;break; +case LD_H_E: R->HL.B.h=R->DE.B.l;break; +case LD_L_E: R->HL.B.l=R->DE.B.l;break; +case LD_A_E: R->AF.B.h=R->DE.B.l;break; +case LD_xHL_E: msx_WrZ80(R->HL.W,R->DE.B.l);break; + +case LD_B_H: R->BC.B.h=R->HL.B.h;break; +case LD_C_H: R->BC.B.l=R->HL.B.h;break; +case LD_D_H: R->DE.B.h=R->HL.B.h;break; +case LD_E_H: R->DE.B.l=R->HL.B.h;break; +case LD_H_H: R->HL.B.h=R->HL.B.h;break; +case LD_L_H: R->HL.B.l=R->HL.B.h;break; +case LD_A_H: R->AF.B.h=R->HL.B.h;break; +case LD_xHL_H: msx_WrZ80(R->HL.W,R->HL.B.h);break; + +case LD_B_L: R->BC.B.h=R->HL.B.l;break; +case LD_C_L: R->BC.B.l=R->HL.B.l;break; +case LD_D_L: R->DE.B.h=R->HL.B.l;break; +case LD_E_L: R->DE.B.l=R->HL.B.l;break; +case LD_H_L: R->HL.B.h=R->HL.B.l;break; +case LD_L_L: R->HL.B.l=R->HL.B.l;break; +case LD_A_L: R->AF.B.h=R->HL.B.l;break; +case LD_xHL_L: msx_WrZ80(R->HL.W,R->HL.B.l);break; + +case LD_B_A: R->BC.B.h=R->AF.B.h;break; +case LD_C_A: R->BC.B.l=R->AF.B.h;break; +case LD_D_A: R->DE.B.h=R->AF.B.h;break; +case LD_E_A: R->DE.B.l=R->AF.B.h;break; +case LD_H_A: R->HL.B.h=R->AF.B.h;break; +case LD_L_A: R->HL.B.l=R->AF.B.h;break; +case LD_A_A: R->AF.B.h=R->AF.B.h;break; +case LD_xHL_A: msx_WrZ80(R->HL.W,R->AF.B.h);break; + +case LD_xBC_A: msx_WrZ80(R->BC.W,R->AF.B.h);break; +case LD_xDE_A: msx_WrZ80(R->DE.W,R->AF.B.h);break; + +case LD_B_xHL: R->BC.B.h=msx_RdZ80(R->HL.W);break; +case LD_C_xHL: R->BC.B.l=msx_RdZ80(R->HL.W);break; +case LD_D_xHL: R->DE.B.h=msx_RdZ80(R->HL.W);break; +case LD_E_xHL: R->DE.B.l=msx_RdZ80(R->HL.W);break; +case LD_H_xHL: R->HL.B.h=msx_RdZ80(R->HL.W);break; +case LD_L_xHL: R->HL.B.l=msx_RdZ80(R->HL.W);break; +case LD_A_xHL: R->AF.B.h=msx_RdZ80(R->HL.W);break; + +case LD_B_BYTE: R->BC.B.h=OpZ80(R->PC.W++);break; +case LD_C_BYTE: R->BC.B.l=OpZ80(R->PC.W++);break; +case LD_D_BYTE: R->DE.B.h=OpZ80(R->PC.W++);break; +case LD_E_BYTE: R->DE.B.l=OpZ80(R->PC.W++);break; +case LD_H_BYTE: R->HL.B.h=OpZ80(R->PC.W++);break; +case LD_L_BYTE: R->HL.B.l=OpZ80(R->PC.W++);break; +case LD_A_BYTE: R->AF.B.h=OpZ80(R->PC.W++);break; +case LD_xHL_BYTE: msx_WrZ80(R->HL.W,OpZ80(R->PC.W++));break; + +case LD_xWORD_HL: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + msx_WrZ80(J.W++,R->HL.B.l); + msx_WrZ80(J.W,R->HL.B.h); + break; + +case LD_HL_xWORD: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + R->HL.B.l=msx_RdZ80(J.W++); + R->HL.B.h=msx_RdZ80(J.W); + break; + +case LD_A_xWORD: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + R->AF.B.h=msx_RdZ80(J.W); + break; + +case LD_xWORD_A: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->AF.B.h); + break; + +case EX_HL_xSP: + J.B.l=msx_RdZ80(R->SP.W);msx_WrZ80(R->SP.W++,R->HL.B.l); + J.B.h=msx_RdZ80(R->SP.W);msx_WrZ80(R->SP.W--,R->HL.B.h); + R->HL.W=J.W; + break; + +case DAA: + J.W=R->AF.B.h; + if(R->AF.B.l&C_FLAG) J.W|=256; + if(R->AF.B.l&H_FLAG) J.W|=512; + if(R->AF.B.l&N_FLAG) J.W|=1024; + R->AF.W=DAATable[J.W]; + break; + +default: + if(R->TrapBadOps) + printf + ( + "[Z80 %lX] Unrecognized instruction: %02X at PC=%04X\n", + (long)R->User,OpZ80(R->PC.W-1),R->PC.W-1 + ); + break; diff --git a/components/msx/fmsx/src/Z80/CodesCB.h b/components/msx/fmsx/src/Z80/CodesCB.h new file mode 100644 index 0000000..db5cefc --- /dev/null +++ b/components/msx/fmsx/src/Z80/CodesCB.h @@ -0,0 +1,204 @@ +/** Z80: portable Z80 emulator *******************************/ +/** **/ +/** CodesCB.h **/ +/** **/ +/** This file contains implementation for the CB table of **/ +/** Z80 commands. It is included from Z80.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +case RLC_B: M_RLC(R->BC.B.h);break; case RLC_C: M_RLC(R->BC.B.l);break; +case RLC_D: M_RLC(R->DE.B.h);break; case RLC_E: M_RLC(R->DE.B.l);break; +case RLC_H: M_RLC(R->HL.B.h);break; case RLC_L: M_RLC(R->HL.B.l);break; +case RLC_xHL: I=msx_RdZ80(R->HL.W);M_RLC(I);msx_WrZ80(R->HL.W,I);break; +case RLC_A: M_RLC(R->AF.B.h);break; + +case RRC_B: M_RRC(R->BC.B.h);break; case RRC_C: M_RRC(R->BC.B.l);break; +case RRC_D: M_RRC(R->DE.B.h);break; case RRC_E: M_RRC(R->DE.B.l);break; +case RRC_H: M_RRC(R->HL.B.h);break; case RRC_L: M_RRC(R->HL.B.l);break; +case RRC_xHL: I=msx_RdZ80(R->HL.W);M_RRC(I);msx_WrZ80(R->HL.W,I);break; +case RRC_A: M_RRC(R->AF.B.h);break; + +case RL_B: M_RL(R->BC.B.h);break; case RL_C: M_RL(R->BC.B.l);break; +case RL_D: M_RL(R->DE.B.h);break; case RL_E: M_RL(R->DE.B.l);break; +case RL_H: M_RL(R->HL.B.h);break; case RL_L: M_RL(R->HL.B.l);break; +case RL_xHL: I=msx_RdZ80(R->HL.W);M_RL(I);msx_WrZ80(R->HL.W,I);break; +case RL_A: M_RL(R->AF.B.h);break; + +case RR_B: M_RR(R->BC.B.h);break; case RR_C: M_RR(R->BC.B.l);break; +case RR_D: M_RR(R->DE.B.h);break; case RR_E: M_RR(R->DE.B.l);break; +case RR_H: M_RR(R->HL.B.h);break; case RR_L: M_RR(R->HL.B.l);break; +case RR_xHL: I=msx_RdZ80(R->HL.W);M_RR(I);msx_WrZ80(R->HL.W,I);break; +case RR_A: M_RR(R->AF.B.h);break; + +case SLA_B: M_SLA(R->BC.B.h);break; case SLA_C: M_SLA(R->BC.B.l);break; +case SLA_D: M_SLA(R->DE.B.h);break; case SLA_E: M_SLA(R->DE.B.l);break; +case SLA_H: M_SLA(R->HL.B.h);break; case SLA_L: M_SLA(R->HL.B.l);break; +case SLA_xHL: I=msx_RdZ80(R->HL.W);M_SLA(I);msx_WrZ80(R->HL.W,I);break; +case SLA_A: M_SLA(R->AF.B.h);break; + +case SRA_B: M_SRA(R->BC.B.h);break; case SRA_C: M_SRA(R->BC.B.l);break; +case SRA_D: M_SRA(R->DE.B.h);break; case SRA_E: M_SRA(R->DE.B.l);break; +case SRA_H: M_SRA(R->HL.B.h);break; case SRA_L: M_SRA(R->HL.B.l);break; +case SRA_xHL: I=msx_RdZ80(R->HL.W);M_SRA(I);msx_WrZ80(R->HL.W,I);break; +case SRA_A: M_SRA(R->AF.B.h);break; + +case SLL_B: M_SLL(R->BC.B.h);break; case SLL_C: M_SLL(R->BC.B.l);break; +case SLL_D: M_SLL(R->DE.B.h);break; case SLL_E: M_SLL(R->DE.B.l);break; +case SLL_H: M_SLL(R->HL.B.h);break; case SLL_L: M_SLL(R->HL.B.l);break; +case SLL_xHL: I=msx_RdZ80(R->HL.W);M_SLL(I);msx_WrZ80(R->HL.W,I);break; +case SLL_A: M_SLL(R->AF.B.h);break; + +case SRL_B: M_SRL(R->BC.B.h);break; case SRL_C: M_SRL(R->BC.B.l);break; +case SRL_D: M_SRL(R->DE.B.h);break; case SRL_E: M_SRL(R->DE.B.l);break; +case SRL_H: M_SRL(R->HL.B.h);break; case SRL_L: M_SRL(R->HL.B.l);break; +case SRL_xHL: I=msx_RdZ80(R->HL.W);M_SRL(I);msx_WrZ80(R->HL.W,I);break; +case SRL_A: M_SRL(R->AF.B.h);break; + +case BIT0_B: M_BIT(0,R->BC.B.h);break; case BIT0_C: M_BIT(0,R->BC.B.l);break; +case BIT0_D: M_BIT(0,R->DE.B.h);break; case BIT0_E: M_BIT(0,R->DE.B.l);break; +case BIT0_H: M_BIT(0,R->HL.B.h);break; case BIT0_L: M_BIT(0,R->HL.B.l);break; +case BIT0_xHL: I=msx_RdZ80(R->HL.W);M_BIT(0,I);break; +case BIT0_A: M_BIT(0,R->AF.B.h);break; + +case BIT1_B: M_BIT(1,R->BC.B.h);break; case BIT1_C: M_BIT(1,R->BC.B.l);break; +case BIT1_D: M_BIT(1,R->DE.B.h);break; case BIT1_E: M_BIT(1,R->DE.B.l);break; +case BIT1_H: M_BIT(1,R->HL.B.h);break; case BIT1_L: M_BIT(1,R->HL.B.l);break; +case BIT1_xHL: I=msx_RdZ80(R->HL.W);M_BIT(1,I);break; +case BIT1_A: M_BIT(1,R->AF.B.h);break; + +case BIT2_B: M_BIT(2,R->BC.B.h);break; case BIT2_C: M_BIT(2,R->BC.B.l);break; +case BIT2_D: M_BIT(2,R->DE.B.h);break; case BIT2_E: M_BIT(2,R->DE.B.l);break; +case BIT2_H: M_BIT(2,R->HL.B.h);break; case BIT2_L: M_BIT(2,R->HL.B.l);break; +case BIT2_xHL: I=msx_RdZ80(R->HL.W);M_BIT(2,I);break; +case BIT2_A: M_BIT(2,R->AF.B.h);break; + +case BIT3_B: M_BIT(3,R->BC.B.h);break; case BIT3_C: M_BIT(3,R->BC.B.l);break; +case BIT3_D: M_BIT(3,R->DE.B.h);break; case BIT3_E: M_BIT(3,R->DE.B.l);break; +case BIT3_H: M_BIT(3,R->HL.B.h);break; case BIT3_L: M_BIT(3,R->HL.B.l);break; +case BIT3_xHL: I=msx_RdZ80(R->HL.W);M_BIT(3,I);break; +case BIT3_A: M_BIT(3,R->AF.B.h);break; + +case BIT4_B: M_BIT(4,R->BC.B.h);break; case BIT4_C: M_BIT(4,R->BC.B.l);break; +case BIT4_D: M_BIT(4,R->DE.B.h);break; case BIT4_E: M_BIT(4,R->DE.B.l);break; +case BIT4_H: M_BIT(4,R->HL.B.h);break; case BIT4_L: M_BIT(4,R->HL.B.l);break; +case BIT4_xHL: I=msx_RdZ80(R->HL.W);M_BIT(4,I);break; +case BIT4_A: M_BIT(4,R->AF.B.h);break; + +case BIT5_B: M_BIT(5,R->BC.B.h);break; case BIT5_C: M_BIT(5,R->BC.B.l);break; +case BIT5_D: M_BIT(5,R->DE.B.h);break; case BIT5_E: M_BIT(5,R->DE.B.l);break; +case BIT5_H: M_BIT(5,R->HL.B.h);break; case BIT5_L: M_BIT(5,R->HL.B.l);break; +case BIT5_xHL: I=msx_RdZ80(R->HL.W);M_BIT(5,I);break; +case BIT5_A: M_BIT(5,R->AF.B.h);break; + +case BIT6_B: M_BIT(6,R->BC.B.h);break; case BIT6_C: M_BIT(6,R->BC.B.l);break; +case BIT6_D: M_BIT(6,R->DE.B.h);break; case BIT6_E: M_BIT(6,R->DE.B.l);break; +case BIT6_H: M_BIT(6,R->HL.B.h);break; case BIT6_L: M_BIT(6,R->HL.B.l);break; +case BIT6_xHL: I=msx_RdZ80(R->HL.W);M_BIT(6,I);break; +case BIT6_A: M_BIT(6,R->AF.B.h);break; + +case BIT7_B: M_BIT(7,R->BC.B.h);break; case BIT7_C: M_BIT(7,R->BC.B.l);break; +case BIT7_D: M_BIT(7,R->DE.B.h);break; case BIT7_E: M_BIT(7,R->DE.B.l);break; +case BIT7_H: M_BIT(7,R->HL.B.h);break; case BIT7_L: M_BIT(7,R->HL.B.l);break; +case BIT7_xHL: I=msx_RdZ80(R->HL.W);M_BIT(7,I);break; +case BIT7_A: M_BIT(7,R->AF.B.h);break; + +case RES0_B: M_RES(0,R->BC.B.h);break; case RES0_C: M_RES(0,R->BC.B.l);break; +case RES0_D: M_RES(0,R->DE.B.h);break; case RES0_E: M_RES(0,R->DE.B.l);break; +case RES0_H: M_RES(0,R->HL.B.h);break; case RES0_L: M_RES(0,R->HL.B.l);break; +case RES0_xHL: I=msx_RdZ80(R->HL.W);M_RES(0,I);msx_WrZ80(R->HL.W,I);break; +case RES0_A: M_RES(0,R->AF.B.h);break; + +case RES1_B: M_RES(1,R->BC.B.h);break; case RES1_C: M_RES(1,R->BC.B.l);break; +case RES1_D: M_RES(1,R->DE.B.h);break; case RES1_E: M_RES(1,R->DE.B.l);break; +case RES1_H: M_RES(1,R->HL.B.h);break; case RES1_L: M_RES(1,R->HL.B.l);break; +case RES1_xHL: I=msx_RdZ80(R->HL.W);M_RES(1,I);msx_WrZ80(R->HL.W,I);break; +case RES1_A: M_RES(1,R->AF.B.h);break; + +case RES2_B: M_RES(2,R->BC.B.h);break; case RES2_C: M_RES(2,R->BC.B.l);break; +case RES2_D: M_RES(2,R->DE.B.h);break; case RES2_E: M_RES(2,R->DE.B.l);break; +case RES2_H: M_RES(2,R->HL.B.h);break; case RES2_L: M_RES(2,R->HL.B.l);break; +case RES2_xHL: I=msx_RdZ80(R->HL.W);M_RES(2,I);msx_WrZ80(R->HL.W,I);break; +case RES2_A: M_RES(2,R->AF.B.h);break; + +case RES3_B: M_RES(3,R->BC.B.h);break; case RES3_C: M_RES(3,R->BC.B.l);break; +case RES3_D: M_RES(3,R->DE.B.h);break; case RES3_E: M_RES(3,R->DE.B.l);break; +case RES3_H: M_RES(3,R->HL.B.h);break; case RES3_L: M_RES(3,R->HL.B.l);break; +case RES3_xHL: I=msx_RdZ80(R->HL.W);M_RES(3,I);msx_WrZ80(R->HL.W,I);break; +case RES3_A: M_RES(3,R->AF.B.h);break; + +case RES4_B: M_RES(4,R->BC.B.h);break; case RES4_C: M_RES(4,R->BC.B.l);break; +case RES4_D: M_RES(4,R->DE.B.h);break; case RES4_E: M_RES(4,R->DE.B.l);break; +case RES4_H: M_RES(4,R->HL.B.h);break; case RES4_L: M_RES(4,R->HL.B.l);break; +case RES4_xHL: I=msx_RdZ80(R->HL.W);M_RES(4,I);msx_WrZ80(R->HL.W,I);break; +case RES4_A: M_RES(4,R->AF.B.h);break; + +case RES5_B: M_RES(5,R->BC.B.h);break; case RES5_C: M_RES(5,R->BC.B.l);break; +case RES5_D: M_RES(5,R->DE.B.h);break; case RES5_E: M_RES(5,R->DE.B.l);break; +case RES5_H: M_RES(5,R->HL.B.h);break; case RES5_L: M_RES(5,R->HL.B.l);break; +case RES5_xHL: I=msx_RdZ80(R->HL.W);M_RES(5,I);msx_WrZ80(R->HL.W,I);break; +case RES5_A: M_RES(5,R->AF.B.h);break; + +case RES6_B: M_RES(6,R->BC.B.h);break; case RES6_C: M_RES(6,R->BC.B.l);break; +case RES6_D: M_RES(6,R->DE.B.h);break; case RES6_E: M_RES(6,R->DE.B.l);break; +case RES6_H: M_RES(6,R->HL.B.h);break; case RES6_L: M_RES(6,R->HL.B.l);break; +case RES6_xHL: I=msx_RdZ80(R->HL.W);M_RES(6,I);msx_WrZ80(R->HL.W,I);break; +case RES6_A: M_RES(6,R->AF.B.h);break; + +case RES7_B: M_RES(7,R->BC.B.h);break; case RES7_C: M_RES(7,R->BC.B.l);break; +case RES7_D: M_RES(7,R->DE.B.h);break; case RES7_E: M_RES(7,R->DE.B.l);break; +case RES7_H: M_RES(7,R->HL.B.h);break; case RES7_L: M_RES(7,R->HL.B.l);break; +case RES7_xHL: I=msx_RdZ80(R->HL.W);M_RES(7,I);msx_WrZ80(R->HL.W,I);break; +case RES7_A: M_RES(7,R->AF.B.h);break; + +case SET0_B: M_SET(0,R->BC.B.h);break; case SET0_C: M_SET(0,R->BC.B.l);break; +case SET0_D: M_SET(0,R->DE.B.h);break; case SET0_E: M_SET(0,R->DE.B.l);break; +case SET0_H: M_SET(0,R->HL.B.h);break; case SET0_L: M_SET(0,R->HL.B.l);break; +case SET0_xHL: I=msx_RdZ80(R->HL.W);M_SET(0,I);msx_WrZ80(R->HL.W,I);break; +case SET0_A: M_SET(0,R->AF.B.h);break; + +case SET1_B: M_SET(1,R->BC.B.h);break; case SET1_C: M_SET(1,R->BC.B.l);break; +case SET1_D: M_SET(1,R->DE.B.h);break; case SET1_E: M_SET(1,R->DE.B.l);break; +case SET1_H: M_SET(1,R->HL.B.h);break; case SET1_L: M_SET(1,R->HL.B.l);break; +case SET1_xHL: I=msx_RdZ80(R->HL.W);M_SET(1,I);msx_WrZ80(R->HL.W,I);break; +case SET1_A: M_SET(1,R->AF.B.h);break; + +case SET2_B: M_SET(2,R->BC.B.h);break; case SET2_C: M_SET(2,R->BC.B.l);break; +case SET2_D: M_SET(2,R->DE.B.h);break; case SET2_E: M_SET(2,R->DE.B.l);break; +case SET2_H: M_SET(2,R->HL.B.h);break; case SET2_L: M_SET(2,R->HL.B.l);break; +case SET2_xHL: I=msx_RdZ80(R->HL.W);M_SET(2,I);msx_WrZ80(R->HL.W,I);break; +case SET2_A: M_SET(2,R->AF.B.h);break; + +case SET3_B: M_SET(3,R->BC.B.h);break; case SET3_C: M_SET(3,R->BC.B.l);break; +case SET3_D: M_SET(3,R->DE.B.h);break; case SET3_E: M_SET(3,R->DE.B.l);break; +case SET3_H: M_SET(3,R->HL.B.h);break; case SET3_L: M_SET(3,R->HL.B.l);break; +case SET3_xHL: I=msx_RdZ80(R->HL.W);M_SET(3,I);msx_WrZ80(R->HL.W,I);break; +case SET3_A: M_SET(3,R->AF.B.h);break; + +case SET4_B: M_SET(4,R->BC.B.h);break; case SET4_C: M_SET(4,R->BC.B.l);break; +case SET4_D: M_SET(4,R->DE.B.h);break; case SET4_E: M_SET(4,R->DE.B.l);break; +case SET4_H: M_SET(4,R->HL.B.h);break; case SET4_L: M_SET(4,R->HL.B.l);break; +case SET4_xHL: I=msx_RdZ80(R->HL.W);M_SET(4,I);msx_WrZ80(R->HL.W,I);break; +case SET4_A: M_SET(4,R->AF.B.h);break; + +case SET5_B: M_SET(5,R->BC.B.h);break; case SET5_C: M_SET(5,R->BC.B.l);break; +case SET5_D: M_SET(5,R->DE.B.h);break; case SET5_E: M_SET(5,R->DE.B.l);break; +case SET5_H: M_SET(5,R->HL.B.h);break; case SET5_L: M_SET(5,R->HL.B.l);break; +case SET5_xHL: I=msx_RdZ80(R->HL.W);M_SET(5,I);msx_WrZ80(R->HL.W,I);break; +case SET5_A: M_SET(5,R->AF.B.h);break; + +case SET6_B: M_SET(6,R->BC.B.h);break; case SET6_C: M_SET(6,R->BC.B.l);break; +case SET6_D: M_SET(6,R->DE.B.h);break; case SET6_E: M_SET(6,R->DE.B.l);break; +case SET6_H: M_SET(6,R->HL.B.h);break; case SET6_L: M_SET(6,R->HL.B.l);break; +case SET6_xHL: I=msx_RdZ80(R->HL.W);M_SET(6,I);msx_WrZ80(R->HL.W,I);break; +case SET6_A: M_SET(6,R->AF.B.h);break; + +case SET7_B: M_SET(7,R->BC.B.h);break; case SET7_C: M_SET(7,R->BC.B.l);break; +case SET7_D: M_SET(7,R->DE.B.h);break; case SET7_E: M_SET(7,R->DE.B.l);break; +case SET7_H: M_SET(7,R->HL.B.h);break; case SET7_L: M_SET(7,R->HL.B.l);break; +case SET7_xHL: I=msx_RdZ80(R->HL.W);M_SET(7,I);msx_WrZ80(R->HL.W,I);break; +case SET7_A: M_SET(7,R->AF.B.h);break; diff --git a/components/msx/fmsx/src/Z80/CodesED.h b/components/msx/fmsx/src/Z80/CodesED.h new file mode 100644 index 0000000..e61d764 --- /dev/null +++ b/components/msx/fmsx/src/Z80/CodesED.h @@ -0,0 +1,283 @@ +/** Z80: portable Z80 emulator *******************************/ +/** **/ +/** CodesED.h **/ +/** **/ +/** This file contains implementation for the ED table of **/ +/** Z80 commands. It is included from Z80.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +/** This is a special patch for emulating BIOS calls: ********/ +case DB_FE: PatchZ80(R);break; +/*************************************************************/ + +case ADC_HL_BC: M_ADCW(BC);break; +case ADC_HL_DE: M_ADCW(DE);break; +case ADC_HL_HL: M_ADCW(HL);break; +case ADC_HL_SP: M_ADCW(SP);break; + +case SBC_HL_BC: M_SBCW(BC);break; +case SBC_HL_DE: M_SBCW(DE);break; +case SBC_HL_HL: M_SBCW(HL);break; +case SBC_HL_SP: M_SBCW(SP);break; + +case LD_xWORDe_HL: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + msx_WrZ80(J.W++,R->HL.B.l); + msx_WrZ80(J.W,R->HL.B.h); + break; +case LD_xWORDe_DE: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + msx_WrZ80(J.W++,R->DE.B.l); + msx_WrZ80(J.W,R->DE.B.h); + break; +case LD_xWORDe_BC: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + msx_WrZ80(J.W++,R->BC.B.l); + msx_WrZ80(J.W,R->BC.B.h); + break; +case LD_xWORDe_SP: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + msx_WrZ80(J.W++,R->SP.B.l); + msx_WrZ80(J.W,R->SP.B.h); + break; + +case LD_HL_xWORDe: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + R->HL.B.l=msx_RdZ80(J.W++); + R->HL.B.h=msx_RdZ80(J.W); + break; +case LD_DE_xWORDe: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + R->DE.B.l=msx_RdZ80(J.W++); + R->DE.B.h=msx_RdZ80(J.W); + break; +case LD_BC_xWORDe: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + R->BC.B.l=msx_RdZ80(J.W++); + R->BC.B.h=msx_RdZ80(J.W); + break; +case LD_SP_xWORDe: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + R->SP.B.l=msx_RdZ80(J.W++); + R->SP.B.h=msx_RdZ80(J.W); + break; + +case RRD: + I=msx_RdZ80(R->HL.W); + J.B.l=(I>>4)|(R->AF.B.h<<4); + msx_WrZ80(R->HL.W,J.B.l); + R->AF.B.h=(I&0x0F)|(R->AF.B.h&0xF0); + R->AF.B.l=PZSTable[R->AF.B.h]|(R->AF.B.l&C_FLAG); + break; +case RLD: + I=msx_RdZ80(R->HL.W); + J.B.l=(I<<4)|(R->AF.B.h&0x0F); + msx_WrZ80(R->HL.W,J.B.l); + R->AF.B.h=(I>>4)|(R->AF.B.h&0xF0); + R->AF.B.l=PZSTable[R->AF.B.h]|(R->AF.B.l&C_FLAG); + break; + +case LD_A_I: + R->AF.B.h=R->I; + R->AF.B.l=(R->AF.B.l&C_FLAG)|(R->IFF&IFF_2? P_FLAG:0)|ZSTable[R->AF.B.h]; + break; + +case LD_A_R: + R->AF.B.h=R->R; + R->AF.B.l=(R->AF.B.l&C_FLAG)|(R->IFF&IFF_2? P_FLAG:0)|ZSTable[R->AF.B.h]; + break; + +case LD_I_A: R->I=R->AF.B.h;break; +case LD_R_A: R->R=R->AF.B.h;break; + +case IM_0: R->IFF&=~(IFF_IM1|IFF_IM2);break; +case IM_1: R->IFF=(R->IFF&~IFF_IM2)|IFF_IM1;break; +case IM_2: R->IFF=(R->IFF&~IFF_IM1)|IFF_IM2;break; + +case RETI: +case RETN: if(R->IFF&IFF_2) R->IFF|=IFF_1; else R->IFF&=~IFF_1; + M_RET;break; + +case NEG: I=R->AF.B.h;R->AF.B.h=0;M_SUB(I);break; + +case IN_B_xC: M_IN(R->BC.B.h);break; +case IN_C_xC: M_IN(R->BC.B.l);break; +case IN_D_xC: M_IN(R->DE.B.h);break; +case IN_E_xC: M_IN(R->DE.B.l);break; +case IN_H_xC: M_IN(R->HL.B.h);break; +case IN_L_xC: M_IN(R->HL.B.l);break; +case IN_A_xC: M_IN(R->AF.B.h);break; +case IN_F_xC: M_IN(J.B.l);break; + +case OUT_xC_B: msx_OutZ80(R->BC.W,R->BC.B.h);break; +case OUT_xC_C: msx_OutZ80(R->BC.W,R->BC.B.l);break; +case OUT_xC_D: msx_OutZ80(R->BC.W,R->DE.B.h);break; +case OUT_xC_E: msx_OutZ80(R->BC.W,R->DE.B.l);break; +case OUT_xC_H: msx_OutZ80(R->BC.W,R->HL.B.h);break; +case OUT_xC_L: msx_OutZ80(R->BC.W,R->HL.B.l);break; +case OUT_xC_A: msx_OutZ80(R->BC.W,R->AF.B.h);break; +case OUT_xC_F: msx_OutZ80(R->BC.W,0);break; + +case INI: + msx_WrZ80(R->HL.W++,msx_InZ80(R->BC.W)); + --R->BC.B.h; + R->AF.B.l=N_FLAG|(R->BC.B.h? 0:Z_FLAG); + break; + +case INIR: + msx_WrZ80(R->HL.W++,msx_InZ80(R->BC.W)); + if(--R->BC.B.h) { R->AF.B.l=N_FLAG;R->ICount-=21;R->PC.W-=2; } + else { R->AF.B.l=Z_FLAG|N_FLAG;R->ICount-=16; } + break; + +case IND: + msx_WrZ80(R->HL.W--,msx_InZ80(R->BC.W)); + --R->BC.B.h; + R->AF.B.l=N_FLAG|(R->BC.B.h? 0:Z_FLAG); + break; + +case INDR: + msx_WrZ80(R->HL.W--,msx_InZ80(R->BC.W)); + if(!--R->BC.B.h) { R->AF.B.l=N_FLAG;R->ICount-=21;R->PC.W-=2; } + else { R->AF.B.l=Z_FLAG|N_FLAG;R->ICount-=16; } + break; + +case OUTI: + --R->BC.B.h; + I=msx_RdZ80(R->HL.W++); + msx_OutZ80(R->BC.W,I); + R->AF.B.l=N_FLAG|(R->BC.B.h? 0:Z_FLAG)|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); + break; + +case OTIR: + --R->BC.B.h; + I=msx_RdZ80(R->HL.W++); + msx_OutZ80(R->BC.W,I); + if(R->BC.B.h) + { + R->AF.B.l=N_FLAG|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); + R->ICount-=21; + R->PC.W-=2; + } + else + { + R->AF.B.l=Z_FLAG|N_FLAG|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); + R->ICount-=16; + } + break; + +case OUTD: + --R->BC.B.h; + I=msx_RdZ80(R->HL.W--); + msx_OutZ80(R->BC.W,I); + R->AF.B.l=N_FLAG|(R->BC.B.h? 0:Z_FLAG)|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); + break; + +case OTDR: + --R->BC.B.h; + I=msx_RdZ80(R->HL.W--); + msx_OutZ80(R->BC.W,I); + if(R->BC.B.h) + { + R->AF.B.l=N_FLAG|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); + R->ICount-=21; + R->PC.W-=2; + } + else + { + R->AF.B.l=Z_FLAG|N_FLAG|(R->HL.B.l+I>255? (C_FLAG|H_FLAG):0); + R->ICount-=16; + } + break; + +case LDI: + msx_WrZ80(R->DE.W++,msx_RdZ80(R->HL.W++)); + --R->BC.W; + R->AF.B.l=(R->AF.B.l&~(N_FLAG|H_FLAG|P_FLAG))|(R->BC.W? P_FLAG:0); + break; + +case LDIR: + msx_WrZ80(R->DE.W++,msx_RdZ80(R->HL.W++)); + if(--R->BC.W) + { + R->AF.B.l=(R->AF.B.l&~(H_FLAG|P_FLAG))|N_FLAG; + R->ICount-=21; + R->PC.W-=2; + } + else + { + R->AF.B.l&=~(N_FLAG|H_FLAG|P_FLAG); + R->ICount-=16; + } + break; + +case LDD: + msx_WrZ80(R->DE.W--,msx_RdZ80(R->HL.W--)); + --R->BC.W; + R->AF.B.l=(R->AF.B.l&~(N_FLAG|H_FLAG|P_FLAG))|(R->BC.W? P_FLAG:0); + break; + +case LDDR: + msx_WrZ80(R->DE.W--,msx_RdZ80(R->HL.W--)); + R->AF.B.l&=~(N_FLAG|H_FLAG|P_FLAG); + if(--R->BC.W) + { + R->AF.B.l=(R->AF.B.l&~(H_FLAG|P_FLAG))|N_FLAG; + R->ICount-=21; + R->PC.W-=2; + } + else + { + R->AF.B.l&=~(N_FLAG|H_FLAG|P_FLAG); + R->ICount-=16; + } + break; + +case CPI: + I=msx_RdZ80(R->HL.W++); + J.B.l=R->AF.B.h-I; + --R->BC.W; + R->AF.B.l = + N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[J.B.l]| + ((R->AF.B.h^I^J.B.l)&H_FLAG)|(R->BC.W? P_FLAG:0); + break; + +case CPIR: + I=msx_RdZ80(R->HL.W++); + J.B.l=R->AF.B.h-I; + if(--R->BC.W&&J.B.l) { R->ICount-=21;R->PC.W-=2; } else R->ICount-=16; + R->AF.B.l = + N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[J.B.l]| + ((R->AF.B.h^I^J.B.l)&H_FLAG)|(R->BC.W? P_FLAG:0); + break; + +case CPD: + I=msx_RdZ80(R->HL.W--); + J.B.l=R->AF.B.h-I; + --R->BC.W; + R->AF.B.l = + N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[J.B.l]| + ((R->AF.B.h^I^J.B.l)&H_FLAG)|(R->BC.W? P_FLAG:0); + break; + +case CPDR: + I=msx_RdZ80(R->HL.W--); + J.B.l=R->AF.B.h-I; + if(--R->BC.W&&J.B.l) { R->ICount-=21;R->PC.W-=2; } else R->ICount-=16; + R->AF.B.l = + N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[J.B.l]| + ((R->AF.B.h^I^J.B.l)&H_FLAG)|(R->BC.W? P_FLAG:0); + break; diff --git a/components/msx/fmsx/src/Z80/CodesXCB.h b/components/msx/fmsx/src/Z80/CodesXCB.h new file mode 100644 index 0000000..767127d --- /dev/null +++ b/components/msx/fmsx/src/Z80/CodesXCB.h @@ -0,0 +1,64 @@ +/** Z80: portable Z80 emulator *******************************/ +/** **/ +/** CodesXCB.h **/ +/** **/ +/** This file contains implementation for FD/DD-CB tables **/ +/** of Z80 commands. It is included from Z80.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +case RLC_xHL: I=msx_RdZ80(J.W);M_RLC(I);msx_WrZ80(J.W,I);break; +case RRC_xHL: I=msx_RdZ80(J.W);M_RRC(I);msx_WrZ80(J.W,I);break; +case RL_xHL: I=msx_RdZ80(J.W);M_RL(I);msx_WrZ80(J.W,I);break; +case RR_xHL: I=msx_RdZ80(J.W);M_RR(I);msx_WrZ80(J.W,I);break; +case SLA_xHL: I=msx_RdZ80(J.W);M_SLA(I);msx_WrZ80(J.W,I);break; +case SRA_xHL: I=msx_RdZ80(J.W);M_SRA(I);msx_WrZ80(J.W,I);break; +case SLL_xHL: I=msx_RdZ80(J.W);M_SLL(I);msx_WrZ80(J.W,I);break; +case SRL_xHL: I=msx_RdZ80(J.W);M_SRL(I);msx_WrZ80(J.W,I);break; + +case BIT0_B: case BIT0_C: case BIT0_D: case BIT0_E: +case BIT0_H: case BIT0_L: case BIT0_A: +case BIT0_xHL: I=msx_RdZ80(J.W);M_BIT(0,I);break; +case BIT1_B: case BIT1_C: case BIT1_D: case BIT1_E: +case BIT1_H: case BIT1_L: case BIT1_A: +case BIT1_xHL: I=msx_RdZ80(J.W);M_BIT(1,I);break; +case BIT2_B: case BIT2_C: case BIT2_D: case BIT2_E: +case BIT2_H: case BIT2_L: case BIT2_A: +case BIT2_xHL: I=msx_RdZ80(J.W);M_BIT(2,I);break; +case BIT3_B: case BIT3_C: case BIT3_D: case BIT3_E: +case BIT3_H: case BIT3_L: case BIT3_A: +case BIT3_xHL: I=msx_RdZ80(J.W);M_BIT(3,I);break; +case BIT4_B: case BIT4_C: case BIT4_D: case BIT4_E: +case BIT4_H: case BIT4_L: case BIT4_A: +case BIT4_xHL: I=msx_RdZ80(J.W);M_BIT(4,I);break; +case BIT5_B: case BIT5_C: case BIT5_D: case BIT5_E: +case BIT5_H: case BIT5_L: case BIT5_A: +case BIT5_xHL: I=msx_RdZ80(J.W);M_BIT(5,I);break; +case BIT6_B: case BIT6_C: case BIT6_D: case BIT6_E: +case BIT6_H: case BIT6_L: case BIT6_A: +case BIT6_xHL: I=msx_RdZ80(J.W);M_BIT(6,I);break; +case BIT7_B: case BIT7_C: case BIT7_D: case BIT7_E: +case BIT7_H: case BIT7_L: case BIT7_A: +case BIT7_xHL: I=msx_RdZ80(J.W);M_BIT(7,I);break; + +case RES0_xHL: I=msx_RdZ80(J.W);M_RES(0,I);msx_WrZ80(J.W,I);break; +case RES1_xHL: I=msx_RdZ80(J.W);M_RES(1,I);msx_WrZ80(J.W,I);break; +case RES2_xHL: I=msx_RdZ80(J.W);M_RES(2,I);msx_WrZ80(J.W,I);break; +case RES3_xHL: I=msx_RdZ80(J.W);M_RES(3,I);msx_WrZ80(J.W,I);break; +case RES4_xHL: I=msx_RdZ80(J.W);M_RES(4,I);msx_WrZ80(J.W,I);break; +case RES5_xHL: I=msx_RdZ80(J.W);M_RES(5,I);msx_WrZ80(J.W,I);break; +case RES6_xHL: I=msx_RdZ80(J.W);M_RES(6,I);msx_WrZ80(J.W,I);break; +case RES7_xHL: I=msx_RdZ80(J.W);M_RES(7,I);msx_WrZ80(J.W,I);break; + +case SET0_xHL: I=msx_RdZ80(J.W);M_SET(0,I);msx_WrZ80(J.W,I);break; +case SET1_xHL: I=msx_RdZ80(J.W);M_SET(1,I);msx_WrZ80(J.W,I);break; +case SET2_xHL: I=msx_RdZ80(J.W);M_SET(2,I);msx_WrZ80(J.W,I);break; +case SET3_xHL: I=msx_RdZ80(J.W);M_SET(3,I);msx_WrZ80(J.W,I);break; +case SET4_xHL: I=msx_RdZ80(J.W);M_SET(4,I);msx_WrZ80(J.W,I);break; +case SET5_xHL: I=msx_RdZ80(J.W);M_SET(5,I);msx_WrZ80(J.W,I);break; +case SET6_xHL: I=msx_RdZ80(J.W);M_SET(6,I);msx_WrZ80(J.W,I);break; +case SET7_xHL: I=msx_RdZ80(J.W);M_SET(7,I);msx_WrZ80(J.W,I);break; diff --git a/components/msx/fmsx/src/Z80/CodesXX.h b/components/msx/fmsx/src/Z80/CodesXX.h new file mode 100644 index 0000000..0a7ecce --- /dev/null +++ b/components/msx/fmsx/src/Z80/CodesXX.h @@ -0,0 +1,396 @@ +/** Z80: portable Z80 emulator *******************************/ +/** **/ +/** CodesXX.h **/ +/** **/ +/** This file contains implementation for FD/DD tables of **/ +/** Z80 commands. It is included from Z80.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +case JR_NZ: if(R->AF.B.l&Z_FLAG) R->PC.W++; else { R->ICount-=5;M_JR; } break; +case JR_NC: if(R->AF.B.l&C_FLAG) R->PC.W++; else { R->ICount-=5;M_JR; } break; +case JR_Z: if(R->AF.B.l&Z_FLAG) { R->ICount-=5;M_JR; } else R->PC.W++; break; +case JR_C: if(R->AF.B.l&C_FLAG) { R->ICount-=5;M_JR; } else R->PC.W++; break; + +case JP_NZ: if(R->AF.B.l&Z_FLAG) R->PC.W+=2; else { M_JP; } break; +case JP_NC: if(R->AF.B.l&C_FLAG) R->PC.W+=2; else { M_JP; } break; +case JP_PO: if(R->AF.B.l&P_FLAG) R->PC.W+=2; else { M_JP; } break; +case JP_P: if(R->AF.B.l&S_FLAG) R->PC.W+=2; else { M_JP; } break; +case JP_Z: if(R->AF.B.l&Z_FLAG) { M_JP; } else R->PC.W+=2; break; +case JP_C: if(R->AF.B.l&C_FLAG) { M_JP; } else R->PC.W+=2; break; +case JP_PE: if(R->AF.B.l&P_FLAG) { M_JP; } else R->PC.W+=2; break; +case JP_M: if(R->AF.B.l&S_FLAG) { M_JP; } else R->PC.W+=2; break; + +case RET_NZ: if(!(R->AF.B.l&Z_FLAG)) { R->ICount-=6;M_RET; } break; +case RET_NC: if(!(R->AF.B.l&C_FLAG)) { R->ICount-=6;M_RET; } break; +case RET_PO: if(!(R->AF.B.l&P_FLAG)) { R->ICount-=6;M_RET; } break; +case RET_P: if(!(R->AF.B.l&S_FLAG)) { R->ICount-=6;M_RET; } break; +case RET_Z: if(R->AF.B.l&Z_FLAG) { R->ICount-=6;M_RET; } break; +case RET_C: if(R->AF.B.l&C_FLAG) { R->ICount-=6;M_RET; } break; +case RET_PE: if(R->AF.B.l&P_FLAG) { R->ICount-=6;M_RET; } break; +case RET_M: if(R->AF.B.l&S_FLAG) { R->ICount-=6;M_RET; } break; + +case CALL_NZ: if(R->AF.B.l&Z_FLAG) R->PC.W+=2; else { R->ICount-=7;M_CALL; } break; +case CALL_NC: if(R->AF.B.l&C_FLAG) R->PC.W+=2; else { R->ICount-=7;M_CALL; } break; +case CALL_PO: if(R->AF.B.l&P_FLAG) R->PC.W+=2; else { R->ICount-=7;M_CALL; } break; +case CALL_P: if(R->AF.B.l&S_FLAG) R->PC.W+=2; else { R->ICount-=7;M_CALL; } break; +case CALL_Z: if(R->AF.B.l&Z_FLAG) { R->ICount-=7;M_CALL; } else R->PC.W+=2; break; +case CALL_C: if(R->AF.B.l&C_FLAG) { R->ICount-=7;M_CALL; } else R->PC.W+=2; break; +case CALL_PE: if(R->AF.B.l&P_FLAG) { R->ICount-=7;M_CALL; } else R->PC.W+=2; break; +case CALL_M: if(R->AF.B.l&S_FLAG) { R->ICount-=7;M_CALL; } else R->PC.W+=2; break; + +case ADD_B: M_ADD(R->BC.B.h);break; +case ADD_C: M_ADD(R->BC.B.l);break; +case ADD_D: M_ADD(R->DE.B.h);break; +case ADD_E: M_ADD(R->DE.B.l);break; +case ADD_H: M_ADD(R->XX.B.h);break; +case ADD_L: M_ADD(R->XX.B.l);break; +case ADD_A: M_ADD(R->AF.B.h);break; +case ADD_xHL: I=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++)); + M_ADD(I);break; +case ADD_BYTE: I=OpZ80(R->PC.W++);M_ADD(I);break; + +case SUB_B: M_SUB(R->BC.B.h);break; +case SUB_C: M_SUB(R->BC.B.l);break; +case SUB_D: M_SUB(R->DE.B.h);break; +case SUB_E: M_SUB(R->DE.B.l);break; +case SUB_H: M_SUB(R->XX.B.h);break; +case SUB_L: M_SUB(R->XX.B.l);break; +case SUB_A: R->AF.B.h=0;R->AF.B.l=N_FLAG|Z_FLAG;break; +case SUB_xHL: I=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++)); + M_SUB(I);break; +case SUB_BYTE: I=OpZ80(R->PC.W++);M_SUB(I);break; + +case AND_B: M_AND(R->BC.B.h);break; +case AND_C: M_AND(R->BC.B.l);break; +case AND_D: M_AND(R->DE.B.h);break; +case AND_E: M_AND(R->DE.B.l);break; +case AND_H: M_AND(R->XX.B.h);break; +case AND_L: M_AND(R->XX.B.l);break; +case AND_A: M_AND(R->AF.B.h);break; +case AND_xHL: I=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++)); + M_AND(I);break; +case AND_BYTE: I=OpZ80(R->PC.W++);M_AND(I);break; + +case OR_B: M_OR(R->BC.B.h);break; +case OR_C: M_OR(R->BC.B.l);break; +case OR_D: M_OR(R->DE.B.h);break; +case OR_E: M_OR(R->DE.B.l);break; +case OR_H: M_OR(R->XX.B.h);break; +case OR_L: M_OR(R->XX.B.l);break; +case OR_A: M_OR(R->AF.B.h);break; +case OR_xHL: I=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++)); + M_OR(I);break; +case OR_BYTE: I=OpZ80(R->PC.W++);M_OR(I);break; + +case ADC_B: M_ADC(R->BC.B.h);break; +case ADC_C: M_ADC(R->BC.B.l);break; +case ADC_D: M_ADC(R->DE.B.h);break; +case ADC_E: M_ADC(R->DE.B.l);break; +case ADC_H: M_ADC(R->XX.B.h);break; +case ADC_L: M_ADC(R->XX.B.l);break; +case ADC_A: M_ADC(R->AF.B.h);break; +case ADC_xHL: I=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++)); + M_ADC(I);break; +case ADC_BYTE: I=OpZ80(R->PC.W++);M_ADC(I);break; + +case SBC_B: M_SBC(R->BC.B.h);break; +case SBC_C: M_SBC(R->BC.B.l);break; +case SBC_D: M_SBC(R->DE.B.h);break; +case SBC_E: M_SBC(R->DE.B.l);break; +case SBC_H: M_SBC(R->XX.B.h);break; +case SBC_L: M_SBC(R->XX.B.l);break; +case SBC_A: M_SBC(R->AF.B.h);break; +case SBC_xHL: I=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++)); + M_SBC(I);break; +case SBC_BYTE: I=OpZ80(R->PC.W++);M_SBC(I);break; + +case XOR_B: M_XOR(R->BC.B.h);break; +case XOR_C: M_XOR(R->BC.B.l);break; +case XOR_D: M_XOR(R->DE.B.h);break; +case XOR_E: M_XOR(R->DE.B.l);break; +case XOR_H: M_XOR(R->XX.B.h);break; +case XOR_L: M_XOR(R->XX.B.l);break; +case XOR_A: R->AF.B.h=0;R->AF.B.l=P_FLAG|Z_FLAG;break; +case XOR_xHL: I=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++)); + M_XOR(I);break; +case XOR_BYTE: I=OpZ80(R->PC.W++);M_XOR(I);break; + +case CP_B: M_CP(R->BC.B.h);break; +case CP_C: M_CP(R->BC.B.l);break; +case CP_D: M_CP(R->DE.B.h);break; +case CP_E: M_CP(R->DE.B.l);break; +case CP_H: M_CP(R->XX.B.h);break; +case CP_L: M_CP(R->XX.B.l);break; +case CP_A: R->AF.B.l=N_FLAG|Z_FLAG;break; +case CP_xHL: I=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++)); + M_CP(I);break; +case CP_BYTE: I=OpZ80(R->PC.W++);M_CP(I);break; + +case LD_BC_WORD: M_LDWORD(BC);break; +case LD_DE_WORD: M_LDWORD(DE);break; +case LD_HL_WORD: M_LDWORD(XX);break; +case LD_SP_WORD: M_LDWORD(SP);break; + +case LD_PC_HL: R->PC.W=R->XX.W;JumpZ80(R->PC.W);break; +case LD_SP_HL: R->SP.W=R->XX.W;break; +case LD_A_xBC: R->AF.B.h=msx_RdZ80(R->BC.W);break; +case LD_A_xDE: R->AF.B.h=msx_RdZ80(R->DE.W);break; + +case ADD_HL_BC: M_ADDW(XX,BC);break; +case ADD_HL_DE: M_ADDW(XX,DE);break; +case ADD_HL_HL: M_ADDW(XX,XX);break; +case ADD_HL_SP: M_ADDW(XX,SP);break; + +case DEC_BC: R->BC.W--;break; +case DEC_DE: R->DE.W--;break; +case DEC_HL: R->XX.W--;break; +case DEC_SP: R->SP.W--;break; + +case INC_BC: R->BC.W++;break; +case INC_DE: R->DE.W++;break; +case INC_HL: R->XX.W++;break; +case INC_SP: R->SP.W++;break; + +case DEC_B: M_DEC(R->BC.B.h);break; +case DEC_C: M_DEC(R->BC.B.l);break; +case DEC_D: M_DEC(R->DE.B.h);break; +case DEC_E: M_DEC(R->DE.B.l);break; +case DEC_H: M_DEC(R->XX.B.h);break; +case DEC_L: M_DEC(R->XX.B.l);break; +case DEC_A: M_DEC(R->AF.B.h);break; +case DEC_xHL: I=msx_RdZ80(R->XX.W+(offset)msx_RdZ80(R->PC.W));M_DEC(I); + msx_WrZ80(R->XX.W+(offset)OpZ80(R->PC.W++),I); + break; + +case INC_B: M_INC(R->BC.B.h);break; +case INC_C: M_INC(R->BC.B.l);break; +case INC_D: M_INC(R->DE.B.h);break; +case INC_E: M_INC(R->DE.B.l);break; +case INC_H: M_INC(R->XX.B.h);break; +case INC_L: M_INC(R->XX.B.l);break; +case INC_A: M_INC(R->AF.B.h);break; +case INC_xHL: I=msx_RdZ80(R->XX.W+(offset)msx_RdZ80(R->PC.W));M_INC(I); + msx_WrZ80(R->XX.W+(offset)OpZ80(R->PC.W++),I); + break; + +case RLCA: + I=(R->AF.B.h&0x80? C_FLAG:0); + R->AF.B.h=(R->AF.B.h<<1)|I; + R->AF.B.l=(R->AF.B.l&~(C_FLAG|N_FLAG|H_FLAG))|I; + break; +case RLA: + I=(R->AF.B.h&0x80? C_FLAG:0); + R->AF.B.h=(R->AF.B.h<<1)|(R->AF.B.l&C_FLAG); + R->AF.B.l=(R->AF.B.l&~(C_FLAG|N_FLAG|H_FLAG))|I; + break; +case RRCA: + I=R->AF.B.h&0x01; + R->AF.B.h=(R->AF.B.h>>1)|(I? 0x80:0); + R->AF.B.l=(R->AF.B.l&~(C_FLAG|N_FLAG|H_FLAG))|I; + break; +case RRA: + I=R->AF.B.h&0x01; + R->AF.B.h=(R->AF.B.h>>1)|(R->AF.B.l&C_FLAG? 0x80:0); + R->AF.B.l=(R->AF.B.l&~(C_FLAG|N_FLAG|H_FLAG))|I; + break; + +case RST00: M_RST(0x0000);break; +case RST08: M_RST(0x0008);break; +case RST10: M_RST(0x0010);break; +case RST18: M_RST(0x0018);break; +case RST20: M_RST(0x0020);break; +case RST28: M_RST(0x0028);break; +case RST30: M_RST(0x0030);break; +case RST38: M_RST(0x0038);break; + +case PUSH_BC: M_PUSH(BC);break; +case PUSH_DE: M_PUSH(DE);break; +case PUSH_HL: M_PUSH(XX);break; +case PUSH_AF: M_PUSH(AF);break; + +case POP_BC: M_POP(BC);break; +case POP_DE: M_POP(DE);break; +case POP_HL: M_POP(XX);break; +case POP_AF: M_POP(AF);break; + +case DJNZ: if(--R->BC.B.h) { R->ICount-=5;M_JR; } else R->PC.W++;break; +case JP: M_JP;break; +case JR: M_JR;break; +case CALL: M_CALL;break; +case RET: M_RET;break; +case SCF: S(C_FLAG);R(N_FLAG|H_FLAG);break; +case CPL: R->AF.B.h=~R->AF.B.h;S(N_FLAG|H_FLAG);break; +case NOP: break; +case OUTA: I=OpZ80(R->PC.W++);msx_OutZ80(I|(R->AF.W&0xFF00),R->AF.B.h);break; +case INA: I=OpZ80(R->PC.W++);R->AF.B.h=msx_InZ80(I|(R->AF.W&0xFF00));break; + +case HALT: + R->PC.W--; + R->IFF|=IFF_HALT; + R->IBackup=0; + R->ICount=0; + break; + +case DI: + if(R->IFF&IFF_EI) R->ICount+=R->IBackup-1; + R->IFF&=~(IFF_1|IFF_2|IFF_EI); + break; + +case EI: + if(!(R->IFF&(IFF_1|IFF_EI))) + { + R->IFF|=IFF_2|IFF_EI; + R->IBackup=R->ICount; + R->ICount=1; + } + break; + +case CCF: + R->AF.B.l^=C_FLAG;R(N_FLAG|H_FLAG); + R->AF.B.l|=R->AF.B.l&C_FLAG? 0:H_FLAG; + break; + +case EXX: + J.W=R->BC.W;R->BC.W=R->BC1.W;R->BC1.W=J.W; + J.W=R->DE.W;R->DE.W=R->DE1.W;R->DE1.W=J.W; + J.W=R->HL.W;R->HL.W=R->HL1.W;R->HL1.W=J.W; + break; + +case EX_DE_HL: J.W=R->DE.W;R->DE.W=R->HL.W;R->HL.W=J.W;break; +case EX_AF_AF: J.W=R->AF.W;R->AF.W=R->AF1.W;R->AF1.W=J.W;break; + +case LD_B_B: R->BC.B.h=R->BC.B.h;break; +case LD_C_B: R->BC.B.l=R->BC.B.h;break; +case LD_D_B: R->DE.B.h=R->BC.B.h;break; +case LD_E_B: R->DE.B.l=R->BC.B.h;break; +case LD_H_B: R->XX.B.h=R->BC.B.h;break; +case LD_L_B: R->XX.B.l=R->BC.B.h;break; +case LD_A_B: R->AF.B.h=R->BC.B.h;break; +case LD_xHL_B: J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->BC.B.h);break; + +case LD_B_C: R->BC.B.h=R->BC.B.l;break; +case LD_C_C: R->BC.B.l=R->BC.B.l;break; +case LD_D_C: R->DE.B.h=R->BC.B.l;break; +case LD_E_C: R->DE.B.l=R->BC.B.l;break; +case LD_H_C: R->XX.B.h=R->BC.B.l;break; +case LD_L_C: R->XX.B.l=R->BC.B.l;break; +case LD_A_C: R->AF.B.h=R->BC.B.l;break; +case LD_xHL_C: J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->BC.B.l);break; + +case LD_B_D: R->BC.B.h=R->DE.B.h;break; +case LD_C_D: R->BC.B.l=R->DE.B.h;break; +case LD_D_D: R->DE.B.h=R->DE.B.h;break; +case LD_E_D: R->DE.B.l=R->DE.B.h;break; +case LD_H_D: R->XX.B.h=R->DE.B.h;break; +case LD_L_D: R->XX.B.l=R->DE.B.h;break; +case LD_A_D: R->AF.B.h=R->DE.B.h;break; +case LD_xHL_D: J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->DE.B.h);break; + +case LD_B_E: R->BC.B.h=R->DE.B.l;break; +case LD_C_E: R->BC.B.l=R->DE.B.l;break; +case LD_D_E: R->DE.B.h=R->DE.B.l;break; +case LD_E_E: R->DE.B.l=R->DE.B.l;break; +case LD_H_E: R->XX.B.h=R->DE.B.l;break; +case LD_L_E: R->XX.B.l=R->DE.B.l;break; +case LD_A_E: R->AF.B.h=R->DE.B.l;break; +case LD_xHL_E: J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->DE.B.l);break; + +case LD_B_H: R->BC.B.h=R->XX.B.h;break; +case LD_C_H: R->BC.B.l=R->XX.B.h;break; +case LD_D_H: R->DE.B.h=R->XX.B.h;break; +case LD_E_H: R->DE.B.l=R->XX.B.h;break; +case LD_H_H: R->XX.B.h=R->XX.B.h;break; +case LD_L_H: R->XX.B.l=R->XX.B.h;break; +case LD_A_H: R->AF.B.h=R->XX.B.h;break; +case LD_xHL_H: J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->HL.B.h);break; + +case LD_B_L: R->BC.B.h=R->XX.B.l;break; +case LD_C_L: R->BC.B.l=R->XX.B.l;break; +case LD_D_L: R->DE.B.h=R->XX.B.l;break; +case LD_E_L: R->DE.B.l=R->XX.B.l;break; +case LD_H_L: R->XX.B.h=R->XX.B.l;break; +case LD_L_L: R->XX.B.l=R->XX.B.l;break; +case LD_A_L: R->AF.B.h=R->XX.B.l;break; +case LD_xHL_L: J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->HL.B.l);break; + +case LD_B_A: R->BC.B.h=R->AF.B.h;break; +case LD_C_A: R->BC.B.l=R->AF.B.h;break; +case LD_D_A: R->DE.B.h=R->AF.B.h;break; +case LD_E_A: R->DE.B.l=R->AF.B.h;break; +case LD_H_A: R->XX.B.h=R->AF.B.h;break; +case LD_L_A: R->XX.B.l=R->AF.B.h;break; +case LD_A_A: R->AF.B.h=R->AF.B.h;break; +case LD_xHL_A: J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->AF.B.h);break; + +case LD_xBC_A: msx_WrZ80(R->BC.W,R->AF.B.h);break; +case LD_xDE_A: msx_WrZ80(R->DE.W,R->AF.B.h);break; + +case LD_B_xHL: R->BC.B.h=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++));break; +case LD_C_xHL: R->BC.B.l=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++));break; +case LD_D_xHL: R->DE.B.h=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++));break; +case LD_E_xHL: R->DE.B.l=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++));break; +case LD_H_xHL: R->HL.B.h=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++));break; +case LD_L_xHL: R->HL.B.l=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++));break; +case LD_A_xHL: R->AF.B.h=msx_RdZ80(R->XX.W+(offset)OpZ80(R->PC.W++));break; + +case LD_B_BYTE: R->BC.B.h=OpZ80(R->PC.W++);break; +case LD_C_BYTE: R->BC.B.l=OpZ80(R->PC.W++);break; +case LD_D_BYTE: R->DE.B.h=OpZ80(R->PC.W++);break; +case LD_E_BYTE: R->DE.B.l=OpZ80(R->PC.W++);break; +case LD_H_BYTE: R->XX.B.h=OpZ80(R->PC.W++);break; +case LD_L_BYTE: R->XX.B.l=OpZ80(R->PC.W++);break; +case LD_A_BYTE: R->AF.B.h=OpZ80(R->PC.W++);break; +case LD_xHL_BYTE: J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + msx_WrZ80(J.W,OpZ80(R->PC.W++));break; + +case LD_xWORD_HL: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + msx_WrZ80(J.W++,R->XX.B.l); + msx_WrZ80(J.W,R->XX.B.h); + break; + +case LD_HL_xWORD: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + R->XX.B.l=msx_RdZ80(J.W++); + R->XX.B.h=msx_RdZ80(J.W); + break; + +case LD_A_xWORD: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + R->AF.B.h=msx_RdZ80(J.W); + break; + +case LD_xWORD_A: + J.B.l=OpZ80(R->PC.W++); + J.B.h=OpZ80(R->PC.W++); + msx_WrZ80(J.W,R->AF.B.h); + break; + +case EX_HL_xSP: + J.B.l=msx_RdZ80(R->SP.W);msx_WrZ80(R->SP.W++,R->XX.B.l); + J.B.h=msx_RdZ80(R->SP.W);msx_WrZ80(R->SP.W--,R->XX.B.h); + R->XX.W=J.W; + break; + +case DAA: + J.W=R->AF.B.h; + if(R->AF.B.l&C_FLAG) J.W|=256; + if(R->AF.B.l&H_FLAG) J.W|=512; + if(R->AF.B.l&N_FLAG) J.W|=1024; + R->AF.W=DAATable[J.W]; + break; diff --git a/components/msx/fmsx/src/Z80/ConDebug.c b/components/msx/fmsx/src/Z80/ConDebug.c new file mode 100644 index 0000000..947cad6 --- /dev/null +++ b/components/msx/fmsx/src/Z80/ConDebug.c @@ -0,0 +1,287 @@ +/** Z80: portable Z80 emulator *******************************/ +/** **/ +/** ConDebug.c **/ +/** **/ +/** This file contains a console version of the built-in **/ +/** debugger, using EMULib's Console.c. When -DCONDEBUG is **/ +/** ommitted, ConDebug.c just includes the default command **/ +/** line based debugger (Debug.c). **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2005-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifdef DEBUG + +#ifndef CONDEBUG +/** Normal DebugZ80() ****************************************/ +/** When CONDEBUG #undefined, we use plain command line. **/ +/*************************************************************/ +#include "Debug.c" + +#else +/** Console DebugZ80() ***************************************/ +/** When CONDEBUG #defined, we use EMULib console. **/ +/*************************************************************/ + +#include "Z80.h" +#include "Console.h" +#include + +#define DebugZ80 OriginalDebugZ80 +#include "Debug.c" +#undef DebugZ80 + +#ifdef SPECCY +#include "Spectrum.h" +#endif + +#define CLR_BACK PIXEL(255,255,255) +#define CLR_TEXT PIXEL(0,0,0) +#define CLR_DIALOG PIXEL(0,100,0) +#define CLR_PC PIXEL(255,0,0) +#define CLR_SP PIXEL(0,0,100) + +static byte ChrDump(byte C) +{ + return((C>=32)&&(C<128)? C:'.'); +} + +/** DebugZ80() ***********************************************/ +/** This function should exist if DEBUG is #defined. When **/ +/** Trace!=0, it is called after each command executed by **/ +/** the CPU, and given the Z80 registers. **/ +/*************************************************************/ +byte DebugZ80(Z80 *R) +{ + char S[1024]; + word A,Addr,ABuf[20]; + int J,I,K,X,Y,MemoryDump,DrawWindow,ExitNow; + + /* If we don't have enough screen estate... */ + if((VideoW<32*8)||(VideoH<23*8)) + { + /* Show warning message */ + CONMsg( + -1,-1,-1,-1,PIXEL(255,255,255),PIXEL(255,0,0), + "Error","Screen is\0too small!\0\0" + ); + /* Continue emulation */ + R->Trace=0; + return(1); + } + +#ifdef SPECCY + /* Show currently refreshed scanline on Speccy */ + RefreshScreen(); +#endif + + X = ((VideoW>>3)-32)>>1; + Y = ((VideoH>>3)-23)>>1; + Addr = R->PC.W; + A = ~Addr; + K = 0; + + for(DrawWindow=1,MemoryDump=ExitNow=0;!ExitNow&&VideoImg;) + { + if(DrawWindow) + { + CONWindow(X,Y,32,23,CLR_TEXT,CLR_BACK,"Z80 Debugger"); + + sprintf(S,"PC %04X",R->PC.W); + CONSetColor(CLR_BACK,CLR_PC); + CONPrint(X+24,Y+18,S); + sprintf(S,"SP %04X",R->SP.W); + CONSetColor(CLR_BACK,CLR_SP); + CONPrint(X+24,Y+19,S); + + CONSetColor(CLR_TEXT,CLR_BACK); + sprintf(S, + " %c%c%c%c%c%c\n\n" + "AF %04X\nBC %04X\nDE %04X\nHL %04X\nIX %04X\nIY %04X\n\n" + "AF'%04X\nBC'%04X\nDE'%04X\nHL'%04X\n\n" + "IR %02X%02X", + R->AF.B.l&0x80? 'S':'.',R->AF.B.l&0x40? 'Z':'.',R->AF.B.l&0x10? 'H':'.', + R->AF.B.l&0x04? 'P':'.',R->AF.B.l&0x02? 'N':'.',R->AF.B.l&0x01? 'C':'.', + R->AF.W,R->BC.W,R->DE.W,R->HL.W, + R->IX.W,R->IY.W, + R->AF1.W,R->BC1.W,R->DE1.W,R->HL1.W, + R->I,R->R + ); + CONPrint(X+24,Y+2,S); + sprintf(S, + "%s %s", + R->IFF&0x04? "IM2":R->IFF&0x02? "IM1":"IM0", + R->IFF&0x01? "EI":"DI" + ); + CONPrint(X+25,Y+21,S); + DrawWindow=0; + A=~Addr; + } + + /* If top address has changed... */ + if(A!=Addr) + { + /* Clear display */ + CONBox((X+1)<<3,(Y+2)<<3,23*8,20*8,CLR_BACK); + + if(MemoryDump) + { + /* Draw memory dump */ + for(J=0,A=Addr;J<20;J++,A+=4) + { + if(A==R->PC.W) CONSetColor(CLR_BACK,CLR_PC); + else if(A==R->SP.W) CONSetColor(CLR_BACK,CLR_SP); + else CONSetColor(CLR_TEXT,CLR_BACK); + sprintf(S,"%04X%c",A,A==R->PC.W? CON_MORE:A==R->SP.W? CON_LESS:':'); + CONPrint(X+1,Y+J+2,S); + + CONSetColor(CLR_TEXT,CLR_BACK); + sprintf(S, + "%02X %02X %02X %02X %c%c%c%c", + RdZ80(A),RdZ80(A+1),RdZ80(A+2),RdZ80(A+3), + ChrDump(RdZ80(A)),ChrDump(RdZ80(A+1)), + ChrDump(RdZ80(A+2)),ChrDump(RdZ80(A+3)) + ); + CONPrint(X+7,Y+J+2,S); + } + } + else + { + /* Draw listing */ + for(J=0,A=Addr;J<20;J++) + { + if(A==R->PC.W) CONSetColor(CLR_BACK,CLR_PC); + else if(A==R->SP.W) CONSetColor(CLR_BACK,CLR_SP); + else CONSetColor(CLR_TEXT,CLR_BACK); + sprintf(S,"%04X%c",A,A==R->PC.W? CON_MORE:A==R->SP.W? CON_LESS:':'); + CONPrint(X+1,Y+J+2,S); + + ABuf[J]=A; + A+=DAsm(S,A); + + CONSetColor(CLR_TEXT,CLR_BACK); + CONPrintN(X+7,Y+J+2,S,23); + } + } + + /* Display redrawn */ + A=Addr; + } + + /* Draw pointer */ + CONChar(X+6,Y+K+2,CON_ARROW); + + /* Show screen buffer */ + ShowVideo(); + + /* Get key code */ + GetKey(); + I=WaitKey(); + + /* Clear pointer */ + CONChar(X+6,Y+K+2,' '); + + /* Get and process key code */ + switch(I) + { + case 'H': + CONMsg( + -1,-1,-1,-1, + CLR_BACK,CLR_DIALOG, + "Debugger Help", + "ENTER - Execute next opcode\0" + " UP - Previous opcode\0" + " DOWN - Next opcode\0" + " LEFT - Page up\0" + "RIGHT - Page down\0" + " H - This help page\0" + " G - Go to address\0" + " D - Disassembler view\0" + " M - Memory dump view\0" + " S - Show stack\0" + " J - Jump to cursor\0" + " R - Run to cursor\0" + " C - Continue execution\0" + " Q - Quit emulator\0" + ); + DrawWindow=1; + break; + case CON_UP: + if(K) --K; + else + if(MemoryDump) Addr-=4; + else for(--Addr;Addr+DAsm(S,Addr)>A;--Addr); + break; + case CON_DOWN: + if(K<19) ++K; + else + if(MemoryDump) Addr+=4; + else Addr+=DAsm(S,Addr); + break; + case CON_LEFT: + if(MemoryDump) + Addr-=4*20; + else + { + for(I=20,Addr=~A;(Addr>A)||((A^Addr)&~Addr&0x8000);++I) + for(J=0,Addr=A-I;J<20;++J) Addr+=DAsm(S,Addr); + Addr=A-I+1; + } + break; + case CON_RIGHT: + if(MemoryDump) + Addr+=4*20; + else + for(J=0;J<20;++J) Addr+=DAsm(S,Addr); + break; + case CON_OK: + ExitNow=1; + break; + case '\0': + case 'Q': + return(0); + case CON_EXIT: + case 'C': + R->Trap=0xFFFF; + R->Trace=0; + ExitNow=1; + break; + case 'R': + R->Trap=ABuf[K]; + R->Trace=0; + ExitNow=1; + break; + case 'M': + MemoryDump=1; + A=~Addr; + break; + case 'S': + MemoryDump=1; + Addr=R->SP.W; + K=0; + A=~Addr; + break; + case 'D': + MemoryDump=0; + A=~Addr; + break; + case 'G': + if(CONInput(-1,-1,CLR_BACK,CLR_DIALOG,"Go to Address:",S,5|CON_HEX)) + { Addr=strtoul(S,0,16);K=0; } + DrawWindow=1; + break; + case 'J': + R->PC.W=ABuf[K]; + A=~Addr; + break; + } + } + + /* Continue emulation */ + return(1); +} + +#endif /* CONDEBUG */ +#endif /* DEBUG */ diff --git a/components/msx/fmsx/src/Z80/Debug.c b/components/msx/fmsx/src/Z80/Debug.c new file mode 100644 index 0000000..ddc5b21 --- /dev/null +++ b/components/msx/fmsx/src/Z80/Debug.c @@ -0,0 +1,433 @@ +/** Z80: portable Z80 emulator *******************************/ +/** **/ +/** Debug.c **/ +/** **/ +/** This file contains the built-in debugging routine for **/ +/** the Z80 emulator which is called on each Z80 step when **/ +/** Trap!=0. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1995-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifdef DEBUG + +#include "Z80.h" + +#include +#include +#include + +#ifdef FMSX +#include "AY8910.h" +extern AY8910 PSG; +#endif + +static const char *Mnemonics[256] = +{ + "NOP","LD BC,#h","LD (BC),A","INC BC","INC B","DEC B","LD B,*h","RLCA", + "EX AF,AF'","ADD HL,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,*h","RRCA", + "DJNZ @h","LD DE,#h","LD (DE),A","INC DE","INC D","DEC D","LD D,*h","RLA", + "JR @h","ADD HL,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,*h","RRA", + "JR NZ,@h","LD HL,#h","LD (#h),HL","INC HL","INC H","DEC H","LD H,*h","DAA", + "JR Z,@h","ADD HL,HL","LD HL,(#h)","DEC HL","INC L","DEC L","LD L,*h","CPL", + "JR NC,@h","LD SP,#h","LD (#h),A","INC SP","INC (HL)","DEC (HL)","LD (HL),*h","SCF", + "JR C,@h","ADD HL,SP","LD A,(#h)","DEC SP","INC A","DEC A","LD A,*h","CCF", + "LD B,B","LD B,C","LD B,D","LD B,E","LD B,H","LD B,L","LD B,(HL)","LD B,A", + "LD C,B","LD C,C","LD C,D","LD C,E","LD C,H","LD C,L","LD C,(HL)","LD C,A", + "LD D,B","LD D,C","LD D,D","LD D,E","LD D,H","LD D,L","LD D,(HL)","LD D,A", + "LD E,B","LD E,C","LD E,D","LD E,E","LD E,H","LD E,L","LD E,(HL)","LD E,A", + "LD H,B","LD H,C","LD H,D","LD H,E","LD H,H","LD H,L","LD H,(HL)","LD H,A", + "LD L,B","LD L,C","LD L,D","LD L,E","LD L,H","LD L,L","LD L,(HL)","LD L,A", + "LD (HL),B","LD (HL),C","LD (HL),D","LD (HL),E","LD (HL),H","LD (HL),L","HALT","LD (HL),A", + "LD A,B","LD A,C","LD A,D","LD A,E","LD A,H","LD A,L","LD A,(HL)","LD A,A", + "ADD B","ADD C","ADD D","ADD E","ADD H","ADD L","ADD (HL)","ADD A", + "ADC B","ADC C","ADC D","ADC E","ADC H","ADC L","ADC (HL)","ADC A", + "SUB B","SUB C","SUB D","SUB E","SUB H","SUB L","SUB (HL)","SUB A", + "SBC B","SBC C","SBC D","SBC E","SBC H","SBC L","SBC (HL)","SBC A", + "AND B","AND C","AND D","AND E","AND H","AND L","AND (HL)","AND A", + "XOR B","XOR C","XOR D","XOR E","XOR H","XOR L","XOR (HL)","XOR A", + "OR B","OR C","OR D","OR E","OR H","OR L","OR (HL)","OR A", + "CP B","CP C","CP D","CP E","CP H","CP L","CP (HL)","CP A", + "RET NZ","POP BC","JP NZ,#h","JP #h","CALL NZ,#h","PUSH BC","ADD *h","RST 00h", + "RET Z","RET","JP Z,#h","PFX_CB","CALL Z,#h","CALL #h","ADC *h","RST 08h", + "RET NC","POP DE","JP NC,#h","OUTA (*h)","CALL NC,#h","PUSH DE","SUB *h","RST 10h", + "RET C","EXX","JP C,#h","INA (*h)","CALL C,#h","PFX_DD","SBC *h","RST 18h", + "RET PO","POP HL","JP PO,#h","EX HL,(SP)","CALL PO,#h","PUSH HL","AND *h","RST 20h", + "RET PE","LD PC,HL","JP PE,#h","EX DE,HL","CALL PE,#h","PFX_ED","XOR *h","RST 28h", + "RET P","POP AF","JP P,#h","DI","CALL P,#h","PUSH AF","OR *h","RST 30h", + "RET M","LD SP,HL","JP M,#h","EI","CALL M,#h","PFX_FD","CP *h","RST 38h" +}; + +static const char *MnemonicsCB[256] = +{ + "RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (HL)","RLC A", + "RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (HL)","RRC A", + "RL B","RL C","RL D","RL E","RL H","RL L","RL (HL)","RL A", + "RR B","RR C","RR D","RR E","RR H","RR L","RR (HL)","RR A", + "SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (HL)","SLA A", + "SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (HL)","SRA A", + "SLL B","SLL C","SLL D","SLL E","SLL H","SLL L","SLL (HL)","SLL A", + "SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (HL)","SRL A", + "BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(HL)","BIT 0,A", + "BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(HL)","BIT 1,A", + "BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(HL)","BIT 2,A", + "BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(HL)","BIT 3,A", + "BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(HL)","BIT 4,A", + "BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(HL)","BIT 5,A", + "BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(HL)","BIT 6,A", + "BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(HL)","BIT 7,A", + "RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(HL)","RES 0,A", + "RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(HL)","RES 1,A", + "RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(HL)","RES 2,A", + "RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(HL)","RES 3,A", + "RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(HL)","RES 4,A", + "RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(HL)","RES 5,A", + "RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(HL)","RES 6,A", + "RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(HL)","RES 7,A", + "SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(HL)","SET 0,A", + "SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(HL)","SET 1,A", + "SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(HL)","SET 2,A", + "SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(HL)","SET 3,A", + "SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(HL)","SET 4,A", + "SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(HL)","SET 5,A", + "SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(HL)","SET 6,A", + "SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(HL)","SET 7,A" +}; + +static const char *MnemonicsED[256] = +{ + "DB EDh,00h","DB EDh,01h","DB EDh,02h","DB EDh,03h", + "DB EDh,04h","DB EDh,05h","DB EDh,06h","DB EDh,07h", + "DB EDh,08h","DB EDh,09h","DB EDh,0Ah","DB EDh,0Bh", + "DB EDh,0Ch","DB EDh,0Dh","DB EDh,0Eh","DB EDh,0Fh", + "DB EDh,10h","DB EDh,11h","DB EDh,12h","DB EDh,13h", + "DB EDh,14h","DB EDh,15h","DB EDh,16h","DB EDh,17h", + "DB EDh,18h","DB EDh,19h","DB EDh,1Ah","DB EDh,1Bh", + "DB EDh,1Ch","DB EDh,1Dh","DB EDh,1Eh","DB EDh,1Fh", + "DB EDh,20h","DB EDh,21h","DB EDh,22h","DB EDh,23h", + "DB EDh,24h","DB EDh,25h","DB EDh,26h","DB EDh,27h", + "DB EDh,28h","DB EDh,29h","DB EDh,2Ah","DB EDh,2Bh", + "DB EDh,2Ch","DB EDh,2Dh","DB EDh,2Eh","DB EDh,2Fh", + "DB EDh,30h","DB EDh,31h","DB EDh,32h","DB EDh,33h", + "DB EDh,34h","DB EDh,35h","DB EDh,36h","DB EDh,37h", + "DB EDh,38h","DB EDh,39h","DB EDh,3Ah","DB EDh,3Bh", + "DB EDh,3Ch","DB EDh,3Dh","DB EDh,3Eh","DB EDh,3Fh", + "IN B,(C)","OUT (C),B","SBC HL,BC","LD (#h),BC", + "NEG","RETN","IM 0","LD I,A", + "IN C,(C)","OUT (C),C","ADC HL,BC","LD BC,(#h)", + "DB EDh,4Ch","RETI","DB EDh,4Eh","LD R,A", + "IN D,(C)","OUT (C),D","SBC HL,DE","LD (#h),DE", + "DB EDh,54h","DB EDh,55h","IM 1","LD A,I", + "IN E,(C)","OUT (C),E","ADC HL,DE","LD DE,(#h)", + "DB EDh,5Ch","DB EDh,5Dh","IM 2","LD A,R", + "IN H,(C)","OUT (C),H","SBC HL,HL","LD (#h),HL", + "DB EDh,64h","DB EDh,65h","DB EDh,66h","RRD", + "IN L,(C)","OUT (C),L","ADC HL,HL","LD HL,(#h)", + "DB EDh,6Ch","DB EDh,6Dh","DB EDh,6Eh","RLD", + "IN F,(C)","DB EDh,71h","SBC HL,SP","LD (#h),SP", + "DB EDh,74h","DB EDh,75h","DB EDh,76h","DB EDh,77h", + "IN A,(C)","OUT (C),A","ADC HL,SP","LD SP,(#h)", + "DB EDh,7Ch","DB EDh,7Dh","DB EDh,7Eh","DB EDh,7Fh", + "DB EDh,80h","DB EDh,81h","DB EDh,82h","DB EDh,83h", + "DB EDh,84h","DB EDh,85h","DB EDh,86h","DB EDh,87h", + "DB EDh,88h","DB EDh,89h","DB EDh,8Ah","DB EDh,8Bh", + "DB EDh,8Ch","DB EDh,8Dh","DB EDh,8Eh","DB EDh,8Fh", + "DB EDh,90h","DB EDh,91h","DB EDh,92h","DB EDh,93h", + "DB EDh,94h","DB EDh,95h","DB EDh,96h","DB EDh,97h", + "DB EDh,98h","DB EDh,99h","DB EDh,9Ah","DB EDh,9Bh", + "DB EDh,9Ch","DB EDh,9Dh","DB EDh,9Eh","DB EDh,9Fh", + "LDI","CPI","INI","OUTI", + "DB EDh,A4h","DB EDh,A5h","DB EDh,A6h","DB EDh,A7h", + "LDD","CPD","IND","OUTD", + "DB EDh,ACh","DB EDh,ADh","DB EDh,AEh","DB EDh,AFh", + "LDIR","CPIR","INIR","OTIR", + "DB EDh,B4h","DB EDh,B5h","DB EDh,B6h","DB EDh,B7h", + "LDDR","CPDR","INDR","OTDR", + "DB EDh,BCh","DB EDh,BDh","DB EDh,BEh","DB EDh,BFh", + "DB EDh,C0h","DB EDh,C1h","DB EDh,C2h","DB EDh,C3h", + "DB EDh,C4h","DB EDh,C5h","DB EDh,C6h","DB EDh,C7h", + "DB EDh,C8h","DB EDh,C9h","DB EDh,CAh","DB EDh,CBh", + "DB EDh,CCh","DB EDh,CDh","DB EDh,CEh","DB EDh,CFh", + "DB EDh,D0h","DB EDh,D1h","DB EDh,D2h","DB EDh,D3h", + "DB EDh,D4h","DB EDh,D5h","DB EDh,D6h","DB EDh,D7h", + "DB EDh,D8h","DB EDh,D9h","DB EDh,DAh","DB EDh,DBh", + "DB EDh,DCh","DB EDh,DDh","DB EDh,DEh","DB EDh,DFh", + "DB EDh,E0h","DB EDh,E1h","DB EDh,E2h","DB EDh,E3h", + "DB EDh,E4h","DB EDh,E5h","DB EDh,E6h","DB EDh,E7h", + "DB EDh,E8h","DB EDh,E9h","DB EDh,EAh","DB EDh,EBh", + "DB EDh,ECh","DB EDh,EDh","DB EDh,EEh","DB EDh,EFh", + "DB EDh,F0h","DB EDh,F1h","DB EDh,F2h","DB EDh,F3h", + "DB EDh,F4h","DB EDh,F5h","DB EDh,F6h","DB EDh,F7h", + "DB EDh,F8h","DB EDh,F9h","DB EDh,FAh","DB EDh,FBh", + "DB EDh,FCh","DB EDh,FDh","DB EDh,FEh","DB EDh,FFh" +}; + +static const char *MnemonicsXX[256] = +{ + "NOP","LD BC,#h","LD (BC),A","INC BC","INC B","DEC B","LD B,*h","RLCA", + "EX AF,AF'","ADD I%,BC","LD A,(BC)","DEC BC","INC C","DEC C","LD C,*h","RRCA", + "DJNZ @h","LD DE,#h","LD (DE),A","INC DE","INC D","DEC D","LD D,*h","RLA", + "JR @h","ADD I%,DE","LD A,(DE)","DEC DE","INC E","DEC E","LD E,*h","RRA", + "JR NZ,@h","LD I%,#h","LD (#h),I%","INC I%","INC I%h","DEC I%h","LD I%h,*h","DAA", + "JR Z,@h","ADD I%,I%","LD I%,(#h)","DEC I%","INC I%l","DEC I%l","LD I%l,*h","CPL", + "JR NC,@h","LD SP,#h","LD (#h),A","INC SP","INC (I%+^h)","DEC (I%+^h)","LD (I%+^h),*h","SCF", + "JR C,@h","ADD I%,SP","LD A,(#h)","DEC SP","INC A","DEC A","LD A,*h","CCF", + "LD B,B","LD B,C","LD B,D","LD B,E","LD B,I%h","LD B,I%l","LD B,(I%+^h)","LD B,A", + "LD C,B","LD C,C","LD C,D","LD C,E","LD C,I%h","LD C,I%l","LD C,(I%+^h)","LD C,A", + "LD D,B","LD D,C","LD D,D","LD D,E","LD D,I%h","LD D,I%l","LD D,(I%+^h)","LD D,A", + "LD E,B","LD E,C","LD E,D","LD E,E","LD E,I%h","LD E,I%l","LD E,(I%+^h)","LD E,A", + "LD I%h,B","LD I%h,C","LD I%h,D","LD I%h,E","LD I%h,I%h","LD I%h,I%l","LD H,(I%+^h)","LD I%h,A", + "LD I%l,B","LD I%l,C","LD I%l,D","LD I%l,E","LD I%l,I%h","LD I%l,I%l","LD L,(I%+^h)","LD I%l,A", + "LD (I%+^h),B","LD (I%+^h),C","LD (I%+^h),D","LD (I%+^h),E","LD (I%+^h),H","LD (I%+^h),L","HALT","LD (I%+^h),A", + "LD A,B","LD A,C","LD A,D","LD A,E","LD A,I%h","LD A,I%l","LD A,(I%+^h)","LD A,A", + "ADD B","ADD C","ADD D","ADD E","ADD I%h","ADD I%l","ADD (I%+^h)","ADD A", + "ADC B","ADC C","ADC D","ADC E","ADC I%h","ADC I%l","ADC (I%+^h)","ADC,A", + "SUB B","SUB C","SUB D","SUB E","SUB I%h","SUB I%l","SUB (I%+^h)","SUB A", + "SBC B","SBC C","SBC D","SBC E","SBC I%h","SBC I%l","SBC (I%+^h)","SBC A", + "AND B","AND C","AND D","AND E","AND I%h","AND I%l","AND (I%+^h)","AND A", + "XOR B","XOR C","XOR D","XOR E","XOR I%h","XOR I%l","XOR (I%+^h)","XOR A", + "OR B","OR C","OR D","OR E","OR I%h","OR I%l","OR (I%+^h)","OR A", + "CP B","CP C","CP D","CP E","CP I%h","CP I%l","CP (I%+^h)","CP A", + "RET NZ","POP BC","JP NZ,#h","JP #h","CALL NZ,#h","PUSH BC","ADD *h","RST 00h", + "RET Z","RET","JP Z,#h","PFX_CB","CALL Z,#h","CALL #h","ADC *h","RST 08h", + "RET NC","POP DE","JP NC,#h","OUTA (*h)","CALL NC,#h","PUSH DE","SUB *h","RST 10h", + "RET C","EXX","JP C,#h","INA (*h)","CALL C,#h","PFX_DD","SBC *h","RST 18h", + "RET PO","POP I%","JP PO,#h","EX I%,(SP)","CALL PO,#h","PUSH I%","AND *h","RST 20h", + "RET PE","LD PC,I%","JP PE,#h","EX DE,I%","CALL PE,#h","PFX_ED","XOR *h","RST 28h", + "RET P","POP AF","JP P,#h","DI","CALL P,#h","PUSH AF","OR *h","RST 30h", + "RET M","LD SP,I%","JP M,#h","EI","CALL M,#h","PFX_FD","CP *h","RST 38h" +}; + +static const char *MnemonicsXCB[256] = +{ + "RLC B","RLC C","RLC D","RLC E","RLC H","RLC L","RLC (I%@h)","RLC A", + "RRC B","RRC C","RRC D","RRC E","RRC H","RRC L","RRC (I%@h)","RRC A", + "RL B","RL C","RL D","RL E","RL H","RL L","RL (I%@h)","RL A", + "RR B","RR C","RR D","RR E","RR H","RR L","RR (I%@h)","RR A", + "SLA B","SLA C","SLA D","SLA E","SLA H","SLA L","SLA (I%@h)","SLA A", + "SRA B","SRA C","SRA D","SRA E","SRA H","SRA L","SRA (I%@h)","SRA A", + "SLL B","SLL C","SLL D","SLL E","SLL H","SLL L","SLL (I%@h)","SLL A", + "SRL B","SRL C","SRL D","SRL E","SRL H","SRL L","SRL (I%@h)","SRL A", + "BIT 0,B","BIT 0,C","BIT 0,D","BIT 0,E","BIT 0,H","BIT 0,L","BIT 0,(I%@h)","BIT 0,A", + "BIT 1,B","BIT 1,C","BIT 1,D","BIT 1,E","BIT 1,H","BIT 1,L","BIT 1,(I%@h)","BIT 1,A", + "BIT 2,B","BIT 2,C","BIT 2,D","BIT 2,E","BIT 2,H","BIT 2,L","BIT 2,(I%@h)","BIT 2,A", + "BIT 3,B","BIT 3,C","BIT 3,D","BIT 3,E","BIT 3,H","BIT 3,L","BIT 3,(I%@h)","BIT 3,A", + "BIT 4,B","BIT 4,C","BIT 4,D","BIT 4,E","BIT 4,H","BIT 4,L","BIT 4,(I%@h)","BIT 4,A", + "BIT 5,B","BIT 5,C","BIT 5,D","BIT 5,E","BIT 5,H","BIT 5,L","BIT 5,(I%@h)","BIT 5,A", + "BIT 6,B","BIT 6,C","BIT 6,D","BIT 6,E","BIT 6,H","BIT 6,L","BIT 6,(I%@h)","BIT 6,A", + "BIT 7,B","BIT 7,C","BIT 7,D","BIT 7,E","BIT 7,H","BIT 7,L","BIT 7,(I%@h)","BIT 7,A", + "RES 0,B","RES 0,C","RES 0,D","RES 0,E","RES 0,H","RES 0,L","RES 0,(I%@h)","RES 0,A", + "RES 1,B","RES 1,C","RES 1,D","RES 1,E","RES 1,H","RES 1,L","RES 1,(I%@h)","RES 1,A", + "RES 2,B","RES 2,C","RES 2,D","RES 2,E","RES 2,H","RES 2,L","RES 2,(I%@h)","RES 2,A", + "RES 3,B","RES 3,C","RES 3,D","RES 3,E","RES 3,H","RES 3,L","RES 3,(I%@h)","RES 3,A", + "RES 4,B","RES 4,C","RES 4,D","RES 4,E","RES 4,H","RES 4,L","RES 4,(I%@h)","RES 4,A", + "RES 5,B","RES 5,C","RES 5,D","RES 5,E","RES 5,H","RES 5,L","RES 5,(I%@h)","RES 5,A", + "RES 6,B","RES 6,C","RES 6,D","RES 6,E","RES 6,H","RES 6,L","RES 6,(I%@h)","RES 6,A", + "RES 7,B","RES 7,C","RES 7,D","RES 7,E","RES 7,H","RES 7,L","RES 7,(I%@h)","RES 7,A", + "SET 0,B","SET 0,C","SET 0,D","SET 0,E","SET 0,H","SET 0,L","SET 0,(I%@h)","SET 0,A", + "SET 1,B","SET 1,C","SET 1,D","SET 1,E","SET 1,H","SET 1,L","SET 1,(I%@h)","SET 1,A", + "SET 2,B","SET 2,C","SET 2,D","SET 2,E","SET 2,H","SET 2,L","SET 2,(I%@h)","SET 2,A", + "SET 3,B","SET 3,C","SET 3,D","SET 3,E","SET 3,H","SET 3,L","SET 3,(I%@h)","SET 3,A", + "SET 4,B","SET 4,C","SET 4,D","SET 4,E","SET 4,H","SET 4,L","SET 4,(I%@h)","SET 4,A", + "SET 5,B","SET 5,C","SET 5,D","SET 5,E","SET 5,H","SET 5,L","SET 5,(I%@h)","SET 5,A", + "SET 6,B","SET 6,C","SET 6,D","SET 6,E","SET 6,H","SET 6,L","SET 6,(I%@h)","SET 6,A", + "SET 7,B","SET 7,C","SET 7,D","SET 7,E","SET 7,H","SET 7,L","SET 7,(I%@h)","SET 7,A" +}; + +/** DAsm() ***************************************************/ +/** DAsm() will disassemble the code at adress A and put **/ +/** the output text into S. It will return the number of **/ +/** bytes disassembled. **/ +/*************************************************************/ +static int DAsm(char *S,word A) +{ + char R[128],H[10],C,*P; + const char *T; + byte J,Offset; + word B; + + Offset=0; + B=A; + C='\0'; + J=0; + + switch(RdZ80(B)) + { + case 0xCB: B++;T=MnemonicsCB[RdZ80(B++)];break; + case 0xED: B++;T=MnemonicsED[RdZ80(B++)];break; + case 0xDD: B++;C='X'; + if(RdZ80(B)!=0xCB) T=MnemonicsXX[RdZ80(B++)]; + else + { B++;Offset=RdZ80(B++);J=1;T=MnemonicsXCB[RdZ80(B++)]; } + break; + case 0xFD: B++;C='Y'; + if(RdZ80(B)!=0xCB) T=MnemonicsXX[RdZ80(B++)]; + else + { B++;Offset=RdZ80(B++);J=1;T=MnemonicsXCB[RdZ80(B++)]; } + break; + default: T=Mnemonics[RdZ80(B++)]; + } + + if((P=strchr(T,'^'))) + { + strncpy(R,T,P-T);R[P-T]='\0'; + sprintf(H,"%02X",RdZ80(B++)); + strcat(R,H);strcat(R,P+1); + } + else strcpy(R,T); + if((P=strchr(R,'%'))) *P=C; + + if((P=strchr(R,'*'))) + { + strncpy(S,R,P-R);S[P-R]='\0'; + sprintf(H,"%02X",RdZ80(B++)); + strcat(S,H);strcat(S,P+1); + } + else if((P=strchr(R,'@'))) + { + strncpy(S,R,P-R);S[P-R]='\0'; + if(!J) Offset=RdZ80(B++); + strcat(S,Offset&0x80? "-":"+"); + J=Offset&0x80? 256-Offset:Offset; + sprintf(H,"%02X",J); + strcat(S,H);strcat(S,P+1); + } + else if((P=strchr(R,'#'))) + { + strncpy(S,R,P-R);S[P-R]='\0'; + sprintf(H,"%04X",RdZ80(B)+256*RdZ80(B+1)); + strcat(S,H);strcat(S,P+1); + B+=2; + } + else strcpy(S,R); + + return(B-A); +} + +/** DebugZ80() ***********************************************/ +/** This function should exist if DEBUG is #defined. When **/ +/** Trace!=0, it is called after each command executed by **/ +/** the CPU, and given the Z80 registers. **/ +/*************************************************************/ +byte DebugZ80(Z80 *R) +{ + static const char Flags[9] = "SZ.H.PNC"; + char S[128],T[10]; + byte J,I; + + DAsm(S,R->PC.W); + for(J=0,I=R->AF.B.l;J<8;J++,I<<=1) T[J]=I&0x80? Flags[J]:'.'; + T[8]='\0'; + + printf + ( + "AF:%04X HL:%04X DE:%04X BC:%04X PC:%04X SP:%04X IX:%04X IY:%04X I:%02X\n", + R->AF.W,R->HL.W,R->DE.W,R->BC.W,R->PC.W,R->SP.W,R->IX.W,R->IY.W,R->I + ); + printf + ( + "AT PC: [%02X - %s] AT SP: [%04X] FLAGS: [%s] %s: %s\n\n", + RdZ80(R->PC.W),S,RdZ80(R->SP.W)+RdZ80(R->SP.W+1)*256,T, + R->IFF&0x04? "IM2":R->IFF&0x02? "IM1":"IM0", + R->IFF&0x01? "EI":"DI" + ); + + while(1) + { + printf("\n[Command,'?']-> "); + fflush(stdout);fflush(stdin); + + if(!fgets(S,50,stdin)) return(1); + + for(J=0;S[J]>=' ';J++) + S[J]=toupper(S[J]); + S[J]='\0'; + + switch(S[0]) + { + case 'H': + case '?': + puts("\n***** Built-in Z80 Debugger Commands *****"); + puts(" : Break at next instruction"); + puts("= : Break at addr"); + puts("+ : Break at PC + offset"); + puts("c : Continue without break"); + puts("j : Continue from addr"); + puts("m : Memory dump at addr"); + puts("d : Disassembly at addr"); + puts("?,h : Show this help text"); + puts("q : Exit Z80 emulation"); + break; + + case '\0': return(1); + case '=': if(strlen(S)>=2) + { sscanf(S+1,"%hX",&(R->Trap));R->Trace=0;return(1); } + break; + case '+': if(strlen(S)>=2) + { + sscanf(S+1,"%hX",&(R->Trap)); + R->Trap+=R->PC.W;R->Trace=0; + return(1); + } + break; + case 'J': if(strlen(S)>=2) + { sscanf(S+1,"%hX",&(R->PC.W));R->Trace=0;return(1); } + break; + case 'C': R->Trap=0xFFFF;R->Trace=0;return(1); + case 'Q': return(0); + + case 'M': + { + word Addr; + + if(strlen(S)>1) sscanf(S+1,"%hX",&Addr); else Addr=R->PC.W; + puts(""); + for(J=0;J<16;J++) + { + printf("%04X: ",Addr); + for(I=0;I<16;I++,Addr++) + printf("%02X ",RdZ80(Addr)); + printf(" | ");Addr-=16; + for(I=0;I<16;I++,Addr++) + putchar(isprint(RdZ80(Addr))? RdZ80(Addr):'.'); + puts(""); + } + } + break; + + case 'D': + { + word Addr; + + if(strlen(S)>1) sscanf(S+1,"%hX",&Addr); else Addr=R->PC.W; + puts(""); + for(J=0;J<16;J++) + { + printf("%04X: ",Addr); + Addr+=DAsm(S,Addr); + puts(S); + } + } + break; + +#ifdef FMSX + case 'S': + for(J=0;J + +/** INLINE ***************************************************/ +/** C99 standard has "inline", but older compilers used **/ +/** __inline for the same purpose. **/ +/*************************************************************/ +#ifdef __C99__ +#define INLINE static inline +#else +#define INLINE static __inline +#endif + +/** System-Dependent Stuff ***********************************/ +/** This is system-dependent code put here to speed things **/ +/** up. It has to stay inlined to be fast. **/ +/*************************************************************/ +#ifdef COLEM +#define FAST_RDOP +extern byte *ROMPage[]; +INLINE byte msx_OpZ80(word A) { return(ROMPage[A>>13][A&0x1FFF]); } +#endif + +#ifdef SPECCY +#define msx_RdZ80 RDZ80 +// @@@ WrZ80() can't be inlined as it contains debugging stuff +//#define WrZ80 WRZ80 +extern byte *Page[],*ROM; +INLINE byte msx_RdZ80(word A) { return(Page[A>>13][A&0x1FFF]); } +//INLINE void WrZ80(word A,byte V) { if(Page[A>>13]>13][A&0x1FFF]=V; } +#endif + +#ifdef MG +#define msx_RdZ80 RDZ80 +extern byte *Page[]; +INLINE byte msx_RdZ80(word A) { return(Page[A>>13][A&0x1FFF]); } +#endif + +#ifdef FMSX +#define FAST_RDOP +extern byte *RAM[]; +INLINE byte msx_OpZ80(word A) { return(RAM[A>>13][A&0x1FFF]); } +#endif + +#ifdef ATI85 +#define msx_RdZ80 RDZ80 +#define msx_WrZ80 WRZ80 +extern byte *Page[],*ROM; +extern void (*DoMEM)(register word Addr,register byte V); +INLINE byte msx_RdZ80(word A) { return(Page[A>>14][A&0x3FFF]); } +INLINE void msx_WrZ80(word A,byte V) { if(Page[A>>14]>14][A&0x3FFF]=V; else DoMEM(A,V); } +#endif + +/** FAST_RDOP ************************************************/ +/** With this #define not present, RdZ80() should perform **/ +/** the functions of OpZ80(). **/ +/*************************************************************/ +#ifndef FAST_RDOP +#define OpZ80(A) msx_RdZ80(A) +#endif + +#define S(Fl) R->AF.B.l|=Fl +#define R(Fl) R->AF.B.l&=~(Fl) +#define FLAGS(Rg,Fl) R->AF.B.l=Fl|ZSTable[Rg] +#define INCR(N) R->R=((R->R+(N))&0x7F)|(R->R&0x80) + +#define M_RLC(Rg) \ + R->AF.B.l=Rg>>7;Rg=(Rg<<1)|R->AF.B.l;R->AF.B.l|=PZSTable[Rg] +#define M_RRC(Rg) \ + R->AF.B.l=Rg&0x01;Rg=(Rg>>1)|(R->AF.B.l<<7);R->AF.B.l|=PZSTable[Rg] +#define M_RL(Rg) \ + if(Rg&0x80) \ + { \ + Rg=(Rg<<1)|(R->AF.B.l&C_FLAG); \ + R->AF.B.l=PZSTable[Rg]|C_FLAG; \ + } \ + else \ + { \ + Rg=(Rg<<1)|(R->AF.B.l&C_FLAG); \ + R->AF.B.l=PZSTable[Rg]; \ + } +#define M_RR(Rg) \ + if(Rg&0x01) \ + { \ + Rg=(Rg>>1)|(R->AF.B.l<<7); \ + R->AF.B.l=PZSTable[Rg]|C_FLAG; \ + } \ + else \ + { \ + Rg=(Rg>>1)|(R->AF.B.l<<7); \ + R->AF.B.l=PZSTable[Rg]; \ + } + +#define M_SLA(Rg) \ + R->AF.B.l=Rg>>7;Rg<<=1;R->AF.B.l|=PZSTable[Rg] +#define M_SRA(Rg) \ + R->AF.B.l=Rg&C_FLAG;Rg=(Rg>>1)|(Rg&0x80);R->AF.B.l|=PZSTable[Rg] + +#define M_SLL(Rg) \ + R->AF.B.l=Rg>>7;Rg=(Rg<<1)|0x01;R->AF.B.l|=PZSTable[Rg] +#define M_SRL(Rg) \ + R->AF.B.l=Rg&0x01;Rg>>=1;R->AF.B.l|=PZSTable[Rg] + +#define M_BIT(Bit,Rg) \ + R->AF.B.l=(R->AF.B.l&C_FLAG)|H_FLAG|PZSTable[Rg&(1<Rg.B.l=OpZ80(R->SP.W++);R->Rg.B.h=OpZ80(R->SP.W++) +#define M_PUSH(Rg) \ + msx_WrZ80(--R->SP.W,R->Rg.B.h);msx_WrZ80(--R->SP.W,R->Rg.B.l) + +#define M_CALL \ + J.B.l=OpZ80(R->PC.W++);J.B.h=OpZ80(R->PC.W++); \ + msx_WrZ80(--R->SP.W,R->PC.B.h);msx_WrZ80(--R->SP.W,R->PC.B.l); \ + R->PC.W=J.W; \ + JumpZ80(J.W) + +#define M_JP J.B.l=OpZ80(R->PC.W++);J.B.h=OpZ80(R->PC.W);R->PC.W=J.W;JumpZ80(J.W) +#define M_JR R->PC.W+=(offset)OpZ80(R->PC.W)+1;JumpZ80(R->PC.W) +#define M_RET R->PC.B.l=OpZ80(R->SP.W++);R->PC.B.h=OpZ80(R->SP.W++);JumpZ80(R->PC.W) + +#define M_RST(Ad) \ + msx_WrZ80(--R->SP.W,R->PC.B.h);msx_WrZ80(--R->SP.W,R->PC.B.l);R->PC.W=Ad;JumpZ80(Ad) + +#define M_LDWORD(Rg) \ + R->Rg.B.l=OpZ80(R->PC.W++);R->Rg.B.h=OpZ80(R->PC.W++) + +#define M_ADD(Rg) \ + J.W=R->AF.B.h+Rg; \ + R->AF.B.l= \ + (~(R->AF.B.h^Rg)&(Rg^J.B.l)&0x80? V_FLAG:0)| \ + J.B.h|ZSTable[J.B.l]| \ + ((R->AF.B.h^Rg^J.B.l)&H_FLAG); \ + R->AF.B.h=J.B.l + +#define M_SUB(Rg) \ + J.W=R->AF.B.h-Rg; \ + R->AF.B.l= \ + ((R->AF.B.h^Rg)&(R->AF.B.h^J.B.l)&0x80? V_FLAG:0)| \ + N_FLAG|-J.B.h|ZSTable[J.B.l]| \ + ((R->AF.B.h^Rg^J.B.l)&H_FLAG); \ + R->AF.B.h=J.B.l + +#define M_ADC(Rg) \ + J.W=R->AF.B.h+Rg+(R->AF.B.l&C_FLAG); \ + R->AF.B.l= \ + (~(R->AF.B.h^Rg)&(Rg^J.B.l)&0x80? V_FLAG:0)| \ + J.B.h|ZSTable[J.B.l]| \ + ((R->AF.B.h^Rg^J.B.l)&H_FLAG); \ + R->AF.B.h=J.B.l + +#define M_SBC(Rg) \ + J.W=R->AF.B.h-Rg-(R->AF.B.l&C_FLAG); \ + R->AF.B.l= \ + ((R->AF.B.h^Rg)&(R->AF.B.h^J.B.l)&0x80? V_FLAG:0)| \ + N_FLAG|-J.B.h|ZSTable[J.B.l]| \ + ((R->AF.B.h^Rg^J.B.l)&H_FLAG); \ + R->AF.B.h=J.B.l + +#define M_CP(Rg) \ + J.W=R->AF.B.h-Rg; \ + R->AF.B.l= \ + ((R->AF.B.h^Rg)&(R->AF.B.h^J.B.l)&0x80? V_FLAG:0)| \ + N_FLAG|-J.B.h|ZSTable[J.B.l]| \ + ((R->AF.B.h^Rg^J.B.l)&H_FLAG) + +#define M_AND(Rg) R->AF.B.h&=Rg;R->AF.B.l=H_FLAG|PZSTable[R->AF.B.h] +#define M_OR(Rg) R->AF.B.h|=Rg;R->AF.B.l=PZSTable[R->AF.B.h] +#define M_XOR(Rg) R->AF.B.h^=Rg;R->AF.B.l=PZSTable[R->AF.B.h] + +#define M_IN(Rg) \ + Rg=msx_InZ80(R->BC.W); \ + R->AF.B.l=PZSTable[Rg]|(R->AF.B.l&C_FLAG) + +#define M_INC(Rg) \ + Rg++; \ + R->AF.B.l= \ + (R->AF.B.l&C_FLAG)|ZSTable[Rg]| \ + (Rg==0x80? V_FLAG:0)|(Rg&0x0F? 0:H_FLAG) + +#define M_DEC(Rg) \ + Rg--; \ + R->AF.B.l= \ + N_FLAG|(R->AF.B.l&C_FLAG)|ZSTable[Rg]| \ + (Rg==0x7F? V_FLAG:0)|((Rg&0x0F)==0x0F? H_FLAG:0) + +#define M_ADDW(Rg1,Rg2) \ + J.W=(R->Rg1.W+R->Rg2.W)&0xFFFF; \ + R->AF.B.l= \ + (R->AF.B.l&~(H_FLAG|N_FLAG|C_FLAG))| \ + ((R->Rg1.W^R->Rg2.W^J.W)&0x1000? H_FLAG:0)| \ + (((long)R->Rg1.W+(long)R->Rg2.W)&0x10000? C_FLAG:0); \ + R->Rg1.W=J.W + +#define M_ADCW(Rg) \ + I=R->AF.B.l&C_FLAG;J.W=(R->HL.W+R->Rg.W+I)&0xFFFF; \ + R->AF.B.l= \ + (((long)R->HL.W+(long)R->Rg.W+(long)I)&0x10000? C_FLAG:0)| \ + (~(R->HL.W^R->Rg.W)&(R->Rg.W^J.W)&0x8000? V_FLAG:0)| \ + ((R->HL.W^R->Rg.W^J.W)&0x1000? H_FLAG:0)| \ + (J.W? 0:Z_FLAG)|(J.B.h&S_FLAG); \ + R->HL.W=J.W + +#define M_SBCW(Rg) \ + I=R->AF.B.l&C_FLAG;J.W=(R->HL.W-R->Rg.W-I)&0xFFFF; \ + R->AF.B.l= \ + N_FLAG| \ + (((long)R->HL.W-(long)R->Rg.W-(long)I)&0x10000? C_FLAG:0)| \ + ((R->HL.W^R->Rg.W)&(R->HL.W^J.W)&0x8000? V_FLAG:0)| \ + ((R->HL.W^R->Rg.W^J.W)&0x1000? H_FLAG:0)| \ + (J.W? 0:Z_FLAG)|(J.B.h&S_FLAG); \ + R->HL.W=J.W + +enum Codes +{ + NOP,LD_BC_WORD,LD_xBC_A,INC_BC,INC_B,DEC_B,LD_B_BYTE,RLCA, + EX_AF_AF,ADD_HL_BC,LD_A_xBC,DEC_BC,INC_C,DEC_C,LD_C_BYTE,RRCA, + DJNZ,LD_DE_WORD,LD_xDE_A,INC_DE,INC_D,DEC_D,LD_D_BYTE,RLA, + JR,ADD_HL_DE,LD_A_xDE,DEC_DE,INC_E,DEC_E,LD_E_BYTE,RRA, + JR_NZ,LD_HL_WORD,LD_xWORD_HL,INC_HL,INC_H,DEC_H,LD_H_BYTE,DAA, + JR_Z,ADD_HL_HL,LD_HL_xWORD,DEC_HL,INC_L,DEC_L,LD_L_BYTE,CPL, + JR_NC,LD_SP_WORD,LD_xWORD_A,INC_SP,INC_xHL,DEC_xHL,LD_xHL_BYTE,SCF, + JR_C,ADD_HL_SP,LD_A_xWORD,DEC_SP,INC_A,DEC_A,LD_A_BYTE,CCF, + LD_B_B,LD_B_C,LD_B_D,LD_B_E,LD_B_H,LD_B_L,LD_B_xHL,LD_B_A, + LD_C_B,LD_C_C,LD_C_D,LD_C_E,LD_C_H,LD_C_L,LD_C_xHL,LD_C_A, + LD_D_B,LD_D_C,LD_D_D,LD_D_E,LD_D_H,LD_D_L,LD_D_xHL,LD_D_A, + LD_E_B,LD_E_C,LD_E_D,LD_E_E,LD_E_H,LD_E_L,LD_E_xHL,LD_E_A, + LD_H_B,LD_H_C,LD_H_D,LD_H_E,LD_H_H,LD_H_L,LD_H_xHL,LD_H_A, + LD_L_B,LD_L_C,LD_L_D,LD_L_E,LD_L_H,LD_L_L,LD_L_xHL,LD_L_A, + LD_xHL_B,LD_xHL_C,LD_xHL_D,LD_xHL_E,LD_xHL_H,LD_xHL_L,HALT,LD_xHL_A, + LD_A_B,LD_A_C,LD_A_D,LD_A_E,LD_A_H,LD_A_L,LD_A_xHL,LD_A_A, + ADD_B,ADD_C,ADD_D,ADD_E,ADD_H,ADD_L,ADD_xHL,ADD_A, + ADC_B,ADC_C,ADC_D,ADC_E,ADC_H,ADC_L,ADC_xHL,ADC_A, + SUB_B,SUB_C,SUB_D,SUB_E,SUB_H,SUB_L,SUB_xHL,SUB_A, + SBC_B,SBC_C,SBC_D,SBC_E,SBC_H,SBC_L,SBC_xHL,SBC_A, + AND_B,AND_C,AND_D,AND_E,AND_H,AND_L,AND_xHL,AND_A, + XOR_B,XOR_C,XOR_D,XOR_E,XOR_H,XOR_L,XOR_xHL,XOR_A, + OR_B,OR_C,OR_D,OR_E,OR_H,OR_L,OR_xHL,OR_A, + CP_B,CP_C,CP_D,CP_E,CP_H,CP_L,CP_xHL,CP_A, + RET_NZ,POP_BC,JP_NZ,JP,CALL_NZ,PUSH_BC,ADD_BYTE,RST00, + RET_Z,RET,JP_Z,PFX_CB,CALL_Z,CALL,ADC_BYTE,RST08, + RET_NC,POP_DE,JP_NC,OUTA,CALL_NC,PUSH_DE,SUB_BYTE,RST10, + RET_C,EXX,JP_C,INA,CALL_C,PFX_DD,SBC_BYTE,RST18, + RET_PO,POP_HL,JP_PO,EX_HL_xSP,CALL_PO,PUSH_HL,AND_BYTE,RST20, + RET_PE,LD_PC_HL,JP_PE,EX_DE_HL,CALL_PE,PFX_ED,XOR_BYTE,RST28, + RET_P,POP_AF,JP_P,DI,CALL_P,PUSH_AF,OR_BYTE,RST30, + RET_M,LD_SP_HL,JP_M,EI,CALL_M,PFX_FD,CP_BYTE,RST38 +}; + +enum CodesCB +{ + RLC_B,RLC_C,RLC_D,RLC_E,RLC_H,RLC_L,RLC_xHL,RLC_A, + RRC_B,RRC_C,RRC_D,RRC_E,RRC_H,RRC_L,RRC_xHL,RRC_A, + RL_B,RL_C,RL_D,RL_E,RL_H,RL_L,RL_xHL,RL_A, + RR_B,RR_C,RR_D,RR_E,RR_H,RR_L,RR_xHL,RR_A, + SLA_B,SLA_C,SLA_D,SLA_E,SLA_H,SLA_L,SLA_xHL,SLA_A, + SRA_B,SRA_C,SRA_D,SRA_E,SRA_H,SRA_L,SRA_xHL,SRA_A, + SLL_B,SLL_C,SLL_D,SLL_E,SLL_H,SLL_L,SLL_xHL,SLL_A, + SRL_B,SRL_C,SRL_D,SRL_E,SRL_H,SRL_L,SRL_xHL,SRL_A, + BIT0_B,BIT0_C,BIT0_D,BIT0_E,BIT0_H,BIT0_L,BIT0_xHL,BIT0_A, + BIT1_B,BIT1_C,BIT1_D,BIT1_E,BIT1_H,BIT1_L,BIT1_xHL,BIT1_A, + BIT2_B,BIT2_C,BIT2_D,BIT2_E,BIT2_H,BIT2_L,BIT2_xHL,BIT2_A, + BIT3_B,BIT3_C,BIT3_D,BIT3_E,BIT3_H,BIT3_L,BIT3_xHL,BIT3_A, + BIT4_B,BIT4_C,BIT4_D,BIT4_E,BIT4_H,BIT4_L,BIT4_xHL,BIT4_A, + BIT5_B,BIT5_C,BIT5_D,BIT5_E,BIT5_H,BIT5_L,BIT5_xHL,BIT5_A, + BIT6_B,BIT6_C,BIT6_D,BIT6_E,BIT6_H,BIT6_L,BIT6_xHL,BIT6_A, + BIT7_B,BIT7_C,BIT7_D,BIT7_E,BIT7_H,BIT7_L,BIT7_xHL,BIT7_A, + RES0_B,RES0_C,RES0_D,RES0_E,RES0_H,RES0_L,RES0_xHL,RES0_A, + RES1_B,RES1_C,RES1_D,RES1_E,RES1_H,RES1_L,RES1_xHL,RES1_A, + RES2_B,RES2_C,RES2_D,RES2_E,RES2_H,RES2_L,RES2_xHL,RES2_A, + RES3_B,RES3_C,RES3_D,RES3_E,RES3_H,RES3_L,RES3_xHL,RES3_A, + RES4_B,RES4_C,RES4_D,RES4_E,RES4_H,RES4_L,RES4_xHL,RES4_A, + RES5_B,RES5_C,RES5_D,RES5_E,RES5_H,RES5_L,RES5_xHL,RES5_A, + RES6_B,RES6_C,RES6_D,RES6_E,RES6_H,RES6_L,RES6_xHL,RES6_A, + RES7_B,RES7_C,RES7_D,RES7_E,RES7_H,RES7_L,RES7_xHL,RES7_A, + SET0_B,SET0_C,SET0_D,SET0_E,SET0_H,SET0_L,SET0_xHL,SET0_A, + SET1_B,SET1_C,SET1_D,SET1_E,SET1_H,SET1_L,SET1_xHL,SET1_A, + SET2_B,SET2_C,SET2_D,SET2_E,SET2_H,SET2_L,SET2_xHL,SET2_A, + SET3_B,SET3_C,SET3_D,SET3_E,SET3_H,SET3_L,SET3_xHL,SET3_A, + SET4_B,SET4_C,SET4_D,SET4_E,SET4_H,SET4_L,SET4_xHL,SET4_A, + SET5_B,SET5_C,SET5_D,SET5_E,SET5_H,SET5_L,SET5_xHL,SET5_A, + SET6_B,SET6_C,SET6_D,SET6_E,SET6_H,SET6_L,SET6_xHL,SET6_A, + SET7_B,SET7_C,SET7_D,SET7_E,SET7_H,SET7_L,SET7_xHL,SET7_A +}; + +enum CodesED +{ + DB_00,DB_01,DB_02,DB_03,DB_04,DB_05,DB_06,DB_07, + DB_08,DB_09,DB_0A,DB_0B,DB_0C,DB_0D,DB_0E,DB_0F, + DB_10,DB_11,DB_12,DB_13,DB_14,DB_15,DB_16,DB_17, + DB_18,DB_19,DB_1A,DB_1B,DB_1C,DB_1D,DB_1E,DB_1F, + DB_20,DB_21,DB_22,DB_23,DB_24,DB_25,DB_26,DB_27, + DB_28,DB_29,DB_2A,DB_2B,DB_2C,DB_2D,DB_2E,DB_2F, + DB_30,DB_31,DB_32,DB_33,DB_34,DB_35,DB_36,DB_37, + DB_38,DB_39,DB_3A,DB_3B,DB_3C,DB_3D,DB_3E,DB_3F, + IN_B_xC,OUT_xC_B,SBC_HL_BC,LD_xWORDe_BC,NEG,RETN,IM_0,LD_I_A, + IN_C_xC,OUT_xC_C,ADC_HL_BC,LD_BC_xWORDe,DB_4C,RETI,DB_,LD_R_A, + IN_D_xC,OUT_xC_D,SBC_HL_DE,LD_xWORDe_DE,DB_54,DB_55,IM_1,LD_A_I, + IN_E_xC,OUT_xC_E,ADC_HL_DE,LD_DE_xWORDe,DB_5C,DB_5D,IM_2,LD_A_R, + IN_H_xC,OUT_xC_H,SBC_HL_HL,LD_xWORDe_HL,DB_64,DB_65,DB_66,RRD, + IN_L_xC,OUT_xC_L,ADC_HL_HL,LD_HL_xWORDe,DB_6C,DB_6D,DB_6E,RLD, + IN_F_xC,OUT_xC_F,SBC_HL_SP,LD_xWORDe_SP,DB_74,DB_75,DB_76,DB_77, + IN_A_xC,OUT_xC_A,ADC_HL_SP,LD_SP_xWORDe,DB_7C,DB_7D,DB_7E,DB_7F, + DB_80,DB_81,DB_82,DB_83,DB_84,DB_85,DB_86,DB_87, + DB_88,DB_89,DB_8A,DB_8B,DB_8C,DB_8D,DB_8E,DB_8F, + DB_90,DB_91,DB_92,DB_93,DB_94,DB_95,DB_96,DB_97, + DB_98,DB_99,DB_9A,DB_9B,DB_9C,DB_9D,DB_9E,DB_9F, + LDI,CPI,INI,OUTI,DB_A4,DB_A5,DB_A6,DB_A7, + LDD,CPD,IND,OUTD,DB_AC,DB_AD,DB_AE,DB_AF, + LDIR,CPIR,INIR,OTIR,DB_B4,DB_B5,DB_B6,DB_B7, + LDDR,CPDR,INDR,OTDR,DB_BC,DB_BD,DB_BE,DB_BF, + DB_C0,DB_C1,DB_C2,DB_C3,DB_C4,DB_C5,DB_C6,DB_C7, + DB_C8,DB_C9,DB_CA,DB_CB,DB_CC,DB_CD,DB_CE,DB_CF, + DB_D0,DB_D1,DB_D2,DB_D3,DB_D4,DB_D5,DB_D6,DB_D7, + DB_D8,DB_D9,DB_DA,DB_DB,DB_DC,DB_DD,DB_DE,DB_DF, + DB_E0,DB_E1,DB_E2,DB_E3,DB_E4,DB_E5,DB_E6,DB_E7, + DB_E8,DB_E9,DB_EA,DB_EB,DB_EC,DB_ED,DB_EE,DB_EF, + DB_F0,DB_F1,DB_F2,DB_F3,DB_F4,DB_F5,DB_F6,DB_F7, + DB_F8,DB_F9,DB_FA,DB_FB,DB_FC,DB_FD,DB_FE,DB_FF +}; + +static void CodesCB(register Z80 *R) +{ + register byte I; + + /* Read opcode and count cycles */ + I=OpZ80(R->PC.W++); + R->ICount-=CyclesCB[I]; + + /* R register incremented on each M1 cycle */ + INCR(1); + + switch(I) + { +#include "CodesCB.h" + default: + if(R->TrapBadOps) + printf + ( + "[Z80 %lX] Unrecognized instruction: CB %02X at PC=%04X\n", + (long)(R->User),OpZ80(R->PC.W-1),R->PC.W-2 + ); + } +} + +static void CodesDDCB(register Z80 *R) +{ + register pair J; + register byte I; + +#define XX IX + /* Get offset, read opcode and count cycles */ + J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + I=OpZ80(R->PC.W++); + R->ICount-=CyclesXXCB[I]; + + switch(I) + { +#include "CodesXCB.h" + default: + if(R->TrapBadOps) + printf + ( + "[Z80 %lX] Unrecognized instruction: DD CB %02X %02X at PC=%04X\n", + (long)(R->User),OpZ80(R->PC.W-2),OpZ80(R->PC.W-1),R->PC.W-4 + ); + } +#undef XX +} + +static void CodesFDCB(register Z80 *R) +{ + register pair J; + register byte I; + +#define XX IY + /* Get offset, read opcode and count cycles */ + J.W=R->XX.W+(offset)OpZ80(R->PC.W++); + I=OpZ80(R->PC.W++); + R->ICount-=CyclesXXCB[I]; + + switch(I) + { +#include "CodesXCB.h" + default: + if(R->TrapBadOps) + printf + ( + "[Z80 %lX] Unrecognized instruction: FD CB %02X %02X at PC=%04X\n", + (long)R->User,OpZ80(R->PC.W-2),OpZ80(R->PC.W-1),R->PC.W-4 + ); + } +#undef XX +} + +static void CodesED(register Z80 *R) +{ + register byte I; + register pair J; + + /* Read opcode and count cycles */ + I=OpZ80(R->PC.W++); + R->ICount-=CyclesED[I]; + + /* R register incremented on each M1 cycle */ + INCR(1); + + switch(I) + { +#include "CodesED.h" + case PFX_ED: + R->PC.W--;break; + default: + if(R->TrapBadOps) + printf + ( + "[Z80 %lX] Unrecognized instruction: ED %02X at PC=%04X\n", + (long)R->User,OpZ80(R->PC.W-1),R->PC.W-2 + ); + } +} + +static void CodesDD(register Z80 *R) +{ + register byte I; + register pair J; + +#define XX IX + /* Read opcode and count cycles */ + I=OpZ80(R->PC.W++); + R->ICount-=CyclesXX[I]; + + /* R register incremented on each M1 cycle */ + INCR(1); + + switch(I) + { +#include "CodesXX.h" + case PFX_FD: + case PFX_DD: + R->PC.W--;break; + case PFX_CB: + CodesDDCB(R);break; + default: + if(R->TrapBadOps) + printf + ( + "[Z80 %lX] Unrecognized instruction: DD %02X at PC=%04X\n", + (long)R->User,OpZ80(R->PC.W-1),R->PC.W-2 + ); + } +#undef XX +} + +static void CodesFD(register Z80 *R) +{ + register byte I; + register pair J; + +#define XX IY + /* Read opcode and count cycles */ + I=OpZ80(R->PC.W++); + R->ICount-=CyclesXX[I]; + + /* R register incremented on each M1 cycle */ + INCR(1); + + switch(I) + { +#include "CodesXX.h" + case PFX_FD: + case PFX_DD: + R->PC.W--;break; + case PFX_CB: + CodesFDCB(R);break; + default: + printf + ( + "Unrecognized instruction: FD %02X at PC=%04X\n", + OpZ80(R->PC.W-1),R->PC.W-2 + ); + } +#undef XX +} + +/** ResetZ80() ***********************************************/ +/** This function can be used to reset the register struct **/ +/** before starting execution with Z80(). It sets the **/ +/** registers to their supposed initial values. **/ +/*************************************************************/ +void msx_ResetZ80(Z80 *R) +{ + R->PC.W = 0x0000; + R->SP.W = 0xF000; + R->AF.W = 0x0000; + R->BC.W = 0x0000; + R->DE.W = 0x0000; + R->HL.W = 0x0000; + R->AF1.W = 0x0000; + R->BC1.W = 0x0000; + R->DE1.W = 0x0000; + R->HL1.W = 0x0000; + R->IX.W = 0x0000; + R->IY.W = 0x0000; + R->I = 0x00; + R->R = 0x00; + R->IFF = 0x00; + R->ICount = R->IPeriod; + R->IRequest = INT_NONE; + R->IBackup = 0; + + JumpZ80(R->PC.W); +} + +/** ExecZ80() ************************************************/ +/** This function will execute given number of Z80 cycles. **/ +/** It will then return the number of cycles left, possibly **/ +/** negative, and current register values in R. **/ +/*************************************************************/ +#ifdef EXECZ80 +int ExecZ80(register Z80 *R,register int RunCycles) +{ + register byte I; + register pair J; + + for(R->ICount=RunCycles;;) + { + while(R->ICount>0) + { +#ifdef DEBUG + /* Turn tracing on when reached trap address */ + if(R->PC.W==R->Trap) R->Trace=1; + /* Call single-step debugger, exit if requested */ + if(R->Trace) + if(!DebugZ80(R)) return(R->ICount); +#endif + + /* Read opcode and count cycles */ + I=OpZ80(R->PC.W++); + R->ICount-=Cycles[I]; + + /* R register incremented on each M1 cycle */ + INCR(1); + + /* Interpret opcode */ + switch(I) + { +#include "Codes.h" + case PFX_CB: CodesCB(R);break; + case PFX_ED: CodesED(R);break; + case PFX_FD: CodesFD(R);break; + case PFX_DD: CodesDD(R);break; + } + } + + /* Unless we have come here after EI, exit */ + if(!(R->IFF&IFF_EI)) return(R->ICount); + else + { + /* Done with AfterEI state */ + R->IFF=(R->IFF&~IFF_EI)|IFF_1; + /* Restore the ICount */ + R->ICount+=R->IBackup-1; + /* Interrupt CPU if needed */ + if((R->IRequest!=INT_NONE)&&(R->IRequest!=INT_QUIT)) msx_IntZ80(R,R->IRequest); + } + } +} +#endif /* EXECZ80 */ + +/** IntZ80() *************************************************/ +/** This function will generate interrupt of given vector. **/ +/*************************************************************/ +void msx_IntZ80(Z80 *R,word Vector) +{ + /* If HALTed, take CPU off HALT instruction */ + if(R->IFF&IFF_HALT) { R->PC.W++;R->IFF&=~IFF_HALT; } + + if((R->IFF&IFF_1)||(Vector==INT_NMI)) + { + /* Save PC on stack */ + M_PUSH(PC); + + /* Automatically reset IRequest if needed */ + if(R->IAutoReset&&(Vector==R->IRequest)) R->IRequest=INT_NONE; + + /* If it is NMI... */ + if(Vector==INT_NMI) + { + /* Clear IFF1 */ + R->IFF&=~(IFF_1|IFF_EI); + /* Jump to hardwired NMI vector */ + R->PC.W=0x0066; + JumpZ80(0x0066); + /* Done */ + return; + } + + /* Further interrupts off */ + R->IFF&=~(IFF_1|IFF_2|IFF_EI); + + /* If in IM2 mode... */ + if(R->IFF&IFF_IM2) + { + /* Make up the vector address */ + Vector=(Vector&0xFF)|((word)(R->I)<<8); + /* Read the vector */ + R->PC.B.l=msx_RdZ80(Vector++); + R->PC.B.h=msx_RdZ80(Vector); + JumpZ80(R->PC.W); + /* Done */ + return; + } + + /* If in IM1 mode, just jump to hardwired IRQ vector */ + if(R->IFF&IFF_IM1) { R->PC.W=0x0038;JumpZ80(0x0038);return; } + + /* If in IM0 mode... */ + + /* Jump to a vector */ + switch(Vector) + { + case INT_RST00: R->PC.W=0x0000;JumpZ80(0x0000);break; + case INT_RST08: R->PC.W=0x0008;JumpZ80(0x0008);break; + case INT_RST10: R->PC.W=0x0010;JumpZ80(0x0010);break; + case INT_RST18: R->PC.W=0x0018;JumpZ80(0x0018);break; + case INT_RST20: R->PC.W=0x0020;JumpZ80(0x0020);break; + case INT_RST28: R->PC.W=0x0028;JumpZ80(0x0028);break; + case INT_RST30: R->PC.W=0x0030;JumpZ80(0x0030);break; + case INT_RST38: R->PC.W=0x0038;JumpZ80(0x0038);break; + } + } +} + +/** RunZ80() *************************************************/ +/** This function will run Z80 code until an LoopZ80() call **/ +/** returns INT_QUIT. It will return the PC at which **/ +/** emulation stopped, and current register values in R. **/ +/*************************************************************/ +#ifndef EXECZ80 +word RunZ80(Z80 *R) +{ + register byte I; + register pair J; + + /* for(;;) */ + /* { */ +#ifdef DEBUG + /* Turn tracing on when reached trap address */ + if(R->PC.W==R->Trap) R->Trace=1; + /* Call single-step debugger, exit if requested */ + if(R->Trace){ + if(!DebugZ80(R)) return(R->PC.W); +} +#endif + + /* Read opcode and count cycles */ + I=OpZ80(R->PC.W++); + R->ICount-=Cycles[I]; + + /* R register incremented on each M1 cycle */ + INCR(1); + + switch(I) + { +#include "Codes.h" + case PFX_CB: CodesCB(R);break; + case PFX_ED: CodesED(R);break; + case PFX_FD: CodesFD(R);break; + case PFX_DD: CodesDD(R);break; + } + + /* If cycle counter expired... */ + if(R->ICount<=0) + { + /* If we have come after EI, get address from IRequest */ + /* Otherwise, get it from the loop handler */ + if(R->IFF&IFF_EI) + { + R->IFF=(R->IFF&~IFF_EI)|IFF_1; /* Done with AfterEI state */ + R->ICount+=R->IBackup-1; /* Restore the ICount */ + + /* Call periodic handler or set pending IRQ */ + if(R->ICount>0) J.W=R->IRequest; + else + { + J.W=msx_LoopZ80(R); /* Call periodic handler */ + R->ICount+=R->IPeriod; /* Reset the cycle counter */ + if(J.W==INT_NONE) J.W=R->IRequest; /* Pending IRQ */ + } + } + else + { + J.W=msx_LoopZ80(R); /* Call periodic handler */ + R->ICount+=R->IPeriod; /* Reset the cycle counter */ + if(J.W==INT_NONE) J.W=R->IRequest; /* Pending IRQ */ + } + + if(J.W==INT_QUIT) return(R->PC.W); /* Exit if INT_QUIT */ + if(J.W!=INT_NONE) msx_IntZ80(R,J.W); /* Int-pt if needed */ + } + /* } */ + + /* /\* Execution stopped *\/ */ + /* return(R->PC.W); */ + return 0; +} +#endif /* !EXECZ80 */ diff --git a/components/msx/fmsx/src/Z80/Z80.h b/components/msx/fmsx/src/Z80/Z80.h new file mode 100644 index 0000000..91054e3 --- /dev/null +++ b/components/msx/fmsx/src/Z80/Z80.h @@ -0,0 +1,188 @@ +/** Z80: portable Z80 emulator *******************************/ +/** **/ +/** Z80.h **/ +/** **/ +/** This file contains declarations relevant to emulation **/ +/** of Z80 CPU. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef Z80_H +#define Z80_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* Compilation options: */ +/* #define DEBUG */ /* Compile debugging version */ +/* #define LSB_FIRST */ /* Compile for low-endian CPU */ +/* #define MSB_FIRST */ /* Compile for hi-endian CPU */ + + /* LoopZ80() may return: */ +#define INT_RST00 0x00C7 /* RST 00h */ +#define INT_RST08 0x00CF /* RST 08h */ +#define INT_RST10 0x00D7 /* RST 10h */ +#define INT_RST18 0x00DF /* RST 18h */ +#define INT_RST20 0x00E7 /* RST 20h */ +#define INT_RST28 0x00EF /* RST 28h */ +#define INT_RST30 0x00F7 /* RST 30h */ +#define INT_RST38 0x00FF /* RST 38h */ +#define INT_IRQ INT_RST38 /* Default IRQ opcode is FFh */ +#define INT_NMI 0xFFFD /* Non-maskable interrupt */ +#define INT_NONE 0xFFFF /* No interrupt required */ +#define INT_QUIT 0xFFFE /* Exit the emulation */ + + /* Bits in Z80 F register: */ +#define S_FLAG 0x80 /* 1: Result negative */ +#define Z_FLAG 0x40 /* 1: Result is zero */ +#define H_FLAG 0x10 /* 1: Halfcarry/Halfborrow */ +#define P_FLAG 0x04 /* 1: Result is even */ +#define V_FLAG 0x04 /* 1: Overflow occured */ +#define N_FLAG 0x02 /* 1: Subtraction occured */ +#define C_FLAG 0x01 /* 1: Carry/Borrow occured */ + + /* Bits in IFF flip-flops: */ +#define IFF_1 0x01 /* IFF1 flip-flop */ +#define IFF_IM1 0x02 /* 1: IM1 mode */ +#define IFF_IM2 0x04 /* 1: IM2 mode */ +#define IFF_2 0x08 /* IFF2 flip-flop */ +#define IFF_EI 0x20 /* 1: EI pending */ +#define IFF_HALT 0x80 /* 1: CPU HALTed */ + +/** Simple Datatypes *****************************************/ +/** NOTICE: sizeof(byte)=1 and sizeof(word)=2 **/ +/*************************************************************/ +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif +#ifndef WORD_TYPE_DEFINED +#define WORD_TYPE_DEFINED +typedef unsigned short word; +#endif +typedef signed char offset; + +/** Structured Datatypes *************************************/ +/** NOTICE: #define LSB_FIRST for machines where least **/ +/** signifcant byte goes first. **/ +/*************************************************************/ +typedef union +{ +#ifdef LSB_FIRST + struct { byte l,h; } B; +#else + struct { byte h,l; } B; +#endif + word W; +} pair; + +typedef struct +{ + pair AF,BC,DE,HL,IX,IY,PC,SP; /* Main registers */ + pair AF1,BC1,DE1,HL1; /* Shadow registers */ + byte IFF,I; /* Interrupt registers */ + byte R; /* Refresh register */ + + int IPeriod,ICount; /* Set IPeriod to number of CPU cycles */ + /* between calls to LoopZ80() */ + int IBackup; /* Private, don't touch */ + word IRequest; /* Set to address of pending IRQ */ + byte IAutoReset; /* Set to 1 to autom. reset IRequest */ + byte TrapBadOps; /* Set to 1 to warn of illegal opcodes */ + word Trap; /* Set Trap to address to trace from */ + byte Trace; /* Set Trace=1 to start tracing */ + unsigned int User; /* Arbitrary user data (ID,RAM*,etc.) */ +} Z80; + +/** ResetZ80() ***********************************************/ +/** This function can be used to reset the registers before **/ +/** starting execution with RunZ80(). It sets registers to **/ +/** their initial values. **/ +/*************************************************************/ +void msx_ResetZ80(register Z80 *R); + +/** ExecZ80() ************************************************/ +/** This function will execute given number of Z80 cycles. **/ +/** It will then return the number of cycles left, possibly **/ +/** negative, and current register values in R. **/ +/*************************************************************/ +#ifdef EXECZ80 +int ExecZ80(register Z80 *R,register int RunCycles); +#endif + +/** IntZ80() *************************************************/ +/** This function will generate interrupt of given vector. **/ +/*************************************************************/ +void msx_IntZ80(register Z80 *R,register word Vector); + +/** RunZ80() *************************************************/ +/** This function will run Z80 code until an LoopZ80() call **/ +/** returns INT_QUIT. It will return the PC at which **/ +/** emulation stopped, and current register values in R. **/ +/*************************************************************/ +#ifndef EXECZ80 +word RunZ80(register Z80 *R); +#endif + +/** RdZ80()/WrZ80() ******************************************/ +/** These functions are called when access to RAM occurs. **/ +/** They allow to control memory access. **/ +/************************************ TO BE WRITTEN BY USER **/ +void msx_WrZ80(register word Addr,register byte Value); +byte msx_RdZ80(register word Addr); + +/** InZ80()/OutZ80() *****************************************/ +/** Z80 emulation calls these functions to read/write from **/ +/** I/O ports. There can be 65536 I/O ports, but only first **/ +/** 256 are usually used. **/ +/************************************ TO BE WRITTEN BY USER **/ +void msx_OutZ80(register word Port,register byte Value); +byte msx_InZ80(register word Port); + +/** PatchZ80() ***********************************************/ +/** Z80 emulation calls this function when it encounters a **/ +/** special patch command (ED FE) provided for user needs. **/ +/** For example, it can be called to emulate BIOS calls, **/ +/** such as disk and tape access. Replace it with an empty **/ +/** macro for no patching. **/ +/************************************ TO BE WRITTEN BY USER **/ +void PatchZ80(register Z80 *R); + +/** DebugZ80() ***********************************************/ +/** This function should exist if DEBUG is #defined. When **/ +/** Trace!=0, it is called after each command executed by **/ +/** the CPU, and given the Z80 registers. Emulation exits **/ +/** if DebugZ80() returns 0. **/ +/*************************************************************/ +#ifdef DEBUG +byte DebugZ80(register Z80 *R); +#endif + +/** LoopZ80() ************************************************/ +/** Z80 emulation calls this function periodically to check **/ +/** if the system hardware requires any interrupts. This **/ +/** function must return an address of the interrupt vector **/ +/** (0x0038, 0x0066, etc.) or INT_NONE for no interrupt. **/ +/** Return INT_QUIT to exit the emulation loop. **/ +/************************************ TO BE WRITTEN BY USER **/ +word msx_LoopZ80(register Z80 *R); + +/** JumpZ80() ************************************************/ +/** Z80 emulation calls this function when it executes a **/ +/** JP, JR, CALL, RST, or RET. You can use JumpZ80() to **/ +/** trap these opcodes and switch memory layout. **/ +/************************************ TO BE WRITTEN BY USER **/ +#ifndef JUMPZ80 +#define JumpZ80(PC) +#else +void JumpZ80(word PC); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* Z80_H */ diff --git a/components/msx/fmsx/src/fMSX/Boot.h b/components/msx/fmsx/src/fMSX/Boot.h new file mode 100644 index 0000000..a780690 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/Boot.h @@ -0,0 +1,48 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** Boot.h **/ +/** **/ +/** This file contains MSX boot sector image used to create **/ +/** new disk images during FORMAT operation. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +static byte BootBlock[] = +{ + 0xEB,0xFE,0x90,0x56,0x46,0x42,0x2D,0x31,0x39,0x38,0x39,0x00,0x02,0x02,0x01,0x00, + 0x02,0x70,0x00,0xA0,0x05,0xF9,0x03,0x00,0x09,0x00,0x02,0x00,0x00,0x00,0xD0,0xED, + 0x53,0x58,0xC0,0x32,0xC2,0xC0,0x36,0x55,0x23,0x36,0xC0,0x31,0x1F,0xF5,0x11,0x9D, + 0xC0,0x0E,0x0F,0xCD,0x7D,0xF3,0x3C,0x28,0x28,0x11,0x00,0x01,0x0E,0x1A,0xCD,0x7D, + 0xF3,0x21,0x01,0x00,0x22,0xAB,0xC0,0x21,0x00,0x3F,0x11,0x9D,0xC0,0x0E,0x27,0xCD, + 0x7D,0xF3,0xC3,0x00,0x01,0x57,0xC0,0xCD,0x00,0x00,0x79,0xE6,0xFE,0xFE,0x02,0x20, + 0x07,0x3A,0xC2,0xC0,0xA7,0xCA,0x22,0x40,0x11,0x77,0xC0,0x0E,0x09,0xCD,0x7D,0xF3, + 0x0E,0x07,0xCD,0x7D,0xF3,0x18,0xB4,0x42,0x6F,0x6F,0x74,0x20,0x65,0x72,0x72,0x6F, + 0x72,0x0D,0x0A,0x50,0x72,0x65,0x73,0x73,0x20,0x61,0x6E,0x79,0x20,0x6B,0x65,0x79, + 0x20,0x66,0x6F,0x72,0x20,0x72,0x65,0x74,0x72,0x79,0x0D,0x0A,0x24,0x00,0x4D,0x53, + 0x58,0x44,0x4F,0x53,0x20,0x20,0x53,0x59,0x53,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF3,0x2A, + 0x51,0xF3,0x11,0x00,0x01,0x19,0x01,0x00,0x01,0x11,0x00,0xC1,0xED,0xB0,0x3A,0xEE, + 0xC0,0x47,0x11,0xEF,0xC0,0x21,0x00,0x00,0xCD,0x51,0x52,0xF3,0x76,0xC9,0x18,0x64, + 0x3A,0xAF,0x80,0xF9,0xCA,0x6D,0x48,0xD3,0xA5,0x0C,0x8C,0x2F,0x9C,0xCB,0xE9,0x89, + 0xD2,0x00,0x32,0x26,0x40,0x94,0x61,0x19,0x20,0xE6,0x80,0x6D,0x8A,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; diff --git a/components/msx/fmsx/src/fMSX/Common.h b/components/msx/fmsx/src/fMSX/Common.h new file mode 100644 index 0000000..7c7d670 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/Common.h @@ -0,0 +1,909 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** Common.h **/ +/** **/ +/** This file contains standard screen refresh drivers **/ +/** common for X11, VGA, and other "chunky" bitmapped video **/ +/** implementations. It also includes dummy sound drivers **/ +/** for fMSX. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +static int FirstLine = 18; /* First scanline in the XBuf */ + +static void Sprites(byte Y,pixel *Line); +static void ColorSprites(byte Y,byte *ZBuf); +static pixel *RefreshBorder(byte Y,pixel C); +static void ClearLine(pixel *P,pixel C); +static pixel YJKColor(int Y,int J,int K); + +/** RefreshScreen() ******************************************/ +/** Refresh screen. This function is called in the end of **/ +/** refresh cycle to show the entire screen. **/ +/*************************************************************/ +void RefreshScreen(void) { PutImage(); } + +/** ClearLine() **********************************************/ +/** Clear 256 pixels from P with color C. **/ +/*************************************************************/ +static void ClearLine(register pixel *P,register pixel C) +{ + register int J; + + for(J=0;J<256;J++) P[J]=C; +} + +/** YJKColor() ***********************************************/ +/** Given a color in YJK format, return the corresponding **/ +/** palette entry. **/ +/*************************************************************/ +INLINE pixel YJKColor(register int Y,register int J,register int K) +{ + register int R,G,B; + + R=Y+J; + G=Y+K; + B=((5*Y-2*J-K)/4); + + R=R<0? 0:R>31? 31:R; + G=G<0? 0:G>31? 31:G; + B=B<0? 0:B>31? 31:B; + + return(BPal[(R&0x1C)|((G&0x1C)<<3)|(B>>3)]); +} + +/** RefreshBorder() ******************************************/ +/** This function is called from RefreshLine#() to refresh **/ +/** the screen border. It returns a pointer to the start of **/ +/** scanline Y in XBuf or 0 if scanline is beyond XBuf. **/ +/*************************************************************/ +pixel *RefreshBorder(register byte Y,register pixel C) +{ + register pixel *P; + register int H; + + /* First line number in the buffer */ + if(!Y) FirstLine=(ScanLines212? 8:18)+VAdjust; + + /* Return 0 if we've run out of the screen buffer due to overscan */ + if(Y+FirstLine>=HEIGHT) return(0); + + /* Set up the transparent color */ + XPal[0]=(!BGColor||SolidColor0)? XPal0:XPal[BGColor]; + + /* Start of the buffer */ + P=(pixel *)XBuf; + + /* Paint top of the screen */ + if(!Y) for(H=WIDTH*FirstLine-1;H>=0;H--) P[H]=C; + + /* Start of the line */ + P+=WIDTH*(FirstLine+Y); + + /* Paint left/right borders */ + for(H=(WIDTH-256)/2+HAdjust;H>0;H--) P[H-1]=C; + for(H=(WIDTH-256)/2-HAdjust;H>0;H--) P[WIDTH-H]=C; + + /* Paint bottom of the screen */ + H=ScanLines212? 212:192; + if(Y==H-1) for(H=WIDTH*(HEIGHT-H-FirstLine+1)-1;H>=WIDTH;H--) P[H]=C; + + /* Return pointer to the scanline in XBuf */ + return(P+(WIDTH-256)/2+HAdjust); +} + +/** Sprites() ************************************************/ +/** This function is called from RefreshLine#() to refresh **/ +/** sprites in SCREENs 1-3. **/ +/*************************************************************/ +void Sprites(register byte Y,register pixel *Line) +{ + static const byte SprHeights[4] = { 8,16,16,32 }; + register pixel *P,C; + register byte OH,IH,*PT,*AT; + register unsigned int M; + register int L,K; + + /* No extra sprites yet */ + VDPStatus[0]&=~0x5F; + + /* Assign initial values before counting */ + OH = SprHeights[VDP[1]&0x03]; + IH = SprHeights[VDP[1]&0x02]; + AT = SprTab-4; + Y += VScroll; + C = MAXSPRITE1+1; + M = 0; + + /* Count displayed sprites */ + for(L=0;L<32;++L) + { + M<<=1;AT+=4; /* Iterating through SprTab */ + K=AT[0]; /* K = sprite Y coordinate */ + if(K==208) break; /* Iteration terminates if Y=208 */ + if(K>256-IH) K-=256; /* Y coordinate may be negative */ + + /* Mark all valid sprites with 1s, break at MAXSPRITE1 sprites */ + if((Y>K)&&(Y<=K+OH)) + { + /* If we exceed the maximum number of sprites per line... */ + if(!--C) + { + /* Set 5thSprite flag in the VDP status register */ + VDPStatus[0]|=0x40; + /* Stop drawing sprites, unless all-sprites option enabled */ + if(!OPTION(MSX_ALLSPRITE)) break; + } + + /* Mark sprite as ready to draw */ + M|=1; + } + } + + /* Mark last checked sprite (5th in line, Y=208, or sprite #31) */ + VDPStatus[0]|=L<32? L:31; + + /* Draw all marked sprites */ + for(;M;M>>=1,AT-=4) + if(M&1) + { + C = AT[3]; /* C = sprite attributes */ + L = C&0x80? AT[1]-32:AT[1]; /* Sprite may be shifted left by 32 */ + C&= 0x0F; /* C = sprite color */ + + if((L<256)&&(L>-OH)&&C) + { + K = AT[0]; /* K = sprite Y coordinate */ + if(K>256-IH) K-=256; /* Y coordinate may be negative */ + + P = Line+L; + K = Y-K-1; + PT = SprGen+((int)(IH>8? AT[2]&0xFC:AT[2])<<3)+(OH>IH? (K>>1):K); + C = XPal[C]; + + /* Mask 1: clip left sprite boundary */ + K=L>=0? 0xFFFF:(0x10000>>(OH>IH? (-L>>1):-L))-1; + + /* Mask 2: clip right sprite boundary */ + L+=(int)OH-257; + if(L>=0) + { + L=(IH>8? 0x0002:0x0200)<<(OH>IH? (L>>1):L); + K&=~(L-1); + } + + /* Get and clip the sprite data */ + K&=((int)PT[0]<<8)|(IH>8? PT[16]:0x00); + + /* If output size is bigger than the input size... */ + if(OH>IH) + { + /* Big (zoomed) sprite */ + + /* Draw left 16 pixels of the sprite */ + if(K&0xFF00) + { + if(K&0x8000) P[1]=P[0]=C; + if(K&0x4000) P[3]=P[2]=C; + if(K&0x2000) P[5]=P[4]=C; + if(K&0x1000) P[7]=P[6]=C; + if(K&0x0800) P[9]=P[8]=C; + if(K&0x0400) P[11]=P[10]=C; + if(K&0x0200) P[13]=P[12]=C; + if(K&0x0100) P[15]=P[14]=C; + } + + /* Draw right 16 pixels of the sprite */ + if(K&0x00FF) + { + if(K&0x0080) P[17]=P[16]=C; + if(K&0x0040) P[19]=P[18]=C; + if(K&0x0020) P[21]=P[20]=C; + if(K&0x0010) P[23]=P[22]=C; + if(K&0x0008) P[25]=P[24]=C; + if(K&0x0004) P[27]=P[26]=C; + if(K&0x0002) P[29]=P[28]=C; + if(K&0x0001) P[31]=P[30]=C; + } + } + else + { + /* Normal (unzoomed) sprite */ + + /* Draw left 8 pixels of the sprite */ + if(K&0xFF00) + { + if(K&0x8000) P[0]=C; + if(K&0x4000) P[1]=C; + if(K&0x2000) P[2]=C; + if(K&0x1000) P[3]=C; + if(K&0x0800) P[4]=C; + if(K&0x0400) P[5]=C; + if(K&0x0200) P[6]=C; + if(K&0x0100) P[7]=C; + } + + /* Draw right 8 pixels of the sprite */ + if(K&0x00FF) + { + if(K&0x0080) P[8]=C; + if(K&0x0040) P[9]=C; + if(K&0x0020) P[10]=C; + if(K&0x0010) P[11]=C; + if(K&0x0008) P[12]=C; + if(K&0x0004) P[13]=C; + if(K&0x0002) P[14]=C; + if(K&0x0001) P[15]=C; + } + } + } + } +} + +/** ColorSprites() *******************************************/ +/** This function is called from RefreshLine#() to refresh **/ +/** color sprites in SCREENs 4-8. The result is returned in **/ +/** ZBuf, whose size must be 320 bytes (32+256+32). **/ +/*************************************************************/ +void ColorSprites(register byte Y,byte *ZBuf) +{ + static const byte SprHeights[4] = { 8,16,16,32 }; + register byte C,IH,OH,J,OrThem; + register byte *P,*PT,*AT; + register int L,K; + register unsigned int M; + + /* No extra sprites yet */ + VDPStatus[0]&=~0x5F; + + /* Clear ZBuffer and exit if sprites are off */ + memset(ZBuf+32,0,256); + if(SpritesOFF) return; + + /* Assign initial values before counting */ + OrThem = 0x00; + OH = SprHeights[VDP[1]&0x03]; + IH = SprHeights[VDP[1]&0x02]; + AT = SprTab-4; + C = MAXSPRITE2+1; + M = 0; + + /* Count displayed sprites */ + for(L=0;L<32;++L) + { + M<<=1;AT+=4; /* Iterating through SprTab */ + K=AT[0]; /* Read Y from SprTab */ + if(K==216) break; /* Iteration terminates if Y=216 */ + K=(byte)(K-VScroll); /* Sprite's actual Y coordinate */ + if(K>256-IH) K-=256; /* Y coordinate may be negative */ + + /* Mark all valid sprites with 1s, break at MAXSPRITE2 sprites */ + if((Y>K)&&(Y<=K+OH)) + { + /* If we exceed the maximum number of sprites per line... */ + if(!--C) + { + /* Set 9thSprite flag in the VDP status register */ + VDPStatus[0]|=0x40; + /* Stop drawing sprites, unless all-sprites option enabled */ + if(!OPTION(MSX_ALLSPRITE)) break; + } + + /* Mark sprite as ready to draw */ + M|=1; + } + } + + /* Mark last checked sprite (9th in line, Y=216, or sprite #31) */ + VDPStatus[0]|=L<32? L:31; + + /* Draw all marked sprites */ + for(;M;M>>=1,AT-=4) + if(M&1) + { + K = (byte)(AT[0]-VScroll); /* K = sprite Y coordinate */ + if(K>256-IH) K-=256; /* Y coordinate may be negative */ + + J = Y-K-1; + J = OH>IH? (J>>1):J; + C = SprTab[-0x0200+((AT-SprTab)<<2)+J]; + OrThem|=C&0x40; + + if(C&0x0F) + { + PT = SprGen+((int)(IH>8? AT[2]&0xFC:AT[2])<<3)+J; + P = ZBuf+AT[1]+(C&0x80? 0:32); + C &= 0x0F; + J = PT[0]; + + if(OrThem&0x20) + { + if(OH>IH) + { + if(J&0x80) { P[0]|=C;P[1]|=C; } + if(J&0x40) { P[2]|=C;P[3]|=C; } + if(J&0x20) { P[4]|=C;P[5]|=C; } + if(J&0x10) { P[6]|=C;P[7]|=C; } + if(J&0x08) { P[8]|=C;P[9]|=C; } + if(J&0x04) { P[10]|=C;P[11]|=C; } + if(J&0x02) { P[12]|=C;P[13]|=C; } + if(J&0x01) { P[14]|=C;P[15]|=C; } + if(IH>8) + { + J=PT[16]; + if(J&0x80) { P[16]|=C;P[17]|=C; } + if(J&0x40) { P[18]|=C;P[19]|=C; } + if(J&0x20) { P[20]|=C;P[21]|=C; } + if(J&0x10) { P[22]|=C;P[23]|=C; } + if(J&0x08) { P[24]|=C;P[25]|=C; } + if(J&0x04) { P[26]|=C;P[27]|=C; } + if(J&0x02) { P[28]|=C;P[29]|=C; } + if(J&0x01) { P[30]|=C;P[31]|=C; } + } + } + else + { + if(J&0x80) P[0]|=C; + if(J&0x40) P[1]|=C; + if(J&0x20) P[2]|=C; + if(J&0x10) P[3]|=C; + if(J&0x08) P[4]|=C; + if(J&0x04) P[5]|=C; + if(J&0x02) P[6]|=C; + if(J&0x01) P[7]|=C; + if(IH>8) + { + J=PT[16]; + if(J&0x80) P[8]|=C; + if(J&0x40) P[9]|=C; + if(J&0x20) P[10]|=C; + if(J&0x10) P[11]|=C; + if(J&0x08) P[12]|=C; + if(J&0x04) P[13]|=C; + if(J&0x02) P[14]|=C; + if(J&0x01) P[15]|=C; + } + } + } + else + { + if(OH>IH) + { + if(J&0x80) P[0]=P[1]=C; + if(J&0x40) P[2]=P[3]=C; + if(J&0x20) P[4]=P[5]=C; + if(J&0x10) P[6]=P[7]=C; + if(J&0x08) P[8]=P[9]=C; + if(J&0x04) P[10]=P[11]=C; + if(J&0x02) P[12]=P[13]=C; + if(J&0x01) P[14]=P[15]=C; + if(IH>8) + { + J=PT[16]; + if(J&0x80) P[16]=P[17]=C; + if(J&0x40) P[18]=P[19]=C; + if(J&0x20) P[20]=P[21]=C; + if(J&0x10) P[22]=P[23]=C; + if(J&0x08) P[24]=P[25]=C; + if(J&0x04) P[26]=P[27]=C; + if(J&0x02) P[28]=P[29]=C; + if(J&0x01) P[30]=P[31]=C; + } + } + else + { + if(J&0x80) P[0]=C; + if(J&0x40) P[1]=C; + if(J&0x20) P[2]=C; + if(J&0x10) P[3]=C; + if(J&0x08) P[4]=C; + if(J&0x04) P[5]=C; + if(J&0x02) P[6]=C; + if(J&0x01) P[7]=C; + if(IH>8) + { + J=PT[16]; + if(J&0x80) P[8]=C; + if(J&0x40) P[9]=C; + if(J&0x20) P[10]=C; + if(J&0x10) P[11]=C; + if(J&0x08) P[12]=C; + if(J&0x04) P[13]=C; + if(J&0x02) P[14]=C; + if(J&0x01) P[15]=C; + } + } + } + } + + /* Update overlapping flag */ + OrThem>>=1; + } +} + +/** RefreshLineF() *******************************************/ +/** Dummy refresh function called for non-existing screens. **/ +/*************************************************************/ +void RefreshLineF(register byte Y) +{ + register pixel *P; + + if(Verbose>1) + printf + ( + "ScrMODE %d: ChrTab=%X ChrGen=%X ColTab=%X SprTab=%X SprGen=%X\n", + ScrMode, + (int)(ChrTab-msx_VRAM), + (int)(ChrGen-msx_VRAM), + (int)(ColTab-msx_VRAM), + (int)(SprTab-msx_VRAM), + (int)(SprGen-msx_VRAM) + ); + + P=RefreshBorder(Y,XPal[BGColor]); + if(P) ClearLine(P,XPal[BGColor]); +} + +/** RefreshLine0() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN0. **/ +/*************************************************************/ +void RefreshLine0(register byte Y) +{ + register pixel *P,FC,BC; + register byte X,*T,*G; + + BC=XPal[BGColor]; + P=RefreshBorder(Y,BC); + if(!P) return; + + if(!ScreenON) ClearLine(P,BC); + else + { + P[0]=P[1]=P[2]=P[3]=P[4]=P[5]=P[6]=P[7]=P[8]=BC; + + G=(FontBuf&&(Mode&MSX_FIXEDFONT)? FontBuf:ChrGen)+((Y+VScroll)&0x07); + T=ChrTab+40*(Y>>3); + FC=XPal[FGColor]; + P+=9; + + for(X=0;X<40;X++,T++,P+=6) + { + Y=G[(int)*T<<3]; + P[0]=Y&0x80? FC:BC;P[1]=Y&0x40? FC:BC; + P[2]=Y&0x20? FC:BC;P[3]=Y&0x10? FC:BC; + P[4]=Y&0x08? FC:BC;P[5]=Y&0x04? FC:BC; + } + + P[0]=P[1]=P[2]=P[3]=P[4]=P[5]=P[6]=BC; + } +} + +/** RefreshLine1() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN1, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine1(register byte Y) +{ + register pixel *P,FC,BC; + register byte K,X,*T,*G; + + P=RefreshBorder(Y,XPal[BGColor]); + if(!P) return; + + if(!ScreenON) ClearLine(P,XPal[BGColor]); + else + { + Y+=VScroll; + G=(FontBuf&&(Mode&MSX_FIXEDFONT)? FontBuf:ChrGen)+(Y&0x07); + T=ChrTab+((int)(Y&0xF8)<<2); + + for(X=0;X<32;X++,T++,P+=8) + { + K=ColTab[*T>>3]; + FC=XPal[K>>4]; + BC=XPal[K&0x0F]; + K=G[(int)*T<<3]; + P[0]=K&0x80? FC:BC;P[1]=K&0x40? FC:BC; + P[2]=K&0x20? FC:BC;P[3]=K&0x10? FC:BC; + P[4]=K&0x08? FC:BC;P[5]=K&0x04? FC:BC; + P[6]=K&0x02? FC:BC;P[7]=K&0x01? FC:BC; + } + + if(!SpritesOFF) Sprites(Y,P-256); + } +} + +/** RefreshLine2() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN2, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine2(register byte Y) +{ + register pixel *P,FC,BC; + register byte K,X,*T; + register int I,J; + + P=RefreshBorder(Y,XPal[BGColor]); + if(!P) return; + + if(!ScreenON) ClearLine(P,XPal[BGColor]); + else + { + Y+=VScroll; + T=ChrTab+((int)(Y&0xF8)<<2); + I=((int)(Y&0xC0)<<5)+(Y&0x07); + + for(X=0;X<32;X++,T++,P+=8) + { + J=(int)*T<<3; + K=ColTab[(I+J)&ColTabM]; + FC=XPal[K>>4]; + BC=XPal[K&0x0F]; + K=ChrGen[(I+J)&ChrGenM]; + P[0]=K&0x80? FC:BC;P[1]=K&0x40? FC:BC; + P[2]=K&0x20? FC:BC;P[3]=K&0x10? FC:BC; + P[4]=K&0x08? FC:BC;P[5]=K&0x04? FC:BC; + P[6]=K&0x02? FC:BC;P[7]=K&0x01? FC:BC; + } + + if(!SpritesOFF) Sprites(Y,P-256); + } +} + +/** RefreshLine3() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN3, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine3(register byte Y) +{ + register pixel *P; + register byte X,K,*T,*G; + + P=RefreshBorder(Y,XPal[BGColor]); + if(!P) return; + + if(!ScreenON) ClearLine(P,XPal[BGColor]); + else + { + Y+=VScroll; + T=ChrTab+((int)(Y&0xF8)<<2); + G=ChrGen+((Y&0x1C)>>2); + + for(X=0;X<32;X++,T++,P+=8) + { + K=G[(int)*T<<3]; + P[0]=P[1]=P[2]=P[3]=XPal[K>>4]; + P[4]=P[5]=P[6]=P[7]=XPal[K&0x0F]; + } + + if(!SpritesOFF) Sprites(Y,P-256); + } +} + +/** RefreshLine4() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN4, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine4(register byte Y) +{ + register pixel *P,FC,BC; + register byte K,X,C,*T,*R; + register int I,J; + byte ZBuf[320]; + + P=RefreshBorder(Y,XPal[BGColor]); + if(!P) return; + + if(!ScreenON) ClearLine(P,XPal[BGColor]); + else + { + ColorSprites(Y,ZBuf); + R=ZBuf+32; + Y+=VScroll; + T=ChrTab+((int)(Y&0xF8)<<2); + I=((int)(Y&0xC0)<<5)+(Y&0x07); + + for(X=0;X<32;X++,R+=8,P+=8,T++) + { + J=(int)*T<<3; + K=ColTab[(I+J)&ColTabM]; + FC=XPal[K>>4]; + BC=XPal[K&0x0F]; + K=ChrGen[(I+J)&ChrGenM]; + + C=R[0];P[0]=C? XPal[C]:(K&0x80)? FC:BC; + C=R[1];P[1]=C? XPal[C]:(K&0x40)? FC:BC; + C=R[2];P[2]=C? XPal[C]:(K&0x20)? FC:BC; + C=R[3];P[3]=C? XPal[C]:(K&0x10)? FC:BC; + C=R[4];P[4]=C? XPal[C]:(K&0x08)? FC:BC; + C=R[5];P[5]=C? XPal[C]:(K&0x04)? FC:BC; + C=R[6];P[6]=C? XPal[C]:(K&0x02)? FC:BC; + C=R[7];P[7]=C? XPal[C]:(K&0x01)? FC:BC; + } + } +} + +/** RefreshLine5() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN5, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine5(register byte Y) +{ + register pixel *P; + register byte I,X,*T,*R; + byte ZBuf[320]; + + P=RefreshBorder(Y,XPal[BGColor]); + if(!P) return; + + if(!ScreenON) ClearLine(P,XPal[BGColor]); + else + { + ColorSprites(Y,ZBuf); + R=ZBuf+32; + T=ChrTab+(((int)(Y+VScroll)<<7)&ChrTabM&0x7FFF); + + for(X=0;X<16;X++,R+=16,P+=16,T+=8) + { + I=R[0];P[0]=XPal[I? I:T[0]>>4]; + I=R[1];P[1]=XPal[I? I:T[0]&0x0F]; + I=R[2];P[2]=XPal[I? I:T[1]>>4]; + I=R[3];P[3]=XPal[I? I:T[1]&0x0F]; + I=R[4];P[4]=XPal[I? I:T[2]>>4]; + I=R[5];P[5]=XPal[I? I:T[2]&0x0F]; + I=R[6];P[6]=XPal[I? I:T[3]>>4]; + I=R[7];P[7]=XPal[I? I:T[3]&0x0F]; + I=R[8];P[8]=XPal[I? I:T[4]>>4]; + I=R[9];P[9]=XPal[I? I:T[4]&0x0F]; + I=R[10];P[10]=XPal[I? I:T[5]>>4]; + I=R[11];P[11]=XPal[I? I:T[5]&0x0F]; + I=R[12];P[12]=XPal[I? I:T[6]>>4]; + I=R[13];P[13]=XPal[I? I:T[6]&0x0F]; + I=R[14];P[14]=XPal[I? I:T[7]>>4]; + I=R[15];P[15]=XPal[I? I:T[7]&0x0F]; + } + } +} + +/** RefreshLine8() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN8, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine8(register byte Y) +{ + static byte SprToScr[16] = + { + 0x00,0x02,0x10,0x12,0x80,0x82,0x90,0x92, + 0x49,0x4B,0x59,0x5B,0xC9,0xCB,0xD9,0xDB + }; + register pixel *P; + register byte C,X,*T,*R; + byte ZBuf[320]; + + P=RefreshBorder(Y,BPal[VDP[7]]); + if(!P) return; + + if(!ScreenON) ClearLine(P,BPal[VDP[7]]); + else + { + ColorSprites(Y,ZBuf); + R=ZBuf+32; + T=ChrTab+(((int)(Y+VScroll)<<8)&ChrTabM&0xFFFF); + + for(X=0;X<32;X++,T+=8,R+=8,P+=8) + { + C=R[0];P[0]=BPal[C? SprToScr[C]:T[0]]; + C=R[1];P[1]=BPal[C? SprToScr[C]:T[1]]; + C=R[2];P[2]=BPal[C? SprToScr[C]:T[2]]; + C=R[3];P[3]=BPal[C? SprToScr[C]:T[3]]; + C=R[4];P[4]=BPal[C? SprToScr[C]:T[4]]; + C=R[5];P[5]=BPal[C? SprToScr[C]:T[5]]; + C=R[6];P[6]=BPal[C? SprToScr[C]:T[6]]; + C=R[7];P[7]=BPal[C? SprToScr[C]:T[7]]; + } + } +} + +/** RefreshLine10() ******************************************/ +/** Refresh line Y (0..191/211) of SCREEN10/11, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine10(register byte Y) +{ + register pixel *P; + register byte C,X,*T,*R; + register int J,K; + byte ZBuf[320]; + + P=RefreshBorder(Y,BPal[VDP[7]]); + if(!P) return; + + if(!ScreenON) ClearLine(P,BPal[VDP[7]]); + else + { + ColorSprites(Y,ZBuf); + R=ZBuf+32; + T=ChrTab+(((int)(Y+VScroll)<<8)&ChrTabM&0xFFFF); + + /* Draw first 4 pixels */ + C=R[0];P[0]=C? XPal[C]:BPal[VDP[7]]; + C=R[1];P[1]=C? XPal[C]:BPal[VDP[7]]; + C=R[2];P[2]=C? XPal[C]:BPal[VDP[7]]; + C=R[3];P[3]=C? XPal[C]:BPal[VDP[7]]; + R+=4;P+=4; + + for(X=0;X<63;X++,T+=4,R+=4,P+=4) + { + K=(T[0]&0x07)|((T[1]&0x07)<<3); + if(K&0x20) K-=64; + J=(T[2]&0x07)|((T[3]&0x07)<<3); + if(J&0x20) J-=64; + + C=R[0];Y=T[0]>>3;P[0]=C? XPal[C]:Y&1? XPal[Y>>1]:YJKColor(Y,J,K); + C=R[1];Y=T[1]>>3;P[1]=C? XPal[C]:Y&1? XPal[Y>>1]:YJKColor(Y,J,K); + C=R[2];Y=T[2]>>3;P[2]=C? XPal[C]:Y&1? XPal[Y>>1]:YJKColor(Y,J,K); + C=R[3];Y=T[3]>>3;P[3]=C? XPal[C]:Y&1? XPal[Y>>1]:YJKColor(Y,J,K); + } + } +} + +/** RefreshLine12() ******************************************/ +/** Refresh line Y (0..191/211) of SCREEN12, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine12(register byte Y) +{ + register pixel *P; + register byte C,X,*T,*R; + register int J,K; + byte ZBuf[320]; + + P=RefreshBorder(Y,BPal[VDP[7]]); + if(!P) return; + + if(!ScreenON) ClearLine(P,BPal[VDP[7]]); + else + { + ColorSprites(Y,ZBuf); + R = ZBuf+32; + T = ChrTab + + (((int)(Y+VScroll)<<8)&ChrTabM&0xFFFF) + + (HScroll512&&(HScroll>255)? 0x10000:0) + + (HScroll&0xFC); + + /* Draw first 4 pixels */ + C=R[0];P[0]=C? XPal[C]:BPal[VDP[7]]; + C=R[1];P[1]=C? XPal[C]:BPal[VDP[7]]; + C=R[2];P[2]=C? XPal[C]:BPal[VDP[7]]; + C=R[3];P[3]=C? XPal[C]:BPal[VDP[7]]; + R+=4;P+=4; + + for(X=1;X<64;X++,T+=4,R+=4,P+=4) + { + K=(T[0]&0x07)|((T[1]&0x07)<<3); + if(K&0x20) K-=64; + J=(T[2]&0x07)|((T[3]&0x07)<<3); + if(J&0x20) J-=64; + + C=R[0];P[0]=C? XPal[C]:YJKColor(T[0]>>3,J,K); + C=R[1];P[1]=C? XPal[C]:YJKColor(T[1]>>3,J,K); + C=R[2];P[2]=C? XPal[C]:YJKColor(T[2]>>3,J,K); + C=R[3];P[3]=C? XPal[C]:YJKColor(T[3]>>3,J,K); + } + } +} + +#ifdef NARROW + +/** RefreshLine6() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN6, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine6(register byte Y) +{ + register pixel *P; + register byte X,*T,*R,C; + byte ZBuf[320]; + + P=RefreshBorder(Y,XPal[BGColor&0x03]); + if(!P) return; + + if(!ScreenON) ClearLine(P,XPal[BGColor&0x03]); + else + { + ColorSprites(Y,ZBuf); + R=ZBuf+32; + T=ChrTab+(((int)(Y+VScroll)<<7)&ChrTabM&0x7FFF); + + for(X=0;X<32;X++) + { + C=R[0];P[0]=XPal[C? C:T[0]>>6]; + C=R[1];P[1]=XPal[C? C:(T[0]>>2)&0x03]; + C=R[2];P[2]=XPal[C? C:T[1]>>6]; + C=R[3];P[3]=XPal[C? C:(T[1]>>2)&0x03]; + C=R[4];P[4]=XPal[C? C:T[2]>>6]; + C=R[5];P[5]=XPal[C? C:(T[2]>>2)&0x03]; + C=R[6];P[6]=XPal[C? C:T[3]>>6]; + C=R[7];P[7]=XPal[C? C:(T[3]>>2)&0x03]; + R+=8;P+=8;T+=4; + } + } +} + +/** RefreshLine7() *******************************************/ +/** Refresh line Y (0..191/211) of SCREEN7, including **/ +/** sprites in this line. **/ +/*************************************************************/ +void RefreshLine7(register byte Y) +{ + register pixel *P; + register byte C,X,*T,*R; + byte ZBuf[320]; + + P=RefreshBorder(Y,XPal[BGColor]); + if(!P) return; + + if(!ScreenON) ClearLine(P,XPal[BGColor]); + else + { + ColorSprites(Y,ZBuf); + R=ZBuf+32; + T=ChrTab+(((int)(Y+VScroll)<<8)&ChrTabM&0xFFFF); + + for(X=0;X<32;X++) + { + C=R[0];P[0]=XPal[C? C:T[0]>>4]; + C=R[1];P[1]=XPal[C? C:T[1]>>4]; + C=R[2];P[2]=XPal[C? C:T[2]>>4]; + C=R[3];P[3]=XPal[C? C:T[3]>>4]; + C=R[4];P[4]=XPal[C? C:T[4]>>4]; + C=R[5];P[5]=XPal[C? C:T[5]>>4]; + C=R[6];P[6]=XPal[C? C:T[6]>>4]; + C=R[7];P[7]=XPal[C? C:T[7]>>4]; + R+=8;P+=8;T+=8; + } + } +} + +/** RefreshLineTx80() ****************************************/ +/** Refresh line Y (0..191/211) of TEXT80. **/ +/*************************************************************/ +void RefreshLineTx80(register byte Y) +{ + register pixel *P,FC,BC; + register byte X,M,*T,*C,*G; + + BC=XPal[BGColor]; + P=RefreshBorder(Y,BC); + if(!P) return; + + if(!ScreenON) ClearLine(P,BC); + else + { + P[0]=P[1]=P[2]=P[3]=P[4]=P[5]=P[6]=P[7]=P[8]=BC; + G=(FontBuf&&(Mode&MSX_FIXEDFONT)? FontBuf:ChrGen)+((Y+VScroll)&0x07); + T=ChrTab+((80*(Y>>3))&ChrTabM); + C=ColTab+((10*(Y>>3))&ColTabM); + P+=9; + + for(X=0,M=0x00;X<80;X++,T++,P+=3) + { + if(!(X&0x07)) M=*C++; + if(M&0x80) { FC=XPal[XFGColor];BC=XPal[XBGColor]; } + else { FC=XPal[FGColor];BC=XPal[BGColor]; } + M<<=1; + Y=*(G+((int)*T<<3)); + P[0]=Y&0xC0? FC:BC; + P[1]=Y&0x30? FC:BC; + P[2]=Y&0x0C? FC:BC; + } + + P[0]=P[1]=P[2]=P[3]=P[4]=P[5]=P[6]=XPal[BGColor]; + } +} + +#endif /* NARROW */ diff --git a/components/msx/fmsx/src/fMSX/CommonMux.h b/components/msx/fmsx/src/fMSX/CommonMux.h new file mode 100644 index 0000000..45cfd41 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/CommonMux.h @@ -0,0 +1,251 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** CommonMux.h **/ +/** **/ +/** This file instantiates MSX screen drivers for every **/ +/** possible screen depth. It includes common driver code **/ +/** from Common.h and Wide.h. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef COMMONMUX_H +#define COMMONMUX_H + +#include "Common.h" +#include "Wide.h" + +#undef BPP8 +#undef BPP16 +#undef BPP24 +#undef BPP32 + +/** Screen Mode Handlers [number of screens + 1] *************/ +extern void (*RefreshLine[MAXSCREEN+2])(byte Y); + +#define BPP8 +#define pixel unsigned char +#define FirstLine FirstLine_8 +#define Sprites Sprites_8 +#define ColorSprites ColorSprites_8 +#define RefreshBorder RefreshBorder_8 +#define RefreshBorder512 RefreshBorder512_8 +#define ClearLine ClearLine_8 +#define ClearLine512 ClearLine512_8 +#define YJKColor YJKColor_8 +#define RefreshScreen RefreshScreen_8 +#define RefreshLineF RefreshLineF_8 +#define RefreshLine0 RefreshLine0_8 +#define RefreshLine1 RefreshLine1_8 +#define RefreshLine2 RefreshLine2_8 +#define RefreshLine3 RefreshLine3_8 +#define RefreshLine4 RefreshLine4_8 +#define RefreshLine5 RefreshLine5_8 +#define RefreshLine6 RefreshLine6_8 +#define RefreshLine7 RefreshLine7_8 +#define RefreshLine8 RefreshLine8_8 +#define RefreshLine10 RefreshLine10_8 +#define RefreshLine12 RefreshLine12_8 +#define RefreshLineTx80 RefreshLineTx80_8 +#include "Common.h" +#include "Wide.h" +#undef pixel +#undef FirstLine +#undef Sprites +#undef ColorSprites +#undef RefreshBorder +#undef RefreshBorder512 +#undef ClearLine +#undef ClearLine512 +#undef YJKColor +#undef RefreshScreen +#undef RefreshLineF +#undef RefreshLine0 +#undef RefreshLine1 +#undef RefreshLine2 +#undef RefreshLine3 +#undef RefreshLine4 +#undef RefreshLine5 +#undef RefreshLine6 +#undef RefreshLine7 +#undef RefreshLine8 +#undef RefreshLine10 +#undef RefreshLine12 +#undef RefreshLineTx80 +#undef BPP8 + +#define BPP16 +#define pixel unsigned short +#define FirstLine FirstLine_16 +#define Sprites Sprites_16 +#define ColorSprites ColorSprites_16 +#define RefreshBorder RefreshBorder_16 +#define RefreshBorder512 RefreshBorder512_16 +#define ClearLine ClearLine_16 +#define ClearLine512 ClearLine512_16 +#define YJKColor YJKColor_16 +#define RefreshScreen RefreshScreen_16 +#define RefreshLineF RefreshLineF_16 +#define RefreshLine0 RefreshLine0_16 +#define RefreshLine1 RefreshLine1_16 +#define RefreshLine2 RefreshLine2_16 +#define RefreshLine3 RefreshLine3_16 +#define RefreshLine4 RefreshLine4_16 +#define RefreshLine5 RefreshLine5_16 +#define RefreshLine6 RefreshLine6_16 +#define RefreshLine7 RefreshLine7_16 +#define RefreshLine8 RefreshLine8_16 +#define RefreshLine10 RefreshLine10_16 +#define RefreshLine12 RefreshLine12_16 +#define RefreshLineTx80 RefreshLineTx80_16 +#include "Common.h" +#include "Wide.h" +#undef pixel +#undef FirstLine +#undef Sprites +#undef ColorSprites +#undef RefreshBorder +#undef RefreshBorder512 +#undef ClearLine +#undef ClearLine512 +#undef YJKColor +#undef RefreshScreen +#undef RefreshLineF +#undef RefreshLine0 +#undef RefreshLine1 +#undef RefreshLine2 +#undef RefreshLine3 +#undef RefreshLine4 +#undef RefreshLine5 +#undef RefreshLine6 +#undef RefreshLine7 +#undef RefreshLine8 +#undef RefreshLine10 +#undef RefreshLine12 +#undef RefreshLineTx80 +#undef BPP16 + +#define BPP32 +#define pixel unsigned int +#define FirstLine FirstLine_32 +#define Sprites Sprites_32 +#define ColorSprites ColorSprites_32 +#define RefreshBorder RefreshBorder_32 +#define RefreshBorder512 RefreshBorder512_32 +#define ClearLine ClearLine_32 +#define ClearLine512 ClearLine512_32 +#define YJKColor YJKColor_32 +#define RefreshScreen RefreshScreen_32 +#define RefreshLineF RefreshLineF_32 +#define RefreshLine0 RefreshLine0_32 +#define RefreshLine1 RefreshLine1_32 +#define RefreshLine2 RefreshLine2_32 +#define RefreshLine3 RefreshLine3_32 +#define RefreshLine4 RefreshLine4_32 +#define RefreshLine5 RefreshLine5_32 +#define RefreshLine6 RefreshLine6_32 +#define RefreshLine7 RefreshLine7_32 +#define RefreshLine8 RefreshLine8_32 +#define RefreshLine10 RefreshLine10_32 +#define RefreshLine12 RefreshLine12_32 +#define RefreshLineTx80 RefreshLineTx80_32 +#include "Common.h" +#include "Wide.h" +#undef pixel +#undef FirstLine +#undef Sprites +#undef ColorSprites +#undef RefreshBorder +#undef RefreshBorder512 +#undef ClearLine +#undef ClearLine512 +#undef YJKColor +#undef RefreshScreen +#undef RefreshLineF +#undef RefreshLine0 +#undef RefreshLine1 +#undef RefreshLine2 +#undef RefreshLine3 +#undef RefreshLine4 +#undef RefreshLine5 +#undef RefreshLine6 +#undef RefreshLine7 +#undef RefreshLine8 +#undef RefreshLine10 +#undef RefreshLine12 +#undef RefreshLineTx80 +#undef BPP32 + +/** SetScreenDepth() *****************************************/ +/** Fill fMSX screen driver array with pointers matching **/ +/** the given image depth. **/ +/*************************************************************/ +int SetScreenDepth(int Depth) +{ + if(Depth<=8) + { + Depth=8; + RefreshLine[0] = RefreshLine0_8; + RefreshLine[1] = RefreshLine1_8; + RefreshLine[2] = RefreshLine2_8; + RefreshLine[3] = RefreshLine3_8; + RefreshLine[4] = RefreshLine4_8; + RefreshLine[5] = RefreshLine5_8; + RefreshLine[6] = RefreshLine6_8; + RefreshLine[7] = RefreshLine7_8; + RefreshLine[8] = RefreshLine8_8; + RefreshLine[9] = 0; + RefreshLine[10] = RefreshLine10_8; + RefreshLine[11] = RefreshLine10_8; + RefreshLine[12] = RefreshLine12_8; + RefreshLine[13] = RefreshLineTx80_8; + } + else if(Depth<=16) + { + Depth=16; + RefreshLine[0] = RefreshLine0_16; + RefreshLine[1] = RefreshLine1_16; + RefreshLine[2] = RefreshLine2_16; + RefreshLine[3] = RefreshLine3_16; + RefreshLine[4] = RefreshLine4_16; + RefreshLine[5] = RefreshLine5_16; + RefreshLine[6] = RefreshLine6_16; + RefreshLine[7] = RefreshLine7_16; + RefreshLine[8] = RefreshLine8_16; + RefreshLine[9] = 0; + RefreshLine[10] = RefreshLine10_16; + RefreshLine[11] = RefreshLine10_16; + RefreshLine[12] = RefreshLine12_16; + RefreshLine[13] = RefreshLineTx80_16; + } + else if(Depth<=32) + { + Depth=32; + RefreshLine[0] = RefreshLine0_32; + RefreshLine[1] = RefreshLine1_32; + RefreshLine[2] = RefreshLine2_32; + RefreshLine[3] = RefreshLine3_32; + RefreshLine[4] = RefreshLine4_32; + RefreshLine[5] = RefreshLine5_32; + RefreshLine[6] = RefreshLine6_32; + RefreshLine[7] = RefreshLine7_32; + RefreshLine[8] = RefreshLine8_32; + RefreshLine[9] = 0; + RefreshLine[10] = RefreshLine10_32; + RefreshLine[11] = RefreshLine10_32; + RefreshLine[12] = RefreshLine12_32; + RefreshLine[13] = RefreshLineTx80_32; + } + else + { + /* Wrong screen depth */ + Depth=0; + } + + /* Done */ + return(Depth); +} + +#endif /* COMMONMUX_H */ diff --git a/components/msx/fmsx/src/fMSX/Help.h b/components/msx/fmsx/src/fMSX/Help.h new file mode 100644 index 0000000..b5c470f --- /dev/null +++ b/components/msx/fmsx/src/fMSX/Help.h @@ -0,0 +1,127 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** Help.h **/ +/** **/ +/** This file contains help information printed out by the **/ +/** main() routine when started with option "-help". **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +static const char *HelpText[] = +{ + "Usage: fmsx [-option1 [-option2...]] [filename1] [filename2]", + "[filename1] = name of file to load as cartridge A", + "[filename2] = name of file to load as cartridge B", +#if defined(ZLIB) + " This program will transparently uncompress singular GZIPped", + " and PKZIPped files.", +#endif + "[-option] =", + " -verbose - Select debugging messages [1]", + " 0 - Silent 1 - Startup messages", + " 2 - V9938 ops 4 - Disk/Tape", + " 8 - Memory 16 - Illegal Z80 ops", + " 32 - I/O", + " -skip - Percentage of frames to skip [25]", + " -pal/-ntsc - Set PAL/NTSC HBlank/VBlank periods [NTSC]", + " -help - Print this help page", + " -home - Set directory with system ROM files [off]", + " -printer - Redirect printer output to file [stdout]", + " -serial - Redirect serial I/O to a file [stdin/stdout]", + " -diska - Set disk image used for drive A: [DRIVEA.DSK]", + " (multiple -diska options accepted)", + " -diskb - Set disk image used for drive B: [DRIVEB.DSK]", + " (multiple -diskb options accepted)", + " -tape - Set tape image file [off]", + " -font - Set fixed font for text modes [DEFAULT.FNT]", + " -logsnd - Set soundtrack log file [LOG.MID]", + " -state - Set emulation state save file [automatic]", + " -auto/-noauto - Use autofire on SPACE [off]", + " -ram - Number of 16kB RAM pages [4/8/8]", + " -vram - Number of 16kB VRAM pages [2/8/8]", + " -rom - Select MegaROM mapper types [8,8]", + " (two -rom options accepted)", + " 0 - Generic 8kB 1 - Generic 16kB (MSXDOS2)", + " 2 - Konami5 8kB 3 - Konami4 8kB", + " 4 - ASCII 8kB 5 - ASCII 16kB", + " 6 - GameMaster2 7 - FMPAC", + " >7 - Try guessing mapper type", + " -msx1/-msx2/-msx2+ - Select MSX model [-msx2]", + " -joy - Select joystick types [0,0]", + " (two -joy options accepted)", + " 0 - No joystick", + " 1 - Normal joystick", + " 2 - Mouse in joystick mode", + " 3 - Mouse in real mode", + " -simbdos/-wd1793 - Simulate DiskROM disk access calls [-wd1793]", + " -sound [] - Sound emulation quality (Hz) [44100]", + " -nosound - Same as '-sound 0'", + +#if defined(DEBUG) + " -trap
- Trap execution when PC reaches address [FFFFh]", + " (when keyword 'now' is used in place of the", + "
, execution will trap immediately)", +#endif /* DEBUG */ + +#if defined(MSDOS) || defined(UNIX) || defined(MAEMO) + " -sync - Sync screen updates to [60]", + " -nosync - Do not sync screen updates", + " -tv/-lcd/-raster - Simulate TV scanlines or LCD raster [off]", + " -linear - Scale display with linear interpolation [off]", + " -soft/-eagle - Scale display with 2xSaI or EAGLE [off]", + " -epx/-scale2x - Scale display with EPX or Scale2X [off]", + " -cmy/-rgb - Simulate CMY/RGB pixel raster [off]", + " -mono/-sepia - Simulate monochrome or sepia CRT [off]", + " -green/-amber - Simulate green or amber CRT [off]", + " -4x3 - Force 4:3 television screen ratio [off]", +#endif /* MSDOS || UNIX || MAEMO */ + +#if defined(UNIX) || defined(MAEMO) + " -saver/-nosaver - Save CPU when inactive [-saver]", +#endif /* UNIX || MAEMO */ + +#if defined(UNIX) +#ifdef MITSHM + " -shm/-noshm - Use MIT SHM extensions for X [-shm]", +#endif + " -scale - Scale window by [2]", +#endif /* UNIX */ + +#if defined(MSDOS) + " -vsync - Sync screen updates to VBlank [-vsync]", +#if defined(BPP8) + " -static/-nostatic - Use static color palette [-nostatic]", +#endif +#if defined(NARROW) + " -480/-200 - Use 640x480 or 320x200 VGA mode [-200]", +#endif +#endif /* MSDOS */ + + "\nKeyboard bindings:", + " [CONTROL] - CONTROL (also: joystick FIRE-A button)", + " [SHIFT] - SHIFT (also: joystick FIRE-B button)", + " [ALT] - GRAPH (also: swap joysticks)", + " [INSERT] - INSERT", + " [DELETE] - DELETE", + " [HOME] - HOME/CLS", + " [END] - SELECT", + " [PGUP] - STOP/BREAK", + " [PGDOWN] - COUNTRY", + " [F6] - Load emulation from .STA file", + " [F7] - Save emulation state to .STA file", + " [F8] - Rewind emulation back in time", + " [F9] - Fast-forward emulation", + " [F10] - Invoke built-in configuration menu", + " [F11] - Reset hardware", + " [F12] - Quit emulation", + " [CONTROL]+[F8] - Toggle scanlines on/off", + " [ALT]+[F8] - Toggle screen softening on/off", +#if defined(DEBUG) + " [CONTROL]+[F10] - Go into the built-in debugger", +#endif + 0 +}; diff --git a/components/msx/fmsx/src/fMSX/I8251.c b/components/msx/fmsx/src/fMSX/I8251.c new file mode 100644 index 0000000..4c85543 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/I8251.c @@ -0,0 +1,196 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** I8251.c **/ +/** **/ +/** This file contains emulation for the Intel 8251 UART **/ +/** chip and the 8253 timer chip implementing a generic **/ +/** RS232 interface. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2004-2021 **/ +/** Maarten ter Huurne 2000 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +#include "I8251.h" + +/** Reset8251 ************************************************/ +/** Reset 8251 chip, assigning In and Out to the input and **/ +/** output streams. **/ +/*************************************************************/ +void Reset8251(register I8251 *D,FILE *In,FILE *Out) +{ + D->IRQMask = 0x0F; /* All interrupts on */ + D->IRQs = 0x00; /* No interrupts yet */ + D->Mode = 1; /* Setting mode next */ + D->Flow = 0x00; /* Flow control off */ + D->NextChr = -1; /* No data yet */ + + /* Assign input and output streams */ + D->In = In? In:stdin; + D->Out = Out? Out:stdout; +} + +/** Rd8251 ***************************************************/ +/** Read a byte from a given 8251 register. All values of R **/ +/** will be truncated to 3 bits as there are only 8 regs. **/ +/*************************************************************/ +byte Rd8251(register I8251 *D,register byte R) +{ + register int J; + + /* We only have 8 addressable ports */ + R&=0x07; + + switch(R) + { + case 0: /* Data */ + if(D->Flow) + { + if(D->NextChr<0) D->NextChr=fgetc(D->In); + J=D->NextChr; + D->NextChr=-1; + return((J<0? 0xFF:J)&((0x20<<((D->Control&0x0C)>>2))-1)); + } + return(0xFF); + + case 1: /* Status */ + if(D->NextChr<0) D->NextChr=fgetc(D->In); + return(D->Flow&&(D->NextChr>=0)? 0x87:0x85); + /* + 76543210 + 1....... data set ready (dsr) + .1...... sync detect, sync only + ..1..... framing error (fe), async only + ...1.... overrun error (oe) + ....1... parity error (pe) + .....1.. transmitter empty + ......1. receiver ready + .......1 transmitter ready + + D->Flow is checked first, so that stdin input doesn't block + fMSX until it's actually being read by the emulated MSX. + */ + + case 0x82: /* Status of CTS, timer/counter 2, RI and CD */ + return(0x7F); + /* + 76543210 + 1....... CTS (Clear To Send) 0=asserted + .1...... timer/counter output-2 from 8253 + ..XXXX.. reserved + ......1. RI (Ring Indicator) 0=asserted + .......1 CD (Carrier Detect) 0=asserted + + RI and CD are optional. If only one of them is implemented, + it must be CD. Implemented like this: + - CTS always returns 0 (asserted) + - Everything else is not implemented + */ + } + + /* Other RS232 ports: + 3: not standardised - unused + 4: 8253 counter 0 - not implemented + 5: 8253 counter 1 - not implemented + 6: 8253 counter 2 - not implemented + 7: 8253 mode register - write only + */ + + return(0xFF); +} + +/** Wr8251 ***************************************************/ +/** Write a byte to a given 8251 register. All values of R **/ +/** will be truncated to 3 bits as there are only 8 regs. **/ +/*************************************************************/ +void Wr8251(register I8251 *D,register byte R,register byte V) +{ + /* We only have 8 addressable ports */ + R&=0x07; + + switch(R) + { + case 0: /* Data */ + fputc(V&((0x20<<((D->Control&0x0C)>>2))-1),D->Out); + fflush(D->Out); + return; + /* + TODO: Flush the stream at appropriate intervals, instead of + flushing for every single character. If flushing after every + character remains, make the stream unbuffered. + */ + + case 1: /* Command */ + if(D->Mode) + { + D->Control=V; + D->Mode=0; + /* Set mode: + ......00 sync mode + ......01 async, baud rate factor is 1 + ......10 async, baud rate factor is 16 + ......11 async, baud rate factor is 64 + ....00.. character length is 5 bits + ....01.. character length is 6 bits + ....10.. character length is 7 bits + ....11.. character length is 8 bits + ...1.... parity enable + ..1..... even/odd parity + .1...... sync: external sync detect (syndet) is an input/output + 1....... sync: single/double character sync + 00...... async: invalid + 01...... async: 1 stop bit + 10...... async: 1.5 stop bits + 11...... async: 2 stop bits + */ + } + else + { + D->Mode=V&0x40; + D->Flow=(V>>4)&0x02; + /* Execute command: + 76543210 + 1....... enter hunt mode (enable search for sync characters) + .1...... internal reset + ..1..... request to send (rts) + ...1.... reset error flags (pe,oe,fe) + ....1... send break character + .....1.. receive enable + ......1. data terminal ready (dtr) + .......1 transmit enable + + Only RESET (bit6) and DTR (bit2) are implemented. + */ + } + return; + + case 2: /* Interrupt mask register */ + D->IRQMask=V; + return; + /* + 76543210 + XXXX.... reserved + ....1... timer interrupt from 8253 channel-2 (0=enable int) + .....1.. sync character detect/break detect (0=enable int) + ......1. transmit data ready (TxReady) (0=enable int) + .......1 receive data ready (RxReady) (0=enable int) + + Initially all interrupts are disabled. RxReady interrupt must be + implemented, the others are optional. + + However, currently no interrupt at all is implemented. The result + is that only one character is read per VDP interrupt, so serial + communication is 50/60 baud. + */ + } + + /* Other RS232 ports: + 3: not standardised - unused + 4: 8253 counter 0 - not implemented + 5: 8253 counter 1 - not implemented + 6: 8253 counter 2 - not implemented + 7: 8253 mode register - not implemented + */ +} diff --git a/components/msx/fmsx/src/fMSX/I8251.h b/components/msx/fmsx/src/fMSX/I8251.h new file mode 100644 index 0000000..2905075 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/I8251.h @@ -0,0 +1,63 @@ +/** EMULib Emulation Library *********************************/ +/** **/ +/** I8251.h **/ +/** **/ +/** This file contains definitions and declarations for **/ +/** routines in I8251.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2004-2021 **/ +/** Maarten ter Huurne 2000 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef I8251_H +#define I8251_H +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef BYTE_TYPE_DEFINED +#define BYTE_TYPE_DEFINED +typedef unsigned char byte; +#endif + +/** I8251 ****************************************************/ +/** This data structure stores I8251 state. **/ +/*************************************************************/ +typedef struct +{ + byte Control; /* 8251 ACIA control reg. */ + byte IRQMask; /* RS232 interrupt mask */ + byte IRQs; /* RS232 interrupts */ + byte Mode; /* 8251 mode/cmd select */ + byte Flow; /* Flow control state */ + int NextChr; /* Next char or -1 */ + FILE *In; /* Input stream */ + FILE *Out; /* Output stream */ +} I8251; + +/** Reset8251 ************************************************/ +/** Reset 8251 chip, assigning In and Out to the input and **/ +/** output streams. **/ +/*************************************************************/ +void Reset8251(register I8251 *D,FILE *In,FILE *Out); + +/** Rd8251 ***************************************************/ +/** Read a byte from a given 8251 register. All values of R **/ +/** will be truncated to 3 bits as there are only 8 regs. **/ +/*************************************************************/ +byte Rd8251(register I8251 *D,register byte R); + +/** Wr8251 ***************************************************/ +/** Write a byte to a given 8251 register. All values of R **/ +/** will be truncated to 3 bits as there are only 8 regs. **/ +/*************************************************************/ +void Wr8251(register I8251 *D,register byte R,register byte V); + +#ifdef __cplusplus +} +#endif +#endif /* I8251_H */ diff --git a/components/msx/fmsx/src/fMSX/MSX.c b/components/msx/fmsx/src/fMSX/MSX.c new file mode 100644 index 0000000..1a789be --- /dev/null +++ b/components/msx/fmsx/src/fMSX/MSX.c @@ -0,0 +1,3438 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** MSX.c **/ +/** **/ +/** This file contains implementation for the MSX-specific **/ +/** hardware: slots, memory mapper, PPIs, VDP, PSG, clock, **/ +/** etc. Initialization code and definitions needed for the **/ +/** machine-dependent drivers are also here. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +#include "MSX.h" +#include "Sound.h" +#include "Floppy.h" +#include "SHA1.h" +#include "MCF.h" + +#include "shared_memory.h" + +#include +#include +#include +#include +#include +#include + +#ifdef __BORLANDC__ +#include +#endif + +#ifdef __WATCOMC__ +#include +#endif + +#ifdef ZLIB +#include +#endif + +#ifdef ANDROID +#include "MemFS.h" +#define puts LOGI +#define printf LOGI +#endif + +#define PRINTOK if(Verbose) puts("OK") +#define PRINTFAILED if(Verbose) puts("FAILED") +#define PRINTRESULT(R) if(Verbose) puts((R)? "OK":"FAILED") + +#define RGB2INT(R,G,B) ((B)|((int)(G)<<8)|((int)(R)<<16)) + +/* MSDOS chdir() is broken and has to be replaced :( */ +#ifdef MSDOS +#include "LibMSDOS.h" +#define chdir(path) ChangeDir(path) +#endif + +/** User-defined parameters for fMSX *************************/ +int Mode = MSX_MSX2|MSX_NTSC|MSX_MSXDOS2|MSX_GUESSA|MSX_GUESSB; +byte Verbose = 1; /* Debug msgs ON/OFF */ +byte UPeriod = 75; /* % of frames to draw */ +int VPeriod = CPU_VPERIOD; /* CPU cycles per VBlank */ +int HPeriod = CPU_HPERIOD; /* CPU cycles per HBlank */ +int RAMPages = 4; /* Number of RAM pages */ +int VRAMPages = 2; /* Number of VRAM pages */ +byte ExitNow = 0; /* 1 = Exit the emulator */ + +/** Main hardware: CPU, RAM, VRAM, mappers *******************/ +Z80 *CPU = NULL; /* Z80 CPU state and regs */ + +byte *msx_VRAM,*VPAGE; /* Video RAM */ + +byte *RAM[8]; /* Main RAM (8x8kB pages) */ +byte *EmptyRAM; /* Empty RAM page (8kB) */ +byte SaveCMOS; /* Save CMOS.ROM on exit */ +byte *MemMap[4][4][8]; /* Memory maps [PPage][SPage][Addr] */ + +byte *RAMData; /* RAM Mapper contents */ +byte RAMMapper[4]; /* RAM Mapper state */ +byte RAMMask; /* RAM Mapper mask */ + +byte *ROMData[MAXSLOTS]; /* ROM Mapper contents */ +byte ROMMapper[MAXSLOTS][4]; /* ROM Mappers state */ +byte ROMMask[MAXSLOTS]; /* ROM Mapper masks */ +byte ROMType[MAXSLOTS]; /* ROM Mapper types */ + +byte EnWrite[4]; /* 1 if write enabled */ +byte PSL[4],SSL[4]; /* Lists of current slots */ +byte PSLReg,SSLReg[4]; /* Storage for A8h port and (FFFFh) */ + +/** Memory blocks to free in TrashMSX() **********************/ +void **Chunks = NULL; /* Memory blocks to free */ +int NChunks; /* Number of memory blcks */ + +/** Working directory names **********************************/ +const char *ProgDir = 0; /* Program directory */ +const char *WorkDir; /* Working directory */ + +/** Cartridge files used by fMSX *****************************/ +const char *ROMName[MAXCARTS] = { "CARTA.ROM","CARTB.ROM" }; + +/** On-cartridge SRAM data ***********************************/ +char *SRAMName[MAXSLOTS] = {0,0,0,0,0,0};/* Filenames (gen-d)*/ +byte SaveSRAM[MAXSLOTS] = {0,0,0,0,0,0}; /* Save SRAM on exit*/ +byte *SRAMData[MAXSLOTS]; /* SRAM (battery backed) */ + +/** Disk images used by fMSX *********************************/ +const char *DSKName[MAXDRIVES] = { "DRIVEA.DSK","DRIVEB.DSK" }; + +/** Soundtrack logging ***************************************/ +const char *SndName = "LOG.MID"; /* Sound log file */ + +/** Emulation state saving ***********************************/ +const char *STAName = "DEFAULT.STA";/* State file (autogen-d)*/ + +/** Fixed font used by fMSX **********************************/ +const char *FNTName = "DEFAULT.FNT"; /* Font file for text */ +byte *FontBuf; /* Font for text modes */ + +/** Printer **************************************************/ +const char *PrnName = 0; /* Printer redirect. file */ +FILE *PrnStream; + +/** Cassette tape ********************************************/ +const char *CasName = "DEFAULT.CAS"; /* Tape image file */ +FILE *CasStream; + +/** Serial port **********************************************/ +const char *ComName = 0; /* Serial redirect. file */ +FILE *ComIStream; +FILE *ComOStream; + +/** Kanji font ROM *******************************************/ +byte *Kanji; /* Kanji ROM 4096x32 */ +int KanLetter; /* Current letter index */ +byte KanCount; /* Byte count 0..31 */ + +/** Keyboard, joystick, and mouse ****************************/ +volatile byte KeyState[16]; /* Keyboard map state */ +word JoyState; /* Joystick states */ +int MouState[2]; /* Mouse states */ +byte MouseDX[2],MouseDY[2]; /* Mouse offsets */ +byte OldMouseX[2],OldMouseY[2]; /* Old mouse coordinates */ +byte MCount[2]; /* Mouse nibble counter */ + +/** General I/O registers: i8255 *****************************/ +I8255 *PPI=NULL; /* i8255 PPI at A8h-ABh */ +byte IOReg; /* Storage for AAh port */ + +/** Disk controller: WD1793 **********************************/ +WD1793 *FDC=NULL; /* WD1793 at 7FF8h-7FFFh */ +FDIDisk *FDD = NULL; /* Floppy disk images */ + +/** Sound hardware: PSG, SCC, OPLL ***************************/ +AY8910 *PSG=NULL; /* PSG registers & state */ +YM2413 *OPLL=NULL; /* OPLL registers & state */ +SCC *SCChip=NULL; /* SCC registers & state */ +byte SCCOn[2]; /* 1 = SCC page active */ +word FMPACKey; /* MAGIC = SRAM active */ + +/** Serial I/O hardware: i8251+i8253 *************************/ +I8251 *SIO=NULL; /* SIO registers & state */ + +/** Real-time clock ******************************************/ +byte RTCReg,RTCMode; /* RTC register numbers */ +byte RTC[4][13]; /* RTC registers */ + +/** Video processor ******************************************/ +byte *ChrGen,*ChrTab,*ColTab; /* VDP tables (screen) */ +byte *SprGen,*SprTab; /* VDP tables (sprites) */ +int ChrGenM,ChrTabM,ColTabM; /* VDP masks (screen) */ +int SprTabM; /* VDP masks (sprites) */ +word VAddr; /* VRAM address in VDP */ +byte VKey,PKey; /* Status keys for VDP */ +byte FGColor,BGColor; /* Colors */ +byte XFGColor,XBGColor; /* Second set of colors */ +byte ScrMode; /* Current screen mode */ +byte VDP[64],VDPStatus[16]; /* VDP registers */ +byte IRQPending; /* Pending interrupts */ +int ScanLine; /* Current scanline */ +byte VDPData; /* VDP data buffer */ +byte PLatch; /* Palette buffer */ +byte ALatch; /* Address buffer */ +int Palette[16]; /* Current palette */ + +/** Cheat entries ********************************************/ +int MCFCount = 0; /* Size of MCFEntries[] */ +MCFEntry *MCFEntries = NULL; /* Entries from .MCF file */ + +/** Cheat codes **********************************************/ +byte CheatsON = 0; /* 1: Cheats are on */ +int CheatCount = 0; /* # cheats, <=MAXCHEATS */ +CheatCode *CheatCodes = NULL; + +/** Places in DiskROM to be patched with ED FE C9 ************/ +static const word DiskPatches[] = +{ 0x4010,0x4013,0x4016,0x401C,0x401F,0 }; + +/** Places in BIOS to be patched with ED FE C9 ***************/ +static const word BIOSPatches[] = +{ 0x00E1,0x00E4,0x00E7,0x00EA,0x00ED,0x00F0,0x00F3,0 }; + +/** Cartridge map, by primary and secondary slots ************/ +static const byte CartMap[4][4] = +{ { 255,3,4,5 },{ 0,0,0,0 },{ 1,1,1,1 },{ 2,255,255,255 } }; + +/** Screen Mode Handlers [number of screens + 1] *************/ +void (*RefreshLine[MAXSCREEN+2])(byte Y) = +{ + RefreshLine0, /* SCR 0: TEXT 40x24 */ + RefreshLine1, /* SCR 1: TEXT 32x24 */ + RefreshLine2, /* SCR 2: BLK 256x192 */ + RefreshLine3, /* SCR 3: 64x48x16 */ + RefreshLine4, /* SCR 4: BLK 256x192 */ + RefreshLine5, /* SCR 5: 256x192x16 */ + RefreshLine6, /* SCR 6: 512x192x4 */ + RefreshLine7, /* SCR 7: 512x192x16 */ + RefreshLine8, /* SCR 8: 256x192x256 */ + 0, /* SCR 9: NONE */ + RefreshLine10, /* SCR 10: YAE 256x192 */ + RefreshLine10, /* SCR 11: YAE 256x192 */ + RefreshLine12, /* SCR 12: YJK 256x192 */ + RefreshLineTx80 /* SCR 0: TEXT 80x24 */ +}; + +/** VDP Address Register Masks *******************************/ +static const struct { byte R2,R3,R4,R5,M2,M3,M4,M5; } MSK[MAXSCREEN+2] = +{ + { 0x7F,0x00,0x3F,0x00,0x00,0x00,0x00,0x00 }, /* SCR 0: TEXT 40x24 */ + { 0x7F,0xFF,0x3F,0xFF,0x00,0x00,0x00,0x00 }, /* SCR 1: TEXT 32x24 */ + { 0x7F,0x80,0x3C,0xFF,0x00,0x7F,0x03,0x00 }, /* SCR 2: BLK 256x192 */ + { 0x7F,0x00,0x3F,0xFF,0x00,0x00,0x00,0x00 }, /* SCR 3: 64x48x16 */ + { 0x7F,0x80,0x3C,0xFC,0x00,0x7F,0x03,0x03 }, /* SCR 4: BLK 256x192 */ + { 0x60,0x00,0x00,0xFC,0x1F,0x00,0x00,0x03 }, /* SCR 5: 256x192x16 */ + { 0x60,0x00,0x00,0xFC,0x1F,0x00,0x00,0x03 }, /* SCR 6: 512x192x4 */ + { 0x20,0x00,0x00,0xFC,0x1F,0x00,0x00,0x03 }, /* SCR 7: 512x192x16 */ + { 0x20,0x00,0x00,0xFC,0x1F,0x00,0x00,0x03 }, /* SCR 8: 256x192x256 */ + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, /* SCR 9: NONE */ + { 0x20,0x00,0x00,0xFC,0x1F,0x00,0x00,0x03 }, /* SCR 10: YAE 256x192 */ + { 0x20,0x00,0x00,0xFC,0x1F,0x00,0x00,0x03 }, /* SCR 11: YAE 256x192 */ + { 0x20,0x00,0x00,0xFC,0x1F,0x00,0x00,0x03 }, /* SCR 12: YJK 256x192 */ + { 0x7C,0xF8,0x3F,0x00,0x03,0x07,0x00,0x00 } /* SCR 0: TEXT 80x24 */ +}; + +/** MegaROM Mapper Names *************************************/ +static const char *ROMNames[MAXMAPPERS+1] = +{ + "GENERIC/8kB","GENERIC/16kB","KONAMI5/8kB", + "KONAMI4/8kB","ASCII/8kB","ASCII/16kB", + "GMASTER2/SRAM","FMPAC/SRAM","UNKNOWN" +}; + +/** Keyboard Mapping *****************************************/ +/** This keyboard mapping is used by KBD_SET()/KBD_RES() **/ +/** macros to modify KeyState[] bits. **/ +/*************************************************************/ +const byte Keys[][2] = +{ + { 0,0x00 },{ 8,0x10 },{ 8,0x20 },{ 8,0x80 }, /* None,LEFT,UP,RIGHT */ + { 8,0x40 },{ 6,0x01 },{ 6,0x02 },{ 6,0x04 }, /* DOWN,SHIFT,CONTROL,GRAPH */ + { 7,0x20 },{ 7,0x08 },{ 6,0x08 },{ 7,0x40 }, /* BS,TAB,CAPSLOCK,SELECT */ + { 8,0x02 },{ 7,0x80 },{ 8,0x08 },{ 8,0x04 }, /* HOME,ENTER,DELETE,INSERT */ + { 6,0x10 },{ 7,0x10 },{ 6,0x20 },{ 6,0x40 }, /* COUNTRY,STOP,F1,F2 */ + { 6,0x80 },{ 7,0x01 },{ 7,0x02 },{ 9,0x08 }, /* F3,F4,F5,PAD0 */ + { 9,0x10 },{ 9,0x20 },{ 9,0x40 },{ 7,0x04 }, /* PAD1,PAD2,PAD3,ESCAPE */ + { 9,0x80 },{ 10,0x01 },{ 10,0x02 },{ 10,0x04 }, /* PAD4,PAD5,PAD6,PAD7 */ + { 8,0x01 },{ 0,0x02 },{ 2,0x01 },{ 0,0x08 }, /* SPACE,[!],["],[#] */ + { 0,0x10 },{ 0,0x20 },{ 0,0x80 },{ 2,0x01 }, /* [$],[%],[&],['] */ + { 1,0x02 },{ 0,0x01 },{ 1,0x01 },{ 1,0x08 }, /* [(],[)],[*],[=] */ + { 2,0x04 },{ 1,0x04 },{ 2,0x08 },{ 2,0x10 }, /* [,],[-],[.],[/] */ + { 0,0x01 },{ 0,0x02 },{ 0,0x04 },{ 0,0x08 }, /* 0,1,2,3 */ + { 0,0x10 },{ 0,0x20 },{ 0,0x40 },{ 0,0x80 }, /* 4,5,6,7 */ + { 1,0x01 },{ 1,0x02 },{ 1,0x80 },{ 1,0x80 }, /* 8,9,[:],[;] */ + { 2,0x04 },{ 1,0x08 },{ 2,0x08 },{ 2,0x10 }, /* [<],[=],[>],[?] */ + { 0,0x04 },{ 2,0x40 },{ 2,0x80 },{ 3,0x01 }, /* [@],A,B,C */ + { 3,0x02 },{ 3,0x04 },{ 3,0x08 },{ 3,0x10 }, /* D,E,F,G */ + { 3,0x20 },{ 3,0x40 },{ 3,0x80 },{ 4,0x01 }, /* H,I,J,K */ + { 4,0x02 },{ 4,0x04 },{ 4,0x08 },{ 4,0x10 }, /* L,M,N,O */ + { 4,0x20 },{ 4,0x40 },{ 4,0x80 },{ 5,0x01 }, /* P,Q,R,S */ + { 5,0x02 },{ 5,0x04 },{ 5,0x08 },{ 5,0x10 }, /* T,U,V,W */ + { 5,0x20 },{ 5,0x40 },{ 5,0x80 },{ 1,0x20 }, /* X,Y,Z,[[] */ + { 1,0x10 },{ 1,0x40 },{ 0,0x40 },{ 1,0x04 }, /* [\],[]],[^],[_] */ + { 2,0x02 },{ 2,0x40 },{ 2,0x80 },{ 3,0x01 }, /* [`],a,b,c */ + { 3,0x02 },{ 3,0x04 },{ 3,0x08 },{ 3,0x10 }, /* d,e,f,g */ + { 3,0x20 },{ 3,0x40 },{ 3,0x80 },{ 4,0x01 }, /* h,i,j,k */ + { 4,0x02 },{ 4,0x04 },{ 4,0x08 },{ 4,0x10 }, /* l,m,n,o */ + { 4,0x20 },{ 4,0x40 },{ 4,0x80 },{ 5,0x01 }, /* p,q,r,s */ + { 5,0x02 },{ 5,0x04 },{ 5,0x08 },{ 5,0x10 }, /* t,u,v,w */ + { 5,0x20 },{ 5,0x40 },{ 5,0x80 },{ 1,0x20 }, /* x,y,z,[{] */ + { 1,0x10 },{ 1,0x40 },{ 2,0x02 },{ 8,0x08 }, /* [|],[}],[~],DEL */ + { 10,0x08 },{ 10,0x10 } /* PAD8,PAD9 */ +}; + +/** Internal Functions ***************************************/ +/** These functions are defined and internally used by the **/ +/** code in MSX.c. **/ +/*************************************************************/ +byte *LoadROM(const char *Name,int Size,byte *Buf); +int GuessROM(const byte *Buf,int Size); +int FindState(const char *Name); +void SetMegaROM(int Slot,byte P0,byte P1,byte P2,byte P3); +void MapROM(word A,byte V); /* Switch MegaROM banks */ +void PSlot(byte V); /* Switch primary slots */ +void SSlot(byte V); /* Switch secondary slots */ +void VDPOut(byte R,byte V); /* Write value into a VDP register */ +void Printer(byte V); /* Send a character to a printer */ +void PPIOut(byte New,byte Old); /* Set PPI bits (key click, etc.) */ +int CheckSprites(void); /* Check for sprite collisions */ +byte RTCIn(byte R); /* Read RTC registers */ +byte SetScreen(void); /* Change screen mode */ +word SetIRQ(byte IRQ); /* Set/Reset IRQ */ +word StateID(void); /* Compute emulation state ID */ +int ApplyCheats(void); /* Apply RAM-based cheats */ + +static int hasext(const char *FileName,const char *Ext); +static byte *GetMemory(int Size); /* Get memory chunk */ +static void FreeMemory(const void *Ptr); /* Free memory chunk */ +static void FreeAllMemory(void); /* Free all memory chunks */ + +/** hasext() *************************************************/ +/** Check if file name has given extension. **/ +/*************************************************************/ +static int hasext(const char *FileName,const char *Ext) +{ + const char *P; + int J; + + /* Start searching from the end, terminate at directory name */ + for(P=FileName+strlen(FileName);(P>=FileName)&&(*P!='/')&&(*P!='\\');--P) + { + /* Locate start of the next extension */ + for(--P;(P>=FileName)&&(*P!='/')&&(*P!='\\')&&(*P!=*Ext);--P); + /* If next extension not found, return FALSE */ + if((P=MAXCHUNKS)) return(0); + P=(byte *)malloc(Size); + if(P) Chunks[NChunks++]=P; + + return(P); +} + +/** FreeMemory() *********************************************/ +/** Free memory allocated by a previous GetMemory() call. **/ +/*************************************************************/ +static void FreeMemory(const void *Ptr) +{ + int J; + + /* Special case: we do not free EmptyRAM! */ + if(!Ptr||(Ptr==(void *)EmptyRAM)) return; + + for(J=0;(J100? 100:UPeriod; + + /* Allocate 16kB for the empty space (scratch RAM) */ + if(Verbose) printf("Allocating 16kB for empty space...\n"); + if(!(EmptyRAM=GetMemory(0x4000))) { PRINTFAILED;return(0); } + memset(EmptyRAM,NORAM,0x4000); + + /* Reset memory map to the empty space */ + for(I=0;I<4;++I) + for(J=0;J<4;++J) + for(K=0;K<8;++K) + MemMap[I][J][K]=EmptyRAM; + + /* Save current directory */ + if(ProgDir&&(WorkDir=getcwd(0,1024))) Chunks[NChunks++]=(void *)WorkDir; + + /* Set invalid modes and RAM/VRAM sizes before calling ResetMSX() */ + Mode = ~NewMode; + RAMPages = 0; + VRAMPages = 0; + + /* Try resetting MSX, allocating memory, loading ROMs */ + if((ResetMSX(NewMode,NewRAMPages,NewVRAMPages)^NewMode)&MSX_MODEL) return(0); + if(!RAMPages||!VRAMPages) return(0); + + /* Change to the program directory */ + if(ProgDir && chdir(ProgDir)) + { if(Verbose) printf("Failed changing to '%s' directory!\n",ProgDir); } + + /* Try loading font */ + if(FNTName) + { + if(Verbose) printf("Loading %s font...",FNTName); + J=LoadFNT(FNTName); + PRINTRESULT(J); + } + + if(Verbose) printf("Loading optional ROMs: "); + + /* Try loading CMOS memory contents */ + if(LoadROM("CMOS.ROM",sizeof(RTC),(byte *)RTC)) + { if(Verbose) printf("CMOS.ROM.."); } + else memcpy(RTC,RTCInit,sizeof(RTC)); + + /* Try loading Kanji alphabet ROM */ + if((Kanji=LoadROM("KANJI.ROM",0x20000,0))) + { if(Verbose) printf("KANJI.ROM.."); } + + /* Try loading RS232 support ROM to slot */ + if((P=LoadROM("RS232.ROM",0x4000,0))) + { + if(Verbose) printf("RS232.ROM.."); + MemMap[3][3][2]=P; + MemMap[3][3][3]=P+0x2000; + } + + PRINTOK; + + /* Start loading system cartridges */ + J=MAXCARTS; + + /* If MSX2 or better and DiskROM present... */ + /* ...try loading MSXDOS2 cartridge into 3:0 */ + if(!MODEL(MSX_MSX1)&&OPTION(MSX_MSXDOS2)&&(MemMap[3][1][2]!=EmptyRAM)&&!ROMData[2]) + if(LoadCart("MSXDOS2.ROM",2,MAP_GEN16)) + SetMegaROM(2,0,1,ROMMask[J]-1,ROMMask[J]); + + /* If MSX2 or better, load PAINTER cartridge */ + if(!MODEL(MSX_MSX1)) + { + for(;(JVerbose=Verbose&0x04; + + /* Open disk images */ + for(J=0;JIPeriod = CPU_H240; + CPU->IAutoReset = 0; + + /* Numbers of RAM/VRAM pages should be power of 2 */ + for(J=1;J256)) + NewRAMPages=MODEL(MSX_MSX1)? 4:8; + if((NewVRAMPages<(MODEL(MSX_MSX1)? 2:8))||(NewVRAMPages>8)) + NewVRAMPages=MODEL(MSX_MSX1)? 2:8; + + /* If changing amount of RAM... */ + if(NewRAMPages!=RAMPages) + { + if(Verbose) printf("Allocating %dkB for RAM...",NewRAMPages*16); + if((P1=GetMemory(NewRAMPages*0x4000))) + { + memset(P1,NORAM,NewRAMPages*0x4000); + FreeMemory(RAMData); + RAMPages = NewRAMPages; + RAMMask = NewRAMPages-1; + RAMData = P1; + } + PRINTRESULT(P1); + } + + /* If changing amount of VRAM... */ + if(NewVRAMPages!=VRAMPages) + { + if(Verbose) printf("Allocating %dkB for VRAM...",NewVRAMPages*16); + if((P1=GetMemory(NewVRAMPages*0x4000))) + { + memset(P1,0x00,NewVRAMPages*0x4000); + FreeMemory(msx_VRAM); + VRAMPages = NewVRAMPages; + msx_VRAM = P1; + } + PRINTRESULT(P1); + } + + /* For all slots... */ + for(J=0;J<4;++J) + { + /* Slot is currently read-only */ + EnWrite[J] = 0; + /* PSL=0:0:0:0, SSL=0:0:0:0 */ + PSL[J] = 0; + SSL[J] = 0; + /* RAMMap=3:2:1:0 */ + MemMap[3][2][J*2] = RAMData+(3-J)*0x4000; + MemMap[3][2][J*2+1] = MemMap[3][2][J*2]+0x2000; + RAMMapper[J] = 3-J; + /* Setting address space */ + RAM[J*2] = MemMap[0][0][J*2]; + RAM[J*2+1] = MemMap[0][0][J*2+1]; + } + + /* For all MegaROMs... */ + for(J=0;J4) + { + /* For normal MegaROMs, set first four pages */ + if((ROMData[J][0]=='A')&&(ROMData[J][1]=='B')) + SetMegaROM(J,0,1,2,3); + /* Some MegaROMs default to last pages on reset */ + else if((ROMData[J][(I-2)<<13]=='A')&&(ROMData[J][((I-2)<<13)+1]=='B')) + SetMegaROM(J,I-2,I-1,I-2,I-1); + /* If 'AB' signature is not found at the beginning or the end */ + /* then it is not a MegaROM but rather a plain 64kB ROM */ + } + + /* Reset sound chips */ + Reset8910(PSG,PSG_CLOCK,0); + ResetSCC(SCChip,AY8910_CHANNELS); + Reset2413(OPLL,AY8910_CHANNELS); + Sync8910(PSG,AY8910_SYNC); + SyncSCC(SCChip,SCC_SYNC); + Sync2413(OPLL,YM2413_SYNC); + + /* Reset serial I/O */ + Reset8251(SIO,ComIStream,ComOStream); + + /* Reset PPI chips and slot selectors */ + Reset8255(PPI); + PPI->Rout[0]=PSLReg=0x00; + PPI->Rout[2]=IOReg=0x00; + SSLReg[0]=0x00; + SSLReg[1]=0x00; + SSLReg[2]=0x00; + SSLReg[3]=0x00; + + /* Reset floppy disk controller */ + Reset1793(FDC,FDD,WD1793_KEEP); + + /* Reset VDP */ + memcpy(VDP,VDPInit,sizeof(VDP)); + memcpy(VDPStatus,VDPSInit,sizeof(VDPStatus)); + + /* Reset keyboard */ + memset((void *)KeyState,0xFF,16); + + /* Set initial palette */ + for(J=0;J<16;++J) + { + Palette[J]=PalInit[J]; + SetColor(J,(Palette[J]>>16)&0xFF,(Palette[J]>>8)&0xFF,Palette[J]&0xFF); + } + + /* Reset mouse coordinates/counters */ + for(J=0;J<2;++J) + MouState[J]=MouseDX[J]=MouseDY[J]=OldMouseX[J]=OldMouseY[J]=MCount[J]=0; + + IRQPending=0x00; /* No IRQs pending */ + SCCOn[0]=SCCOn[1]=0; /* SCCs off for now */ + RTCReg=RTCMode=0; /* Clock registers */ + KanCount=0;KanLetter=0; /* Kanji extension */ + ChrTab=ColTab=ChrGen=msx_VRAM; /* VDP tables */ + SprTab=SprGen=msx_VRAM; + ChrTabM=ColTabM=ChrGenM=SprTabM=~0; /* VDP addr. masks */ + VPAGE=msx_VRAM; /* VRAM page */ + FGColor=BGColor=XFGColor=XBGColor=0; /* VDP colors */ + ScrMode=0; /* Screen mode */ + VKey=PKey=1; /* VDP keys */ + VAddr=0x0000; /* VRAM access addr */ + ScanLine=0; /* Current scanline */ + VDPData=NORAM; /* VDP data buffer */ + JoyState=0; /* Joystick state */ + + /* Set "V9958" VDP version for MSX2+ */ + if(MODEL(MSX_MSX2P)) VDPStatus[1]|=0x04; + + /* Reset CPU */ + msx_ResetZ80(CPU); + + /* Done */ + return(Mode); +} + +/** RdZ80() **************************************************/ +/** Z80 emulation calls this function to read a byte from **/ +/** address A in the Z80 address space. Also see OpZ80() in **/ +/** Z80.c which is a simplified code-only RdZ80() version. **/ +/*************************************************************/ +byte msx_RdZ80(word A) +{ + /* Filter out everything but [xx11 1111 1xxx 1xxx] */ + if((A&0x3F88)!=0x3F88) return(RAM[A>>13][A&0x1FFF]); + + /* Secondary slot selector */ + if(A==0xFFFF) return(~SSLReg[PSL[3]]); + + /* Floppy disk controller */ + /* 7FF8h..7FFFh Standard DiskROM */ + /* BFF8h..BFFFh MSX-DOS BDOS */ + /* 7F80h..7F87h Arabic DiskROM */ + /* 7FB8h..7FBFh SV738/TechnoAhead */ + if((PSL[A>>14]==3)&&(SSL[A>>14]==1)) + switch(A) + { + /* Standard MSX-DOS Arabic SV738 */ + case 0x7FF8: case 0xBFF8: case 0x7F80: case 0x7FB8: /* STATUS */ + case 0x7FF9: case 0xBFF9: case 0x7F81: case 0x7FB9: /* TRACK */ + case 0x7FFA: case 0xBFFA: case 0x7F82: case 0x7FBA: /* SECTOR */ + case 0x7FFB: case 0xBFFB: case 0x7F83: case 0x7FBB: /* DATA */ + return(Read1793(FDC,A&0x0003)); + case 0x7FFF: case 0xBFFF: case 0x7F84: case 0x7FBC: /* SYSTEM */ + return(Read1793(FDC,WD1793_READY)); + } + + /* Default to reading memory */ + return(RAM[A>>13][A&0x1FFF]); +} + +/** WrZ80() **************************************************/ +/** Z80 emulation calls this function to write byte V to **/ +/** address A of Z80 address space. **/ +/*************************************************************/ +void msx_WrZ80(word A,byte V) +{ + /* Secondary slot selector */ + if(A==0xFFFF) { SSlot(V);return; } + + /* Floppy disk controller */ + /* 7FF8h..7FFFh Standard DiskROM */ + /* BFF8h..BFFFh MSX-DOS BDOS */ + /* 7F80h..7F87h Arabic DiskROM */ + /* 7FB8h..7FBFh SV738/TechnoAhead */ + if(((A&0x3F88)==0x3F88)&&(PSL[A>>14]==3)&&(SSL[A>>14]==1)) + switch(A) + { + /* Standard MSX-DOS Arabic SV738 */ + case 0x7FF8: case 0xBFF8: case 0x7F80: case 0x7FB8: /* COMMAND */ + case 0x7FF9: case 0xBFF9: case 0x7F81: case 0x7FB9: /* TRACK */ + case 0x7FFA: case 0xBFFA: case 0x7F82: case 0x7FBA: /* SECTOR */ + case 0x7FFB: case 0xBFFB: case 0x7F83: case 0x7FBB: /* DATA */ + Write1793(FDC,A&0x0003,V); + return; + case 0xBFFC: /* Standard/MSX-DOS */ + case 0x7FFC: /* Side: [xxxxxxxS] */ + Write1793(FDC,WD1793_SYSTEM,FDC->Drive|S_DENSITY|(V&0x01? 0:S_SIDE)); + return; + case 0xBFFD: /* Standard/MSX-DOS */ + case 0x7FFD: /* Drive: [xxxxxxxD] */ + Write1793(FDC,WD1793_SYSTEM,(V&0x01)|S_DENSITY|(FDC->Side? 0:S_SIDE)); + return; + case 0x7FBC: /* Arabic/SV738 */ + case 0x7F84: /* Side/Drive/Motor: [xxxxMSDD] */ + Write1793(FDC,WD1793_SYSTEM,(V&0x03)|S_DENSITY|(V&0x04? 0:S_SIDE)); + return; + } + + /* Write to RAM, if enabled */ + if(EnWrite[A>>14]) { RAM[A>>13][A&0x1FFF]=V;return; } + + /* Switch MegaROM pages */ + if((A>0x3FFF)&&(A<0xC000)) MapROM(A,V); +} + +/** InZ80() **************************************************/ +/** Z80 emulation calls this function to read a byte from **/ +/** a given I/O port. **/ +/*************************************************************/ +byte msx_InZ80(word Port) +{ + /* MSX only uses 256 IO ports */ + Port&=0xFF; + + /* Return an appropriate port value */ + switch(Port) + { + +case 0x90: return(0xFD); /* Printer READY signal */ +case 0xB5: return(RTCIn(RTCReg)); /* RTC registers */ + +case 0xA8: /* Primary slot state */ +case 0xA9: /* Keyboard port */ +case 0xAA: /* General IO register */ +case 0xAB: /* PPI control register */ + PPI->Rin[1]=KeyState[PPI->Rout[2]&0x0F]; + return(Read8255(PPI,Port-0xA8)); + +case 0xFC: /* Mapper page at 0000h */ +case 0xFD: /* Mapper page at 4000h */ +case 0xFE: /* Mapper page at 8000h */ +case 0xFF: /* Mapper page at C000h */ + return(RAMMapper[Port-0xFC]|~RAMMask); + +case 0xD9: /* Kanji support */ + Port=Kanji? Kanji[KanLetter+KanCount]:NORAM; + KanCount=(KanCount+1)&0x1F; + return(Port); + +case 0x80: /* SIO data */ +case 0x81: +case 0x82: +case 0x83: +case 0x84: +case 0x85: +case 0x86: +case 0x87: + return(NORAM); + /*return(Rd8251(SIO,Port&0x07));*/ + +case 0x98: /* VRAM read port */ + /* Read from VRAM data buffer */ + Port=VDPData; + /* Reset VAddr latch sequencer */ + VKey=1; + /* Fill data buffer with a new value */ + VDPData=VPAGE[VAddr]; + /* Increment VRAM address */ + VAddr=(VAddr+1)&0x3FFF; + /* If rolled over, modify VRAM page# */ + if(!VAddr&&(ScrMode>3)) + { + VDP[14]=(VDP[14]+1)&(VRAMPages-1); + VPAGE=msx_VRAM+((int)VDP[14]<<14); + } + return(Port); + +case 0x99: /* VDP status registers */ + /* Read an appropriate status register */ + Port=VDPStatus[VDP[15]]; + /* Reset VAddr latch sequencer */ +// @@@ This breaks Sir Lancelot on ColecoVision, so it must be wrong! +// VKey=1; + /* Update status register's contents */ + switch(VDP[15]) + { + case 0: VDPStatus[0]&=0x5F;SetIRQ(~INT_IE0);break; + case 1: VDPStatus[1]&=0xFE;SetIRQ(~INT_IE1);break; + case 7: VDPStatus[7]=VDP[44]=VDPRead();break; + } + /* Return the status register value */ + return(Port); + +case 0xA2: /* PSG input port */ + /* PSG[14] returns joystick/mouse data */ + if(PSG->Latch==14) + { + int DX,DY,L,J; + + /* Number of a joystick port */ + Port = (PSG->R[15]&0x40)>>6; + L = JOYTYPE(Port); + + /* If no joystick, return dummy value */ + if(L==JOY_NONE) return(0x7F); + + /* Compute mouse offsets, if needed */ + if(MCount[Port]==1) + { + /* Get new mouse coordinates */ + DX=MouState[Port]&0xFF; + DY=(MouState[Port]>>8)&0xFF; + /* Compute offsets and store coordinates */ + J=OldMouseX[Port]-DX;OldMouseX[Port]=DX;DX=J; + J=OldMouseY[Port]-DY;OldMouseY[Port]=DY;DY=J; + /* For 512-wide mode, double horizontal offset */ + if((ScrMode==6)||((ScrMode==7)&&!ModeYJK)||(ScrMode==MAXSCREEN+1)) DX<<=1; + /* Adjust offsets */ + MouseDX[Port]=(DX>127? 127:(DX<-127? -127:DX))&0xFF; + MouseDY[Port]=(DY>127? 127:(DY<-127? -127:DY))&0xFF; + } + + /* Get joystick state */ + J=~(Port? (JoyState>>8):JoyState)&0x3F; + + /* Determine return value */ + switch(MCount[Port]) + { + case 0: Port=PSG->R[15]&(0x10<>4)|(J&0x30);break; + case 2: Port=(MouseDX[Port]&0x0F)|(J&0x30);break; + case 3: Port=(MouseDY[Port]>>4)|(J&0x30);break; + case 4: Port=(MouseDY[Port]&0x0F)|(J&0x30);break; + } + + /* 6th bit is always 1 */ + return(Port|0x40); + } + + /* PSG[15] resets mouse counters (???) */ + if(PSG->Latch==15) + { + /* @@@ For debugging purposes */ + /*printf("Reading from PSG[15]\n");*/ + + /*MCount[0]=MCount[1]=0;*/ + return(PSG->R[15]&0xF0); + } + + /* Return PSG[0-13] as they are */ + return(RdData8910(PSG)); + +case 0xD0: /* FDC status */ +case 0xD1: /* FDC track */ +case 0xD2: /* FDC sector */ +case 0xD3: /* FDC data */ +case 0xD4: /* FDC IRQ/DRQ */ + /* Brazilian DiskROM I/O ports */ + return(Read1793(FDC,Port-0xD0)); + + } + + /* Return NORAM for non-existing ports */ + if(Verbose&0x20) printf("I/O: Read from unknown PORT[%02Xh]\n",Port); + return(NORAM); +} + +/** OutZ80() *************************************************/ +/** Z80 emulation calls this function to write byte V to a **/ +/** given I/O port. **/ +/*************************************************************/ +void msx_OutZ80(word Port,byte Value) +{ + register byte I,J; + + Port&=0xFF; + switch(Port) + { + +case 0x7C: WrCtrl2413(OPLL,Value);return; /* OPLL Register# */ +case 0x7D: WrData2413(OPLL,Value);return; /* OPLL Data */ +case 0x91: Printer(Value);return; /* Printer Data */ +case 0xA0: WrCtrl8910(PSG,Value);return; /* PSG Register# */ +case 0xB4: RTCReg=Value&0x0F;return; /* RTC Register# */ + +case 0xD8: /* Upper bits of Kanji ROM address */ + KanLetter=(KanLetter&0x1F800)|((int)(Value&0x3F)<<5); + KanCount=0; + return; + +case 0xD9: /* Lower bits of Kanji ROM address */ + KanLetter=(KanLetter&0x007E0)|((int)(Value&0x3F)<<11); + KanCount=0; + return; + +case 0x80: /* SIO data */ +case 0x81: +case 0x82: +case 0x83: +case 0x84: +case 0x85: +case 0x86: +case 0x87: + return; + /*Wr8251(SIO,Port&0x07,Value); + return;*/ + +case 0x98: /* VDP Data */ + VKey=1; + VDPData=VPAGE[VAddr]=Value; + VAddr=(VAddr+1)&0x3FFF; + /* If VAddr rolled over, modify VRAM page# */ + if(!VAddr&&(ScrMode>3)) + { + VDP[14]=(VDP[14]+1)&(VRAMPages-1); + VPAGE=msx_VRAM+((int)VDP[14]<<14); + } + return; + +case 0x99: /* VDP Address Latch */ + if(VKey) { ALatch=Value;VKey=0; } + else + { + VKey=1; + switch(Value&0xC0) + { + case 0x80: + /* Writing into VDP registers */ + VDPOut(Value&0x3F,ALatch); + break; + case 0x00: + case 0x40: + /* Set the VRAM access address */ + VAddr=(((word)Value<<8)+ALatch)&0x3FFF; + /* When set for reading, perform first read */ + if(!(Value&0x40)) + { + VDPData=VPAGE[VAddr]; + VAddr=(VAddr+1)&0x3FFF; + if(!VAddr&&(ScrMode>3)) + { + VDP[14]=(VDP[14]+1)&(VRAMPages-1); + VPAGE=msx_VRAM+((int)VDP[14]<<14); + } + } + break; + } + } + return; + +case 0x9A: /* VDP Palette Latch */ + if(PKey) { PLatch=Value;PKey=0; } + else + { + byte R,G,B; + /* New palette entry written */ + PKey=1; + J=VDP[16]; + /* Compute new color components */ + R=(PLatch&0x70)*255/112; + G=(Value&0x07)*255/7; + B=(PLatch&0x07)*255/7; + /* Set new color for palette entry J */ + Palette[J]=RGB2INT(R,G,B); + SetColor(J,R,G,B); + /* Next palette entry */ + VDP[16]=(J+1)&0x0F; + } + return; + +case 0x9B: /* VDP Register Access */ + J=VDP[17]&0x3F; + if(J!=17) VDPOut(J,Value); + if(!(VDP[17]&0x80)) VDP[17]=(J+1)&0x3F; + return; + +case 0xA1: /* PSG Data */ + /* PSG[15] is responsible for joystick/mouse */ + if(PSG->Latch==15) + { + /* @@@ For debugging purposes */ + /*printf("Writing PSG[15] <- %02Xh\n",Value);*/ + + /* For mouse, update nibble counter */ + /* For joystick, set nibble counter to 0 */ + if((Value&0x0C)==0x0C) MCount[1]=0; + else if((JOYTYPE(1)==JOY_MOUSE)&&((Value^PSG->R[15])&0x20)) + MCount[1]+=MCount[1]==4? -3:1; + + /* For mouse, update nibble counter */ + /* For joystick, set nibble counter to 0 */ + if((Value&0x03)==0x03) MCount[0]=0; + else if((JOYTYPE(0)==JOY_MOUSE)&&((Value^PSG->R[15])&0x10)) + MCount[0]+=MCount[0]==4? -3:1; + } + + /* Put value into a register */ + WrData8910(PSG,Value); + return; + +case 0xA8: /* Primary slot state */ +case 0xA9: /* Keyboard port */ +case 0xAA: /* General IO register */ +case 0xAB: /* PPI control register */ + /* Write to PPI */ + Write8255(PPI,Port-0xA8,Value); + /* If general I/O register has changed... */ + if(PPI->Rout[2]!=IOReg) { PPIOut(PPI->Rout[2],IOReg);IOReg=PPI->Rout[2]; } + /* If primary slot state has changed... */ + if(PPI->Rout[0]!=PSLReg) PSlot(PPI->Rout[0]); + /* Done */ + return; + +case 0xB5: /* RTC Data */ + if(RTCReg<13) + { + /* J = register bank# now */ + J=RTCMode&0x03; + /* Store the value */ + RTC[J][RTCReg]=Value; + /* If CMOS modified, we need to save it */ + if(J>1) SaveCMOS=1; + return; + } + /* RTC[13] is a register bank# */ + if(RTCReg==13) RTCMode=Value; + return; + +case 0xD0: /* FDC command */ +case 0xD1: /* FDC track */ +case 0xD2: /* FDC sector */ +case 0xD3: /* FDC data */ + /* Brazilian DiskROM I/O ports */ + Write1793(FDC,Port-0xD0,Value); + return; + +case 0xD4: /* FDC system */ + /* Brazilian DiskROM drive/side: [xxxSxxDx] */ + Value=((Value&0x02)>>1)|S_DENSITY|(Value&0x10? 0:S_SIDE); + Write1793(FDC,WD1793_SYSTEM,Value); + return; + +case 0xFC: /* Mapper page at 0000h */ +case 0xFD: /* Mapper page at 4000h */ +case 0xFE: /* Mapper page at 8000h */ +case 0xFF: /* Mapper page at C000h */ + J=Port-0xFC; + Value&=RAMMask; + if(RAMMapper[J]!=Value) + { + if(Verbose&0x08) printf("RAM-MAPPER: block %d at %Xh\n",Value,J*0x4000); + I=J<<1; + RAMMapper[J] = Value; + MemMap[3][2][I] = RAMData+((int)Value<<14); + MemMap[3][2][I+1] = MemMap[3][2][I]+0x2000; + if((PSL[J]==3)&&(SSL[J]==2)) + { + EnWrite[J] = 1; + RAM[I] = MemMap[3][2][I]; + RAM[I+1] = MemMap[3][2][I+1]; + } + } + return; + + } + + /* Unknown port */ + if(Verbose&0x20) + printf("I/O: Write to unknown PORT[%02Xh]=%02Xh\n",Port,Value); +} + +/** MapROM() *************************************************/ +/** Switch ROM Mapper pages. This function is supposed to **/ +/** be called when ROM page registers are written to. **/ +/*************************************************************/ +void MapROM(register word A,register byte V) +{ + byte I,J,PS,SS,*P; + +/* @@@ For debugging purposes +printf("(%04Xh) = %02Xh at PC=%04Xh\n",A,V,CPU.PC.W); +*/ + + J = A>>14; /* 16kB page number 0-3 */ + PS = PSL[J]; /* Primary slot number */ + SS = SSL[J]; /* Secondary slot number */ + I = CartMap[PS][SS]; /* Cartridge number */ + + /* Drop out if no cartridge in that slot */ + if(I>=MAXSLOTS) return; + + /* SCC: enable/disable for no cart */ + if(!ROMData[I]&&(A==0x9000)) SCCOn[I]=(V==0x3F)? 1:0; + + /* If writing to SCC... */ + if(SCCOn[I]&&((A&0xDF00)==0x9800)) + { + /* Compute SCC register number */ + J=A&0x00FF; + + /* If using SCC+... */ + if(A&0x2000) + { + /* When no MegaROM present, we allow the program */ + /* to write into SCC wave buffer using EmptyRAM */ + /* as a scratch pad. */ + if(!ROMData[I]&&(J<0xA0)) EmptyRAM[0x1800+J]=V; + + /* Output data to SCC chip */ + WriteSCCP(SCChip,J,V); + } + else + { + /* When no MegaROM present, we allow the program */ + /* to write into SCC wave buffer using EmptyRAM */ + /* as a scratch pad. */ + if(!ROMData[I]&&(J<0x80)) EmptyRAM[0x1800+J]=V; + + /* Output data to SCC chip */ + WriteSCC(SCChip,J,V); + } + + /* Done writing to SCC */ + return; + } + + /* If no cartridge or no mapper, exit */ + if(!ROMData[I]||!ROMMask[I]) return; + + switch(ROMType[I]) + { + case MAP_GEN8: /* Generic 8kB cartridges (Konami, etc.) */ + /* Only interested in writes to 4000h-BFFFh */ + if((A<0x4000)||(A>0xBFFF)) break; + J=(A-0x4000)>>13; + /* Turn SCC on/off on writes to 8000h-9FFFh */ + if(J==2) SCCOn[I]=(V==0x3F)? 1:0; + /* Switch ROM pages */ + V&=ROMMask[I]; + if(V!=ROMMapper[I][J]) + { + RAM[J+2]=MemMap[PS][SS][J+2]=ROMData[I]+((int)V<<13); + ROMMapper[I][J]=V; + } + if(Verbose&0x08) + printf("ROM-MAPPER %c: 8kB ROM page #%d at %d:%d:%04Xh\n",I+'A',V,PS,SS,J*0x2000+0x4000); + return; + + case MAP_GEN16: /* Generic 16kB cartridges (MSXDOS2, HoleInOneSpecial) */ + /* Only interested in writes to 4000h-BFFFh */ + if((A<0x4000)||(A>0xBFFF)) break; + J=(A&0x8000)>>14; + /* Switch ROM pages */ + V=(V<<1)&ROMMask[I]; + if(V!=ROMMapper[I][J]) + { + RAM[J+2]=MemMap[PS][SS][J+2]=ROMData[I]+((int)V<<13); + RAM[J+3]=MemMap[PS][SS][J+3]=RAM[J+2]+0x2000; + ROMMapper[I][J]=V; + ROMMapper[I][J+1]=V|1; + } + if(Verbose&0x08) + printf("ROM-MAPPER %c: 16kB ROM page #%d at %d:%d:%04Xh\n",I+'A',V>>1,PS,SS,J*0x2000+0x4000); + return; + + case MAP_KONAMI5: /* KONAMI5 8kB cartridges */ + /* Only interested in writes to 5000h/7000h/9000h/B000h */ + if((A<0x5000)||(A>0xB000)||((A&0x1FFF)!=0x1000)) break; + J=(A-0x5000)>>13; + /* Turn SCC on/off on writes to 9000h */ + if(J==2) SCCOn[I]=(V==0x3F)? 1:0; + /* Switch ROM pages */ + V&=ROMMask[I]; + if(V!=ROMMapper[I][J]) + { + RAM[J+2]=MemMap[PS][SS][J+2]=ROMData[I]+((int)V<<13); + ROMMapper[I][J]=V; + } + if(Verbose&0x08) + printf("ROM-MAPPER %c: 8kB ROM page #%d at %d:%d:%04Xh\n",I+'A',V,PS,SS,J*0x2000+0x4000); + return; + + case MAP_KONAMI4: /* KONAMI4 8kB cartridges */ + /* Only interested in writes to 6000h/8000h/A000h */ + /* (page at 4000h is fixed) */ + if((A<0x6000)||(A>0xA000)||(A&0x1FFF)) break; + J=(A-0x4000)>>13; + /* Switch ROM pages */ + V&=ROMMask[I]; + if(V!=ROMMapper[I][J]) + { + RAM[J+2]=MemMap[PS][SS][J+2]=ROMData[I]+((int)V<<13); + ROMMapper[I][J]=V; + } + if(Verbose&0x08) + printf("ROM-MAPPER %c: 8kB ROM page #%d at %d:%d:%04Xh\n",I+'A',V,PS,SS,J*0x2000+0x4000); + return; + + case MAP_ASCII8: /* ASCII 8kB cartridges */ + /* If switching pages... */ + if((A>=0x6000)&&(A<0x8000)) + { + J=(A&0x1800)>>11; + /* If selecting SRAM... */ + if(V&(ROMMask[I]+1)) + { + /* Select SRAM page */ + V=0xFF; + P=SRAMData[I]; + if(Verbose&0x08) + printf("ROM-MAPPER %c: 8kB SRAM at %d:%d:%04Xh\n",I+'A',PS,SS,J*0x2000+0x4000); + } + else + { + /* Select ROM page */ + V&=ROMMask[I]; + P=ROMData[I]+((int)V<<13); + if(Verbose&0x08) + printf("ROM-MAPPER %c: 8kB ROM page #%d at %d:%d:%04Xh\n",I+'A',V,PS,SS,J*0x2000+0x4000); + } + /* If page was actually changed... */ + if(V!=ROMMapper[I][J]) + { + MemMap[PS][SS][J+2]=P; + ROMMapper[I][J]=V; + /* Only update memory when cartridge's slot selected */ + if((PSL[(J>>1)+1]==PS)&&(SSL[(J>>1)+1]==SS)) RAM[J+2]=P; + } + /* Done with page switch */ + return; + } + /* Write to SRAM */ + if((A>=0x8000)&&(A<0xC000)&&(ROMMapper[I][((A>>13)&1)+2]==0xFF)) + { + RAM[A>>13][A&0x1FFF]=V; + SaveSRAM[I]=1; + /* Done with SRAM write */ + return; + } + break; + + case MAP_ASCII16: /*** ASCII 16kB cartridges ***/ + /* NOTE: Vauxall writes garbage to to 7xxxh */ + /* NOTE: Darwin writes valid data to 6x00h (ASCII8 mapper) */ + /* NOTE: Androgynus writes valid data to 77FFh */ + /* If switching pages... */ + if((A>=0x6000)&&(A<0x8000)&&((V<=ROMMask[I]+1)||!(A&0x0FFF))) + { + J=(A&0x1000)>>11; + /* If selecting SRAM... */ + if(V&(ROMMask[I]+1)) + { + /* Select SRAM page */ + V=0xFF; + P=SRAMData[I]; + if(Verbose&0x08) + printf("ROM-MAPPER %c: 2kB SRAM at %d:%d:%04Xh\n",I+'A',PS,SS,J*0x2000+0x4000); + } + else + { + /* Select ROM page */ + V=(V<<1)&ROMMask[I]; + P=ROMData[I]+((int)V<<13); + if(Verbose&0x08) + printf("ROM-MAPPER %c: 16kB ROM page #%d at %d:%d:%04Xh\n",I+'A',V>>1,PS,SS,J*0x2000+0x4000); + } + /* If page was actually changed... */ + if(V!=ROMMapper[I][J]) + { + MemMap[PS][SS][J+2]=P; + MemMap[PS][SS][J+3]=P+0x2000; + ROMMapper[I][J]=V; + ROMMapper[I][J+1]=V|1; + /* Only update memory when cartridge's slot selected */ + if((PSL[(J>>1)+1]==PS)&&(SSL[(J>>1)+1]==SS)) + { + RAM[J+2]=P; + RAM[J+3]=P+0x2000; + } + } + /* Done with page switch */ + return; + } + /* Write to SRAM */ + if((A>=0x8000)&&(A<0xC000)&&(ROMMapper[I][2]==0xFF)) + { + P=RAM[A>>13]; + A&=0x07FF; + P[A+0x0800]=P[A+0x1000]=P[A+0x1800]= + P[A+0x2000]=P[A+0x2800]=P[A+0x3000]= + P[A+0x3800]=P[A]=V; + SaveSRAM[I]=1; + /* Done with SRAM write */ + return; + } + break; + + case MAP_GMASTER2: /* Konami GameMaster2+SRAM cartridge */ + /* Switch ROM and SRAM pages, page at 4000h is fixed */ + if((A>=0x6000)&&(A<=0xA000)&&!(A&0x1FFF)) + { + /* Figure out which ROM page gets switched */ + J=(A-0x4000)>>13; + /* If changing SRAM page... */ + if(V&0x10) + { + /* Select SRAM page */ + RAM[J+2]=MemMap[PS][SS][J+2]=SRAMData[I]+(V&0x20? 0x2000:0); + /* SRAM is now on */ + ROMMapper[I][J]=0xFF; + if(Verbose&0x08) + printf("GMASTER2 %c: 4kB SRAM page #%d at %d:%d:%04Xh\n",I+'A',(V&0x20)>>5,PS,SS,J*0x2000+0x4000); + } + else + { + /* Compute new ROM page number */ + V&=ROMMask[I]; + /* If ROM page number has changed... */ + if(V!=ROMMapper[I][J]) + { + RAM[J+2]=MemMap[PS][SS][J+2]=ROMData[I]+((int)V<<13); + ROMMapper[I][J]=V; + } + if(Verbose&0x08) + printf("GMASTER2 %c: 8kB ROM page #%d at %d:%d:%04Xh\n",I+'A',V,PS,SS,J*0x2000+0x4000); + } + /* Done with page switch */ + return; + } + /* Write to SRAM */ + if((A>=0xB000)&&(A<0xC000)&&(ROMMapper[I][3]==0xFF)) + { + RAM[5][(A&0x0FFF)|0x1000]=RAM[5][A&0x0FFF]=V; + SaveSRAM[I]=1; + /* Done with SRAM write */ + return; + } + break; + + case MAP_FMPAC: /* Panasonic FMPAC+SRAM cartridge */ + /* See if any switching occurs */ + switch(A) + { + case 0x7FF7: /* ROM page select */ + V=(V<<1)&ROMMask[I]; + ROMMapper[I][0]=V; + ROMMapper[I][1]=V|1; + /* 4000h-5FFFh contains SRAM when correct FMPACKey supplied */ + if(FMPACKey!=FMPAC_MAGIC) + { + P=ROMData[I]+((int)V<<13); + RAM[2]=MemMap[PS][SS][2]=P; + RAM[3]=MemMap[PS][SS][3]=P+0x2000; + } + if(Verbose&0x08) + printf("FMPAC %c: 16kB ROM page #%d at %d:%d:4000h\n",I+'A',V>>1,PS,SS); + return; + case 0x7FF6: /* OPL1 enable/disable? */ + if(Verbose&0x08) + printf("FMPAC %c: (7FF6h) = %02Xh\n",I+'A',V); + V&=0x11; + return; + case 0x5FFE: /* Write 4Dh, then (5FFFh)=69h to enable SRAM */ + case 0x5FFF: /* (5FFEh)=4Dh, then write 69h to enable SRAM */ + FMPACKey=A&1? ((FMPACKey&0x00FF)|((int)V<<8)) + : ((FMPACKey&0xFF00)|V); + P=FMPACKey==FMPAC_MAGIC? + SRAMData[I]:(ROMData[I]+((int)ROMMapper[I][0]<<13)); + RAM[2]=MemMap[PS][SS][2]=P; + RAM[3]=MemMap[PS][SS][3]=P+0x2000; + if(Verbose&0x08) + printf("FMPAC %c: 8kB SRAM %sabled at %d:%d:4000h\n",I+'A',FMPACKey==FMPAC_MAGIC? "en":"dis",PS,SS); + return; + } + /* Write to SRAM */ + if((A>=0x4000)&&(A<0x5FFE)&&(FMPACKey==FMPAC_MAGIC)) + { + RAM[A>>13][A&0x1FFF]=V; + SaveSRAM[I]=1; + return; + } + break; + } + + /* No MegaROM mapper or there is an incorrect write */ + if(Verbose&0x08) printf("MEMORY: Bad write (%d:%d:%04Xh) = %02Xh\n",PS,SS,A,V); +} + +/** PSlot() **************************************************/ +/** Switch primary memory slots. This function is called **/ +/** when value in port A8h changes. **/ +/*************************************************************/ +void PSlot(register byte V) +{ + register byte J,I; + + if(PSLReg!=V) + for(PSLReg=V,J=0;J<4;++J,V>>=2) + { + I = J<<1; + PSL[J] = V&3; + SSL[J] = (SSLReg[PSL[J]]>>I)&3; + RAM[I] = MemMap[PSL[J]][SSL[J]][I]; + RAM[I+1] = MemMap[PSL[J]][SSL[J]][I+1]; + EnWrite[J] = (PSL[J]==3)&&(SSL[J]==2)&&(MemMap[3][2][I]!=EmptyRAM); + } +} + +/** SSlot() **************************************************/ +/** Switch secondary memory slots. This function is called **/ +/** when value in (FFFFh) changes. **/ +/*************************************************************/ +void SSlot(register byte V) +{ + register byte J,I; + + /* Cartridge slots do not have subslots, fix them at 0:0:0:0 */ + if((PSL[3]==1)||(PSL[3]==2)) V=0x00; + /* In MSX1, slot 0 does not have subslots either */ + if(!PSL[3]&&((Mode&MSX_MODEL)==MSX_MSX1)) V=0x00; + + if(SSLReg[PSL[3]]!=V) + for(SSLReg[PSL[3]]=V,J=0;J<4;++J,V>>=2) + { + if(PSL[J]==PSL[3]) + { + I = J<<1; + SSL[J] = V&3; + RAM[I] = MemMap[PSL[J]][SSL[J]][I]; + RAM[I+1] = MemMap[PSL[J]][SSL[J]][I+1]; + EnWrite[J] = (PSL[J]==3)&&(SSL[J]==2)&&(MemMap[3][2][I]!=EmptyRAM); + } + } +} + +/** SetIRQ() *************************************************/ +/** Set or reset IRQ. Returns IRQ vector assigned to **/ +/** CPU.IRequest. When upper bit of IRQ is 1, IRQ is reset. **/ +/*************************************************************/ +word SetIRQ(register byte IRQ) +{ + if(IRQ&0x80) IRQPending&=IRQ; else IRQPending|=IRQ; + CPU->IRequest=IRQPending? INT_IRQ:INT_NONE; + return(CPU->IRequest); +} + +/** SetScreen() **********************************************/ +/** Change screen mode. Returns new screen mode. **/ +/*************************************************************/ +byte SetScreen(void) +{ + register byte I,J; + + switch(((VDP[0]&0x0E)>>1)|(VDP[1]&0x18)) + { + case 0x10: J=0;break; + case 0x00: J=1;break; + case 0x01: J=2;break; + case 0x08: J=3;break; + case 0x02: J=4;break; + case 0x03: J=5;break; + case 0x04: J=6;break; + case 0x05: J=7;break; + case 0x07: J=8;break; + case 0x12: J=MAXSCREEN+1;break; + default: J=ScrMode;break; + } + + /* Recompute table addresses */ + I=(J>6)&&(J!=MAXSCREEN+1)? 11:10; + ChrTab = msx_VRAM+((int)(VDP[2]&MSK[J].R2)<=MAXSLOTS)) return; + /* Find primary/secondary slots */ + for(PS=0;PS<4;++PS) + { + for(SS=0;(SS<4)&&(CartMap[PS][SS]!=Slot);++SS); + if(SS<4) break; + } + /* Drop out if slots not found */ + if(PS>=4) return; + + /* Apply masks to ROM pages */ + P0&=ROMMask[Slot]; + P1&=ROMMask[Slot]; + P2&=ROMMask[Slot]; + P3&=ROMMask[Slot]; + /* Set memory map */ + MemMap[PS][SS][2]=ROMData[Slot]+P0*0x2000; + MemMap[PS][SS][3]=ROMData[Slot]+P1*0x2000; + MemMap[PS][SS][4]=ROMData[Slot]+P2*0x2000; + MemMap[PS][SS][5]=ROMData[Slot]+P3*0x2000; + /* Set ROM mappers */ + ROMMapper[Slot][0]=P0; + ROMMapper[Slot][1]=P1; + ROMMapper[Slot][2]=P2; + ROMMapper[Slot][3]=P3; +} + +/** VDPOut() *************************************************/ +/** Write value into a given VDP register. **/ +/*************************************************************/ +void VDPOut(register byte R,register byte V) +{ + register byte J; + + switch(R) + { + case 0: /* Reset HBlank interrupt if disabled */ + if((VDPStatus[1]&0x01)&&!(V&0x10)) + { + VDPStatus[1]&=0xFE; + SetIRQ(~INT_IE1); + } + /* Set screen mode */ + if(VDP[0]!=V) { VDP[0]=V;SetScreen(); } + break; + case 1: /* Set/Reset VBlank interrupt if enabled or disabled */ + if(VDPStatus[0]&0x80) SetIRQ(V&0x20? INT_IE0:~INT_IE0); + /* Set screen mode */ + if(VDP[1]!=V) { VDP[1]=V;SetScreen(); } + break; + case 2: J=(ScrMode>6)&&(ScrMode!=MAXSCREEN+1)? 11:10; + ChrTab = msx_VRAM+((int)(V&MSK[ScrMode].R2)<>4;BGColor=V&0x0F;break; + case 10: V&=0x07; + ColTab=msx_VRAM+((int)(VDP[3]&MSK[ScrMode].R3)<<6)+((int)V<<14); + break; + case 11: V&=0x03; + SprTab=msx_VRAM+((int)(VDP[5]&MSK[ScrMode].R5)<<7)+((int)V<<15); + break; + case 14: V&=VRAMPages-1;VPAGE=msx_VRAM+((int)V<<14); + break; + case 15: V&=0x0F;break; + case 16: V&=0x0F;PKey=1;break; + case 17: V&=0xBF;break; + case 25: VDP[25]=V; + SetScreen(); + break; + case 44: VDPWrite(V);break; + case 46: VDPDraw(V);break; + } + + /* Write value into a register */ + VDP[R]=V; +} + +/** Printer() ************************************************/ +/** Send a character to the printer. **/ +/*************************************************************/ +void Printer(byte V) +{ + if(!PrnStream) + { + PrnStream = PrnName? fopen(PrnName,"ab"):0; + PrnStream = PrnStream? PrnStream:stdout; + } + fputc(V,PrnStream); +} + +/** PPIOut() *************************************************/ +/** This function is called on each write to PPI to make **/ +/** key click sound, motor relay clicks, and so on. **/ +/*************************************************************/ +void PPIOut(register byte New,register byte Old) +{ + /* Keyboard click bit */ + if((New^Old)&0x80) Drum(DRM_CLICK,64); + /* Motor relay bit */ + if((New^Old)&0x10) Drum(DRM_CLICK,255); +} + +/** RTCIn() **************************************************/ +/** Read value from a given RTC register. **/ +/*************************************************************/ +byte RTCIn(register byte R) +{ + static time_t PrevTime; + static struct tm TM; + register byte J; + time_t CurTime; + + /* Only 16 registers/mode */ + R&=0x0F; + + /* Bank mode 0..3 */ + J=RTCMode&0x03; + + if(R>12) J=R==13? RTCMode:NORAM; + else + if(J) J=RTC[J][R]; + else + { + /* Retrieve system time if any time passed */ + CurTime=time(NULL); + if(CurTime!=PrevTime) + { + TM=*localtime(&CurTime); + PrevTime=CurTime; + } + + /* Parse contents of last retrieved TM */ + switch(R) + { + case 0: J=TM.tm_sec%10;break; + case 1: J=TM.tm_sec/10;break; + case 2: J=TM.tm_min%10;break; + case 3: J=TM.tm_min/10;break; + case 4: J=TM.tm_hour%10;break; + case 5: J=TM.tm_hour/10;break; + case 6: J=TM.tm_wday;break; + case 7: J=TM.tm_mday%10;break; + case 8: J=TM.tm_mday/10;break; + case 9: J=(TM.tm_mon+1)%10;break; + case 10: J=(TM.tm_mon+1)/10;break; + case 11: J=(TM.tm_year-80)%10;break; + case 12: J=((TM.tm_year-80)/10)%10;break; + default: J=0x0F;break; + } + } + + /* Four upper bits are always high */ + return(J|0xF0); +} + +/** LoopZ80() ************************************************/ +/** Refresh screen, check keyboard and sprites. Call this **/ +/** function on each interrupt. **/ +/*************************************************************/ +word msx_LoopZ80(Z80 *R) +{ + static byte BFlag=0; + static byte BCount=0; + static int UCount=0; + static byte ACount=0; + static byte Drawing=0; + register int J; + + /* Flip HRefresh bit */ + VDPStatus[2]^=0x20; + + /* If HRefresh is now in progress... */ + if(!(VDPStatus[2]&0x20)) + { + /* HRefresh takes most of the scanline */ + R->IPeriod=!ScrMode||(ScrMode==MAXSCREEN+1)? CPU_H240:CPU_H256; + + /* New scanline */ + ScanLine=ScanLine<(PALVideo? 312:261)? ScanLine+1:0; + + /* If first scanline of the screen... */ + if(!ScanLine) + { + /* Drawing now... */ + Drawing=1; + + /* Reset VRefresh bit */ + VDPStatus[2]&=0xBF; + + /* Refresh display */ + if(UCount>=100) { UCount-=100;RefreshScreen(); } + UCount+=UPeriod; + + /* Blinking for TEXT80 */ + if(BCount) BCount--; + else + { + BFlag=!BFlag; + if(!VDP[13]) { XFGColor=FGColor;XBGColor=BGColor; } + else + { + BCount=(BFlag? VDP[13]&0x0F:VDP[13]>>4)*10; + if(BCount) + { + if(BFlag) { XFGColor=FGColor;XBGColor=BGColor; } + else { XFGColor=VDP[12]>>4;XBGColor=VDP[12]&0x0F; } + } + } + } + } + + /* Line coincidence is active at 0..255 */ + /* in PAL and 0..234/244 in NTSC */ + J=PALVideo? 256:ScanLines212? 245:235; + + /* When reaching end of screen, reset line coincidence */ + if(ScanLine==J) + { + VDPStatus[1]&=0xFE; + SetIRQ(~INT_IE1); + } + + /* When line coincidence is active... */ + if(ScanLineIRequest=IRQPending? INT_IRQ:INT_NONE; + return(R->IRequest); + } + + /*********************************/ + /* We come here for HBlanks only */ + /*********************************/ + + /* HBlank takes HPeriod-HRefresh */ + R->IPeriod=!ScrMode||(ScrMode==MAXSCREEN+1)? CPU_H240:CPU_H256; + R->IPeriod=HPeriod-R->IPeriod; + + /* If last scanline of VBlank, see if we need to wait more */ + J=PALVideo? 313:262; + if(ScanLine>=J-1) + { + J*=CPU_HPERIOD; + if(VPeriod>J) R->IPeriod+=VPeriod-J; + } + + /* If first scanline of the bottom border... */ + if(ScanLine==(ScanLines212? 212:192)) Drawing=0; + + /* If first scanline of VBlank... */ + J=PALVideo? (ScanLines212? 212+42:192+52):(ScanLines212? 212+18:192+28); + if(!Drawing&&(ScanLine==J)) + { + /* Set VBlank bit, set VRefresh bit */ + VDPStatus[0]|=0x80; + VDPStatus[2]|=0x40; + + /* Generate VBlank interrupt */ + if(VDP[1]&0x20) SetIRQ(INT_IE0); + } + + /* Run V9938 engine */ + LoopVDP(); + + /* Refresh scanline, possibly with the overscan */ + if((UCount>=100)&&Drawing&&(ScanLine<256)) + { + if(!ModeYJK||(ScrMode<7)||(ScrMode>8)) + (RefreshLine[ScrMode])(ScanLine); + else + if(ModeYAE) RefreshLine10(ScanLine); + else RefreshLine12(ScanLine); + } + + /* Every few scanlines, update sound */ + if(!(ScanLine&0x07)) + { + /* Compute number of microseconds */ + J = (int)(1000000L*(CPU_HPERIOD<<3)/CPU_CLOCK); + + /* Update AY8910 state */ + Loop8910(PSG,J); + + /* Flush changes to sound channels, only hit drums once a frame */ + Sync8910(PSG,AY8910_FLUSH|(!ScanLine&&OPTION(MSX_DRUMS)? AY8910_DRUMS:0)); + SyncSCC(SCChip,SCC_FLUSH); + Sync2413(OPLL,YM2413_FLUSH); + + /* Render and play all sound now */ + PlayAllSound(J); + } + + /* Keyboard, sound, and other stuff always runs at line 192 */ + /* This way, it can't be shut off by overscan tricks (Maarten) */ + if(ScanLine==192) + { + /* Clear 5thSprite fields (wrong place to do it?) */ + VDPStatus[0]=(VDPStatus[0]&~0x40)|0x1F; + + /* Check sprites and set Collision bit */ + if(!(VDPStatus[0]&0x20)&&CheckSprites()) VDPStatus[0]|=0x20; + + /* Count MIDI ticks */ + MIDITicks(1000*VPeriod/CPU_CLOCK); + + /* Apply RAM-based cheats */ + if(CheatsON&&CheatCount) ApplyCheats(); + + /* Check joystick */ + JoyState=Joystick(); + + /* Check keyboard */ + Keyboard(); + + /* Exit emulation if requested */ + if(ExitNow) return(INT_QUIT); + + /* Check mouse in joystick port #1 */ + if(JOYTYPE(0)>=JOY_MOUSTICK) + { + /* Get new mouse state */ + MouState[0]=Mouse(0); + /* Merge mouse buttons into joystick buttons */ + JoyState|=(MouState[0]>>12)&0x0030; + /* If mouse-as-joystick... */ + if(JOYTYPE(0)==JOY_MOUSTICK) + { + J=MouState[0]&0xFF; + JoyState|=J>OldMouseX[0]? 0x0008:J>8)&0xFF; + JoyState|=J>OldMouseY[0]? 0x0002:J=JOY_MOUSTICK) + { + /* Get new mouse state */ + MouState[1]=Mouse(1); + /* Merge mouse buttons into joystick buttons */ + JoyState|=(MouState[1]>>4)&0x3000; + /* If mouse-as-joystick... */ + if(JOYTYPE(1)==JOY_MOUSTICK) + { + J=MouState[1]&0xFF; + JoyState|=J>OldMouseX[1]? 0x0800:J>8)&0xFF; + JoyState|=J>OldMouseY[1]? 0x0200:J3) + { + /* Autofire spacebar if needed */ + if(OPTION(MSX_AUTOSPACE)) KBD_RES(' '); + /* Autofire FIRE-A if needed */ + if(OPTION(MSX_AUTOFIREA)) JoyState&=~(JST_FIREA|(JST_FIREA<<8)); + /* Autofire FIRE-B if needed */ + if(OPTION(MSX_AUTOFIREB)) JoyState&=~(JST_FIREB|(JST_FIREB<<8)); + } + } + + /* Return whatever interrupt is pending */ + R->IRequest=IRQPending? INT_IRQ:INT_NONE; + return(R->IRequest); +} + +/** CheckSprites() *******************************************/ +/** Check for sprite collisions. **/ +/*************************************************************/ +int CheckSprites(void) +{ + unsigned int I,J,LS,LD; + byte DH,DV,*S,*D,*PS,*PD,*T; + + /* Must be showing sprites */ + if(SpritesOFF||!ScrMode||(ScrMode>=MAXSCREEN+1)) return(0); + + /* Find bottom/top scanlines */ + DH = ScrMode>3? 216:208; + LD = 255-(Sprites16x16? 16:8); + LS = ScanLines212? 211:191; + + /* Find valid, displayed sprites */ + for(I=J=0,S=SprTab;(I<32)&&(S[0]!=DH);++I,S+=4) + if((S[0]LD)) J|=1<>=1,S+=4) + if(J&1) + for(I=J>>1,D=S+4;I;I>>=1,D+=4) + if(I&1) + { + DV=S[0]-D[0]; + if((DV<16)||(DV>240)) + { + DH=S[1]-D[1]; + if((DH<16)||(DH>240)) + { + PS=SprGen+((int)(S[2]&0xFC)<<3); + PD=SprGen+((int)(D[2]&0xFC)<<3); + if(DV<16) PD+=DV; else { DV=256-DV;PS+=DV; } + if(DH>240) { DH=256-DH;T=PS;PS=PD;PD=T; } + while(DV<16) + { + LS=((unsigned int)*PS<<8)+*(PS+16); + LD=((unsigned int)*PD<<8)+*(PD+16); + if(LD&(LS>>DH)) break; + else { ++DV;++PS;++PD; } + } + if(DV<16) return(1); + } + } + } + } + else + { + for(S=SprTab;J;J>>=1,S+=4) + if(J&1) + for(I=J>>1,D=S+4;I;I>>=1,D+=4) + if(I&1) + { + DV=S[0]-D[0]; + if((DV<8)||(DV>248)) + { + DH=S[1]-D[1]; + if((DH<8)||(DH>248)) + { + PS=SprGen+((int)S[2]<<3); + PD=SprGen+((int)D[2]<<3); + if(DV<8) PD+=DV; else { DV=256-DV;PS+=DV; } + if(DH>248) { DH=256-DH;T=PS;PS=PD;PD=T; } + while((DV<8)&&!(*PD&(*PS>>DH))) { ++DV;++PS;++PD; } + if(DV<8) return(1); + } + } + } + } + + /* No collisions */ + return(0); +} + +/** StateID() ************************************************/ +/** Compute 16bit emulation state ID used to identify .STA **/ +/** files. **/ +/*************************************************************/ +word StateID(void) +{ + word ID; + int J,I; + + ID=0x0000; + + /* Add up cartridge ROMs, BIOS, BASIC, ExtBIOS, and DiskBIOS bytes */ + for(I=0;IP2)? P3:P2; + P3 = strrchr(Result,':'); + P2 = P3 && (P3>P2)? P3:P2; + + if(P1 && (!P2 || (P1>P2))) strcpy(P1,Ext); + else strcat(Result,Ext); + + return(Result); +} + +/** ChangeTape() *********************************************/ +/** Change tape image. ChangeTape(0) closes current image. **/ +/** Returns 1 on success, 0 on failure. **/ +/*************************************************************/ +byte ChangeTape(const char *FileName) +{ + /* Close previous tape image, if open */ + if(CasStream) { fclose(CasStream);CasStream=0; } + + /* If opening a new tape image... */ + if(FileName) + { + /* Try read+append first, then read-only */ + CasStream = fopen(FileName,"r+b"); + CasStream = CasStream? CasStream:fopen(FileName,"rb"); + } + + /* Done */ + return(!FileName||CasStream); +} + +/** RewindTape() *********************************************/ +/** Rewind currenly open tape. **/ +/*************************************************************/ +void RewindTape(void) { if(CasStream) rewind(CasStream); } + +/** ChangePrinter() ******************************************/ +/** Change printer output to a given file. The previous **/ +/** file is closed. ChangePrinter(0) redirects output to **/ +/** stdout. **/ +/*************************************************************/ +void ChangePrinter(const char *FileName) +{ + if(PrnStream&&(PrnStream!=stdout)) fclose(PrnStream); + PrnName = FileName; + PrnStream = 0; +} + +/** ChangeDisk() *********************************************/ +/** Change disk image in a given drive. Closes current disk **/ +/** image if Name=0 was given. Creates a new disk image if **/ +/** Name="" was given. Returns 1 on success or 0 on failure.**/ +/*************************************************************/ +byte ChangeDisk(byte N,const char *FileName) +{ + int NeedState; + byte *P; + + /* We only have MAXDRIVES drives */ + if(N>=MAXDRIVES) return(0); + + /* Load state when inserting first disk into drive A: */ + NeedState = FileName && *FileName && !N && !FDD[N].Data; + + /* Reset FDC, in case it was running a command */ + Reset1793(FDC,FDD,WD1793_KEEP); + + /* Eject disk if requested */ + if(!FileName) { EjectFDI(&FDD[N]);return(1); } + + /* If FileName not empty, try loading disk image */ + if(*FileName&&LoadFDI(&FDD[N],FileName,FMT_AUTO)) + { + /* If first disk, also try loading state */ + if(NeedState) FindState(FileName); + /* Done */ + return(1); + } + + /* If failed opening existing image, create a new 720kB disk image */ + P = FormatFDI(&FDD[N],FMT_MSXDSK); + + /* If FileName not empty, treat it as directory, otherwise new disk */ + if(P&&!(*FileName? DSKLoad(FileName,P,"MSX-DISK"):DSKCreate(P,"MSX-DISK"))) + { EjectFDI(&FDD[N]);return(0); } + + /* Done */ + return(!!P); +} + +/** LoadFile() ***********************************************/ +/** Simple utility function to load cartridge, state, font **/ +/** or a disk image, based on the file extension, etc. **/ +/*************************************************************/ +int LoadFile(const char *FileName) +{ + int J; + + /* Try loading as a disk */ + if(hasext(FileName,".DSK")||hasext(FileName,".FDI")) + { + /* Change disk image in drive A: */ + if(!ChangeDisk(0,FileName)) return(0); + /* Eject all user cartridges if successful */ + for(J=0;J=MCFCount)||(MCFEntries[N].Addr>0xFFFF)||(MCFEntries[N].Size>2)) + return(0); + + /* Switch cheats off for now and remove all present cheats */ + Status = Cheats(CHTS_QUERY); + Cheats(CHTS_OFF); + ResetCheats(); + + /* Insert cheat codes from the MCF entry */ + CheatCodes[0].Addr = MCFEntries[N].Addr; + CheatCodes[0].Data = MCFEntries[N].Data; + CheatCodes[0].Size = MCFEntries[N].Size; + sprintf( + (char *)CheatCodes[0].Text, + CheatCodes[0].Size>1? "%04X-%04X":"%04X-%02X", + CheatCodes[0].Addr, + CheatCodes[0].Data + ); + + /* Have one cheat code now */ + CheatCount = 1; + + /* Turn cheats back on, if they were on */ + Cheats(Status); + + /* Done */ + return(CheatCount); +} + +/** AddCheat() ***********************************************/ +/** Add a new cheat. Returns 0 on failure or the number of **/ +/** cheats on success. **/ +/*************************************************************/ +int AddCheat(const char *Cheat) +{ + static const char *Hex = "0123456789ABCDEF"; + unsigned int A,D; + char *P; + int J,N; + + /* Table full: no more cheats */ + if(CheatCount>=MAXCHEATS) return(0); + + /* Check cheat length and decode */ + N=strlen(Cheat); + + if(((N==13)||(N==11))&&(Cheat[8]=='-')) + { + for(J=0,A=0;J<8;J++) + { + P=strchr(Hex,toupper(Cheat[J])); + if(!P) return(0); else A=(A<<4)|(P-Hex); + } + for(J=9,D=0;J>24)==0x01) + { + msx_WrZ80(CheatCodes[J].Addr&0xFFFF,CheatCodes[J].Data&0xFF); + if(CheatCodes[J].Size>1) + msx_WrZ80((CheatCodes[J].Addr+1)&0xFFFF,CheatCodes[J].Data>>8); + ++I; + } + + /* Return number of applied cheats */ + return(I); +} + +/** Cheats() *************************************************/ +/** Toggle cheats on (1), off (0), inverse state (2) or **/ +/** query (3). **/ +/*************************************************************/ +int Cheats(int Switch) +{ + byte *P,*Base; + int J,Size; + + switch(Switch) + { + case CHTS_ON: + case CHTS_OFF: if(Switch==CheatsON) return(CheatsON); + case CHTS_TOGGLE: Switch=!CheatsON;break; + default: return(CheatsON); + } + + /* Find valid cartridge */ + for(J=1;(J<=2)&&!ROMData[J];++J); + + /* Must have ROM */ + if(J>2) return(Switch=CHTS_OFF); + + /* Compute ROM address and size */ + Base = ROMData[J]; + Size = ((int)ROMMask[J]+1)<<14; + + /* If toggling cheats... */ + if(Switch!=CheatsON) + { + /* If enabling cheats... */ + if(Switch) + { + /* Patch ROM with the cheat values */ + for(J=0;J>24)&&(CheatCodes[J].Addr+CheatCodes[J].Size<=Size)) + { + P = Base + CheatCodes[J].Addr; + CheatCodes[J].Orig = P[0]; + P[0] = CheatCodes[J].Data; + if(CheatCodes[J].Size>1) + { + CheatCodes[J].Orig |= (int)P[1]<<8; + P[1] = CheatCodes[J].Data>>8; + } + } + } + else + { + /* Restore original ROM values */ + for(J=0;J>24)&&(CheatCodes[J].Addr+CheatCodes[J].Size<=Size)) + { + P = Base + CheatCodes[J].Addr; + P[0] = CheatCodes[J].Orig; + if(CheatCodes[J].Size>1) + P[1] = CheatCodes[J].Orig>>8; + } + } + + /* Done toggling cheats */ + CheatsON = Switch; + } + + /* Done */ + if(Verbose) printf("Cheats %s\n",CheatsON? "ON":"OFF"); + return(CheatsON); +} + +#if defined(ANDROID) +#undef feof +#define fopen mopen +#define fclose mclose +#define fread mread +#define fwrite mwrite +#define fgets mgets +#define fseek mseek +#define rewind mrewind +#define fgetc mgetc +#define ftell mtell +#define feof meof +#elif defined(ZLIB) +#undef feof +#define fopen(N,M) (FILE *)gzopen(N,M) +#define fclose(F) gzclose((gzFile)(F)) +#define fread(B,L,N,F) gzread((gzFile)(F),B,(L)*(N)) +#define fwrite(B,L,N,F) gzwrite((gzFile)(F),B,(L)*(N)) +#define fgets(B,L,F) gzgets((gzFile)(F),B,L) +#define fseek(F,O,W) gzseek((gzFile)(F),O,W) +#define rewind(F) gzrewind((gzFile)(F)) +#define fgetc(F) gzgetc((gzFile)(F)) +#define ftell(F) gztell((gzFile)(F)) +#define feof(F) gzeof((gzFile)(F)) +#endif + +/** GuessROM() ***********************************************/ +/** Guess MegaROM mapper of a ROM. **/ +/*************************************************************/ +int GuessROM(const byte *Buf,int Size) +{ + int J,I,K,Result,ROMCount[MAXMAPPERS]; + char S[256]; + FILE *F; + + /* No result yet */ + Result = -1; + + /* Change to the program directory */ + if(ProgDir && chdir(ProgDir)) + { if(Verbose) printf("Failed changing to '%s' directory!\n",ProgDir); } + + /* Try opening file with CRCs */ + if((F=fopen("CARTS.CRC","rb"))) + { + /* Compute ROM's CRC */ + for(J=K=0;J=0) return(Result); + + /* Clear all counters */ + for(J=0;JROMCount[I]) I=J; + + /* Return the most likely mapper type */ + return(I); +} + +/** LoadFNT() ************************************************/ +/** Load fixed 8x8 font used in text screen modes when **/ +/** MSX_FIXEDFONT option is enabled. LoadFNT(0) frees the **/ +/** font buffer. Returns 1 on success, 0 on failure. **/ +/*************************************************************/ +byte LoadFNT(const char *FileName) +{ + FILE *F; + + /* Drop out if no new font requested */ + if(!FileName) { FreeMemory(FontBuf);FontBuf=0;return(1); } + /* Try opening font file */ + if(!(F=fopen(FileName,"rb"))) return(0); + /* Allocate memory for 256 8x8 characters, if needed */ + if(!FontBuf) FontBuf=GetMemory(256*8); + /* Drop out if failed memory allocation */ + if(!FontBuf) { fclose(F);return(0); } + /* Read font, ignore short reads */ + fread(FontBuf,1,256*8,F); + /* Done */ + fclose(F); + return(1); +} + +/** LoadROM() ************************************************/ +/** Load a file, allocating memory as needed. Returns addr. **/ +/** of the alocated space or 0 if failed. **/ +/*************************************************************/ +byte *LoadROM(const char *Name,int Size,byte *Buf) +{ + FILE *F; + byte *P; + int J; + + /* Can't give address without size! */ + if(Buf&&!Size) return(0); + + /* Open file */ + if(!(F=fopen(Name,"rb"))) return(0); + + /* Determine data size, if wasn't given */ + if(!Size) + { + /* Determine size via ftell() or by reading entire [GZIPped] stream */ + if(!fseek(F,0,SEEK_END)) Size=ftell(F); + else + { + /* Read file in 16kB increments */ + while((J=fread(EmptyRAM,1,0x4000,F))==0x4000) Size+=J; + if(J>0) Size+=J; + /* Clean up the EmptyRAM! */ + memset(EmptyRAM,NORAM,0x4000); + } + /* Rewind file to the beginning */ + rewind(F); + } + + /* Allocate memory */ + P=Buf? Buf:GetMemory(Size); + if(!P) + { + fclose(F); + return(0); + } + + /* Read data */ + if((J=fread(P,1,Size,F))!=Size) + { + if(!Buf) FreeMemory(P); + fclose(F); + return(0); + } + + /* Done */ + fclose(F); + return(P); +} + +/** FindState() **********************************************/ +/** Compute state file name corresponding to given filename **/ +/** and try loading state. Returns 1 on success, 0 on **/ +/** failure. **/ +/*************************************************************/ +int FindState(const char *Name) +{ + int J,I; + char *P; + + /* No result yet */ + J = 0; + + /* Remove old state name */ + FreeMemory(STAName); + + /* If STAName gets created... */ + if((STAName=MakeFileName(Name,".sta"))) + { + /* Try loading state */ + if(Verbose) printf("Loading state from %s...",STAName); + J=LoadSTA(STAName); + PRINTRESULT(J); + } + + /* Generate .CHT cheat file name and try loading it */ + if((P=MakeFileName(Name,".cht"))) + { + I=LoadCHT(P); + if(I&&Verbose) printf("Loaded %d cheats from %s\n",I,P); + FreeMemory(P); + } + + /* Generate .MCF cheat file name and try loading it */ + if((P=MakeFileName(Name,".mcf"))) + { + I=LoadMCF(P); + if(I&&Verbose) printf("Loaded %d cheat entries from %s\n",I,P); + FreeMemory(P); + } + + /* Generate palette file name and try loading it */ + if((P=MakeFileName(Name,".pal"))) + { + I=LoadPAL(P); + if(I&&Verbose) printf("Loaded palette from %s\n",P); + FreeMemory(P); + } + + /* Done */ + return(J); +} + +/** LoadCart() ***********************************************/ +/** Load cartridge into given slot. Returns cartridge size **/ +/** in 16kB pages on success, 0 on failure. **/ +/*************************************************************/ +int LoadCart(const char *FileName,int Slot,int Type) +{ + int C1,C2,Len,Pages,ROM64,BASIC; + byte *P,PS,SS; + char *T; + FILE *F; + + /* Slot number must be valid */ + if((Slot<0)||(Slot>=MAXSLOTS)) return(0); + /* Find primary/secondary slots */ + for(PS=0;PS<4;++PS) + { + for(SS=0;(SS<4)&&(CartMap[PS][SS]!=Slot);++SS); + if(SS<4) break; + } + /* Drop out if slots not found */ + if(PS>=4) return(0); + + /* If there is a SRAM in this cartridge slot... */ + if(SRAMData[Slot]&&SaveSRAM[Slot]&&SRAMName[Slot]) + { + /* Open .SAV file */ + if(Verbose) printf("Writing %s...",SRAMName[Slot]); + if(!(F=fopen(SRAMName[Slot],"wb"))) SaveSRAM[Slot]=0; + else + { + /* Write .SAV file */ + switch(ROMType[Slot]) + { + case MAP_ASCII8: + case MAP_FMPAC: + if(fwrite(SRAMData[Slot],1,0x2000,F)!=0x2000) SaveSRAM[Slot]=0; + break; + case MAP_ASCII16: + if(fwrite(SRAMData[Slot],1,0x0800,F)!=0x0800) SaveSRAM[Slot]=0; + break; + case MAP_GMASTER2: + if(fwrite(SRAMData[Slot],1,0x1000,F)!=0x1000) SaveSRAM[Slot]=0; + if(fwrite(SRAMData[Slot]+0x2000,1,0x1000,F)!=0x1000) SaveSRAM[Slot]=0; + break; + } + + /* Done with .SAV file */ + fclose(F); + } + + /* Done saving SRAM */ + PRINTRESULT(SaveSRAM[Slot]); + } + + /* If ejecting cartridge... */ + if(!FileName) + { + if(ROMData[Slot]) + { + /* Free memory if present */ + FreeMemory(ROMData[Slot]); + ROMData[Slot] = 0; + ROMMask[Slot] = 0; + /* Set memory map to dummy RAM */ + for(C1=0;C1<8;++C1) MemMap[PS][SS][C1]=EmptyRAM; + /* Restart MSX */ + ResetMSX(Mode,RAMPages,VRAMPages); + /* Cartridge ejected */ + if(Verbose) printf("Ejected cartridge from slot %c\n",Slot+'A'); + } + + /* Nothing else to do */ + return(0); + } + + /* Try opening file */ + if(!(F=fopen(FileName,"rb"))) return(0); + if(Verbose) printf("Found %s:\n",FileName); + + /* Determine size via ftell() or by reading entire [GZIPped] stream */ + if(!fseek(F,0,SEEK_END)) Len=ftell(F); + else + { + /* Read file in 16kB increments */ + for(Len=0;(C2=fread(EmptyRAM,1,0x4000,F))==0x4000;Len+=C2); + if(C2>0) Len+=C2; + /* Clean up the EmptyRAM! */ + memset(EmptyRAM,NORAM,0x4000); + } + + /* Rewind file */ + rewind(F); + + /* Length in 8kB pages */ + Len = Len>>13; + + /* Calculate 2^n closest to number of 8kB pages */ + for(Pages=1;Pages=0) + { + C1=fgetc(F); + C2=fgetc(F); + ROM64=(C1=='A')&&(C2=='B'); + } + + /* Maybe it is the last 16kB page that contains "AB" signature? */ + if((Len>=2)&&((C1!='A')||(C2!='B'))) + if(fseek(F,0x2000*(Len-2),SEEK_SET)>=0) + { + C1=fgetc(F); + C2=fgetc(F); + } + + /* If we can't find "AB" signature, drop out */ + if((C1!='A')||(C2!='B')) + { + if(Verbose) puts(" Not a valid cartridge ROM"); + fclose(F); + return(0); + } + + if(Verbose) printf(" Cartridge %c: ",'A'+Slot); + + /* Done with the file */ + fclose(F); + + /* Show ROM type and size */ + if(Verbose) + printf + ( + "%dkB %s ROM..",Len*8, + ROM64||(Len<=0x8000)? "NORMAL":Type>=MAP_GUESS? "UNKNOWN":ROMNames[Type] + ); + + /* Assign ROMMask for MegaROMs */ + ROMMask[Slot]=!ROM64&&(Len>4)? (Pages-1):0x00; + /* Allocate space for the ROM */ + ROMData[Slot]=P=GetMemory(Pages<<13); + if(!P) { PRINTFAILED;return(0); } + + /* Try loading ROM */ + if(!LoadROM(FileName,Len<<13,P)) { PRINTFAILED;return(0); } + + /* Mirror ROM if it is smaller than 2^n pages */ + if(Len=MAP_GUESS)&&(ROMMask[Slot]+1>4)) + { + Type=GuessROM(P,Len<<13); + if(Verbose) printf("guessed %s..",ROMNames[Type]); + if(Slot4)) + SetMegaROM(Slot,0,1,ROMMask[Slot]-1,ROMMask[Slot]); + + /* If cartridge may need a SRAM... */ + if(MAP_SRAM(Type)) + { + /* Free previous SRAM resources */ + FreeMemory(SRAMData[Slot]); + FreeMemory(SRAMName[Slot]); + + /* Get SRAM memory */ + SRAMData[Slot]=GetMemory(0x4000); + if(!SRAMData[Slot]) + { + if(Verbose) printf("scratch SRAM.."); + SRAMData[Slot]=EmptyRAM; + } + else + { + if(Verbose) printf("got 16kB SRAM.."); + memset(SRAMData[Slot],NORAM,0x4000); + } + + /* Generate SRAM file name and load SRAM contents */ + if((SRAMName[Slot]=(char *)GetMemory(strlen(FileName)+5))) + { + /* Compose SRAM file name */ + strcpy(SRAMName[Slot],FileName); + T=strrchr(SRAMName[Slot],'.'); + if(T) strcpy(T,".sav"); else strcat(SRAMName[Slot],".sav"); + /* Try opening file... */ + if((F=fopen(SRAMName[Slot],"rb"))) + { + /* Read SRAM file */ + Len=fread(SRAMData[Slot],1,0x4000,F); + fclose(F); + /* Print information if needed */ + if(Verbose) printf("loaded %d bytes from %s..",Len,SRAMName[Slot]); + /* Mirror data according to the mapper type */ + P=SRAMData[Slot]; + switch(Type) + { + case MAP_FMPAC: + memset(P+0x2000,NORAM,0x2000); + P[0x1FFE]=FMPAC_MAGIC&0xFF; + P[0x1FFF]=FMPAC_MAGIC>>8; + break; + case MAP_GMASTER2: + memcpy(P+0x2000,P+0x1000,0x1000); + memcpy(P+0x3000,P+0x1000,0x1000); + memcpy(P+0x1000,P,0x1000); + break; + case MAP_ASCII16: + memcpy(P+0x0800,P,0x0800); + memcpy(P+0x1000,P,0x0800); + memcpy(P+0x1800,P,0x0800); + memcpy(P+0x2000,P,0x0800); + memcpy(P+0x2800,P,0x0800); + memcpy(P+0x3000,P,0x0800); + memcpy(P+0x3800,P,0x0800); + break; + } + } + } + } + + /* Done setting up cartridge */ + ResetMSX(Mode,RAMPages,VRAMPages); + PRINTOK; + + /* If first used user slot, try loading state */ + if(!Slot||((Slot==1)&&!ROMData[0])) FindState(FileName); + + /* Done loading cartridge */ + return(Pages); +} + +/** LoadCHT() ************************************************/ +/** Load cheats from .CHT file. Cheat format is either **/ +/** 00XXXXXX-XX (one byte) or 00XXXXXX-XXXX (two bytes) for **/ +/** ROM-based cheats and XXXX-XX or XXXX-XXXX for RAM-based **/ +/** cheats. Returns the number of cheats on success, 0 on **/ +/** failure. **/ +/*************************************************************/ +int LoadCHT(const char *Name) +{ + char Buf[256],S[16]; + int Status; + FILE *F; + + /* Open .CHT text file with cheats */ + F = fopen(Name,"rb"); + if(!F) return(0); + + /* Switch cheats off for now and remove all present cheats */ + Status = Cheats(CHTS_QUERY); + Cheats(CHTS_OFF); + ResetCheats(); + + /* Try adding cheats loaded from file */ + while(!feof(F)) + if(fgets(Buf,sizeof(Buf),F) && (sscanf(Buf,"%13s",S)==1)) + AddCheat(S); + + /* Done with the file */ + fclose(F); + + /* Turn cheats back on, if they were on */ + Cheats(Status); + + /* Done */ + return(CheatCount); +} + +/** LoadPAL() ************************************************/ +/** Load new palette from .PAL file. Returns number of **/ +/** loaded colors on success, 0 on failure. **/ +/*************************************************************/ +int LoadPAL(const char *Name) +{ + static const char *Hex = "0123456789ABCDEF"; + char S[256],*P,*T,*H; + FILE *F; + int J,I; + + if(!(F=fopen(Name,"rb"))) return(0); + + for(J=0;(J<16)&&fgets(S,sizeof(S),F);++J) + { + /* Skip white space and optional '#' character */ + for(P=S;*P&&(*P<=' ');++P); + if(*P=='#') ++P; + /* Parse six hexadecimal digits */ + for(T=P,I=0;*T&&(H=strchr(Hex,toupper(*T)));++T) I=(I<<4)+(H-Hex); + /* If we have got six digits, parse and set color */ + if(T-P==6) SetColor(J,I>>16,(I>>8)&0xFF,I&0xFF); + } + + fclose(F); + return(J); +} + +/** LoadMCF() ************************************************/ +/** Load cheats from .MCF file. Returns number of loaded **/ +/** cheat entries or 0 on failure. **/ +/*************************************************************/ +int LoadMCF(const char *Name) +{ + MCFCount = LoadFileMCF(Name,MCFEntries,MAXCHEATS); + return(MCFCount); +} + +/** SaveMCF() ************************************************/ +/** Save cheats to .MCF file. Returns number of loaded **/ +/** cheat entries or 0 on failure. **/ +/*************************************************************/ +int SaveMCF(const char *Name) +{ + return(MCFCount>0? SaveFileMCF(Name,MCFEntries,MCFCount):0); +} + +/** State.h **************************************************/ +/** SaveState(), LoadState(), SaveSTA(), and LoadSTA() **/ +/** functions are implemented here. **/ +/*************************************************************/ +#include "State.h" + +#if defined(ZLIB) || defined(ANDROID) +#undef fopen +#undef fclose +#undef fread +#undef fwrite +#undef fgets +#undef fseek +#undef ftell +#undef fgetc +#undef feof +#endif diff --git a/components/msx/fmsx/src/fMSX/MSX.h b/components/msx/fmsx/src/fMSX/MSX.h new file mode 100644 index 0000000..525948e --- /dev/null +++ b/components/msx/fmsx/src/fMSX/MSX.h @@ -0,0 +1,519 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** MSX.h **/ +/** **/ +/** This file contains declarations relevant to the drivers **/ +/** and MSX emulation itself. See Z80.h for #defines **/ +/** related to Z80 emulation. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef MSX_H +#define MSX_H + +#include "Z80.h" /* Z80 CPU emulation */ +#include "V9938.h" /* V9938 VDP opcode emulation */ +#include "AY8910.h" /* AY8910 PSG emulation */ +#include "YM2413.h" /* YM2413 OPLL emulation */ +#include "SCC.h" /* Konami SCC chip emulation */ +#include "I8255.h" /* Intel 8255 PPI emulation */ +#include "I8251.h" /* Intel 8251 UART emulation */ +#include "WD1793.h" /* WD1793 FDC emulation */ + +#include + +/** INLINE ***************************************************/ +/** C99 standard has "inline", but older compilers've used **/ +/** __inline for the same purpose. **/ +/*************************************************************/ +#ifdef __C99__ +#define INLINE static inline +#else +#define INLINE static __inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CPU_CLOCK 3579545 /* CPU clock frequency Hz */ +#define VDP_CLOCK (CPU_CLOCK*6) /* VDP clock frequency Hz */ +#define PSG_CLOCK (CPU_CLOCK/2) /* PSG clock frequency Hz */ + +#define HPERIOD 1368 /* HPeriod, VDP cycles */ +#define VPERIOD_PAL (HPERIOD*313) /* PAL VPeriod, VDP ccls */ +#define VPERIOD_NTSC (HPERIOD*262) /* NTSC VPeriod, VDP ccls */ +#define HREFRESH_240 960 /* 240dot scanline refresh */ +#define HREFRESH_256 1024 /* 256dot scanline refresh */ + +#define CPU_VPERIOD (VPERIOD_NTSC/6) +#define CPU_V262 (VPERIOD_NTSC/6) +#define CPU_V313 (VPERIOD_PAL/6) +#define CPU_HPERIOD (HPERIOD/6) +#define CPU_H240 (HREFRESH_240/6) +#define CPU_H256 (HREFRESH_256/6) + +/* Maximum state data size */ +#define MAX_STASIZE (0x8000+(RAMPages*0x4000)+(VRAMPages*0x4000)) + +#define INT_IE0 0x01 /* VDP interrupt modes */ +#define INT_IE1 0x02 +#define INT_IE2 0x04 + +#define JST_UP 0x01 /* Joystick() bits (shift by 8 */ +#define JST_DOWN 0x02 /* for the second joystick) */ +#define JST_LEFT 0x04 +#define JST_RIGHT 0x08 +#define JST_FIREA 0x10 +#define JST_FIREB 0x20 + + /* Joystick/Mouse types: */ +#define JOY_NONE 0 /* No joystick */ +#define JOY_STICK 1 /* Joystick */ +#define JOY_MOUSTICK 2 /* Mouse acting as joystick */ +#define JOY_MOUSE 3 /* Mouse */ + + /* ROM mapper types: */ +#define MAP_GEN8 0 /* Generic switch, 8kB pages */ +#define MAP_GEN16 1 /* Generic switch, 16kB pages */ +#define MAP_KONAMI5 2 /* Konami 5000/7000/9000/B000h */ +#define MAP_KONAMI4 3 /* Konami 4000/6000/8000/A000h */ +#define MAP_ASCII8 4 /* ASCII 6000/6800/7000/7800h */ +#define MAP_ASCII16 5 /* ASCII 6000/7000h */ +#define MAP_GMASTER2 6 /* Konami GameMaster2 cartridge */ +#define MAP_FMPAC 7 /* Panasonic FMPAC cartridge */ +#define MAP_GUESS 8 /* Guess mapper automatically */ + +#define MAP_SRAM(N) \ + (((N)==MAP_ASCII8)||((N)==MAP_ASCII16)|| \ + ((N)==MAP_GMASTER2)||((N)==MAP_FMPAC)) + +#define FMPAC_MAGIC 0x694D /* FMPAC SRAM "magic value" */ + +#define PGSIZE 0x4000L /* Size of a RAM page */ +#define NORAM 0xFF /* Byte to be returned from */ + /* non-existing pages and ports */ +#define MAXSCREEN 12 /* Highest screen mode supported */ +#define MAXSPRITE1 4 /* Sprites/line in SCREEN 1-3 */ +#define MAXSPRITE2 8 /* Sprites/line in SCREEN 4-8 */ +#define MAXDRIVES 2 /* Number of disk drives */ +#define MAXDISKS 32 /* Number of disks for a drive */ +#define MAXSLOTS 6 /* Number of cartridge slots */ +#define MAXCARTS 2 /* Number of user cartridges */ +#define MAXMAPPERS 8 /* Total defined MegaROM mappers */ +#define MAXCHUNKS 256 /* Max number of memory blocks */ +#define MAXCHEATS 256 /* Max number of cheats */ + +#define MAXCHANNELS (AY8910_CHANNELS+YM2413_CHANNELS) + /* Number of sound channels used by the emulation */ + +/** Model and options bits and macros ************************/ +#define MODEL(M) ((Mode&MSX_MODEL)==(M)) +#define VIDEO(M) ((Mode&MSX_VIDEO)==(M)) +#define OPTION(M) (Mode&(M)) +#define ROMTYPE(N) ((Mode>>(8+4*(N)))&0x0F) +#define ROMGUESS(N) (Mode&(MSX_GUESSA<<(N))) +#define JOYTYPE(N) ((Mode>>(4+2*(N)))&0x03) +#define SETROMTYPE(N,T) Mode=(Mode&~(0xF00<<(4*(N))))|((T)<<(8+4*(N))) +#define SETJOYTYPE(N,T) Mode=(Mode&~(0x030<<(2*(N))))|((T)<<(4+2*(N))) + +#define MSX_MODEL 0x00000003 /* Hardware Model: */ +#define MSX_MSX1 0x00000000 /* MSX1 computer (TMS9918) */ +#define MSX_MSX2 0x00000001 /* MSX2 computer (V9938) */ +#define MSX_MSX2P 0x00000002 /* MSX2+ computer (V9958) */ + +#define MSX_VIDEO 0x00000004 /* Video System: */ +#define MSX_NTSC 0x00000000 /* NTSC computer (US/Japan) */ +#define MSX_PAL 0x00000004 /* PAL computer (Europe) */ + +#define MSX_JOYSTICKS 0x000000F0 /* Joystick Sockets: */ +#define MSX_SOCKET1 0x00000030 /* Joystick socket #1 */ +#define MSX_SOCKET2 0x000000C0 /* Joystick socket #2 */ +#define MSX_NOJOY1 0x00000000 /* No joystick in socket #1 */ +#define MSX_JOY1 0x00000010 /* Joystick in socket #1 */ +#define MSX_MOUSTICK1 0x00000020 /* Mouse as joystick #1 */ +#define MSX_MOUSE1 0x00000030 /* Mouse in socket #1 */ +#define MSX_NOJOY2 0x00000000 /* No joystick in socket #2 */ +#define MSX_JOY2 0x00000040 /* Joystick in socket #2 */ +#define MSX_MOUSTICK2 0x00000080 /* Mouse as joystick #2 */ +#define MSX_MOUSE2 0x000000C0 /* Mouse in socket #2 */ + +#define MSX_MAPPERS 0x0003FF00 /* Cartridge Slots: */ +#define MSX_SLOTA 0x00010F00 /* Cartridge slot A (1:x) */ +#define MSX_SLOTB 0x0002F000 /* Cartridge slot B (2:x) */ +#define MSX_MAPPERA 0x00000F00 /* ROM mapper A (0..15) */ +#define MSX_MAPPERB 0x0000F000 /* ROM mapper B (0..15) */ +#define MSX_GUESSA 0x00010000 /* Guess ROM mapper type A */ +#define MSX_GUESSB 0x00020000 /* Guess ROM mapper type B */ + +#define MSX_OPTIONS 0x7FFC0000 /* Miscellaneous Options: */ +#define MSX_ALLSPRITE 0x00800000 /* Show ALL sprites */ +#define MSX_AUTOFIREA 0x01000000 /* Autofire joystick FIRE-A */ +#define MSX_AUTOFIREB 0x02000000 /* Autofire joystick FIRE-B */ +#define MSX_AUTOSPACE 0x04000000 /* Autofire SPACE button */ +#define MSX_DRUMS 0x08000000 /* Hit MIDI drums for noise */ +#define MSX_PATCHBDOS 0x10000000 /* Patch DiskROM routines */ +#define MSX_FIXEDFONT 0x20000000 /* Use fixed 8x8 text font */ +#define MSX_MSXDOS2 0x40000000 /* Load MSXDOS2 ROM on boot */ +/*************************************************************/ + +/** Keyboard codes and macros ********************************/ +extern const byte Keys[130][2]; +extern volatile byte KeyState[16]; + +#define KBD_SET(K) KeyState[Keys[K][0]]&=~Keys[K][1] +#define KBD_RES(K) KeyState[Keys[K][0]]|=Keys[K][1] + +#define KBD_LEFT 0x01 +#define KBD_UP 0x02 +#define KBD_RIGHT 0x03 +#define KBD_DOWN 0x04 +#define KBD_SHIFT 0x05 +#define KBD_CONTROL 0x06 +#define KBD_GRAPH 0x07 +#define KBD_BS 0x08 +#define KBD_TAB 0x09 +#define KBD_CAPSLOCK 0x0A +#define KBD_SELECT 0x0B +#define KBD_HOME 0x0C +#define KBD_ENTER 0x0D +#define KBD_DELETE 0x0E +#define KBD_INSERT 0x0F +#define KBD_COUNTRY 0x10 +#define KBD_STOP 0x11 +#define KBD_F1 0x12 +#define KBD_F2 0x13 +#define KBD_F3 0x14 +#define KBD_F4 0x15 +#define KBD_F5 0x16 +#define KBD_NUMPAD0 0x17 +#define KBD_NUMPAD1 0x18 +#define KBD_NUMPAD2 0x19 +#define KBD_NUMPAD3 0x1A +#define KBD_ESCAPE 0x1B +#define KBD_NUMPAD4 0x1C +#define KBD_NUMPAD5 0x1D +#define KBD_NUMPAD6 0x1E +#define KBD_NUMPAD7 0x1F +#define KBD_SPACE 0x20 +#define KBD_NUMPAD8 0x80 +#define KBD_NUMPAD9 0x81 +/*************************************************************/ + +/** Cheats() arguments ***************************************/ +#define CHTS_OFF 0 /* Turn all cheats off */ +#define CHTS_ON 1 /* Turn all cheats on */ +#define CHTS_TOGGLE 2 /* Toggle cheats state */ +#define CHTS_QUERY 3 /* Query cheats state */ +/*************************************************************/ + +/** Following macros can be used in screen drivers ***********/ +#define BigSprites (VDP[1]&0x01) /* Zoomed sprites */ +#define Sprites16x16 (VDP[1]&0x02) /* 16x16/8x8 sprites */ +#define ScreenON (VDP[1]&0x40) /* Show screen */ +#define SpritesOFF (VDP[8]&0x02) /* Don't show sprites */ +#define SolidColor0 (VDP[8]&0x20) /* Solid/Tran. COLOR 0 */ +#define PALVideo (VDP[9]&0x02) /* PAL/NTSC video */ +#define FlipEvenOdd (VDP[9]&0x04) /* Flip even/odd pages */ +#define InterlaceON (VDP[9]&0x08) /* Interlaced screen */ +#define ScanLines212 (VDP[9]&0x80) /* 212/192 scanlines */ +#define HScroll512 (VDP[25]&0x01) /* HScroll both pages */ +#define MaskEdges (VDP[25]&0x02) /* Mask 8-pixel edges */ +#define ModeYJK (VDP[25]&0x08) /* YJK screen mode */ +#define ModeYAE (VDP[25]&0x10) /* YJK/YAE screen mode */ +#define VScroll VDP[23] +#define HScroll ((VDP[27]&0x07)|((int)(VDP[26]&0x3F)<<3)) +#define VAdjust (-((signed char)(VDP[18])>>4)) +#define HAdjust (-((signed char)(VDP[18]<<4)>>4)) +/*************************************************************/ + +/** Variables used to control emulator behavior **************/ +extern byte Verbose; /* Debug msgs ON/OFF */ +extern int Mode; /* ORed MSX_* bits */ +extern int RAMPages,VRAMPages; /* Number of RAM pages */ +extern byte UPeriod; /* % of frames to draw */ +/*************************************************************/ + +/** Screen Mode Handlers [number of screens + 1] *************/ +extern void (*RefreshLine[MAXSCREEN+2])(byte Y); +/*************************************************************/ + +extern Z80 *CPU; /* CPU state/registers */ +extern byte *msx_VRAM; /* Video RAM */ +extern byte VDP[64]; /* VDP control reg-ers */ +extern byte VDPStatus[16]; /* VDP status reg-ers */ +extern byte *ChrGen,*ChrTab,*ColTab; /* VDP tables (screen) */ +extern byte *SprGen,*SprTab; /* VDP tables (sprites)*/ +extern int ChrGenM,ChrTabM,ColTabM; /* VDP masks (screen) */ +extern int SprTabM; /* VDP masks (sprites) */ +extern byte FGColor,BGColor; /* Colors */ +extern byte XFGColor,XBGColor; /* Alternative colors */ +extern byte ScrMode; /* Current screen mode */ +extern int ScanLine; /* Current scanline */ +extern byte *FontBuf; /* Optional fixed font */ + +extern byte ExitNow; /* 1: Exit emulator */ + +extern byte PSLReg; /* Primary slot reg. */ +extern byte SSLReg[4]; /* Secondary slot reg. */ + +extern const char *ProgDir; /* Program directory */ +extern const char *ROMName[MAXCARTS]; /* Cart A/B ROM files */ +extern const char *DSKName[MAXDRIVES];/* Disk A/B images */ +extern const char *SndName; /* Soundtrack log file */ +extern const char *PrnName; /* Printer redir. file */ +extern const char *CasName; /* Tape image file */ +extern const char *ComName; /* Serial redir. file */ +extern const char *STAName; /* State save name */ +extern const char *FNTName; /* Font file for text */ + +extern FDIDisk *FDD; /* Floppy disk images */ +extern FILE *CasStream; /* Cassette I/O stream */ + +typedef struct +{ + unsigned int Addr; + word Data,Orig; + byte Size; + byte Text[14]; +} CheatCode; + +/** StartMSX() ***********************************************/ +/** Allocate memory, load ROM image, initialize hardware, **/ +/** CPU and start the emulation. This function returns 0 in **/ +/** the case of failure. **/ +/*************************************************************/ +int StartMSX(int NewMode,int NewRAMPages,int NewVRAMPages); + +/** TrashMSX() ***********************************************/ +/** Free memory allocated by StartMSX(). **/ +/*************************************************************/ +void TrashMSX(void); + +/** ResetMSX() ***********************************************/ +/** Reset MSX hardware to new operating modes. Returns new **/ +/** modes, possibly not the same as NewMode. **/ +/*************************************************************/ +int ResetMSX(int NewMode,int NewRAMPages,int NewVRAMPages); + +/** MenuMSX() ************************************************/ +/** Invoke a menu system allowing to configure the emulator **/ +/** and perform several common tasks. **/ +/*************************************************************/ +void MenuMSX(void); + +/** LoadFile() ***********************************************/ +/** Simple utility function to load cartridge, state, font **/ +/** or a disk image, based on the file extension, etc. **/ +/*************************************************************/ +int LoadFile(const char *FileName); + +/** LoadCart() ***********************************************/ +/** Load cartridge into given slot. Returns cartridge size **/ +/** in 16kB pages on success, 0 on failure. **/ +/*************************************************************/ +int LoadCart(const char *FileName,int Slot,int Type); + +/** SaveSTA() ************************************************/ +/** Save emulation state to a .STA file. **/ +/*************************************************************/ +int SaveSTA(const char *FileName); + +/** LoadSTA() ************************************************/ +/** Load emulation state from a .STA file. **/ +/*************************************************************/ +int LoadSTA(const char *FileName); + +/** LoadMCF() ************************************************/ +/** Load cheats from .MCF file. Returns number of loaded **/ +/** cheat entries or 0 on failure. **/ +/*************************************************************/ +int LoadMCF(const char *Name); + +/** SaveMCF() ************************************************/ +/** Save cheats to .MCF file. Returns number of loaded **/ +/** cheat entries or 0 on failure. **/ +/*************************************************************/ +int SaveMCF(const char *Name); + +/** LoadCHT() ************************************************/ +/** Load cheats from .CHT file. Cheat format is either **/ +/** 00XXXXXX-XX (one byte) or 00XXXXXX-XXXX (two bytes) for **/ +/** ROM-based cheats and XXXX-XX or XXXX-XXXX for RAM-based **/ +/** cheats. Returns the number of cheats on success, 0 on **/ +/** failure. **/ +/*************************************************************/ +int LoadCHT(const char *Name); + +/** SaveCHT() ************************************************/ +/** Save cheats to a given text file. Returns the number of **/ +/** cheats on success, 0 on failure. **/ +/*************************************************************/ +int SaveCHT(const char *Name); + +/** LoadPAL() ************************************************/ +/** Load new palette from .PAL file. Returns number of **/ +/** loaded colors on success, 0 on failure. **/ +/*************************************************************/ +int LoadPAL(const char *Name); + +/** MakeFileName() *******************************************/ +/** Make a copy of the file name, replacing the extension. **/ +/** Returns allocated new name or 0 on failure. **/ +/*************************************************************/ +char *MakeFileName(const char *FileName,const char *Extension); + +/** ChangePrinter() ******************************************/ +/** Change printer output to a given file. The previous **/ +/** file is closed. ChangePrinter(0) redirects output to **/ +/** stdout. **/ +/*************************************************************/ +void ChangePrinter(const char *FileName); + +/** ChangeTape() *********************************************/ +/** Change tape image. ChangeTape(0) closes current image. **/ +/** Returns 1 on success, 0 on failure. **/ +/*************************************************************/ +byte ChangeTape(const char *FileName); + +/** RewindTape() *********************************************/ +/** Rewind currenly open tape. **/ +/*************************************************************/ +void RewindTape(void); + +/** ChangeDisk() *********************************************/ +/** Change disk image in a given drive. Closes current disk **/ +/** image if Name=0 was given. Creates a new disk image if **/ +/** Name="" was given. Returns 1 on success or 0 on failure.**/ +/*************************************************************/ +byte ChangeDisk(byte N,const char *FileName); + +/** LoadFNT() ************************************************/ +/** Load fixed 8x8 font used in text screen modes when **/ +/** MSX_FIXEDFONT option is enabled. LoadFNT(0) frees the **/ +/** font buffer. Returns 1 on success, 0 on failure. **/ +/*************************************************************/ +byte LoadFNT(const char *FileName); + +/** SetScreenDepth() *****************************************/ +/** Set screen depth for the display drivers. Returns 1 on **/ +/** success, 0 on failure. **/ +/*************************************************************/ +int SetScreenDepth(int Depth); + +/** ApplyMCFCheat() ******************************************/ +/** Apply given MCF cheat entry. Returns 0 on failure or 1 **/ +/** on success. **/ +/*************************************************************/ +int ApplyMCFCheat(int N); + +/** AddCheat() ***********************************************/ +/** Add a new cheat. Returns 0 on failure or the number of **/ +/** cheats on success. **/ +/*************************************************************/ +int AddCheat(const char *Cheat); + +/** DelCheat() ***********************************************/ +/** Delete a cheat. Returns 0 on failure, 1 on success. **/ +/*************************************************************/ +int DelCheat(const char *Cheat); + +/** ResetCheats() ********************************************/ +/** Remove all cheats. **/ +/*************************************************************/ +void ResetCheats(void); + +/** Cheats() *************************************************/ +/** Toggle cheats on (1), off (0), inverse state (2) or **/ +/** query (3). **/ +/*************************************************************/ +int Cheats(int Switch); + +/** SaveState() **********************************************/ +/** Save emulation state to a memory buffer. Returns size **/ +/** on success, 0 on failure. **/ +/*************************************************************/ +unsigned int SaveState(unsigned char *Buf,unsigned int MaxSize); + +/** LoadState() **********************************************/ +/** Load emulation state from a memory buffer. Returns size **/ +/** on success, 0 on failure. **/ +/*************************************************************/ +unsigned int LoadState(unsigned char *Buf,unsigned int MaxSize); + +/** InitMachine() ********************************************/ +/** Allocate resources needed by the machine-dependent code.**/ +/************************************ TO BE WRITTEN BY USER **/ +int InitMachine(void); + +/** TrashMachine() *******************************************/ +/** Deallocate all resources taken by InitMachine(). **/ +/************************************ TO BE WRITTEN BY USER **/ +void TrashMachine(void); + +/** Keyboard() ***********************************************/ +/** This function is periodically called to poll keyboard. **/ +/************************************ TO BE WRITTEN BY USER **/ +void Keyboard(void); + +/** Joystick() ***********************************************/ +/** Query positions of two joystick connected to ports 0/1. **/ +/** Returns 0.0.B2.A2.R2.L2.D2.U2.0.0.B1.A1.R1.L1.D1.U1. **/ +/************************************ TO BE WRITTEN BY USER **/ +unsigned int Joystick(void); + +/** Mouse() **************************************************/ +/** Query coordinates of a mouse connected to port N. **/ +/** Returns F2.F1.Y.Y.Y.Y.Y.Y.Y.Y.X.X.X.X.X.X.X.X. **/ +/************************************ TO BE WRITTEN BY USER **/ +unsigned int Mouse(byte N); + +/** DiskPresent()/DiskRead()/DiskWrite() *********************/ +/*** These three functions are called to check for floppy **/ +/*** disk presence in the "drive", and to read/write given **/ +/*** sector to the disk. **/ +/************************************ TO BE WRITTEN BY USER **/ +byte DiskPresent(byte ID); +byte DiskRead(byte ID,byte *Buf,int N); +byte DiskWrite(byte ID,const byte *Buf,int N); + +/** PlayAllSound() *******************************************/ +/** Render and play given number of microseconds of sound. **/ +/************************************ TO BE WRITTEN BY USER **/ +void PlayAllSound(int uSec); + +/** SetColor() ***********************************************/ +/** Set color N (0..15) to (R,G,B). **/ +/************************************ TO BE WRITTEN BY USER **/ +void SetColor(byte N,byte R,byte G,byte B); + +/** RefreshScreen() ******************************************/ +/** Refresh screen. This function is called in the end of **/ +/** refresh cycle to show the entire screen. **/ +/************************************ TO BE WRITTEN BY USER **/ +void RefreshScreen(void); + +/** RefreshLine#() *******************************************/ +/** Refresh line Y (0..191/211), on an appropriate SCREEN#, **/ +/** including sprites in this line. **/ +/************************************ TO BE WRITTEN BY USER **/ +void RefreshLineTx80(byte Y); +void RefreshLine0(byte Y); +void RefreshLine1(byte Y); +void RefreshLine2(byte Y); +void RefreshLine3(byte Y); +void RefreshLine4(byte Y); +void RefreshLine5(byte Y); +void RefreshLine6(byte Y); +void RefreshLine7(byte Y); +void RefreshLine8(byte Y); +void RefreshLine10(byte Y); +void RefreshLine12(byte Y); + +#ifdef __cplusplus +} +#endif +#endif /* MSX_H */ diff --git a/components/msx/fmsx/src/fMSX/Menu.c b/components/msx/fmsx/src/fMSX/Menu.c new file mode 100644 index 0000000..45a14a9 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/Menu.c @@ -0,0 +1,667 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** Menu.c **/ +/** **/ +/** This file contains runtime menu code for configuring **/ +/** the emulator. It uses console functions from Console.h. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 2005-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "MSX.h" +#include "Console.h" +#include "Sound.h" +#include "Hunt.h" +#include "MCF.h" + +#include +#include +#include + +#define CLR_BACK PIXEL(255,255,255) +#define CLR_BACK2 PIXEL(255,200,150) +#define CLR_BACK3 PIXEL(150,255,255) +#define CLR_BACK4 PIXEL(255,255,150) +#define CLR_BACK5 PIXEL(255,150,255) +#define CLR_TEXT PIXEL(0,0,0) +#define CLR_WHITE PIXEL(255,255,255) +#define CLR_ERROR PIXEL(200,0,0) +#define CLR_INFO PIXEL(0,128,0) + +static char SndNameBuf[256]; + +extern byte *MemMap[4][4][8]; /* [PPage][SPage][Adr] */ +extern byte *EmptyRAM; /* Dummy memory area */ + +/** Cheat Structures *****************************************/ +extern int CheatCount; /* # of cheats in CheatCodes[] */ +extern int MCFCount; /* # of entries in MCFEntries[] */ +extern CheatCode CheatCodes[MAXCHEATS]; +extern MCFEntry MCFEntries[MAXCHEATS]; + +/** MenuMSX() ************************************************/ +/** Invoke a menu system allowing to configure the emulator **/ +/** and perform several common tasks. **/ +/*************************************************************/ +void MenuMSX(void) +{ + const char *P; + char S[512],*T,*PP; + int I,J,K,N,V,M; + + /* Display and activate top menu */ + for(J=1;J;) + { + /* Compose menu */ + sprintf(S, + "fMSX\n" + "Load file\n" + "Save file\n" + " \n" + "Hardware model\n" + "Input devices\n" + "Cartridge slots\n" + "Disk drives\n" + "Cheats\n" + "Search cheats\n" + " \n" + "Log soundtrack %c\n" + "Hit MIDI drums %c\n" + " \n" + "Use fixed font %c\n" + "Show all sprites %c\n" + "Patch DiskROM %c\n" + " \n" + "POKE &hFFFF,&hAA\n" + "Rewind tape\n" + "Reset emulator\n" + "Quit emulator\n" + " \n" + "Done\n", + MIDILogging(MIDI_QUERY)? CON_CHECK:' ', + OPTION(MSX_DRUMS)? CON_CHECK:' ', + OPTION(MSX_FIXEDFONT)? CON_CHECK:' ', + OPTION(MSX_ALLSPRITE)? CON_CHECK:' ', + OPTION(MSX_PATCHBDOS)? CON_CHECK:' ' + ); + + /* Replace all EOLNs with zeroes */ + for(I=0;S[I];I++) if(S[I]=='\n') S[I]='\0'; + /* Run menu */ + J=CONMenu(-1,-1,-1,-1,CLR_TEXT,CLR_BACK,S,J); + /* Handle menu selection */ + switch(J) + { + case 1: /* Load cartridge, disk image, state, or font */ + /* Request file name */ + P=CONFile(CLR_TEXT,CLR_BACK3,".rom\0.rom.gz\0.mx1\0.mx1.gz\0.mx2\0.mx2.gz\0.dsk\0.dsk.gz\0.sta\0.sta.gz\0.cas\0.fnt\0.fnt.gz\0.cht\0.pal\0"); + /* Try loading file, show error on failure */ + if(P&&!LoadSTA(P)&&!LoadFile(P)) + CONMsg(-1,-1,-1,-1,CLR_BACK,CLR_ERROR,"Error","Cannot load file.\0\0"); + /* Exit top menu */ + J=0; + break; + + case 2: /* Save state, printer output, or soundtrack */ + /* Run menu */ + switch(CONMenu(-1,-1,-1,-1,CLR_TEXT,CLR_BACK4, + "Save File\0Emulation state\0Printer output\0MIDI soundtrack\0",1 + )) + { + case 1: /* Save state */ + /* Request file name */ + P=CONFile(CLR_TEXT,CLR_BACK2,".sta\0"); + /* Try saving state, show error on failure */ + if(P&&!SaveSTA(P)) + CONMsg(-1,-1,-1,-1,CLR_BACK,CLR_ERROR,"Error","Cannot save state.\0\0"); + /* Exit top menu */ + J=0; + break; + case 2: /* Printer output file */ + /* Request file name */ + P=CONFile(CLR_TEXT,CLR_BACK2,".prn\0.out\0.txt\0"); + /* Try changing printer output */ + if(P) ChangePrinter(P); + /* Exit top menu */ + J=0; + break; + case 3: /* Soundtrack output file */ + /* Request file name */ + P=CONFile(CLR_TEXT,CLR_BACK2,".mid\0.rmi\0"); + if(P) + { + /* Try changing MIDI log output, show error on failure */ + if(strlen(P)+1>sizeof(SndNameBuf)) + CONMsg(-1,-1,-1,-1,CLR_BACK,CLR_ERROR,"Error","Name too long.\0\0"); + else + { + strcpy(SndNameBuf,P); + SndName=SndNameBuf; + InitMIDI(SndName); + MIDILogging(MIDI_ON); + } + } + /* Exit top menu */ + J=0; + break; + } + break; + + case 4: /* Hardware model */ + for(K=1;K;) + { + /* Compose menu */ + sprintf(S, + "Hardware Model\n" + "MSX1 (TMS9918) %c\n" + "MSX2 (V9938) %c\n" + "MSX2+ (V9958) %c\n" + " \n" + "NTSC (US/Japan) %c\n" + "PAL (Europe) %c\n" + " \n" + "Main memory %3dkB\n" + "Video memory %3dkB\n" + " \n" + "Done\n", + MODEL(MSX_MSX1)? CON_CHECK:' ', + MODEL(MSX_MSX2)? CON_CHECK:' ', + MODEL(MSX_MSX2P)? CON_CHECK:' ', + VIDEO(MSX_NTSC)? CON_CHECK:' ', + VIDEO(MSX_PAL)? CON_CHECK:' ', + RAMPages*16, + VRAMPages*16 + ); + /* Replace all EOLNs with zeroes */ + for(I=0;S[I];I++) if(S[I]=='\n') S[I]='\0'; + /* Run menu */ + K=CONMenu(-1,-1,-1,-1,CLR_TEXT,CLR_BACK4,S,K); + /* Handle menu selection */ + switch(K) + { + case 1: if(!MODEL(MSX_MSX1)) ResetMSX((Mode&~MSX_MODEL)|MSX_MSX1,RAMPages,VRAMPages);break; + case 2: if(!MODEL(MSX_MSX2)) ResetMSX((Mode&~MSX_MODEL)|MSX_MSX2,RAMPages,VRAMPages);break; + case 3: if(!MODEL(MSX_MSX2P)) ResetMSX((Mode&~MSX_MODEL)|MSX_MSX2P,RAMPages,VRAMPages);break; + case 5: if(!VIDEO(MSX_NTSC)) ResetMSX((Mode&~MSX_VIDEO)|MSX_NTSC,RAMPages,VRAMPages);break; + case 6: if(!VIDEO(MSX_PAL)) ResetMSX((Mode&~MSX_VIDEO)|MSX_PAL,RAMPages,VRAMPages);break; + case 8: ResetMSX(Mode,RAMPages<32? RAMPages*2:4,VRAMPages);break; + case 9: ResetMSX(Mode,RAMPages,VRAMPages<32? VRAMPages*2:2);break; + case 11: K=0;break; + } + } + /* Exit top menu */ + J=0; + break; + + case 5: /* Input devices */ + for(K=1;K;) + { + /* Compose menu */ + sprintf(S, + "Input Devices\n" + "SOCKET1:Empty %c\n" + "SOCKET1:Joystick %c\n" + "SOCKET1:JoyMouse %c\n" + "SOCKET1:Mouse %c\n" + " \n" + "SOCKET2:Empty %c\n" + "SOCKET2:Joystick %c\n" + "SOCKET2:JoyMouse %c\n" + "SOCKET2:Mouse %c\n" + " \n" + "Autofire on SPACE %c\n" + "Autofire on FIRE-A %c\n" + "Autofire on FIRE-B %c\n" + " \n" + "Done\n", + JOYTYPE(0)==JOY_NONE? CON_CHECK:' ', + JOYTYPE(0)==JOY_STICK? CON_CHECK:' ', + JOYTYPE(0)==JOY_MOUSTICK? CON_CHECK:' ', + JOYTYPE(0)==JOY_MOUSE? CON_CHECK:' ', + JOYTYPE(1)==JOY_NONE? CON_CHECK:' ', + JOYTYPE(1)==JOY_STICK? CON_CHECK:' ', + JOYTYPE(1)==JOY_MOUSTICK? CON_CHECK:' ', + JOYTYPE(1)==JOY_MOUSE? CON_CHECK:' ', + OPTION(MSX_AUTOSPACE)? CON_CHECK:' ', + OPTION(MSX_AUTOFIREA)? CON_CHECK:' ', + OPTION(MSX_AUTOFIREB)? CON_CHECK:' ' + ); + /* Replace all EOLNs with zeroes */ + for(I=0;S[I];I++) if(S[I]=='\n') S[I]='\0'; + /* Run menu */ + K=CONMenu(-1,-1,-1,-1,CLR_TEXT,CLR_BACK4,S,K); + /* Handle menu selection */ + switch(K) + { + case 1: SETJOYTYPE(0,JOY_NONE);break; + case 2: SETJOYTYPE(0,JOY_STICK);break; + case 3: SETJOYTYPE(0,JOY_MOUSTICK);break; + case 4: SETJOYTYPE(0,JOY_MOUSE);break; + case 6: SETJOYTYPE(1,JOY_NONE);break; + case 7: SETJOYTYPE(1,JOY_STICK);break; + case 8: SETJOYTYPE(1,JOY_MOUSTICK);break; + case 9: SETJOYTYPE(1,JOY_MOUSE);break; + case 11: Mode^=MSX_AUTOSPACE;break; + case 12: Mode^=MSX_AUTOFIREA;break; + case 13: Mode^=MSX_AUTOFIREB;break; + case 15: K=0;break; + } + } + /* Exit top menu */ + J=0; + break; + + case 6: /* Cartridge slots */ + /* Create slot selection menu */ + sprintf(S, + "Cartridges\n" + "Slot A:%c\n" + "Slot B:%c\n", + MemMap[1][0][2]!=EmptyRAM? CON_FILE:' ', + MemMap[2][0][2]!=EmptyRAM? CON_FILE:' ' + ); + /* Replace all EOLNs with zeroes */ + for(I=0;S[I];I++) if(S[I]=='\n') S[I]='\0'; + /* Get cartridge slot number */ + N=CONMenu(-1,-1,-1,-1,CLR_TEXT,CLR_BACK4,S,1); + /* Exit to top menu if cancelled */ + if(!N) break; else --N; + /* Run slot-specific menu */ + for(K=1;K;) + { + /* Compose menu */ + sprintf(S, + "Cartridge Slot %c\n" + "Load cartridge\n" + "Eject cartridge\n" + "Guess MegaROM mapper %c\n" + " \n" + "Generic 8kB switch %c\n" + "Generic 16kB switch %c\n" + "Konami 5000h mapper %c\n" + "Konami 4000h mapper %c\n" + "ASCII 8kB mapper %c\n" + "ASCII 16kB mapper %c\n" + "Konami GameMaster2 %c\n" + "Panasonic FMPAC %c\n" + " \n" + "Done\n", + 'A'+N, + ROMGUESS(N)? CON_CHECK:' ', + ROMTYPE(N)==MAP_GEN8? CON_CHECK:' ', + ROMTYPE(N)==MAP_GEN16? CON_CHECK:' ', + ROMTYPE(N)==MAP_KONAMI5? CON_CHECK:' ', + ROMTYPE(N)==MAP_KONAMI4? CON_CHECK:' ', + ROMTYPE(N)==MAP_ASCII8? CON_CHECK:' ', + ROMTYPE(N)==MAP_ASCII16? CON_CHECK:' ', + ROMTYPE(N)==MAP_GMASTER2? CON_CHECK:' ', + ROMTYPE(N)==MAP_FMPAC? CON_CHECK:' ' + ); + /* Replace all EOLNs with zeroes */ + for(I=0;S[I];I++) if(S[I]=='\n') S[I]='\0'; + /* Run menu */ + K=CONMenu(-1,-1,-1,-1,CLR_TEXT,CLR_BACK4,S,K); + /* Handle menu selection */ + switch(K) + { + case 1: + /* Request file name */ + P=CONFile(CLR_TEXT,CLR_BACK3,".rom\0.rom.gz\0.mx1\0.mx1.gz\0.mx2\0.mx2.gz\0"); + /* Try loading file, show error on failure */ + if(P&&!LoadCart(P,N,ROMGUESS(N)|ROMTYPE(N))) + CONMsg(-1,-1,-1,-1,CLR_BACK,CLR_ERROR,"Error","Cannot load file.\0\0"); + /* Exit to top menu */ + K=0; + break; + case 2: LoadCart(0,N,ROMGUESS(N)|ROMTYPE(N));K=0;break; + case 3: Mode^=MSX_GUESSA<=0x100)||(V>=0x100)) M|=HUNT_16BIT; + + sprintf(S, + "Search for %d\n" + "New value %5d\n" + " \n" + "8bit value %c\n" + "16bit value %c\n" + " \n" + "Constant %c\n" + "Changes by +1 %c\n" + "Changes by -1 %c\n" + "Changes by +N %c\n" + "Changes by -N %c\n" + " \n" + "Search now\n", + K,V, + !(M&HUNT_16BIT)? CON_CHECK:' ', + M&HUNT_16BIT? CON_CHECK:' ', + (M&HUNT_MASK_CHANGE)==HUNT_CONSTANT? CON_CHECK:' ', + (M&HUNT_MASK_CHANGE)==HUNT_PLUSONE? CON_CHECK:' ', + (M&HUNT_MASK_CHANGE)==HUNT_MINUSONE? CON_CHECK:' ', + (M&HUNT_MASK_CHANGE)==HUNT_PLUSMANY? CON_CHECK:' ', + (M&HUNT_MASK_CHANGE)==HUNT_MINUSMANY? CON_CHECK:' ' + ); + + /* Replace all EOLNs with zeroes */ + for(J=0;S[J];J++) if(S[J]=='\n') S[J]='\0'; + + /* Run menu */ + I=CONMenu(-1,-1,-1,-1,CLR_TEXT,CLR_BACK2,S,I); + + /* Change options */ + switch(I) + { + case 1: + /* Ask for replacement value in 0..65535 range */ + P = CONInput(-1,-1,CLR_TEXT,CLR_BACK4,"New Value",S,6|CON_DEC); + I = P? strtoul(P,0,10):-1; + V = (I>=0)&&(I<0x10000)? I:V; + I = 1; + break; + case 3: M&=~HUNT_16BIT;break; + case 4: M|=HUNT_16BIT;break; + case 6: M=(M&~HUNT_MASK_CHANGE)|HUNT_CONSTANT;break; + case 7: M=(M&~HUNT_MASK_CHANGE)|HUNT_PLUSONE;break; + case 8: M=(M&~HUNT_MASK_CHANGE)|HUNT_MINUSONE;break; + case 9: M=(M&~HUNT_MASK_CHANGE)|HUNT_PLUSMANY;break; + case 10: M=(M&~HUNT_MASK_CHANGE)|HUNT_MINUSMANY;break; + case 12: + /* Search for value RAM */ + J = AddHUNT(0xC000,0x4000,K,V,M); + I = 0; + /* Show number of found locations */ + sprintf(S,"Found %d locations.\n",J); + for(J=0;S[J];J++) if(S[J]=='\n') S[J]='\0'; + CONMsg(-1,-1,-1,-1,CLR_WHITE,CLR_INFO,"Initial Search",S); + break; + } + } + I=0; + break; + + case 3: ScanHUNT(); + /* Fall through */ + case 4: /* Show current cheats */ + /* Find current number of locations, limit it to 32 */ + K = TotalHUNT(); + K = K<32? K:32; + + /* If no locations, show a warning */ + if(!K) + { + CONMsg(-1,-1,-1,-1,CLR_WHITE,CLR_INFO,"Empty","No cheats found.\0\0"); + I=0; + break; + } + + /* Show cheat selection dialog */ + for(I=1,M=0;I;) + { + /* Compose dialog */ + sprintf(S,"Found %d Cheats\n",K); + for(J=0;(J9) { PP[8]=CON_DOTS;PP[9]='\0'; } + sprintf(S+strlen(S),"%-9s %c\n",PP,M&(1<=1)&&(I<=K)) M^=1<<(I-1); + else if(I) + { + /* If there are cheats to add, drop out */ + if(!M) + { + CONMsg(-1,-1,-1,-1,CLR_WHITE,CLR_INFO,"Empty","No cheats chosen.\0\0"); + I=0; + break; + } + + /* Disable and clear current cheats */ + ResetCheats(); + + /* Add found cheats */ + for(J=0;J +#include +#include +#include + +void SSlot(byte Value); /* Used to switch secondary slots */ +void PSlot(byte Value); /* Used to switch primary slots */ + +/** DiskPresent() ********************************************/ +/** Return 1 if disk drive with a given ID is present. **/ +/*************************************************************/ +byte DiskPresent(byte ID) +{ + return((IDPC.W-2) + { + +case 0x4010: +/** PHYDIO: Read/write sectors to disk ************************** +*** Input: *** +*** [F] CARRY=WRITE [A] Drive number (0=A:) *** +*** [B] Number of sectors to write [C] Media descriptor *** +*** [DE] Logical sector number (starts at 0) *** +*** [HL] Transfer address *** +*** Output: *** +*** [F] CARRY=ERROR [A] If error: errorcode *** +*** [B] Number of sectors remaining (not read/written) *** +*** Error codes in [A] can be: *** +*** 0 Write protected 8 Record not found *** +*** 2 Not ready 10 Write fault *** +*** 4 Data (CRC) error 12 Other errors *** +*** 6 Seek error *** +****************************************************************/ +{ + if(Verbose&0x04) + printf + ( + "%s DISK %c: %d sectors starting from %04Xh [buffer at %04Xh]\n", + R->AF.B.l&C_FLAG? "WRITE":"READ",R->AF.B.h+'A',R->BC.B.h, + R->DE.W,R->HL.W + ); + + R->IFF|=1; + Addr = R->HL.W; + Count = R->BC.B.h; + + if(!DiskPresent(R->AF.B.h)) + { R->AF.W=0x0201;return; } /* No disk -> "Not ready" */ + if((int)(R->DE.W)+Count>Info[R->BC.B.l-0xF8].Sectors) + { R->AF.W=0x0801;return; } /* Wrong sector -> "Record not found" */ + + /* If data does not fit into 64kB address space, trim it */ + if((int)(R->HL.W)+Count*512>0x10000) Count=(0x10000-R->HL.W)/512; + + /* Save slot states */ + PS=PSLReg; + SS=SSLReg[3]; + + /* Turn on RAM in all slots */ + msx_OutZ80(0xA8,0xFF); + SSlot(0xAA); + + if(R->AF.B.l&C_FLAG) + for(Sector=R->DE.W;Count--;Sector++) /* WRITE */ + { + for(J=0;J<512;J++) Buf[J]=msx_RdZ80(Addr++); + + if(DiskWrite(R->AF.B.h,Buf,Sector)) R->BC.B.h--; + else + { + R->AF.W=0x0A01; + SSlot(SS); + msx_OutZ80(0xA8,PS); + return; + } + } + else + for(Sector=R->DE.W;Count--;Sector++) /* READ */ + { + if(DiskRead(R->AF.B.h,Buf,Sector)) R->BC.B.h--; + else + { + R->AF.W=0x0401; + SSlot(SS); + msx_OutZ80(0xA8,PS); + return; + } + + for(J=0;J<512;J++) msx_WrZ80(Addr++,Buf[J]); + } + + /* Restore slot states */ + SSlot(SS); + msx_OutZ80(0xA8,PS); + + /* Return "Success" */ + R->AF.B.l&=~C_FLAG; + return; +} + +case 0x4013: +/** DSKCHG: Check if the disk was changed *********************** +*** Input: *** +*** [A] Drive number (0=A:) [B] Media descriptor *** +*** [C] Media descriptor [HL] Base address of DPB *** +*** Output: *** +*** [F] CARRY=ERROR [A] If error: errorcode (see DSKIO) *** +*** [B] If success: 1=Unchanged, 0=Unknown, -1=Changed *** +*** Note: *** +*** If the disk has been changed or may have been changed *** +*** (unknown) read the boot sector or the FAT sector for disk *** +*** media descriptor and transfer a new DPB as with GETDPB. *** +****************************************************************/ +{ + if(Verbose&0x04) printf("CHECK DISK %c\n",R->AF.B.h+'A'); + + R->IFF|=1; + + /* If no disk, return "Not ready": */ + if(!DiskPresent(R->AF.B.h)) { R->AF.W=0x0201;return; } + + /* This requires some major work to be done: */ + R->BC.B.h=0;R->AF.B.l&=~C_FLAG; + + /* We continue with GETDPB now... */ +} + +case 0x4016: +/** GETDPB: Disk format ***************************************** +*** Input: *** +*** [A] Drive number [B] 1st byte of FAT (media descriptor) *** +*** [C] Media descriptor [HL] Base address of DPB *** +*** Output: *** +*** [HL+1] .. [HL+18] = DPB for specified drive *** +*** DPB consists of: *** +*** Name Offset Size Description *** +*** MEDIA 0 1 Media type (F8..FF) *** +*** SECSIZ 1 2 Sector size (must be 2^n) *** +*** DIRMSK 3 1 (SECSIZE/32)-1 *** +*** DIRSHFT 4 1 Number of one bits in DIRMSK *** +*** CLUSMSK 5 1 (Sectors per cluster)-1 *** +*** CLUSSHFT 6 1 (Number of one bits in CLUSMSK)+1 *** +*** FIRFAT 7 2 Logical sector number of first FAT *** +*** FATCNT 8 1 Number of FATs *** +*** MAXENT A 1 Number of directory entries (max 254) *** +*** FIRREC B 2 Logical sector number of first data *** +*** MAXCLUS D 2 Number of clusters (not including *** +*** reserved, FAT and directory sectors)+1 *** +*** FATSIZ F 1 Number of sectors used *** +*** FIRDIR 10 2 FAT logical sector number of start of *** +*** directory *** +****************************************************************/ +{ + int BytesPerSector,SectorsPerDisk,SectorsPerFAT,ReservedSectors; + + /* If no disk, return "Not ready": */ + if(!DiskPresent(R->AF.B.h)) { R->AF.W=0x0201;return; } + /* If can't read, return "Other error": */ + if(!DiskRead(R->AF.B.h,Buf,0)) { R->AF.W=0x0C01;return; } + + BytesPerSector = (int)Buf[0x0C]*256+Buf[0x0B]; + SectorsPerDisk = (int)Buf[0x14]*256+Buf[0x13]; + SectorsPerFAT = (int)Buf[0x17]*256+Buf[0x16]; + ReservedSectors = (int)Buf[0x0F]*256+Buf[0x0E]; + + Addr=R->HL.W+1; + msx_WrZ80(Addr++,Buf[0x15]); /* Format ID [F8h-FFh] */ + msx_WrZ80(Addr++,Buf[0x0B]); /* Sector size */ + msx_WrZ80(Addr++,Buf[0x0C]); + J=(BytesPerSector>>5)-1; + for(I=0;J&(1<>8)&0xFF); + J=(SectorsPerDisk-J)/Buf[0x0D]; + msx_WrZ80(Addr++,J&0xFF); /* Number of clusters */ + msx_WrZ80(Addr++,(J>>8)&0xFF); + msx_WrZ80(Addr++,Buf[0x16]); /* Sectors per FAT */ + J=ReservedSectors+Buf[0x10]*SectorsPerFAT; + msx_WrZ80(Addr++,J&0xFF); /* Sector # of dir. */ + msx_WrZ80(Addr,(J>>8)&0xFF); + + /* Return success */ + R->AF.B.l&=~C_FLAG; + return; +} + +case 0x401C: +/** DSKFMT: Disk format ***************************************** +*** Input: *** +*** [A] Specified choice (1-9) [D] Drive number (0=A:) *** +*** [HL] Begin address of work area [BC] Length of work area *** +*** Output: *** +*** [F] CARRY=ERROR *** +*** Notes: *** +*** 1) Also writes a MSX boot sector at sector 0, clears all *** +*** FATs (media descriptor at first byte, 0FFh at second/ *** +*** third byte and rest zero) and clears the directory *** +*** filling it with zeros. *** +*** 2) Error codes are: *** +*** 0 Write protected 10 Write fault *** +*** 2 Not ready 12 Bad parameter *** +*** 4 Data (CRC) error 14 Insufficient memory *** +*** 6 Seek error 16 Other errors *** +*** 8 Record not found *** +****************************************************************/ +{ + R->IFF|=1; + + /* If invalid choice, return "Bad parameter": */ + if(!R->AF.B.h||(R->AF.B.h>2)) { R->AF.W=0x0C01;return; } + /* If no disk, return "Not ready": */ + if(!DiskPresent(R->DE.B.h)) { R->AF.W=0x0201;return; } + + /* Fill bootblock with data: */ + P=BootBlock+3; + N=2-R->AF.B.h; + memcpy(P,"fMSXdisk",8);P+=10; /* Manufacturer's ID */ + *P=Info[N].PerCluster;P+=4; /* Sectors per cluster */ + *P++=Info[N].Names;*P++=0x00; /* Number of names */ + *P++=Info[N].Sectors&0xFF; /* Number of sectors */ + *P++=(Info[N].Sectors>>8)&0xFF; + *P++=N+0xF8; /* Format ID [F8h-FFh] */ + *P++=Info[N].PerFAT;*P++=0x00; /* Sectors per FAT */ + *P++=Info[N].PerTrack;*P++=0x00; /* Sectors per track */ + *P++=Info[N].Heads;*P=0x00; /* Number of heads */ + + /* If can't write bootblock, return "Write protected": */ + if(!DiskWrite(R->DE.B.h,BootBlock,0)) { R->AF.W=0x0001;return; }; + + /* Writing FATs: */ + for(Sector=1,J=0;J<2;J++) + { + Buf[0]=N+0xF8; + Buf[1]=Buf[2]=0xFF; + memset(Buf+3,0x00,509); + + if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; } + + memset(Buf,0x00,512); + + for(I=Info[N].PerFAT;I>1;I--) + if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; } + } + + J=Info[N].Names/16; /* Directory size */ + I=Info[N].Sectors-2*Info[N].PerFAT-J-1; /* Data size */ + + for(memset(Buf,0x00,512);J;J--) + if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; } + for(memset(Buf,0xFF,512);I;I--) + if(!DiskWrite(R->DE.B.h,Buf,Sector++)) { R->AF.W=0x0A01;return; } + + /* Return success */ + R->AF.B.l&=~C_FLAG; + return; +} + +case 0x401F: +/** DRVOFF: Stop drives ***************************************** +*** Input: None *** +*** Output: None *** +****************************************************************/ + return; + +case 0x00E1: +/** TAPION: Open for read and read header *********************** +****************************************************************/ +{ + long Pos; + + if(Verbose&0x04) printf("TAPE: Looking for header..."); + + R->AF.B.l|=C_FLAG; + if(CasStream) + { + Pos=ftell(CasStream); + if(Pos&7) + if(fseek(CasStream,8-(Pos&7),SEEK_CUR)) + { + if(Verbose&0x04) puts("FAILED"); + rewind(CasStream);return; + } + + while(fread(Buf,1,8,CasStream)==8) + if(!memcmp(Buf,TapeHeader,8)) + { + if(Verbose&0x04) puts("OK"); + R->AF.B.l&=~C_FLAG;return; + } + + rewind(CasStream); + } + + if(Verbose&0x04) puts("FAILED"); + return; +} + +case 0x00E4: +/** TAPIN: Read tape ******************************************** +****************************************************************/ +{ + R->AF.B.l|=C_FLAG; + + if(CasStream) + { + J=fgetc(CasStream); + if(J<0) rewind(CasStream); + else { R->AF.B.h=J;R->AF.B.l&=~C_FLAG; } + } + + return; +} + +case 0x00E7: +/** TAPIOF: ***************************************************** +****************************************************************/ + R->AF.B.l&=~C_FLAG; + return; + +case 0x00EA: +/** TAPOON: ***************************************************** +****************************************************************/ +{ + long Pos; + + R->AF.B.l|=C_FLAG; + + if(CasStream) + { + Pos=ftell(CasStream); + if(Pos&7) + if(fseek(CasStream,8-(Pos&7),SEEK_CUR)) + { R->AF.B.l|=C_FLAG;return; } + + fwrite(TapeHeader,1,8,CasStream); + R->AF.B.l&=~C_FLAG; + } + + return; +} + +case 0x00ED: +/** TAPOUT: Write tape ****************************************** +****************************************************************/ + R->AF.B.l|=C_FLAG; + + if(CasStream) + { + fputc(R->AF.B.h,CasStream); + R->AF.B.l&=~C_FLAG; + } + + return; + +case 0x00F0: +/** TAPOOF: ***************************************************** +****************************************************************/ + R->AF.B.l&=~C_FLAG; + return; + +case 0x00F3: +/** STMOTR: ***************************************************** +****************************************************************/ + R->AF.B.l&=~C_FLAG; + return; + +default: + printf("Unknown BIOS trap called at PC=%04Xh\n",R->PC.W-2); + + } +} diff --git a/components/msx/fmsx/src/fMSX/State.h b/components/msx/fmsx/src/fMSX/State.h new file mode 100644 index 0000000..3637fe2 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/State.h @@ -0,0 +1,329 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** State.h **/ +/** **/ +/** This file contains routines to save and load emulation **/ +/** state. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** The contents of this file are property of Marat **/ +/** Fayzullin and should only be used as agreed with **/ +/** him. The file is confidential. Absolutely no **/ +/** distribution allowed. **/ +/*************************************************************/ +#ifndef STATE_H +#define STATE_H + +#define SaveSTRUCT(Name) \ + if(Size+sizeof(Name)>MaxSize) return(0); \ + else { memcpy(Buf+Size,&(Name),sizeof(Name));Size+=sizeof(Name); } + +#define SaveSTRUCTPTR(Name, TYPENAME) \ + if(Size+sizeof(TYPENAME)>MaxSize) return(0); \ + else { memcpy(Buf+Size,(Name),sizeof(TYPENAME));Size+=sizeof(TYPENAME); } + +#define SaveARRAY(Name) \ + if(Size+sizeof(Name)>MaxSize) return(0); \ + else { memcpy(Buf+Size,(Name),sizeof(Name));Size+=sizeof(Name); } + +#define SaveDATA(Name,DataSize) \ + if(Size+(DataSize)>MaxSize) return(0); \ + else { memcpy(Buf+Size,(Name),(DataSize));Size+=(DataSize); } + +#define LoadSTRUCT(Name) \ + if(Size+sizeof(Name)>MaxSize) return(0); \ + else { memcpy(&(Name),Buf+Size,sizeof(Name));Size+=sizeof(Name); } + +#define LoadSTRUCTPTR(Name, TYPENAME) \ + if(Size+sizeof(TYPENAME)>MaxSize) return(0); \ + else { memcpy((Name),Buf+Size,sizeof(TYPENAME));Size+=sizeof(TYPENAME); } + +#define SkipSTRUCT(Name) \ + if(Size+sizeof(Name)>MaxSize) return(0); \ + else Size+=sizeof(Name) + +#define LoadARRAY(Name) \ + if(Size+sizeof(Name)>MaxSize) return(0); \ + else { memcpy((Name),Buf+Size,sizeof(Name));Size+=sizeof(Name); } + +#define LoadDATA(Name,DataSize) \ + if(Size+(DataSize)>MaxSize) return(0); \ + else { memcpy((Name),Buf+Size,(DataSize));Size+=(DataSize); } + +#define SkipDATA(DataSize) \ + if(Size+(DataSize)>MaxSize) return(0); \ + else Size+=(DataSize) + +/** SaveState() **********************************************/ +/** Save emulation state to a memory buffer. Returns size **/ +/** on success, 0 on failure. **/ +/*************************************************************/ +unsigned int SaveState(unsigned char *Buf,unsigned int MaxSize) +{ + unsigned int State[256],Size; + int J,I,K; + + /* No data written yet */ + Size = 0; + + /* Fill out hardware state */ + J=0; + memset(State,0,sizeof(State)); + State[J++] = VDPData; + State[J++] = PLatch; + State[J++] = ALatch; + State[J++] = VAddr; + State[J++] = VKey; + State[J++] = PKey; + State[J++] = 0; /* was WKey (deprecated) */ + State[J++] = IRQPending; + State[J++] = ScanLine; + State[J++] = RTCReg; + State[J++] = RTCMode; + State[J++] = KanLetter; + State[J++] = KanCount; + State[J++] = IOReg; + State[J++] = PSLReg; + State[J++] = FMPACKey; + + /* Memory setup */ + for(I=0;I<4;++I) + { + State[J++] = SSLReg[I]; + State[J++] = PSL[I]; + State[J++] = SSL[I]; + State[J++] = EnWrite[I]; + State[J++] = RAMMapper[I]; + } + + /* Cartridge setup */ + for(I=0;I>16)&0xFF,(Palette[I]>>8)&0xFF,Palette[I]&0xFF); + + /* Set screen mode and VRAM table addresses */ + SetScreen(); + + /* Set some other variables */ + VPAGE = msx_VRAM+((int)VDP[14]<<14); + FGColor = VDP[7]>>4; + BGColor = VDP[7]&0x0F; + XFGColor = FGColor; + XBGColor = BGColor; + + /* All sound channels could have been changed */ + PSG->Changed = (1<Changed = (1<WChanged = (1<Changed = (1<PChanged = (1<DChanged = (1<>8; + + /* Write out the header and the data */ + if(F && (fwrite(Header,1,16,F)!=16)) { fclose(F);F=0; } + if(F && (fwrite(Buf,1,Size,F)!=Size)) { fclose(F);F=0; } + + /* If failed writing state, delete open file */ + if(F) fclose(F); else unlink(Name); + + /* Done */ + free(Buf); + return(!!F); +} + +/** LoadSTA() ************************************************/ +/** Load emulation state from a .STA file. Returns 1 on **/ +/** success, 0 on failure. **/ +/*************************************************************/ +int LoadSTA(const char *Name) +{ + int Size,OldMode,OldRAMPages,OldVRAMPages; + byte Header[16],*Buf; + FILE *F; + + /* Fail if no state file */ + if(!Name) return(0); + /* Open saved state file */ + if(!(F=fopen(Name,"rb"))) return(0); + + /* Read and check the header */ + if(fread(Header,1,16,F)!=16) { fclose(F);return(0); } + if(memcmp(Header,"STE\032\003",5)) { fclose(F);return(0); } + if(Header[7]+Header[8]*256!=StateID()) { fclose(F);return(0); } + if((Header[5]!=(RAMPages&0xFF))||(Header[6]!=(VRAMPages&0xFF))) + { fclose(F);return(0); } + + /* Allocate temporary buffer */ + Buf = malloc(MAX_STASIZE); + if(!Buf) { fclose(F);return(0); } + + /* Save current configuration */ + OldMode = Mode; + OldRAMPages = RAMPages; + OldVRAMPages = VRAMPages; + + /* Read state into temporary buffer, then load it */ + Size = fread(Buf,1,MAX_STASIZE,F); + Size = Size>0? LoadState(Buf,Size):0; + + /* If failed loading state, reset hardware */ + if(!Size) ResetMSX(OldMode,OldRAMPages,OldVRAMPages); + + /* Done */ + free(Buf); + fclose(F); + return(!!Size); +} + +#endif /* STATE_H */ diff --git a/components/msx/fmsx/src/fMSX/Unix/CARTS.SHA b/components/msx/fmsx/src/fMSX/Unix/CARTS.SHA new file mode 100644 index 0000000..bb79d32 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/Unix/CARTS.SHA @@ -0,0 +1,754 @@ +0733cd627467a866846e15caf1770a5594eaf4cc 4 +da397e783d677d1a78fff222d9d6cb48b915dada 4 +ba07b1b585386f887d4c7e457210b3fce819709a 3 +dd1e87a16e5fb38d9d729ef7edc6da21146a99fe 3 +9c544426a02312b7148249ef20ae6e2021b45db4 3 +937464eb371c68add2236bcef91d24a8ce7c4ed1 2 +d76c7a5c2d28684f0d67114cc7b9707b296e68a2 4 +f9e8cc6e47d5632e3b28500197f8e9187295c52b 4 +91d3f1463dac4d5fed8d4ff34bed0d388c61f164 2 +5380e913d8dac23470446844cab21f6921101af8 5 +0d9c472cf7687b86f3fe2e5af6545a26a0efd5fc 5 +2639792df6f7c7cfaffc2616b0e1849f18897ace 5 +1e7ac21796642c3ccb66b0ef0f00ba4752428aca 5 +6bde4e6761286a2909858ecef04155e17072996e 4 +495876c504bdc4de24860ea15b25dda8f3b06c49 4 +db33011d006201b3bd3bbc4c7c952da2990f36e4 0 +72815a7213f899a454bcf76733834ba499d35cd8 5 +f0fee046198a076e664811f4e3a788d5f0b47183 2 +709fb35338f21897e275237cc4c5615d0a5c2753 4 +3739d841abfef971db76ba10915b19b9df833476 4 +908bb09ba2883c3ecdec96ed7e0211ccb05c5018 2 +2588b9ade775b93f03fd4b17fd3f78ba70b556d6 5 +482cd650220e6931f85ee8532c61dac561365e30 4 +16c3ced0fb2e360bc7c43d372a0a30eb6bd3963d 5 +855cabd1ad1abae3a9d26999d48c66de8096d7c5 5 +c1219e0735d31f1c41995795e58b04b97bf1b71a 2 +e32d7334ace26a570a08fe7c01ad01a2c08b1991 4 +10907d8e9845d8f9d8b5283affd7bb824963194f 4 +9e0312e72f30a20f556b64fe37dbbfe0d4471823 5 +a731d3d3b5badf33c7602febd32cc4e6ec98c646 3 +25b28cfe8d6d51f619d182c774f6ceb05b577eeb 5 +bb902e82a2bdda61101a9b3646462adecdd18c8d 7 +2418c0302abbce8b0f8556b63169c60a849f60ee 4 +6159cdfc79c68bf7dfe2653fc0b5893ae913c72b 4 +1833ffc252d43d3d8239e57d5ac2c015b4367988 3 +e6419519c2d3247ea395e4feaa494a2e23e469ce 4 +d82135a5e28b750c44995af116db890a15f6428a 4 +d5b164797bc969b55c1a6f4006a4535c3fb03cf0 3 +03b42b77b1a7412f1d7bd0998cf8f2f003f77d0a 3 +86fdfe9e26d6e77f41cbcb47e10e2f32e5518549 3 +7b94a728a5945a53d518c18994e1e09a09ec3c1b 4 +3df7c19f739d74d6efdfd8151343e5a55d4ac842 4 +d7b46aece68c924e09f07e3df45711f337d35d6a 4 +68691348a29ce59046f993e9abaf3c8651bdda3c 3 +6c1814c70d69a50ec60e39ef281f0b8cd7bf8598 4 +88cd90595fbb7dbabd73ac9c0985c0a013992dc6 4 +fcdbc5e15dd6b973e0f1112f4599dad985f48042 3 +2f0db48fbcf3444f52b9c7c76ba9c4bd38bc2a15 5 +a52c37c1f16ba13d3f39bb5403c82c0187cbff51 5 +f100a76117e95ab0335e89a901e47d844bbc0ab6 4 +52a9173b22c8a72376003764d73e07579dc2015d 2 +d1fbdbdf2e830139584d7dc796806aa3327720dd 5 +2799d221d5486d48226bbbd3941207e1fc7c985e 5 +d7109cf20a22558f923c833ff0b4e2311340acb1 5 +3880b064dcb851ca221ff67e435137a7cf1141f8 4 +6e5acdfb1c1610257a7aabf3d5aa858866dbcf2e 2 +ff72a1788d47f9876d9fabd720f6a289fb409090 2 +42fbb18722df3e34e5b0f935a2dc0ce0d85099e9 2 +8b252fe784692fc15290a04fa59848f609216f16 2 +f0be97ceca47b65b46c9518e6157b295f8c99c38 2 +e11afc03db4e1d03976d02796b29da9c65d4ff3d 4 +2bb837a9051277ba574d8351a9f91f9e57033074 4 +84566b5f37ab4f04a2e5b950c5beecbd27b88ea0 3 +21380bee0748625e9951a7a924016cc6cec46fb8 3 +2b10234debd2a6a9a02e0750ba6563768bc4a2f3 4 +dccdb2d18c70a94e48b3ec5e0cb986c5d708bbc9 3 +048737f995eecb1dd8dd341d750efd005267796f 4 +6868e7050d989d1ecbd13393ed360addefa4bcd3 4 +442a39b196f19a22fc7fb8f14bf17386a292b60e 3 +6d800787f5f5d63827b841aa29fe26ed89ea2cef 4 +75eb441c8298b7fd3982999f772e4c8b983ef17f 2 +59e35d24ab5f8e3b812ba4a1a4feb5be1ab269fe 2 +a553c3f204c105c23b227a1e5aeb290671ccdbeb 4 +90ea059c57f011a4fb33a558e868ee639882fe5e 3 +50422ba24fd158ca5f0b9728b8e41829048cfdfd 5 +a3fc0a55a91ed0041221f8da788d824d9d795913 3 +de95b99dffeab5c2b453778dbdc8723dc15cbfd7 3 +453eac7568d5d04c8cf7da86f2e8dc79777343da 4 +2bd311a4baf59cb85b839cca1f55b7462aa96952 3 +e8ab46785e75e19dac497c4a82ac2f5b37ac0580 5 +3425eea336140da03d7d7f09a94fd928d70e2212 5 +abe52920e895c148851649ecb9c029fdc41a275f 5 +2117a3375684fa3c5bedebced4d7c1019a1ccdca 5 +c3c67787e2a69e2fdc739d8d1542726ec4ee7c47 5 +89073c052b0fe29b6de077c8bdf5373474081edf 4 +b2cca92b7a51a43c310e58e011f3ede1e7125a96 5 +3f0a7b0cbaa3d4c3c6ce744708c68c89eff82289 7 +2de7891988a868f89f0c02fc84f762597d542d6f 7 +5f912637565932c2bd7c0d7b9be6ad3dc971f96f 7 +30b41d0e9b3c59e8826112c618206e12e187fb5d 4 +a4a75ed32c2af3c5f009661bb76be4f65e2e9a1f 7 +edd109e4b518c41eb30a2b6f2a492598c9a08008 7 +12b3c31f0fd10ff5823dcc8bf6dfeb785a8af2f7 7 +8a25afaed4bf87468ad7a0c5cf12375554d67741 4 +f416b424fb913ca067fe75279c924a20fac5c6a1 4 +7393f677e0fae5fc83071c6b74756117b7d75e2d 2 +0413bb3aeacb0c28429b8c85b42796dbe48bef6d 2 +40a0f35dccc7572ae53bcd4be70abfe477d49bc9 2 +5692e41b3a4c5e767cf290fd6c24942d0fd7b2e3 2 +b4788d85e67950177882ea4cf6a5fa3219f3fbd0 2 +520739caa1e0aac1b8eabc4305556aa75f3f5a3b 2 +c84ea9e7a02609c71ff75893cbb948fc1f1cf21c 2 +edc238c1eb254cbf90f1dfdc276ff7ccc93a940e 2 +d605458c96bbb538c4624deeeed3294ea489a61e 2 +7a4126934f9e68c34bf00dd3d9a9e753c05ee73f 5 +930df58762e7b5bcf2d362462f03335bad732398 5 +d2ccc4a8de5da21745209d0731671e8f3cbea515 5 +8b7e4bc408a8c715943bfa00069f2d517b39a748 5 +05767fec34beaaee390a86be496d98d2b27de641 5 +678efe599b8085bd27f9d3d42cfbbd7ca74ceb89 5 +91505fccdcc43230550d101967010bed27f9b573 3 +959a2ddefc9dc1a751cf30779b1aaac775f1612f 5 +50efb7040339632cf8bddbc1d3eaae1fb2e2188f 4 +e96888c96c3044ca7ce57dbdf1744df7469f7465 4 +e31ac6520e912c27ce96431a1dfb112bf71cb7b9 3 +f0e4168ea18188fca2581526c2503223b9a28581 3 +98748c364e7bff50cf073c1a421ebe5b5d8b7025 3 +910b156b562880ace789fb3f9b11848163c2df20 3 +f4a1e82c533677ed9923b11a0b970264d9ecf8ca 3 +30a7f3ea1612f20c0f62b1a121bc7d79ae515e89 3 +fd7b23a4f1c2058b966b5ddd52cf7ae44a0eebb0 4 +ab30cdeaacbdf14e6366d43d881338178fc665cb 2 +d63e20369f98487767810a0c57603bef6a2a07e5 2 +076c45e152598777cdb1ace82b11994018bfed83 2 +2ae97f3c8152f56695c3d3a2dedaa781bf0a15f1 2 +c66483cd0d83292e4f2b54a3e89bd96b8bf9abb2 2 +4127844955388f812e33437f618936dc98944c0a 2 +4c15375910461a7a34a2fd54c7f51e712f06bc69 2 +76666da5870d1901026e3dded3f22ce7a706689d 2 +d441ccfede2c089fbef54e20b90c0d4b513c5196 2 +06aa7bdc1d8f01e67d1a1736084384393074e613 2 +4c2f015685a17db7a8c3893e868e0a84a8dbf1e5 2 +6844758ff5c2c410115d4d7cf12498c42e931732 2 +87b7c80069a15fdb90b40dfed736fb8492104cd9 2 +d09d75c7f242a696931407458e717c9bdaadb7d9 5 +44b23a175d04ca21236a5fef18600b01b12aaf4d 4 +10758fb5d42b97a05b537db9b009bd644c153c93 4 +26a7b0118d158e9bd3ea947fbe68f57b54e0b847 5 +0dbc7defd96b71c1fe2d63cbc725f5e58aea2db0 7 +a18b1d49df954a3316ed215b6facfc1ec020ce13 7 +b40b178c6876cf3dedf4ae48262a5914c5f674fd 7 +b5bcbe2dfeaf7cc156c0d8bbf4fdcbe97b2007d4 2 +a3de07612da7986387a4f5c41bbbc7e3b244e077 5 +3626b5dd3188ec2a16e102d05c79f8f242fbd892 7 +da4b44c734029f60388b7cea4ab97c3d5c6a09e9 5 +a3537934a4d9dfbf27aca5aaf42e0f18e4975366 5 +b18d36cc60d0e3b325138bb98472b685cca89f90 5 +552a758f77a7c35969523dbaca74587f0b59eb2b 3 +74e9ea381e2fed07d989d1056002de5737125aaf 4 +217b21d10d80fe2d5d04636ee8c2f97f6edfe34a 4 +77f2d9d27724a7c71c688a598d34bd0c8a8e56d0 2 +c654b842cff7227d9e85fda25279259e837cee9e 2 +093d5ab39878ed4cdd42fb7c2a1502b7e65b17c0 2 +46f995308d736da235ba7f17841d8afeff403c99 2 +32cdc5c98915ba035865fb134e08ce5b75084099 2 +1aeae1180471e9a6e8e866993031b881a341f921 5 +6fefbe448674b6ea846d0c6b9c8a0d57a11aa410 5 +3b6200f88561a59ae5d2b3e94515b89cdb96044b 3 +842009e0f7d0e977e47be7a56fe60707f477ed93 3 +4180544158a57c99162269e33e4f2c77c9fce84e 5 +1c462c3629d43297a006ba9055b39a2dccba9f6c 4 +122f659250a0ae10ce0be0dde626dd3e384affa7 5 +a2ca7e6e216f8b450eb8db10a4120f0353275b6b 5 +46062d3393c49884f84c2dc437ff27854e9d2e49 5 +438bbb3367db4938b3d90fa9d2cfb1f08c072bb7 3 +f3306e6f25d111da21ce66db3404f5f48acb25a1 4 +cfd872d005b7bd4cdd6e06c4c0162191f0b0415d 2 +0f298581b85e838c1fbaa24273d292217f81662c 2 +ee60e88ae409ddd93d4259b79586dacb2e5ee372 4 +4d51d3c5036311392b173a576bc7d91dc9fed6cb 3 +d94327745bbb865a394e31e150a6c77263ee95da 3 +7a786959d8fc0b518b35341422f096dd6019468d 3 +e7273feaa30b676941b25061ef0631ec09ccdb7c 3 +7d07fdf58c7a86d9f93af35a35acf2d7ded02ff1 3 +0c425d8fc1ad36c9b8e248e477b1796cd62d9bed 3 +fb053becf87f12e196e00b3e153ec8029ebf1ff8 3 +240e15fb5d918aa821f226002772dc800d9f20a4 4 +d33508c89656b18063893cadb74573e0b656792d 4 +f999e0187023413d839a67337d0595e150b2398f 4 +1ff0afe393f89b6517025dc39a282c11c7da87ca 4 +25f5adeca8a2ddb754d25eb18cff0d84e5b003bc 3 +3b6f140c99cba5bfa510200df49d1e573d687e4d 3 +4323eb37b9a795be32a2b64824fc4acd7afe4e9d 3 +d0dfc1927461d9473d0ed471375dcedddba42279 3 +8c609b8bee4245a1bb81e37d888ac5efb66533cf 3 +64321138e03337d0e6178ac30bb98e6623c640ba 5 +63d4e39c59f24f880809caa534d7a46ae83f4c9f 5 +728340e95ad58ab989a59f61317a45ab1f6b0bdc 2 +ee533b8e4bdd6cdf2c273f7c80753cf8db60f545 3 +f2eef7402d2e6e7f7c15a1f3d8bd251603eabf12 2 +0099492bd11dfa3ddf87af3aeb4c92b29f78bd82 4 +fe74b4df9698a61dffd3ac88f47619675514ba1c 6 +5c00f9e3a48411e08fac89b1dfdd2a6e95853919 6 +f02281aa13cb34d2f10add301529b95adff7f768 6 +720b629e2e22e0af15b482b154a1473e8ab6627c 2 +0a7bed652592ed6325acd037f1f5bbbd1d31b239 4 +2cea9cdd501d77f2b6bd78ae2ae9a63aba64cfed 4 +6a98d5787dd0e76f04283ce0aec55d45ba81565d 3 +598eb258cbe532e98b4a3c8d87ecd9536d09ce56 3 +e4401a78869ffa879a55f239e5da4990cb6797a6 2 +0442dd29ea0f7b78c9cd5849ec7ef5bb21ec0bf5 5 +0f2c5a7c6cc9ff591c911e4f3f204e425bd3cf50 7 +079bacc9708f711af90310f3e08f2abafa8fc919 7 +e348e62e3c4c02d87ff2aa4d55ddebb60c3ea507 7 +145552b3f62885efcfe7484008fa4cebe2feae84 7 +9c5c1c40ec30c34b1b436cf8cc494c0b509e81fc 5 +3243a5cc562451de527917e9ca85657286b2852f 5 +f7dd6841d280cbffa9f0f2da7af3549f23270ddb 4 +db7e4c8dc56a0e8e0e3df08b14a65030738d5ac4 2 +7dc7f7e3966943280f34836656a7d1bd3ace67cd 4 +0cb11c766bd357d203879bd6bee041a4690cc3df 5 +e92baa5fdfb2715e68700024d964098ef35704d9 5 +98b7a6ac44b82ccfc45eb51595e2905adabac1c7 5 +cbe6c0db6201f8ac09310c788adc634873251164 3 +cf344e0f58fd918c089c4d4575caec8843944ff6 5 +bf0014ca7dd0815b2385a778c5328ae63c692229 2 +abeccfe49a6874702d65eb4d06a970d8fcf55123 2 +7abf89652396a648a84ae06e6dabc09735a75798 4 +176ec8e65a9fdbf59edc245b9e8388cc94195db9 4 +7341efc039394ec159feebcfaa9d4a61ebf08a18 3 +0ec71916791e05d207d7fe0a461a79a76eab52c5 3 +1c238009e77c5536d0059631954c2c4a52527283 2 +ca45da2f9cd8edf4ab04ffd542b9c60ef6a9a254 2 +aa6055ba0e433aa9c93df4444783cacb9628a7e2 2 +7b8015f919c8dd95db683f3f3d0799cf7ef1bc22 2 +9f2fb5f31c2bcff949e08449f197b0c31bdebe50 2 +b493dd7d082e25fc40ad1cccc364e1945590a2f9 2 +3ff276af654c7dc712abed79d9123317ce79b4e4 2 +65160f0d59068dbbf76b11bc79e17a9369c8dcee 2 +72783ba5a0b9f33b4573952a1c721fc5d5025fd5 4 +168d59fca3170f56858f39d0b53067c0c0d414a8 2 +473bc0ea85ed1b218974796d819416e711ac193f 4 +5d55fc272184d160af6318855c9d2f728b3f25c0 3 +8557dcdb4cf1e15180d17acc8e6d515bf7bf7e7c 4 +7752b6624fb80d9499aaad28dd5776ec74565576 4 +c2f4df1bd91f7dee1536774b9d7089086b9bf835 4 +ec1bb5c36952b12e6c0805e9a8d5bf31c20a68c4 4 +22f9b0238ff48840fb7d45ac93a7f15495393920 3 +4b138e041947990309c228890914689908d46685 3 +76b7a5e75d5091f0482746e5a0c0a71de07ddf83 4 +ce3f9356eaa5fd6c1b5ff1ade244ebd9130f1ceb 4 +e6de8bbd60123444de2a90928853985ceb0b4cbf 7 +66802dd98370da809250367365df234f9a6422b7 5 +0b02dda5316a318a7f75a811aa54200ddd7abc30 4 +7c066cb763f7a4fec0474b5a09e3ef43bbf9248b 4 +55adf3b4576b370dd2680d80c333ebde 4 +2220363ae56ef707ab2471fcdb36f4816ad1d32c 2 +75d3b72d9ceeaa55c76223d935629a30ae4124d6 2 +ee66334fbac1c926ed05c1c3f38df3920f501fe6 2 +2111cec4b5ea698d772bb80664f6be690b47391c 2 +04e4eb1cae7cf379977d238b74d727bb929c8d23 2 +f1de76dfcd8c4e354a8a8058a76191167fd112e3 2 +9ce36aeaa93f969b6c2ea91598b0b014 2 +240a23b63e901b988951dda1ded561c281ccbbc1 2 +8c218c317e1e213e1c5e9439870929009d645a47 4 +390fb09a9d944d1d17c328ae138325751d1b4203 2 +5b25d93259a358f2064adc44f6382c91c9394861 4 +c95f8edb24edca9f5d38c26d0e8a34c6b61efb0c 5 +ac109dac24df4dae813ca5d9f0938cc48a162bb1 5 +16c692a2c9babfdadd8408d2f0f8fae3a8d96fd5 4 +4e864cc0045c85c4f5c5c0efcdf26340a178686a 2 +37bd4680a36c3a1a078e2bc47b631d858d9296b8 5 +9b6cb224f2a7a4af69221f1280d3344a5c88450e 7 +9c886fff02779267041efe45dadefc5fd7f4b9a2 7 +0b379610cb7085005a56b24a0890b79dd5a7e817 7 +441dacb74a47ca4b7d1ce47300d182bc7424ffac 2 +74ae85d44cb8ef1bae428c90200cb74be6d56d3a 4 +94d7a756e199457ea9958c8f5d6860c9bf334126 2 +9a611007b58a001639676728614ffea61cd5ff85 2 +b7f36450d1ac76834046fd582d6e77a6d3881a8c 2 +c960c1b895557ef1b336035029d5c6d553c64085 2 +46c98a3143f5a80b2090311a770f9b73000881c0 5 +f6e0dff9f8674383a866902fddb72a280d331d9d 3 +64c49fdabbdd5e13c4310b03a183ea1a018154a4 4 +0d76a069726fec7326541f75b809b8b72148ed3a 4 +3b1fc45b04bd03f21d5b88330a0c5d16a9bd2d65 4 +a676173780dc3ef4523ff3123fdf179279d22932 2 +0d459788b6c464b50cbc2436e67a2cef248e0c4a 2 +a1830ad66feb55f471d52210c6f07937f38238fe 2 +c19f67882b2586c4ba577ffc5d04197e757e19a5 2 +655b15e8fd81866492bcf2b1d6609211b30efce1 2 +d30c17109fa1c4a81e39dca57b79464b0aa0b7c2 2 +5261006ca4245653f5153074dfcc8ca27315bb59 2 +df15daf1e68e9c8cd95c4431803d86fe6c2f985e 2 +14ff6fe464362c6b7dbb47b2ecda3a8c5f05ef79 4 +3f741ba2ab08c5e9fb658882b36b8e3d01682f58 4 +96e2a7163c755fbea77abfd9d09a798687a5a993 3 +7ad40ae512bbf4ba688467ba23e16354b8421d0a 3 +fd0e46e4aef2d9713d20b51d2ba57d55bde7dcee 2 +84831671935bf80c250aff223a99684669a0ce72 2 +1ec94d049fcbf59c5493d5de76bdfb891d24249e 2 +54a1d356fd7bc6098f5c3d94f9d8bfcc2c6d5935 2 +0b3f5dbdbf5636e22b3a6bc12eeb4ac64b0501e5 2 +3b4143e5769d6235bca8977b41241295493eaf5e 2 +2b255c3e5615b2f5e2365419a35e4115a060e93c 4 +7a0a1c5570a3361ad75406eea07c93fdad9fde02 3 +e6ba7a6ecda80a1c844542be0b2a44a4a33dc113 3 +e7351ef9606bd6d5f38acb7c17aed8a00170b85d 4 +6715aea31525869220d7acb638b6a432875e85a1 4 +5a12439f74ca5d1c2664f01ff6a8302d3ce907a8 4 +3caeec19423a960d98e4719b301a3d276339e5ae 4 +d554710317482127f2e1597e91e72be5ee0726ce 4 +fe39af5eb457bba8c5b96b5b500120cfc05c265e 2 +0809b47cf385328c3faa6a80f8378fa5c6690829 2 +1d129bde09c9db2433b334d36762704de94983ba 3 +72afbebb12098ade1ec26dcd65890b60b2c705d0 2 +65da0e5c5de057a1ed836ec19b1f07e10f4b738f 4 +dacc9ed6a80fb15c3e82c4081c07211e033ba820 3 +a3e8e076b9c8d4060082233a2f16184cf4911a85 3 +9bafce699964f4aabc6b60c71a71c9ff5b0cc82d 4 +14faf5da6ab76de0b62f1ede91e27d46b9db77a2 4 +5d65a308bb9ec8c4b4ce334b18d699b1f53227ef 3 +2dfbca8f5cc3a9e9d151382ebe0da410f5393eaf 4 +c69165e84e8350a6726901f2ab6e4688f38f03fc 3 +244399d67d7851f3daa9bb87a14f5b8ef6d8c160 3 +cf6fe49b28fcf81511f39fdf68f37af82bd724c0 3 +81b85b1152a59bb10d0e0118f80f0a4ed363c785 3 +0d2f86dbb70f4b4a4e4dc1bc95df232f48856037 7 +cf088b4eefe7f426e438dd2fe7a3433140abba52 4 +16351e6a7d7fc38c23b54ee15ac6f0275621ba96 4 +a8ac24a702d2c850c7a15b5196893817b0c93123 4 +ebceb3de22e4c0444a969d83ee487f8ee34204e4 5 +16f06109c57de5a39fbe150de9c25754483ee9fb 4 +fd1eecd9c89f47741276433b99b75eca2930b58d 2 +d147f2cac0600527ce49bfffc865c54eb783e5e5 5 +500d7a1d81621adf8c6914c6a42d65aa44ad4588 2 +bfe8046f8ccc6d6016d7752c04f0654420ef81e7 5 +22d8ac99ef692675210f101eb4e688cde9b2d7f5 2 +b550757c8f5b55dbde16b4a7888f10999f01acfd 3 +4340a2580c949d498d2c8e71699fff860214e9ea 5 +47de7524c32e72e849daee4d7f3e6cf393e3b7dc 4 +b188980c998d1dfb5dfbc21837ffbbb5affdbb3c 4 +badcb43e490e3713bf291e83cff204e1c57ecdfe 4 +f50d0c66b08c47b59925952276243e2008668185 4 +818d91505ad39bba2eaf7f4857c7d41e95fcb233 4 +e19cfcbe92ec3682bee869dfad5f6b4058b1f2ce 4 +97e173dac64dbde7d6a60de7606cba0c860813db 4 +b6a5552effcee708b665fa74e5ce7b0fa2541c03 3 +d7e85888117188a6d6f7914e9649fc4821c594a2 3 +fa6c059e14092d023b1f9f2df28e482f966287db 4 +d53e0c8bcd98820afe820f756af35cc97911bfe4 3 +632bdb164acab52ee715c7eefed60e0b1ae629e0 3 +d0706fd10e418eba2929d515cc0994f49376a63f 3 +d3d411c8b7891aef9c59cbc20bb4fa3ff0ca03ea 3 +898bda19f882c6d1dffdb2173db84a97dc21f7d6 3 +bae8548faded657cca06c2b02a59e00cf1e55484 3 +4f35f676d2f382078078343921374c2889d06e11 3 +05a531043d13ba261b0db37e4ef8b257986ce05f 3 +ad3d8fc2201a245e352875918e421b42764e4148 4 +c9135b1e451193bd4c06d8143eaca934cc89fe05 4 +d9826cb2044ed0224124f2f31d0bdc1fb840d81c 4 +2fc15ef2c728a2472cfa3a34053e07f821dc18d4 3 +66508bbdc3b133b9a69ea96789bc134eea46796a 5 +5fd07ed5b7c0492d42ed91a54a0fe80ce6e4e40d 5 +1f17e46190635a3214057d33ad4a768939d5a071 5 +cb544661b6da6a35c20bfe503974f2b79a3c4550 4 +5ef7d03b138a2023f6def241b671c666f97ed83b 3 +eb58b57f468e12c4300d3a88337e6c45e532c7d3 3 +5460a88c25386b1b950b57fc1325fd75587cc825 3 +b58e77f819dfbde72845801cff4b4e0a2f20ef5f 3 +e78511a2b8cca37a96aa396c938863c92a20c797 3 +43084db72dd13112450a7af4b513e13e9266defb 3 +aa13b346e11644f8426fc546b359bc6fd7c6b357 3 +90b57b582675360916ad68ba65e9284926d93f4a 3 +12f6f31f495bfb384c9ca9067bfbf8f98af6adf9 5 +fda4422b86daa233fa4e6104e45305e7cb383053 5 +11b712633bd0e44e8d59ceba5a2d6b1c20a43ef9 5 +2761df1ff6bf826e00dbd4f9a346aa145e91c6c6 5 +be2d023f5f1ee4cfbdc712c8ef9eda9d07278d88 5 +f4f7c789dd320192fd544c666ceab720c7f3a747 5 +4b506234dfd3b6aa1c315b40cd8ddca024dd15c9 3 +030557706d72199acf17df4480f6b1a8ff798d1e 3 +54b05b9c064232d75299fd31b53335f530c20aea 3 +937f8d412e9810c8aeb9d92182cd3b007ace30d2 3 +07793d57b00ad8927a3c1f0b1ed61c606441dbbb 4 +1545566bd7decb1612009daa2daa71758fa6e4a1 4 +4a8eed643993b86e71ea46098a580ec15038cbba 4 +55183b7b7df66950dbb65ef1065d7d987e0fdf5a 5 +a8186cd3ef78662c87665df52026ecbae4866bfd 5 +ae3c3c2c022f566d2e790aba193470d5826f36cf 5 +875a262be44a27a8357f170080fb8466c9599b62 5 +1064aa5c8a884dddc0a27147ca70881e04988725 5 +e8e416977f5dae3635362c513bba004ea89a0bf4 5 +9c89215dd36eded769691708eb8c82dec1ce5109 5 +1642891b693df616f27431299352e1d7750ac94b 4 +452deac5b3341437e70c5b2726aa26cc1d074fba 4 +9e790499af6916f82809e35d68cc159efe580f99 3 +c843b8b0e5ed98d3bae68e701fb709ea178ed968 3 +b210047af5ccc352cc044bf6c1419552223bf8dc 3 +c4a919f31d20008b5d6540aa5d2be59aa4cef5cf 4 +1aee947b1ad6d478c0def550868a3851c2910809 4 +a3fbf7c2392c1f97d3a0946a5d1654bf4f7d9b0a 3 +e89fc7437d8f9aed1640689a32ebbf047699b147 4 +ae8172bcf76adfa3d401467cbc89cbec2db86ee2 4 +5d82713f36e9395092f16663084fbc85f05c664a 4 +a8059dc1820b461418713acf2af14f3a25734bd3 4 +e839f3ac7d52c427ecc011d01e6e241f88ca850c 4 +f57b46154cf0711e3f5218e00f67c375d95d1d5d 4 +a8eeac428a5992a2390b1827db1358f4c733e200 3 +bd1435a19ba895149fa5b88425316171d165c323 3 +efdf63f01293251dc593163a42f31556fd042282 4 +4edb5ed12c1b9bc7efa571ca67ffc732f53d3a4a 0 +e58c1dd46067e2abaaf3ee6a70a7e5541a32017e 3 +32210a9cbbb51e30ff59ed44bf32e628fdf6c42c 3 +6fd90e7573abdb5a7bd6e4e8dc44db80fc163e30 4 +bf96a360083c5c26430940546827e91a69ade63e 4 +2e388978f765bc5fe1fa9a42e1f059de5053721d 3 +8a65786d9c6b2fe7c1643c27316d6cbb35fbfd24 3 +022a53d79bb72c736a5fdad6d82434a98f33e6e7 4 +599c824ec92be0369accaaaf31340df6f471f202 4 +e73946f1f26589d77926276ca4f9dbee60c53fde 4 +c5edd1c74e72ddaeba1027da1e848e2c4fda94f5 4 +a40674b5e0ba449078cbb07406edfd2627306122 3 +d66068f5a8b6cad09ed57893ceb0f3dd37ce8715 3 +e36e16acfdfa76fa72b218da2bebc668db39d21e 5 +1302d258c952e93666ecec12429d6d2c2f841f43 4 +90003c78975d00b1e5612fd00dffabb70d616ecd 2 +cc46a737acd729b2839ecd237d38b4a63cfb16cb 2 +7964ba4c3c27b6a32d397157fd38dd1dc2f1e543 2 +bc06bd3d6f138da8f5d38b47e459b4d1942e49e4 2 +424320762bcbbb6081b1e186e21accf758aeb935 2 +a21adba681956ec35816af673a9d4f6c7743253a 4 +2a4aef32db28c059c30f5f972f1c09708ef6ce62 7 +10c38f91d8f5db2438414ea58f26dcac78352088 5 +3f072673752badfbd234d6005ddad60a494a9438 4 +d4da7236bd09d735dcb92127c73e43b3ca94dd7f 4 +3bb2f9f4be38b5b08da03ee190c39caec30e2f08 4 +fc9adbd816109f7705f342c01f168bb09ca4b2ff 4 +35c02d8d590d92053f083b0c0c897277a5375af6 4 +772ad4eabbf0941a9b4655f5615f2a5cf22246b1 4 +1ba6b699b4e34c23ee60db4e58e1265dc03f0b50 4 +87a7fa43602f74c47151e79f1aaf7f641331ba51 4 +ecbf33a376a7a108176f8d1009d042e01519241c 4 +e0a8aff980ed84ec12acbf63b40cf9895997faf3 4 +81befaa45ee3f1196b0e1ea657b0161495601a87 3 +6030005c4401b3cbcb09f802cd48f5c1c585303b 3 +5577e483caca553cfbfc0cba26f3ab2444569f81 4 +7e9a9ce7c18206b325830e9cdcbb27179118de96 3 +7c3aa9cca2f1f6e81ae9d24b3a3c675f08a203ea 3 +27f9144fb24b434f7a75737aa694283084782766 0 +05280f0a26ac5c42ab63b80a487bb909683e7461 4 +77fcce7b7c9c915fb0adff059141d4d4307f5a22 4 +028eb2de64ee129f9403d114b3059e8b62fef6c2 4 +b63fc17c1d004ca4d7f3b1ef8962210a779ca665 5 +23f3e1b597bee95375f380638340995b55232e7c 3 +3f87c3413c8cc2a25e1f64cfdcd01155a13711f0 4 +fa2d5f56273a0faa5b39096448f82998655de60d 3 +6d7a8ea1b283ecc1cc94c359e5f053a6e82ab3f5 4 +b11c0ded529af18c29ec46b9e0a795ed0405e7ec 4 +af3524caccd33ccb7a5b42b1ce4314d055e01672 4 +d0bbf730725806384756f5306ceccd91dc01d975 3 +bd70b00ccb8fafca883c7160585e9c5920cca58f 5 +a6d285e515ad1e2c15b4352d9b699ebe060b1dba 5 +cd8ee800822ff0cd295621faafafc73be4bacad0 4 +caeca37113c56e696d7053d90f5f9f3e02de8ad1 4 +98f6769741a1c9df4fd203ed19d70b283c94e41a 3 +475c3af7495922759a5f424d2e22d7e4073d5992 7 +39bc111c953bdf25683db7ff4c0bad79b5c49958 4 +f12ad34dc3b4279da0fc7094f3d50d6b8bb517dc 4 +06132ead4caadb2395302381bf72c60831500dce 4 +f04f5c6936301eed31a79c23571f8fc497864dbb 3 +b45172e4628f68c32cd13c73cf17c4dd1bed1bb2 3 +18eada2178fc1a89895087aa513305d1ba73c926 3 +924fcf87b3747dae06e621975903ee73c42016f2 4 +6d72a04ef018fa1fa3708c8b5ed3333b2c5e8ead 4 +c74c0a9b973f4dbfc132b60fe4d666600ae12552 3 +d78d763e72e23418f31dc3ce91deea1e8ee10490 4 +66620207ca6c21bcbae8f868bb55532e72e521a6 3 +31dbd1c08659c649c49da8d2e6b80328cb6b59e8 3 +970e26f3c2c085e39267115991add6ccc6e9ff80 5 +a58ca651f4c12bf07cf3840d5e37b534f5fc075e 5 +1f39c472f1e02511fa7eaaf8ed9fcce4d45861b9 5 +df10b3c8470acebbc445f7c24a50b4f24db0187d 5 +cdeadd6cf7bf5b53aa7d901446b0ce586542c580 4 +8cbfd94324f297a418e7eec6920dfead7b9bed08 4 +32dd4df2b39461c0694f6bb84de0709478a69d31 5 +1f3f586255dd49b0f912bf41e07e88fe3eb3fb61 4 +273a3dab994b5b9c975fd12711dd19e0c6367caa 4 +fd71df03b0e9f3caf701ecfc6d5232060cab3b13 3 +f5009c44ba1e72b64e29fc734741d518b4167aea 3 +55bad6c00e6a676efc7ff349fa1b5422d624f5ef 3 +f924980511d2ea191481c1e4ff5ab7adc3f082ab 3 +02cfc41132d518e2b9fc632ec6ad00656bd9ec92 3 +f4da2be2ee712ba2e7cccf56fcd15483fe0023ed 5 +4b872dac11a720e2b9e86f22df1e2214c1059281 3 +1f26dfa026b116c80c951a995633b4c2f7fa033d 4 +2a1c91bcc9b1b621773232061d74b7098420f1d4 4 +4690b2f6ce28e55d56ab185c573fc27a9b414468 4 +b8f4ee146c3cdb8f506371d001e6398ecda85251 2 +e22a6317747903475095008d0d5ad9427ddb34af 2 +ad995ede603a28c72dad06c245fc7d4dad85d230 2 +d89922ed43ea4fc0b87d7e5baef3f072bb566849 2 +93b07e3cd5ae2f00b36824a74f83a3ef0d65d0d5 2 +dbed87a53c604f7140577d6707ca0fc8490463a6 2 +faa5f0bf79fde9ce74b91a7091d203771cae8499 2 +74d1c7f66242335c736da29413037bd23384efaa 7 +8a4bcf0f38c9f243443a4d4865278f695bd83dc8 7 +bb99fe7702733ab0f9e8856e91f5ddcb55d30b5f 5 +b74f759ba3837968a04e9b64321b47cf087e76ff 4 +920f141db0d42952130855ac1dce446c327e7d9c 5 +14403ca71d287084569e6c2f4124d2712c665219 4 +90722d413913ab18462aa741f85b820e751bb50f 3 +c34a1c225d1a5fc41a998e23d1209b59b3b759bb 3 +f5e199a5b39bad10256d8e963f0da272ad359517 3 +4376342bf42334d98dbd3e35ef35460974269c58 3 +25022c4f5cfc805e93026fd5fda781e9a1807474 7 +3ff54468a5acf1ad44e85bdc993044aaa3165a63 7 +d08d4e2a8d92c01551ff012a71a1f3e57fe2d09c 7 +751571d21457d89e5c52c45657bfe9a617ae8e14 7 +32151ddc4303290c09889bf8107493dd34296043 4 +4ee3e785e6949bd535804a32c6a1a31eeef30031 3 +ead699582641d103a94775a08121ecc336b99eae 4 +6e1d2d5b0688860b0a04d53fe9e5e3939dfba547 4 +13fbb273a95481330d33c9edc4263e30ccf1774d 4 +45760316a4ebc74ddd55ee163c5aeaaafe965d2c 4 +d723f2eb1c887db5448fd56be5e7c57e60d88e63 3 +612c18616bc800d9eb69b9da3cd29eca31f50c1c 3 +300f20adde3941ab10eb158db48250e8e856a911 3 +357c0c2e9b3b2970c587c96f86a6b7272160f54a 3 +875bbb27f6a54e5d123dc8a6df4bf46f97c23a57 3 +fed52c0455ce1e7cef80cd144c3d0ef3636134e5 3 +7e5024e6e6aecd05ec6c885786a4aac287ed110b 3 +3837d3a72498caa070866d947956a2e30fb94fbe 5 +facb967717c21fed9b375481908aa337b3361c45 5 +14537ad9a551245261776bf9b64068fe4921ad47 5 +f182d4785e3eed00292f7ff4795c9dcc6c990a0a 4 +5d2062ecc7176ca2b100724c2a17f877878d721d 3 +219e9acc2c023c25a8bbe9fb7d1764ffcff66034 4 +e065238580b9e118b6c9e789d797cdd99e0a1c69 5 +25b80aa670e7fdc84b51628b6dbe930eb234beb7 5 +fe8aae62fdec28631d8f7b6659dd95135c1bcbb7 5 +0cc6c30c6918fcf780e57d40f58a4cf2a82225cd 7 +97f2cc1cd4c51dc20b86cd190016b8f0c006a968 7 +6420d61002da325403fcb6f41afb1d348eeba3b4 7 +9afdee40e7b243f99cc50dd1ad710ab0928dd820 4 +0eb3a48a8ab90826726f0c06daadf4bf13390758 5 +d78293133b075f48c9dc5c4cf0c4a985ae964b08 5 +f4dba5ba267b5dd1a35eb74a6ec4fd4d5ca9a1b4 5 +a0c486d0f25a3dc586717d5d3e1f5a46d36735fc 5 +6dd28797f9d1fe7cc041077ebe77d08f2ccc0937 4 +11f1b9fda9a44f59a6de479aebc18fbc6347f5a7 4 +38a103e540eb5fbe2627fab2991bda08092635e0 4 +6d3008b97b48d99522e45056f52ed944e3d8e208 3 +dcc1ecdd0e3a2a33b994e472ea367a1637cc3799 3 +99330820cf8aef8bb34d56a666accb4b1ca90e30 4 +ecb538238fc7c5e374d723f4d6120d1b2ef5d80e 4 +fe4bd35e87dd50fddd255172129b9bec38691188 3 +c6d2aa3e7303e0e2244256bfcfc113bafe2d6b59 3 +206b2a47f437ba9dfc85f6d01c9e2c18 3 +e178917d1974ac85fd58cb93b936eb9a6f44ba0d 3 +2b4c386a56dddd2a3354bdf1a0041d3faa0ccc72 3 +e178917d1974ac85fd58cb93b936eb9a6f44ba0d 3 +bd9852ea114e8b8ff781115d1d6e6da6f8dfafff 3 +65a5bda34d1e1b22cfa697e4e7fdf1829cf2238d 3 +502d7cf7bc2595af6f83bb2b95baa4db5b0f6a4d 3 +de2349227d765db73cdc44efc6338f6b530eb133 3 +a5963fa6c95fbe34482862b0f7221b7766e3a194 3 +2e0436303648097e8e936c5467574aff5e3f1517 2 +f25628e474035e6bf0440b274c5df04f94c49fb8 4 +5ec8811254dc762c6852289a08acf22954404f0d 2 +b5acea41aeabb5daa4183953f800d47db0c39fb5 2 +2e2543ac99193f76b4f35463142294c016f0cf3d 4 +f90064fe4ffed170f9e9c79196aa0b17e0bbd86b 2 +523330d609ca0e527f3e28a078635b3d28489ec9 5 +f9486dd810d6321bad4bc9d7703a67d630167f98 4 +22f5998b4de47bf2ccc077d5881ac448e6b0484d 7 +e003d57d2102e8fdf5d9eea7bd78a3cf67d75778 4 +51e4231e245ffd733590e3205fa3ffea92ad509f 4 +68158290666f4d7ac1ada40d674a1160c162b193 4 +d57fc4121d716182a091bb53c666b46c10142668 4 +13c59dd8f7320856b15c834038af3c846db33e4e 3 +db1513dff6dd456fc973286816129ace81bcdfdd 5 +4c44939a95a20d701f60830499254e5dabedeeaa 4 +a53feafa4c70d2378998f0dcb837905e5070c541 4 +ee501fde62b25ddd1c82798ee1b233b8813e717b 4 +4fe1d66e8f3cbd5ecd9b22b6aea2b37a604f2492 3 +55fb4e3d3a762b613b635ee9745d92f0f3711272 4 +de0eb1c46c47924879e2a49f9eafba0ab344fe5f 5 +e6c01bd90cf0b2793282fc53e9a3150a6d7804c5 5 +c5e3845c74da6aea2cb68ccd765e4f606f10b5b6 5 +e3b9241291b5f19ab239726360258a31e02565a3 7 +845b9263d75d89ef02c23fd05cccc13db5d512ea 4 +495eb918ced94ef47885df40ceceef8b56e96691 4 +31b8b69de88d84aabdb762af1adeac815723562c 4 +5ff1254dccd5dde58d3475a468178738d6260b05 3 +a52021f1b257c7c35d626d5d642134091c45e4f4 3 +7d685547c2a4b92fa62e00854e4a9689d495f93d 3 +b656bd19df58fc1ba4628342b87beeec5948e0b3 3 +7b959c609deaf67d29e3f79f55a03f53961f129e 3 +c461efa942e1a3a1e580f9d04bf8d15c5e834f8b 3 +804be21493a151a0be2075e86c94759dd96f0bf7 3 +8dfe236a6a5734824035725a4f513b8fd31b310a 3 +aa881a2936f368ab887c296b837a8f7f553a3fb2 3 +847841ad88ff2eff6602f03b7231cd1e03f89ffc 3 +8449cdf161babec85508c35c8ca64a5c1fd7671d 3 +1417f9e8ca99deb3b422c5968fdfa03cfa633ec6 3 +65040fb1e9bc4b2d027d7d81e44be30e7620e368 3 +356a936987e62967597ec64a0d35a1e8ad1aa20b 4 +e7b8574a42b1029539632ce0f5161b1c38d5e95f 2 +4ad4898db3bdcf87b2acd5755e06d45c73d338cd 4 +8262ac069e395ba020025caf0519e8eb5a63a0b8 4 +ac71117eb20ec8a84035acbbd380df8235a5c665 4 +af567ea6e27912d6d5bf1c8e9bc18e9b393fd1ab 2 +983eb5deeb61af5ce82ad598692192b8ae8a2a08 2 +ad621247f95f80a1a32e67def5e978c845d19208 2 +a7b196f7e934faa76602c8c00bd2f6e6d2d70787 2 +7be2f14fd0df5718cdb69b972c5f7c1f284b03e6 2 +53e1fb8ee77757cd20bf4c7aa4a5c6c786c664d6 2 +c18adae0ca1243e13a15d381a431cc0dfc68223b 2 +ea0824ea0692d2cdff816a2b3c3eb9a587a5f536 2 +e9e778c3d103cc8c32093cd6880fa8d7238bbd9e 2 +4b78514753008cabda2ff3d3cb537b2429efd7d2 2 +21ac0ae7da47abf30986b479489134c79405f95d 2 +3a0b0bdd77a1f3a11533f5ef030370bcc90c6334 2 +1c17e58f1a602e07f185bce1aa8589fae76e8b44 4 +d53a8efefe3714269538a91e5256c2938fe13239 4 +1cbd5c7849f97dea1e3375a842f784ff9283310c 7 +ab087d04eb5c2c8894758c7cee32ccafd80989a7 4 +4c2a8cde9b40c4936f512365a7cd6c8cef92095e 3 +b1cf453206beef8ab2ede2b061ed95a3cd91eae3 3 +04a135910588ef8753b04a49c66d138c081284d2 3 +721a2ded692d34d864d0440ac347c60880942d19 4 +1d97922a5ab63e73611d0edbe952f041af3385b3 4 +688b70e60dc272654ac6b0a72237fefdce2ef9ff 4 +6075cd96bc85864f22c643b023302f006b565049 3 +5c278deb1390eb45c79b7667b1ea1c54183123ac 4 +cb6bd26961387def757c7faeec7563d77d404d01 7 +89943ae938fb308f4b5149ebc0cbea597b9b0223 7 +fbfdd89ec183ceae63d3d66b0ab45faa951b525a 7 +c9c272ffa69d3e0af5be42e7636b02dc2af6ea8c 5 +82397096d5ef436b99b943c2a52d6eff87694183 5 +8e66731c99f4d041ddcd33088d0ab0d4cae8bf75 5 +0f10d77d1536993b8455b3b47278ca5782ef9275 4 +6933de9a128e90a34383f3a9b2660d5bae5d9fe3 4 +d5902afbd8dfb1ea96fc8cf4d62cf6e4fa40d4f7 4 +78507f3f8b8601d99bbedb6517bec6cddf0bd5b4 4 +4607ed284bd4602affa00600c749ce43b2b89b6d 5 +b7104477c8801fe54d1ba91f3a77ac6e4f399f52 5 +788c53657d9ea180d04f29ccaf1f1c29a9b75519 5 +ce4c5ce07afd297156899eb6c1056eef3f83b803 5 +cbec713b32517a503ae6c02497357a8361eccb76 3 +b20e02cbffef692c7aee6281eb36729ee1e928ef 4 +ae6ad52d5ebcad7722ea070109a286fe13b06cb7 4 +d7f8c34e3ae9dea51d0c87857e0ff78028d402b7 4 +f52690157a51c662ffdf4ae3592ada93d7be4032 4 +81143d2a8ef7cc593a604f3cc890312b42bdff01 5 +8afe57c287adc0f93f234e8e8c136ae3c00947c1 5 +418f6248221c79039fe6c44fc5a9514842e3ec71 4 +07c4c39c6e6c03241d2353300f630b367322e78a 4 +91afe2ee3a5b8e80d67f6c7a30bafbc2d63b2421 3 +29a42c2b679bac55a0318ce8c97708e402e2779b 4 +0c62591033aa4e5e6b9eeb21eb2e5e60dcd8ee8e 2 +2312bff70c83e60d1ca0fb9ca78a50637d02771c 2 +1ccac7caeaaa1c5a89503221b0a5eec2f34b1c91 2 +c82c6e054313f4c8864e64897baea8b63776020d 4 +f787271597abbcb1297e4e8a4a9561959a195649 4 +c08a3c1478e51db62624814acbf36c198d6561e6 4 +b81dd702afaaa5946e4ec065042c891ad2f76c2b 3 +02c3ec94190c6fce6fa4984d92be48b2697e7706 4 +4bd533cd7859489a3b26787fb5c7e4d8662cc420 4 +b5749b4cac530e261da9acc0d494a81766d2acc3 3 +afb693fd0a138cfc59a2bf81eabd7ca2abd4e162 4 +81bbbd4ea86350ebba03e8d8cb29f050a3d93a83 4 +d17acc97bf2f853a5fff58da733e4cd53cdaa3ed 5 +59f679c5ebf75de98b5e909da9182dbd80413364 4 +a80b5426fd0f4ab19eba1c06620f5efb8fbade3a 3 +a57f8ec4f89ab84936f46821969de6bca9f46d8e 4 +d3a936e5ef24fdbc7a02a81644ab6cd4b88119d3 3 +522a53034cf40448d441683f3308fcb4b65e6d7c 4 +dab5a9f93a35fcb92c01e4250b01928fe8bd294b 7 +25ffc7e8ca90274566c31e10b5998cca6522673a 7 +c1923ab5c5dcaf67af3e23fd1ac42e2d4c61aca7 4 +24384e63bfe450bce6abd327eae3e27969025379 4 +3f9d72bdafecfa640639cf66721e7380e41c6e6d 3 +c5b4cd4b49144e8f77455e9ff9077ff840ab278c 3 +c74a5a6164536750d6b3ea90e19798ab6fad79d7 4 +510f6d854f813269f67ee15a4cc2ed77d96d2903 4 +a5f3ada0610ad32f59d2f114167612d4f62bec4e 3 +d076d7185078b8adddbacfee7e46c61e3e02fe7e 3 +77d95640c80bc4868d04a913f9dbf4b5f6d877d2 5 +dbcfa55870a267112c9a71bcf85b481b11d98e04 3 +ee095ee6361f0a767250fe19c9bc805bba8da4e9 3 +39d500fbf22168be2a12ac89ff5a50131bb1c30b 4 +3194153a9c93aa24b37804a764f112bdef091bf8 4 +eca36fe80a916eb6b196ec56edfc1296d793e350 3 +d452a79b25f18e6a6eadc5c592d5101f115ef0f0 3 +ebed19269b7625c93eccefc2bd758555e2027431 3 +e388e8d8546e329d2c58269d4fa026624a640f1b 4 +f6199f48ff994fc9a8e33a8581bb3bb16dd301ab 2 +9447e50d1266b7cb96a414eaf9ee16c0f99e55f3 2 +ad1081f398ac69c0c92e4afadde482574fe875bb 2 +17583adb63d16d64ae51820055cd32c0e935425a 2 +2fc050dbc44ba9dfb72299129d48e5e5e1c892c3 4 +2bfa08db95502ee9bde61c5fe7b6557c0e4aef5a 4 +dc56cff1211bacd94f59e188e44a58d6f40a0c26 5 +de251a0af1df3be18469578dc00d414e572ee33e 7 +d1cc2a77fb17608d542c50e2a1404cf5b4231521 5 +8666f3109ad01093b0afab11986cfd60ba37d2a0 5 +fbb22cea9227f6f40080a42a1a169c0747129c95 5 +e171d0cec1d32aaf084e3c8150705c177522cbe6 3 +4164dcdfe0613d0f4c53b6f3581548bbbad5eb43 7 +ef6c6cde14eb551b74640ed6b38660572f121b86 7 +b9d46267d081882dfd89342d3e09b82d7e8f618d 7 +2a3c7ad70e34989fa5170028bad760e65ebe9d57 5 +3202af113265e67e130a0b948dc167a9f9e9cc79 4 +1ab38b014480745eaae143c9e6f67b542e62442f 3 +795d47c6aae378d0d5bedc9f104a597bcc1ee16b 4 +414fd2a22076f45502c2329b45bda9f8730dcaae 4 +b2ce0bc86f82f727db956dfbbe2a3fd0cb72f5fc 7 +00c70cf37c71cb8f99567ae407ff6dd52c4774c2 4 +c3ab3edb7f9ba31124092dc0f1dd2f13d3b52f8f 4 +aba839d554733850902f3073f7974e2e87c488ba 4 +19e79a79be13ac62bb002ed3323502be97886e17 3 +db22cd03db61a495d55c460cfaa266271c8a5926 3 +17dcbe8e9ab792483a542b0230897baf0240dcc9 3 +af310ee21b5534f32ea92c0ae34a44d828d157a7 4 +e3036a7bc6ed3ece1c85d5e37fb2ff39ba98ba47 4 +2300aa82bfcbcf95329eb04e58bcd7e4ca0a7889 3 +7fe5f17ad7e6b70dd56cad2c8bdc516a86f03207 4 +632cb0834441626d20cc113cfad2e3a7ec0e0dc7 3 +d7fb2ca6004c5fdf77c42a5eab102b95a9a862d7 3 +4ff2aad8371e382e203c7f29b665612a8c9d937c 3 +7c7d0bff4a51bb0f1f0cdb4132fdb11f8a71c623 3 +216b2c181c9ee0fb48e2d9efdb88ea918c8ff04f 3 +338460029d65ebce0d9119d811187a35c866a958 4 +108e9164ef6cf1ffe6e3c5303bfbbc3bd807b6df 4 +18be429f8b5c7bc4775c99292110aa09341cd644 4 +92bada2d019fcd6eee6d332a8068a9ddfa30d518 4 +c7377eaeb7f91639296c3df095ba403dbf4f8e79 2 +2f2c7559a5ba7fb938aba152bd84879c0bd3cae2 5 +7ed1308179c41ae62cfb46124ab0ce22d8b0513b 5 +6a95b7f3a4cff9c9b3d7b8b30b0b0f752dadaa37 5 +34635bf3518c7b33164f3d96da769c7a01102d28 4 +ebe20c85c5314957fafc223c26bf393847e3bb03 5 +f2361230358bd945690ca1c2a9dcc1cbce83941b 5 +ab3f375b6890b2a9bb1b6124f0fa690950c749f2 5 +f84d7fc089b31440cac1f4fbee3d45b2f6c90e37 5 +28b54294007ec345aef7322a2fcaa18d3363a73e 5 +3e57fad130be79223068ab29f1e77ecb7f2ad237 4 +efed48854beb2803d9f2b5644edff2aacf7c6af9 4 +9347890b025cc8c521a4abbff309ffc0466f22b3 4 +6fe7a6bb86897e579393e70961a3a3204adfa48b 3 +c570eccb1bf0c993e375e094d803ef112d0763ad 3 +2724871629b46e05a0c8b5554a962211e700de0e 3 +9200fd6f1e92f78e3d9c6d082837f719ad0500d4 5 +23e702084e3031158a50a0560746767bbc1b7bee 5 +289ac36381905685dda39946127ab2af3317a010 5 +36eda820b734ac7a42653af370a475f36bd36b1b 5 +d526249c1a35cf1174300df75bc28662f579290a 5 +a322d4e572467be5e1b452214143f88db526b49f 4 +67fa6e9005d23d9c49f77884a856cfb376194083 3 +4cc5f197d2bd3f0306c07a880590b4b4a5574e12 4 +06c527db4fbe929b4da005e7463078c05944f477 4 +094d11ebf8a20b6bfc07902c7f136bdd18b1e7ca 4 diff --git a/components/msx/fmsx/src/fMSX/Unix/Makefile b/components/msx/fmsx/src/fMSX/Unix/Makefile new file mode 100644 index 0000000..2208d62 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/Unix/Makefile @@ -0,0 +1,18 @@ +include ../../EMULib/Rules.Unix + +# Depending on your CPU endianess, use either -DLSB_FIRST or -DMSB_FIRST. +# Depending on your X11 display mode, use -DBPP8, -DBPP16, or -DBPP32. +DEFINES+= -DFMSX -DLSB_FIRST -DCONDEBUG -DDEBUG +CFLAGS += -Wall -I$(LIBZ80) +OBJECTS+= $(SHA1) $(FLOPPY) $(FDIDISK) $(MCF) $(HUNT) \ + $(Z80) $(I8255) $(YM2413) $(AY8910) $(SCC) $(WD1793) \ + ../fMSX.o ../MSX.o ../V9938.o ../I8251.o ../Patch.o \ + ../Menu.o Unix.o + +all: fmsx + +fmsx: Makefile $(OBJECTS) + $(CC) -o $@ $(CFLAGS) $(OBJECTS) $(LIBS) + +clean: + rm -f $(OBJECTS) diff --git a/components/msx/fmsx/src/fMSX/Unix/Unix.c b/components/msx/fmsx/src/fMSX/Unix/Unix.c new file mode 100644 index 0000000..f32465a --- /dev/null +++ b/components/msx/fmsx/src/fMSX/Unix/Unix.c @@ -0,0 +1,431 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** Unix.c **/ +/** **/ +/** This file contains Unix-dependent subroutines and **/ +/** drivers. It includes screen drivers via CommonMux.h. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#include "MSX.h" +#include "Console.h" +#include "EMULib.h" +#include "NetPlay.h" +#include "Sound.h" +#include "Record.h" + +#include +#include + +#define WIDTH 272 /* Buffer width */ +#define HEIGHT 228 /* Buffer height */ + +/* Press/Release keys in the background KeyState */ +#define XKBD_SET(K) XKeyState[Keys[K][0]]&=~Keys[K][1] +#define XKBD_RES(K) XKeyState[Keys[K][0]]|=Keys[K][1] + +/* Combination of EFF_* bits */ +int UseEffects = EFF_SCALE|EFF_SAVECPU|EFF_MITSHM|EFF_VARBPP|EFF_SYNC; + +int InMenu; /* 1: In MenuMSX(), ignore keys */ +int UseZoom = 2; /* Zoom factor (1=no zoom) */ +int UseSound = 22050; /* Audio sampling frequency (Hz) */ +int SyncFreq = 60; /* Sync frequency (0=sync off) */ +int FastForward; /* Fast-forwarded UPeriod backup */ +int SndSwitch; /* Mask of enabled sound channels */ +int SndVolume; /* Master volume for audio */ +int OldScrMode; /* fMSX "ScrMode" variable storage*/ + +const char *Title = "fMSX 6.0"; /* Program version */ + +Image NormScreen; /* Main screen image */ +Image WideScreen; /* Wide screen image */ +static pixel *WBuf; /* From Wide.h */ +static pixel *XBuf; /* From Common.h */ +static unsigned int XPal[80]; +static unsigned int BPal[256]; +static unsigned int XPal0; + +const char *Disks[2][MAXDISKS+1]; /* Disk names */ +volatile byte XKeyState[20]; /* Temporary KeyState array */ + +void HandleKeys(unsigned int Key); +void PutImage(void); + +/** CommonMux.h **********************************************/ +/** Display drivers for all possible screen depths. **/ +/*************************************************************/ +#include "CommonMux.h" + +/** InitMachine() ********************************************/ +/** Allocate resources needed by machine-dependent code. **/ +/*************************************************************/ +int InitMachine(void) +{ + int J; + + /* Initialize variables */ + UseZoom = UseZoom<1? 1:UseZoom>5? 5:UseZoom; + InMenu = 0; + FastForward = 0; + OldScrMode = 0; + NormScreen.Data = 0; + WideScreen.Data = 0; + + /* Initialize system resources */ + InitUnix(Title,UseZoom*WIDTH,UseZoom*HEIGHT); + + /* Set visual effects */ + SetEffects(UseEffects); + + /* Create main image buffer */ + if(!NewImage(&NormScreen,WIDTH,HEIGHT)) { TrashUnix();return(0); } + XBuf = NormScreen.Data; + +#ifndef NARROW + /* Create wide image buffer */ + if(!NewImage(&WideScreen,WIDTH*2,HEIGHT)) { TrashUnix();return(0); } + WBuf = WideScreen.Data; +#endif + + /* Set correct screen drivers */ + if(!SetScreenDepth(NormScreen.D)) { TrashUnix();return(0); } + + /* Initialize video to main image */ + SetVideo(&NormScreen,0,0,WIDTH,HEIGHT); + + /* Set all colors to black */ + for(J=0;J<80;J++) SetColor(J,0,0,0); + + /* Create SCREEN8 palette (GGGRRRBB) */ + for(J=0;J<256;J++) + BPal[J]=X11GetColor(((J>>2)&0x07)*255/7,((J>>5)&0x07)*255/7,(J&0x03)*255/3); + + /* Initialize temporary keyboard array */ + memset((void *)XKeyState,0xFF,sizeof(XKeyState)); + + /* Attach keyboard handler */ + SetKeyHandler(HandleKeys); + + /* Initialize sound */ + InitSound(UseSound,150); + SndSwitch=(1<0)&&!SetSyncTimer(SyncFreq*UPeriod/100)) SyncFreq=0; + + /* Initialize record/replay */ + RPLInit(SaveState,LoadState,MAX_STASIZE); + RPLRecord(RPL_RESET); + + /* Done */ + return(1); +} + +/** TrashMachine() *******************************************/ +/** Deallocate all resources taken by InitMachine(). **/ +/*************************************************************/ +void TrashMachine(void) +{ + /* Flush and free recording buffers */ + RPLTrash(); + +#ifndef NARROW + FreeImage(&WideScreen); +#endif + FreeImage(&NormScreen); + TrashSound(); + TrashUnix(); +} + +/** PutImage() ***********************************************/ +/** Put an image on the screen. **/ +/*************************************************************/ +void PutImage(void) +{ +#ifndef NARROW + /* If screen mode changed... */ + if(ScrMode!=OldScrMode) + { + /* Switch to the new screen mode */ + OldScrMode=ScrMode; + /* Depending on the new screen width... */ + if((ScrMode==6)||((ScrMode==7)&&!ModeYJK)||(ScrMode==MAXSCREEN+1)) + SetVideo(&WideScreen,0,0,WIDTH*2,HEIGHT); + else + SetVideo(&NormScreen,0,0,WIDTH,HEIGHT); + } +#endif + + /* Show replay icon */ + if(RPLPlay(RPL_QUERY)) RPLShow(VideoImg,VideoX+10,VideoY+10); + + /* Show display buffer */ + ShowVideo(); +} + +/** PlayAllSound() *******************************************/ +/** Render and play given number of microseconds of sound. **/ +/*************************************************************/ +void PlayAllSound(int uSec) +{ + /* @@@ Twice the argument to avoid skipping */ + RenderAndPlayAudio(2*uSec*UseSound/1000000); +} + +/** Joystick() ***********************************************/ +/** Query positions of two joystick connected to ports 0/1. **/ +/** Returns 0.0.B2.A2.R2.L2.D2.U2.0.0.B1.A1.R1.L1.D1.U1. **/ +/*************************************************************/ +unsigned int Joystick(void) +{ + byte RemoteKeyState[20]; + unsigned int J,I; + + /* Get joystick state */ + J = GetJoystick(); + + /* Make SHIFT/CONTROL act as fire buttons */ + if(J&BTN_SHIFT) J|=(J&BTN_ALT? (BTN_FIREB<<16):BTN_FIREB); + if(J&BTN_CONTROL) J|=(J&BTN_ALT? (BTN_FIREA<<16):BTN_FIREA); + + /* Store joystick state for NetPlay transmission */ + *(unsigned int *)&XKeyState[sizeof(XKeyState)-sizeof(int)]=J; + + /* If failed exchanging KeyStates with remote... */ + if(!NETExchange((void *)RemoteKeyState,(const void *)XKeyState,sizeof(XKeyState))) + /* Copy temporary keyboard map into permanent one */ + memcpy((void *)KeyState,(const void *)XKeyState,sizeof(KeyState)); + else + { + /* Merge local and remote KeyStates */ + for(I=0;I>16)-(HEIGHT-X)/2-VAdjust; + Y = Y<0? 0:Y>=X? X-1:Y; + X = J&MSE_XPOS; + X = (ScrMode==6)||((ScrMode==7)&&!ModeYJK)||(ScrMode==MAXSCREEN+1)? (X>>1):X; + X = X-(WIDTH-256)/2-HAdjust; + X = X<0? 0:X>=256? 255:X; + + return(((J&MSE_BUTTONS)>>14)|X|(Y<<8)); +} + +/** SetColor() ***********************************************/ +/** Set color N to (R,G,B). **/ +/*************************************************************/ +void SetColor(byte N,byte R,byte G,byte B) +{ + if(N) XPal[N]=X11GetColor(R,G,B); else XPal0=X11GetColor(R,G,B); +} + +/** HandleKeys() *********************************************/ +/** Keyboard handler. **/ +/*************************************************************/ +void HandleKeys(unsigned int Key) +{ + unsigned int J; + + /* When in MenuMSX() or ConDebug(), ignore keypresses */ + if(InMenu||CPU->Trace) return; + + /* Handle special keys */ + if(Key&CON_RELEASE) + { + switch(Key&CON_KEYCODE) + { + case XK_F9: + if(FastForward) + { + SetEffects(UseEffects); + UPeriod=FastForward; + FastForward=0; + } + break; + + case XK_Shift_L: + case XK_Shift_R: XKBD_RES(KBD_SHIFT);break; + case XK_Control_L: + case XK_Control_R: XKBD_RES(KBD_CONTROL);break; + case XK_Alt_L: + case XK_Alt_R: XKBD_RES(KBD_GRAPH);break; + case XK_Caps_Lock: XKBD_RES(KBD_CAPSLOCK);break; + case XK_Escape: XKBD_RES(KBD_ESCAPE);break; + case XK_Return: XKBD_RES(KBD_ENTER);break; + case XK_BackSpace: XKBD_RES(KBD_BS);break; + case XK_Tab: XKBD_RES(KBD_TAB);break; + case XK_End: XKBD_RES(KBD_SELECT);break; + case XK_Home: XKBD_RES(KBD_HOME);break; + case XK_Page_Up: XKBD_RES(KBD_STOP);break; + case XK_Page_Down: XKBD_RES(KBD_COUNTRY);break; + case XK_Insert: XKBD_RES(KBD_INSERT);break; + case XK_Delete: XKBD_RES(KBD_DELETE);break; + case XK_Up: XKBD_RES(KBD_UP);break; + case XK_Down: XKBD_RES(KBD_DOWN);break; + case XK_Left: XKBD_RES(KBD_LEFT);break; + case XK_Right: XKBD_RES(KBD_RIGHT);break; + case XK_F1: XKBD_RES(KBD_F1);break; + case XK_F2: XKBD_RES(KBD_F2);break; + case XK_F3: XKBD_RES(KBD_F3);break; + case XK_F4: XKBD_RES(KBD_F4);break; + case XK_F5: XKBD_RES(KBD_F5);break; + + default: + Key&=CON_KEYCODE; + if(Key<128) XKBD_RES(Key); + break; + } + } + else + { + /* Cancel replay when a key is pressed */ + J = Key&CON_KEYCODE; + if((J!=XK_F9)&&(J!=XK_Left)&&(J!=XK_Right)&&(J!=XK_Up)) RPLPlay(RPL_OFF); + + switch(Key&CON_KEYCODE) + { + case XK_F6: + LoadSTA(STAName? STAName:"DEFAULT.STA"); + RPLPlay(RPL_OFF); + break; + case XK_F7: + SaveSTA(STAName? STAName:"DEFAULT.STA"); + break; + case XK_F8: + if(!(Key&(CON_ALT|CON_CONTROL))) RPLPlay(RPL_RESET); + else + { + /* [ALT]+[F8] toggles screen softening */ + /* [CTRL]+[F8] toggles scanlines */ + UseEffects^=Key&CON_ALT? EFF_SOFTEN:EFF_TVLINES; + SetEffects(UseEffects); + } + break; + case XK_F9: + if(!FastForward) + { + SetEffects(UseEffects&~EFF_SYNC); + FastForward=UPeriod; + UPeriod=10; + } + break; + case XK_F10: +#ifdef DEBUG + /* [CTRL]+[F10] invokes built-in debugger */ + if(Key&CON_CONTROL) + { XKBD_RES(KBD_CONTROL);CPU->Trace=1;break; } +#endif + /* [ALT]+[F10] invokes NetPlay */ + if(Key&CON_ALT) + { + XKBD_RES(KBD_GRAPH); + InMenu=1; + if(NETPlay(NET_TOGGLE)) ResetMSX(Mode,RAMPages,VRAMPages); + InMenu=0; + break; + } + /* [F10] invokes built-in menu */ + InMenu=1; + MenuMSX(); + InMenu=0; + break; + case XK_F11: + ResetMSX(Mode,RAMPages,VRAMPages); + RPLPlay(RPL_OFF); + break; + case XK_F12: + ExitNow=1; + break; + + case XK_Shift_L: + case XK_Shift_R: XKBD_SET(KBD_SHIFT);break; + case XK_Control_L: + case XK_Control_R: XKBD_SET(KBD_CONTROL);break; + case XK_Alt_L: + case XK_Alt_R: XKBD_SET(KBD_GRAPH);break; + case XK_Caps_Lock: XKBD_SET(KBD_CAPSLOCK);break; + case XK_Escape: XKBD_SET(KBD_ESCAPE);break; + case XK_Return: XKBD_SET(KBD_ENTER);break; + case XK_BackSpace: XKBD_SET(KBD_BS);break; + case XK_Tab: XKBD_SET(KBD_TAB);break; + case XK_End: XKBD_SET(KBD_SELECT);break; + case XK_Home: XKBD_SET(KBD_HOME);break; + case XK_Page_Up: XKBD_SET(KBD_STOP);break; + case XK_Page_Down: XKBD_SET(KBD_COUNTRY);break; + case XK_Insert: XKBD_SET(KBD_INSERT);break; + case XK_Delete: XKBD_SET(KBD_DELETE);break; + case XK_Up: XKBD_SET(KBD_UP);break; + case XK_Down: XKBD_SET(KBD_DOWN);break; + case XK_Left: XKBD_SET(KBD_LEFT);break; + case XK_Right: XKBD_SET(KBD_RIGHT);break; + case XK_F1: XKBD_SET(KBD_F1);break; + case XK_F2: XKBD_SET(KBD_F2);break; + case XK_F3: XKBD_SET(KBD_F3);break; + case XK_F4: XKBD_SET(KBD_F4);break; + case XK_F5: XKBD_SET(KBD_F5);break; + + default: + Key&=CON_KEYCODE; + if(Key<128) XKBD_SET(Key); + break; + } + } +} diff --git a/components/msx/fmsx/src/fMSX/V9938.c b/components/msx/fmsx/src/fMSX/V9938.c new file mode 100644 index 0000000..87d851c --- /dev/null +++ b/components/msx/fmsx/src/fMSX/V9938.c @@ -0,0 +1,1046 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** V9938.c **/ +/** **/ +/** This file contains implementation for the V9938 special **/ +/** graphical operations. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/** **/ +/** Completely rewritten by Alex Wulms: **/ +/** - VDP Command execution 'in parallel' with CPU **/ +/** - Corrected behaviour of VDP commands **/ +/** - Made it easier to implement correct S7/8 mapping **/ +/** by concentrating VRAM access in one single place **/ +/** - Made use of the 'in parallel' VDP command exec **/ +/** and correct timing. You must call the function **/ +/** LoopVDP() from LoopZ80 in MSX.c. You must call it **/ +/** exactly 256 times per screen refresh. **/ +/** Started on : 11-11-1999 **/ +/** Beta release 1 on: 9-12-1999 **/ +/** Beta release 2 on: 20-01-2000 **/ +/** - Corrected behaviour of VRM <-> Z80 transfer **/ +/** - Improved performance of the code **/ +/** Public release 1.0: 20-04-2000 **/ +/*************************************************************/ +#include "V9938.h" +#include + +/*************************************************************/ +/** Other usefull defines **/ +/*************************************************************/ +#define VDP_VRMP5(X, Y) (msx_VRAM + ((Y&1023)<<7) + ((X&255)>>1)) +#define VDP_VRMP6(X, Y) (msx_VRAM + ((Y&1023)<<7) + ((X&511)>>2)) +#define VDP_VRMP7(X, Y) (msx_VRAM + ((Y&511)<<8) + ((X&511)>>1)) +#define VDP_VRMP8(X, Y) (msx_VRAM + ((Y&511)<<8) + (X&255)) + +#define VDP_VRMP(M, X, Y) VDPVRMP(M, X, Y) +#define VDP_POINT(M, X, Y) VDPpoint(M, X, Y) +#define VDP_PSET(M, X, Y, C, O) VDPpset(M, X, Y, C, O) + +#define CM_ABRT 0x0 +#define CM_POINT 0x4 +#define CM_PSET 0x5 +#define CM_SRCH 0x6 +#define CM_LINE 0x7 +#define CM_LMMV 0x8 +#define CM_LMMM 0x9 +#define CM_LMCM 0xA +#define CM_LMMC 0xB +#define CM_HMMV 0xC +#define CM_HMMM 0xD +#define CM_YMMM 0xE +#define CM_HMMC 0xF + +/*************************************************************/ +/* Many VDP commands are executed in some kind of loop but */ +/* essentially, there are only a few basic loop structures */ +/* that are re-used. We define the loop structures that are */ +/* re-used here so that they have to be entered only once */ +/*************************************************************/ +#define pre_loop \ + while ((cnt-=delta) > 0) { + +/* Loop over DX, DY */ +#define post__x_y(MX) \ + if (!--ANX || ((ADX+=TX)&MX)) { \ + if (!(--NY&1023) || (DY+=TY)==-1) \ + break; \ + else { \ + ADX=DX; \ + ANX=NX; \ + } \ + } \ + } + +/* Loop over DX, SY, DY */ +#define post__xyy(MX) \ + if ((ADX+=TX)&MX) { \ + if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \ + break; \ + else \ + ADX=DX; \ + } \ + } + +/* Loop over SX, DX, SY, DY */ +#define post_xxyy(MX) \ + if (!--ANX || ((ASX+=TX)&MX) || ((ADX+=TX)&MX)) { \ + if (!(--NY&1023) || (SY+=TY)==-1 || (DY+=TY)==-1) \ + break; \ + else { \ + ASX=SX; \ + ADX=DX; \ + ANX=NX; \ + } \ + } \ + } + +/*************************************************************/ +/** Structures and stuff **/ +/*************************************************************/ +static struct { + int SX,SY; + int DX,DY; + int TX,TY; + int NX,NY; + int MX; + int ASX,ADX,ANX; + byte CL; + byte LO; + byte CM; +} MMC; + +/*************************************************************/ +/** Function prototypes **/ +/*************************************************************/ +static byte *VDPVRMP(register byte M, register int X, register int Y); + +static byte VDPpoint5(register int SX, register int SY); +static byte VDPpoint6(register int SX, register int SY); +static byte VDPpoint7(register int SX, register int SY); +static byte VDPpoint8(register int SX, register int SY); + +static byte VDPpoint(register byte SM, + register int SX, register int SY); + +static void VDPpsetlowlevel(register byte *P, register byte CL, + register byte M, register byte OP); + +static void VDPpset5(register int DX, register int DY, + register byte CL, register byte OP); +static void VDPpset6(register int DX, register int DY, + register byte CL, register byte OP); +static void VDPpset7(register int DX, register int DY, + register byte CL, register byte OP); +static void VDPpset8(register int DX, register int DY, + register byte CL, register byte OP); + +static void VDPpset(register byte SM, + register int DX, register int DY, + register byte CL, register byte OP); + +static int GetVdpTimingValue(register int *); + +static void SrchEngine(void); +static void LineEngine(void); +static void LmmvEngine(void); +static void LmmmEngine(void); +static void LmcmEngine(void); +static void LmmcEngine(void); +static void HmmvEngine(void); +static void HmmmEngine(void); +static void YmmmEngine(void); +static void HmmcEngine(void); + +void ReportVdpCommand(register byte Op); + +/*************************************************************/ +/** Variables visible only in this module **/ +/*************************************************************/ +static byte Mask[4] = { 0x0F,0x03,0x0F,0xFF }; +static int PPB[4] = { 2,4,2,1 }; +static int PPL[4] = { 256,512,512,256 }; +static int VdpOpsCnt=1; +static void (*VdpEngine)(void)=0; + + /* SprOn SprOn SprOf SprOf */ + /* ScrOf ScrOn ScrOf ScrOn */ +static int srch_timing[8]={ 818, 1025, 818, 830, /* ntsc */ + 696, 854, 696, 684 }; /* pal */ +static int line_timing[8]={ 1063, 1259, 1063, 1161, + 904, 1026, 904, 953 }; +static int hmmv_timing[8]={ 439, 549, 439, 531, + 366, 439, 366, 427 }; +static int lmmv_timing[8]={ 873, 1135, 873, 1056, + 732, 909, 732, 854 }; +static int ymmm_timing[8]={ 586, 952, 586, 610, + 488, 720, 488, 500 }; +static int hmmm_timing[8]={ 818, 1111, 818, 854, + 684, 879, 684, 708 }; +static int lmmm_timing[8]={ 1160, 1599, 1160, 1172, + 964, 1257, 964, 977 }; + + +/** VDPVRMP() **********************************************/ +/** Calculate addr of a pixel in vram **/ +/*************************************************************/ +INLINE byte *VDPVRMP(byte M,int X,int Y) +{ + switch(M) + { + case 0: return VDP_VRMP5(X,Y); + case 1: return VDP_VRMP6(X,Y); + case 2: return VDP_VRMP7(X,Y); + case 3: return VDP_VRMP8(X,Y); + } + + return(msx_VRAM); +} + +/** VDPpoint5() ***********************************************/ +/** Get a pixel on screen 5 **/ +/*************************************************************/ +INLINE byte VDPpoint5(int SX, int SY) +{ + return (*VDP_VRMP5(SX, SY) >> + (((~SX)&1)<<2) + )&15; +} + +/** VDPpoint6() ***********************************************/ +/** Get a pixel on screen 6 **/ +/*************************************************************/ +INLINE byte VDPpoint6(int SX, int SY) +{ + return (*VDP_VRMP6(SX, SY) >> + (((~SX)&3)<<1) + )&3; +} + +/** VDPpoint7() ***********************************************/ +/** Get a pixel on screen 7 **/ +/*************************************************************/ +INLINE byte VDPpoint7(int SX, int SY) +{ + return (*VDP_VRMP7(SX, SY) >> + (((~SX)&1)<<2) + )&15; +} + +/** VDPpoint8() ***********************************************/ +/** Get a pixel on screen 8 **/ +/*************************************************************/ +INLINE byte VDPpoint8(int SX, int SY) +{ + return *VDP_VRMP8(SX, SY); +} + +/** VDPpoint() ************************************************/ +/** Get a pixel on a screen **/ +/*************************************************************/ +INLINE byte VDPpoint(byte SM, int SX, int SY) +{ + switch(SM) + { + case 0: return VDPpoint5(SX,SY); + case 1: return VDPpoint6(SX,SY); + case 2: return VDPpoint7(SX,SY); + case 3: return VDPpoint8(SX,SY); + } + + return(0); +} + +/** VDPpsetlowlevel() ****************************************/ +/** Low level function to set a pixel on a screen **/ +/** Make it inline to make it fast **/ +/*************************************************************/ +INLINE void VDPpsetlowlevel(byte *P, byte CL, byte M, byte OP) +{ + switch (OP) + { + case 0: *P = (*P & M) | CL; break; + case 1: *P = *P & (CL | M); break; + case 2: *P |= CL; break; + case 3: *P ^= CL; break; + case 4: *P = (*P & M) | ~(CL | M); break; + case 8: if (CL) *P = (*P & M) | CL; break; + case 9: if (CL) *P = *P & (CL | M); break; + case 10: if (CL) *P |= CL; break; + case 11: if (CL) *P ^= CL; break; + case 12: if (CL) *P = (*P & M) | ~(CL|M); break; + } +} + +/** VDPpset5() ***********************************************/ +/** Set a pixel on screen 5 **/ +/*************************************************************/ +INLINE void VDPpset5(int DX, int DY, byte CL, byte OP) +{ + register byte SH = ((~DX)&1)<<2; + + VDPpsetlowlevel(VDP_VRMP5(DX, DY), + CL << SH, ~(15<>6)&1)|(VDP[8]&2)|((VDP[9]<<1)&4)]); +} + +/** SrchEgine()** ********************************************/ +/** Search a dot **/ +/*************************************************************/ +void SrchEngine(void) +{ + register int SX=MMC.SX; + register int SY=MMC.SY; + register int TX=MMC.TX; + register int ANX=MMC.ANX; + register byte CL=MMC.CL; + register int cnt; + register int delta; + + delta = GetVdpTimingValue(srch_timing); + cnt = VdpOpsCnt; + +#define pre_srch \ + pre_loop \ + if (( +#define post_srch(MX) \ + ==CL) ^ANX) { \ + VDPStatus[2]|=0x10; /* Border detected */ \ + break; \ + } \ + if ((SX+=TX) & MX) { \ + VDPStatus[2]&=0xEF; /* Border not detected */ \ + break; \ + } \ + } + + switch (ScrMode) { + case 5: pre_srch VDPpoint5(SX, SY) post_srch(256) + break; + case 6: pre_srch VDPpoint6(SX, SY) post_srch(512) + break; + case 7: pre_srch VDPpoint7(SX, SY) post_srch(512) + break; + case 8: pre_srch VDPpoint8(SX, SY) post_srch(256) + break; + } + + if ((VdpOpsCnt=cnt)>0) { + /* Command execution done */ + VDPStatus[2]&=0xFE; + VdpEngine=0; + /* Update SX in VDP registers */ + VDPStatus[8]=SX&0xFF; + VDPStatus[9]=(SX>>8)|0xFE; + } + else { + MMC.SX=SX; + } +} + +/** LineEgine()** ********************************************/ +/** Draw a line **/ +/*************************************************************/ +void LineEngine(void) +{ + register int DX=MMC.DX; + register int DY=MMC.DY; + register int TX=MMC.TX; + register int TY=MMC.TY; + register int NX=MMC.NX; + register int NY=MMC.NY; + register int ASX=MMC.ASX; + register int ADX=MMC.ADX; + register byte CL=MMC.CL; + register byte LO=MMC.LO; + register int cnt; + register int delta; + + delta = GetVdpTimingValue(line_timing); + cnt = VdpOpsCnt; + +#define post_linexmaj(MX) \ + DX+=TX; \ + if ((ASX-=NY)<0) { \ + ASX+=NX; \ + DY+=TY; \ + } \ + ASX&=1023; /* Mask to 10 bits range */ \ + if (ADX++==NX || (DX&MX)) \ + break; \ + } +#define post_lineymaj(MX) \ + DY+=TY; \ + if ((ASX-=NY)<0) { \ + ASX+=NX; \ + DX+=TX; \ + } \ + ASX&=1023; /* Mask to 10 bits range */ \ + if (ADX++==NX || (DX&MX)) \ + break; \ + } + + if ((VDP[45]&0x01)==0) + /* X-Axis is major direction */ + switch (ScrMode) { + case 5: pre_loop VDPpset5(DX, DY, CL, LO); post_linexmaj(256) + break; + case 6: pre_loop VDPpset6(DX, DY, CL, LO); post_linexmaj(512) + break; + case 7: pre_loop VDPpset7(DX, DY, CL, LO); post_linexmaj(512) + break; + case 8: pre_loop VDPpset8(DX, DY, CL, LO); post_linexmaj(256) + break; + } + else + /* Y-Axis is major direction */ + switch (ScrMode) { + case 5: pre_loop VDPpset5(DX, DY, CL, LO); post_lineymaj(256) + break; + case 6: pre_loop VDPpset6(DX, DY, CL, LO); post_lineymaj(512) + break; + case 7: pre_loop VDPpset7(DX, DY, CL, LO); post_lineymaj(512) + break; + case 8: pre_loop VDPpset8(DX, DY, CL, LO); post_lineymaj(256) + break; + } + + if ((VdpOpsCnt=cnt)>0) { + /* Command execution done */ + VDPStatus[2]&=0xFE; + VdpEngine=0; + VDP[38]=DY & 0xFF; + VDP[39]=(DY>>8) & 0x03; + } + else { + MMC.DX=DX; + MMC.DY=DY; + MMC.ASX=ASX; + MMC.ADX=ADX; + } +} + +/** LmmvEngine() *********************************************/ +/** VDP -> Vram **/ +/*************************************************************/ +void LmmvEngine(void) +{ + register int DX=MMC.DX; + register int DY=MMC.DY; + register int TX=MMC.TX; + register int TY=MMC.TY; + register int NX=MMC.NX; + register int NY=MMC.NY; + register int ADX=MMC.ADX; + register int ANX=MMC.ANX; + register byte CL=MMC.CL; + register byte LO=MMC.LO; + register int cnt; + register int delta; + + delta = GetVdpTimingValue(lmmv_timing); + cnt = VdpOpsCnt; + + switch (ScrMode) { + case 5: pre_loop VDPpset5(ADX, DY, CL, LO); post__x_y(256) + break; + case 6: pre_loop VDPpset6(ADX, DY, CL, LO); post__x_y(512) + break; + case 7: pre_loop VDPpset7(ADX, DY, CL, LO); post__x_y(512) + break; + case 8: pre_loop VDPpset8(ADX, DY, CL, LO); post__x_y(256) + break; + } + + if ((VdpOpsCnt=cnt)>0) { + /* Command execution done */ + VDPStatus[2]&=0xFE; + VdpEngine=0; + if (!NY) + DY+=TY; + VDP[38]=DY & 0xFF; + VDP[39]=(DY>>8) & 0x03; + VDP[42]=NY & 0xFF; + VDP[43]=(NY>>8) & 0x03; + } + else { + MMC.DY=DY; + MMC.NY=NY; + MMC.ANX=ANX; + MMC.ADX=ADX; + } +} + +/** LmmmEngine() *********************************************/ +/** Vram -> Vram **/ +/*************************************************************/ +void LmmmEngine(void) +{ + register int SX=MMC.SX; + register int SY=MMC.SY; + register int DX=MMC.DX; + register int DY=MMC.DY; + register int TX=MMC.TX; + register int TY=MMC.TY; + register int NX=MMC.NX; + register int NY=MMC.NY; + register int ASX=MMC.ASX; + register int ADX=MMC.ADX; + register int ANX=MMC.ANX; + register byte LO=MMC.LO; + register int cnt; + register int delta; + + delta = GetVdpTimingValue(lmmm_timing); + cnt = VdpOpsCnt; + + switch (ScrMode) { + case 5: pre_loop VDPpset5(ADX, DY, VDPpoint5(ASX, SY), LO); post_xxyy(256) + break; + case 6: pre_loop VDPpset6(ADX, DY, VDPpoint6(ASX, SY), LO); post_xxyy(512) + break; + case 7: pre_loop VDPpset7(ADX, DY, VDPpoint7(ASX, SY), LO); post_xxyy(512) + break; + case 8: pre_loop VDPpset8(ADX, DY, VDPpoint8(ASX, SY), LO); post_xxyy(256) + break; + } + + if ((VdpOpsCnt=cnt)>0) { + /* Command execution done */ + VDPStatus[2]&=0xFE; + VdpEngine=0; + if (!NY) { + SY+=TY; + DY+=TY; + } + else + if (SY==-1) + DY+=TY; + VDP[42]=NY & 0xFF; + VDP[43]=(NY>>8) & 0x03; + VDP[34]=SY & 0xFF; + VDP[35]=(SY>>8) & 0x03; + VDP[38]=DY & 0xFF; + VDP[39]=(DY>>8) & 0x03; + } + else { + MMC.SY=SY; + MMC.DY=DY; + MMC.NY=NY; + MMC.ANX=ANX; + MMC.ASX=ASX; + MMC.ADX=ADX; + } +} + +/** LmcmEngine() *********************************************/ +/** Vram -> CPU **/ +/*************************************************************/ +void LmcmEngine() +{ + if ((VDPStatus[2]&0x80)!=0x80) { + + VDPStatus[7]=VDP[44]=VDP_POINT(ScrMode-5, MMC.ASX, MMC.SY); + VdpOpsCnt-=GetVdpTimingValue(lmmv_timing); + VDPStatus[2]|=0x80; + + if (!--MMC.ANX || ((MMC.ASX+=MMC.TX)&MMC.MX)) { + if (!(--MMC.NY & 1023) || (MMC.SY+=MMC.TY)==-1) { + VDPStatus[2]&=0xFE; + VdpEngine=0; + if (!MMC.NY) + MMC.DY+=MMC.TY; + VDP[42]=MMC.NY & 0xFF; + VDP[43]=(MMC.NY>>8) & 0x03; + VDP[34]=MMC.SY & 0xFF; + VDP[35]=(MMC.SY>>8) & 0x03; + } + else { + MMC.ASX=MMC.SX; + MMC.ANX=MMC.NX; + } + } + } +} + +/** LmmcEngine() *********************************************/ +/** CPU -> Vram **/ +/*************************************************************/ +void LmmcEngine(void) +{ + if ((VDPStatus[2]&0x80)!=0x80) { + register byte SM=ScrMode-5; + + VDPStatus[7]=VDP[44]&=Mask[SM]; + VDP_PSET(SM, MMC.ADX, MMC.DY, VDP[44], MMC.LO); + VdpOpsCnt-=GetVdpTimingValue(lmmv_timing); + VDPStatus[2]|=0x80; + + if (!--MMC.ANX || ((MMC.ADX+=MMC.TX)&MMC.MX)) { + if (!(--MMC.NY&1023) || (MMC.DY+=MMC.TY)==-1) { + VDPStatus[2]&=0xFE; + VdpEngine=0; + if (!MMC.NY) + MMC.DY+=MMC.TY; + VDP[42]=MMC.NY & 0xFF; + VDP[43]=(MMC.NY>>8) & 0x03; + VDP[38]=MMC.DY & 0xFF; + VDP[39]=(MMC.DY>>8) & 0x03; + } + else { + MMC.ADX=MMC.DX; + MMC.ANX=MMC.NX; + } + } + } +} + +/** HmmvEngine() *********************************************/ +/** VDP --> Vram **/ +/*************************************************************/ +void HmmvEngine(void) +{ + register int DX=MMC.DX; + register int DY=MMC.DY; + register int TX=MMC.TX; + register int TY=MMC.TY; + register int NX=MMC.NX; + register int NY=MMC.NY; + register int ADX=MMC.ADX; + register int ANX=MMC.ANX; + register byte CL=MMC.CL; + register int cnt; + register int delta; + + delta = GetVdpTimingValue(hmmv_timing); + cnt = VdpOpsCnt; + + switch (ScrMode) { + case 5: pre_loop *VDP_VRMP5(ADX, DY) = CL; post__x_y(256) + break; + case 6: pre_loop *VDP_VRMP6(ADX, DY) = CL; post__x_y(512) + break; + case 7: pre_loop *VDP_VRMP7(ADX, DY) = CL; post__x_y(512) + break; + case 8: pre_loop *VDP_VRMP8(ADX, DY) = CL; post__x_y(256) + break; + } + + if ((VdpOpsCnt=cnt)>0) { + /* Command execution done */ + VDPStatus[2]&=0xFE; + VdpEngine=0; + if (!NY) + DY+=TY; + VDP[42]=NY & 0xFF; + VDP[43]=(NY>>8) & 0x03; + VDP[38]=DY & 0xFF; + VDP[39]=(DY>>8) & 0x03; + } + else { + MMC.DY=DY; + MMC.NY=NY; + MMC.ANX=ANX; + MMC.ADX=ADX; + } +} + +/** HmmmEngine() *********************************************/ +/** Vram -> Vram **/ +/*************************************************************/ +void HmmmEngine(void) +{ + register int SX=MMC.SX; + register int SY=MMC.SY; + register int DX=MMC.DX; + register int DY=MMC.DY; + register int TX=MMC.TX; + register int TY=MMC.TY; + register int NX=MMC.NX; + register int NY=MMC.NY; + register int ASX=MMC.ASX; + register int ADX=MMC.ADX; + register int ANX=MMC.ANX; + register int cnt; + register int delta; + + delta = GetVdpTimingValue(hmmm_timing); + cnt = VdpOpsCnt; + + switch (ScrMode) { + case 5: pre_loop *VDP_VRMP5(ADX, DY) = *VDP_VRMP5(ASX, SY); post_xxyy(256) + break; + case 6: pre_loop *VDP_VRMP6(ADX, DY) = *VDP_VRMP6(ASX, SY); post_xxyy(512) + break; + case 7: pre_loop *VDP_VRMP7(ADX, DY) = *VDP_VRMP7(ASX, SY); post_xxyy(512) + break; + case 8: pre_loop *VDP_VRMP8(ADX, DY) = *VDP_VRMP8(ASX, SY); post_xxyy(256) + break; + } + + if ((VdpOpsCnt=cnt)>0) { + /* Command execution done */ + VDPStatus[2]&=0xFE; + VdpEngine=0; + if (!NY) { + SY+=TY; + DY+=TY; + } + else + if (SY==-1) + DY+=TY; + VDP[42]=NY & 0xFF; + VDP[43]=(NY>>8) & 0x03; + VDP[34]=SY & 0xFF; + VDP[35]=(SY>>8) & 0x03; + VDP[38]=DY & 0xFF; + VDP[39]=(DY>>8) & 0x03; + } + else { + MMC.SY=SY; + MMC.DY=DY; + MMC.NY=NY; + MMC.ANX=ANX; + MMC.ASX=ASX; + MMC.ADX=ADX; + } +} + +/** YmmmEngine() *********************************************/ +/** Vram -> Vram **/ +/*************************************************************/ +void YmmmEngine(void) +{ + register int SY=MMC.SY; + register int DX=MMC.DX; + register int DY=MMC.DY; + register int TX=MMC.TX; + register int TY=MMC.TY; + register int NY=MMC.NY; + register int ADX=MMC.ADX; + register int cnt; + register int delta; + + delta = GetVdpTimingValue(ymmm_timing); + cnt = VdpOpsCnt; + + switch (ScrMode) { + case 5: pre_loop *VDP_VRMP5(ADX, DY) = *VDP_VRMP5(ADX, SY); post__xyy(256) + break; + case 6: pre_loop *VDP_VRMP6(ADX, DY) = *VDP_VRMP6(ADX, SY); post__xyy(512) + break; + case 7: pre_loop *VDP_VRMP7(ADX, DY) = *VDP_VRMP7(ADX, SY); post__xyy(512) + break; + case 8: pre_loop *VDP_VRMP8(ADX, DY) = *VDP_VRMP8(ADX, SY); post__xyy(256) + break; + } + + if ((VdpOpsCnt=cnt)>0) { + /* Command execution done */ + VDPStatus[2]&=0xFE; + VdpEngine=0; + if (!NY) { + SY+=TY; + DY+=TY; + } + else + if (SY==-1) + DY+=TY; + VDP[42]=NY & 0xFF; + VDP[43]=(NY>>8) & 0x03; + VDP[34]=SY & 0xFF; + VDP[35]=(SY>>8) & 0x03; + VDP[38]=DY & 0xFF; + VDP[39]=(DY>>8) & 0x03; + } + else { + MMC.SY=SY; + MMC.DY=DY; + MMC.NY=NY; + MMC.ADX=ADX; + } +} + +/** HmmcEngine() *********************************************/ +/** CPU -> Vram **/ +/*************************************************************/ +void HmmcEngine(void) +{ + if ((VDPStatus[2]&0x80)!=0x80) { + + *VDP_VRMP(ScrMode-5, MMC.ADX, MMC.DY)=VDP[44]; + VdpOpsCnt-=GetVdpTimingValue(hmmv_timing); + VDPStatus[2]|=0x80; + + if (!--MMC.ANX || ((MMC.ADX+=MMC.TX)&MMC.MX)) { + if (!(--MMC.NY&1023) || (MMC.DY+=MMC.TY)==-1) { + VDPStatus[2]&=0xFE; + VdpEngine=0; + if (!MMC.NY) + MMC.DY+=MMC.TY; + VDP[42]=MMC.NY & 0xFF; + VDP[43]=(MMC.NY>>8) & 0x03; + VDP[38]=MMC.DY & 0xFF; + VDP[39]=(MMC.DY>>8) & 0x03; + } + else { + MMC.ADX=MMC.DX; + MMC.ANX=MMC.NX; + } + } + } +} + +/** VDPWrite() ***********************************************/ +/** Use this function to transfer pixel(s) from CPU to VDP. **/ +/*************************************************************/ +void VDPWrite(byte V) +{ + VDPStatus[2]&=0x7F; + VDPStatus[7]=VDP[44]=V; + if(VdpEngine&&(VdpOpsCnt>0)) VdpEngine(); +} + +/** VDPRead() ************************************************/ +/** Use this function to transfer pixel(s) from VDP to CPU. **/ +/*************************************************************/ +byte VDPRead(void) +{ + VDPStatus[2]&=0x7F; + if(VdpEngine&&(VdpOpsCnt>0)) VdpEngine(); + return(VDP[44]); +} + +/** ReportVdpCommand() ***************************************/ +/** Report VDP Command to be executed **/ +/*************************************************************/ +void ReportVdpCommand(register byte Op) +{ + static char *Ops[16] = + { + "SET ","AND ","OR ","XOR ","NOT ","NOP ","NOP ","NOP ", + "TSET","TAND","TOR ","TXOR","TNOT","NOP ","NOP ","NOP " + }; + static char *Commands[16] = + { + " ABRT"," ????"," ????"," ????","POINT"," PSET"," SRCH"," LINE", + " LMMV"," LMMM"," LMCM"," LMMC"," HMMV"," HMMM"," YMMM"," HMMC" + }; + register byte CL, CM, LO; + register int SX,SY, DX,DY, NX,NY; + + /* Fetch arguments */ + CL = VDP[44]; + SX = (VDP[32]+((int)VDP[33]<<8)) & 511; + SY = (VDP[34]+((int)VDP[35]<<8)) & 1023; + DX = (VDP[36]+((int)VDP[37]<<8)) & 511; + DY = (VDP[38]+((int)VDP[39]<<8)) & 1023; + NX = (VDP[40]+((int)VDP[41]<<8)) & 1023; + NY = (VDP[42]+((int)VDP[43]<<8)) & 1023; + CM = Op>>4; + LO = Op&0x0F; + + printf("V9938: Opcode %02Xh %s-%s (%d,%d)->(%d,%d),%d [%d,%d]%s\n", + Op, Commands[CM], Ops[LO], + SX,SY, DX,DY, CL, VDP[45]&0x04? -NX:NX, + VDP[45]&0x08? -NY:NY, + VDP[45]&0x70? " on ExtVRAM":"" + ); +} + +/** VDPDraw() ************************************************/ +/** Perform a given V9938 operation Op. **/ +/*************************************************************/ +byte VDPDraw(byte Op) +{ + register int SM; + + /* V9938 ops only work in SCREENs 5-8 */ + if (ScrMode<5) + return(0); + + SM = ScrMode-5; /* Screen mode index 0..3 */ + + MMC.CM = Op>>4; + if ((MMC.CM & 0x0C) != 0x0C && MMC.CM != 0) + /* Dot operation: use only relevant bits of color */ + VDPStatus[7]=(VDP[44]&=Mask[SM]); + + if(Verbose&0x02) + ReportVdpCommand(Op); + + switch(Op>>4) { + case CM_ABRT: + VDPStatus[2]&=0xFE; + VdpEngine=0; + return 1; + case CM_POINT: + VDPStatus[2]&=0xFE; + VdpEngine=0; + VDPStatus[7]=VDP[44]= + VDP_POINT(SM, VDP[32]+((int)VDP[33]<<8), + VDP[34]+((int)VDP[35]<<8)); + return 1; + case CM_PSET: + VDPStatus[2]&=0xFE; + VdpEngine=0; + VDP_PSET(SM, + VDP[36]+((int)VDP[37]<<8), + VDP[38]+((int)VDP[39]<<8), + VDP[44], + Op&0x0F); + return 1; + case CM_SRCH: + VdpEngine=SrchEngine; + break; + case CM_LINE: + VdpEngine=LineEngine; + break; + case CM_LMMV: + VdpEngine=LmmvEngine; + break; + case CM_LMMM: + VdpEngine=LmmmEngine; + break; + case CM_LMCM: + VdpEngine=LmcmEngine; + break; + case CM_LMMC: + VdpEngine=LmmcEngine; + break; + case CM_HMMV: + VdpEngine=HmmvEngine; + break; + case CM_HMMM: + VdpEngine=HmmmEngine; + break; + case CM_YMMM: + VdpEngine=YmmmEngine; + break; + case CM_HMMC: + VdpEngine=HmmcEngine; + break; + default: + if(Verbose&0x02) printf("V9938: Unrecognized opcode %02Xh\n",Op); + return(0); + } + + /* Fetch unconditional arguments */ + MMC.SX = (VDP[32]+((int)VDP[33]<<8)) & 511; + MMC.SY = (VDP[34]+((int)VDP[35]<<8)) & 1023; + MMC.DX = (VDP[36]+((int)VDP[37]<<8)) & 511; + MMC.DY = (VDP[38]+((int)VDP[39]<<8)) & 1023; + MMC.NY = (VDP[42]+((int)VDP[43]<<8)) & 1023; + MMC.TY = VDP[45]&0x08? -1:1; + MMC.MX = PPL[SM]; + MMC.CL = VDP[44]; + MMC.LO = Op&0x0F; + + /* Argument depends on byte or dot operation */ + if ((MMC.CM & 0x0C) == 0x0C) { + MMC.TX = VDP[45]&0x04? -PPB[SM]:PPB[SM]; + MMC.NX = ((VDP[40]+((int)VDP[41]<<8)) & 1023)/PPB[SM]; + } + else { + MMC.TX = VDP[45]&0x04? -1:1; + MMC.NX = (VDP[40]+((int)VDP[41]<<8)) & 1023; + } + + /* X loop variables are treated specially for LINE command */ + if (MMC.CM == CM_LINE) { + MMC.ASX=((MMC.NX-1)>>1); + MMC.ADX=0; + } + else { + MMC.ASX = MMC.SX; + MMC.ADX = MMC.DX; + } + + /* NX loop variable is treated specially for SRCH command */ + if (MMC.CM == CM_SRCH) + MMC.ANX=(VDP[45]&0x02)!=0; /* Do we look for "==" or "!="? */ + else + MMC.ANX = MMC.NX; + + /* Command execution started */ + VDPStatus[2]|=0x01; + + /* Start execution if we still have time slices */ + if(VdpEngine&&(VdpOpsCnt>0)) VdpEngine(); + + /* Operation successfull initiated */ + return(1); +} + +/** LoopVDP() ************************************************/ +/** Run X steps of active VDP command **/ +/*************************************************************/ +void LoopVDP(void) +{ + if(VdpOpsCnt<=0) + { + VdpOpsCnt+=12500; + if(VdpEngine&&(VdpOpsCnt>0)) VdpEngine(); + } + else + { + VdpOpsCnt=12500; + if(VdpEngine) VdpEngine(); + } +} + diff --git a/components/msx/fmsx/src/fMSX/V9938.h b/components/msx/fmsx/src/fMSX/V9938.h new file mode 100644 index 0000000..55a6e76 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/V9938.h @@ -0,0 +1,38 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** V9938.h **/ +/** **/ +/** This file contains declarations for V9938 special **/ +/** graphical operations support implemented in V9938.c. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef V9938_H +#define V9938_H + +#include "MSX.h" + +/** VDPWrite() ***********************************************/ +/** Use this function to transfer pixel(s) from CPU to VDP. **/ +/*************************************************************/ +void VDPWrite(register byte V); + +/** VDPRead() ************************************************/ +/** Use this function to transfer pixel(s) from VDP to CPU. **/ +/*************************************************************/ +byte VDPRead(void); + +/** VDPDraw() ************************************************/ +/** Perform a given V9938 graphical operation. **/ +/*************************************************************/ +byte VDPDraw(register byte Op); + +/** LoopVDP() ************************************************/ +/** Perform a number of steps of the active operation **/ +/*************************************************************/ +void LoopVDP(void); + +#endif /* V9938_H */ diff --git a/components/msx/fmsx/src/fMSX/Wide.h b/components/msx/fmsx/src/fMSX/Wide.h new file mode 100644 index 0000000..2a40f09 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/Wide.h @@ -0,0 +1,179 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** Wide.h **/ +/** **/ +/** This file contains wide (512pix) screen refresh drivers **/ +/** common for X11, VGA, and other "chunky" bitmapped video **/ +/** implementations. It also includes dummy sound drivers **/ +/** for fMSX. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ +#ifndef NARROW + +/** ClearLine512() *******************************************/ +/** Clear 512 pixels from P with color C. **/ +/*************************************************************/ +static void ClearLine512(register pixel *P,register pixel C) +{ + register int J; + + for(J=0;J<512;J++) P[J]=C; +} + +/** RefreshBorder512() ***************************************/ +/** This function is called from RefreshLine#() to refresh **/ +/** the screen border. It returns a pointer to the start of **/ +/** scanline Y in XBuf or 0 if scanline is beyond XBuf. **/ +/*************************************************************/ +pixel *RefreshBorder512(register byte Y,register pixel C) +{ + register pixel *P; + register int H; + + /* First line number in the buffer */ + if(!Y) FirstLine=(ScanLines212? 8:18)+VAdjust; + + /* Return 0 if we've run out of the screen buffer due to overscan */ + if(Y+FirstLine>=HEIGHT) return(0); + + /* Set up the transparent color */ + XPal[0]=(!BGColor||SolidColor0)? XPal0:XPal[BGColor]; + + /* Start of the buffer */ + P=(pixel *)WBuf; + + /* Paint top of the screen */ + if(!Y) for(H=2*WIDTH*FirstLine-1;H>=0;H--) P[H]=C; + + /* Start of the line */ + P+=2*WIDTH*(FirstLine+Y); + + /* Paint left/right borders */ + for(H=(WIDTH-256)+2*HAdjust;H>0;H--) P[H-1]=C; + for(H=(WIDTH-256)-2*HAdjust;H>0;H--) P[2*WIDTH-H]=C; + + /* Paint bottom of the screen */ + H=ScanLines212? 212:192; + if(Y==H-1) for(H=2*WIDTH*(HEIGHT-H-FirstLine+1)-2;H>=2*WIDTH;H--) P[H]=C; + + /* Return pointer to the scanline in XBuf */ + return(P+WIDTH-256+2*HAdjust); +} + +/** RefreshScr6() ********************************************/ +/** Function to be called to update SCREEN 6. **/ +/*************************************************************/ +void RefreshLine6(register byte Y) +{ + register pixel *P; + register byte X,*T,*R,C; + byte ZBuf[304]; + + P=RefreshBorder512(Y,XPal[BGColor&0x03]); + if(!P) return; + + if(!ScreenON) ClearLine512(P,XPal[BGColor&0x03]); + else + { + ColorSprites(Y,ZBuf); + R=ZBuf+32; + T=ChrTab+(((int)(Y+VScroll)<<7)&ChrTabM&0x7FFF); + + for(X=0;X<64;X++) + { + C=R[0];P[0]=XPal[C? C:T[0]>>6]; + C=R[0];P[1]=XPal[C? C:(T[0]>>4)&0x03]; + C=R[1];P[2]=XPal[C? C:(T[0]>>2)&0x03]; + C=R[1];P[3]=XPal[C? C:T[0]&0x03]; + C=R[2];P[4]=XPal[C? C:T[1]>>6]; + C=R[2];P[5]=XPal[C? C:(T[1]>>4)&0x03]; + C=R[3];P[6]=XPal[C? C:(T[1]>>2)&0x03]; + C=R[3];P[7]=XPal[C? C:T[1]&0x03]; + R+=4;P+=8;T+=2; + } + } +} + +/** RefreshScr7() ********************************************/ +/** Function to be called to update SCREEN 7. **/ +/*************************************************************/ +void RefreshLine7(register byte Y) +{ + register pixel *P; + register byte C,X,*T,*R; + byte ZBuf[304]; + + P=RefreshBorder512(Y,XPal[BGColor]); + if(!P) return; + + if(!ScreenON) ClearLine512(P,XPal[BGColor]); + else + { + ColorSprites(Y,ZBuf); + R=ZBuf+32; + T=ChrTab+(((int)(Y+VScroll)<<8)&ChrTabM&0xFFFF); + + for(X=0;X<64;X++) + { + C=R[0];P[0]=XPal[C? C:T[0]>>4]; + C=R[0];P[1]=XPal[C? C:T[0]&0x0F]; + C=R[1];P[2]=XPal[C? C:T[1]>>4]; + C=R[1];P[3]=XPal[C? C:T[1]&0x0F]; + C=R[2];P[4]=XPal[C? C:T[2]>>4]; + C=R[2];P[5]=XPal[C? C:T[2]&0x0F]; + C=R[3];P[6]=XPal[C? C:T[3]>>4]; + C=R[3];P[7]=XPal[C? C:T[3]&0x0F]; + R+=4;P+=8;T+=4; + } + } +} + +/** RefreshTx80() ********************************************/ +/** Function to be called to update TEXT80. **/ +/*************************************************************/ +void RefreshLineTx80(register byte Y) +{ + register pixel *P,FC,BC; + register byte X,M,*T,*C,*G; + + BC=XPal[BGColor]; + P=RefreshBorder512(Y,BC); + if(!P) return; + + if(!ScreenON) ClearLine512(P,BC); + else + { + G=(FontBuf&&(Mode&MSX_FIXEDFONT)? FontBuf:ChrGen)+(Y&0x07); + T=ChrTab+((80*(Y>>3))&ChrTabM); + C=ColTab+((10*(Y>>3))&ColTabM); + + P[0]=P[1]=P[2]=P[3]=P[4]=P[5]=P[6]=P[7]=P[8]= + P[9]=P[10]=P[11]=P[12]=P[13]=P[14]=P[15]= + P[16]=P[17]=XPal[BGColor]; + P+=18; + + for(X=M=0;X<80;X++,T++,P+=6) + { + if(!(X&0x07)) M=*C++; + if(M&0x80) { FC=XPal[XFGColor];BC=XPal[XBGColor]; } + else { FC=XPal[FGColor];BC=XPal[BGColor]; } + M<<=1; + Y=*(G+((int)*T<<3)); + P[0]=Y&0x80? FC:BC; + P[1]=Y&0x40? FC:BC; + P[2]=Y&0x20? FC:BC; + P[3]=Y&0x10? FC:BC; + P[4]=Y&0x08? FC:BC; + P[5]=Y&0x04? FC:BC; + } + + P[0]=P[1]=P[2]=P[3]=P[4]=P[5]=P[6]=P[7]=P[8]= + P[9]=P[10]=P[11]=P[12]=P[13]=XPal[BGColor]; + } +} + +#endif /* !NARROW */ diff --git a/components/msx/fmsx/src/fMSX/fMSX.c b/components/msx/fmsx/src/fMSX/fMSX.c new file mode 100644 index 0000000..b7e8078 --- /dev/null +++ b/components/msx/fmsx/src/fMSX/fMSX.c @@ -0,0 +1,273 @@ +/** fMSX: portable MSX emulator ******************************/ +/** **/ +/** fMSX.c **/ +/** **/ +/** This file contains generic main() procedure statrting **/ +/** the emulation. **/ +/** **/ +/** Copyright (C) Marat Fayzullin 1994-2021 **/ +/** You are not allowed to distribute this software **/ +/** commercially. Please, notify me, if you make any **/ +/** changes to this file. **/ +/*************************************************************/ + +#include "MSX.h" +#include "Help.h" +#include "EMULib.h" + +#include +#include +#include +#include + +static const char *Options[]= +{ + "verbose","skip","pal","ntsc","help", + "printer","serial","diska","diskb","tape","font","logsnd","state", + "ram","vram","rom","auto","noauto","msx1","msx2","msx2+","joy", + "home","simbdos","wd1793","sound","nosound","trap","sync","nosync", + "scale","static","nostatic","vsync","480","200", + 0 +}; + +extern const char *Title;/* Program title */ +extern int UseSound; /* Sound mode */ +extern int UseZoom; /* Zoom factor (#ifdef UNIX) */ +extern int UseEffects; /* EFF_* bits, ORed (UNIX/MAEMO/MSDOS) */ +extern int UseStatic; /* Use static colors (#ifdef MSDOS) */ +extern int FullScreen; /* Use 640x480 screen (#ifdef MSDOS) */ +extern int SyncFreq; /* Sync scr updates (UNIX/MAEMO/MSDOS) */ +extern int ARGC; /* argc/argv from main (#ifdef UNIX) */ +extern char **ARGV; + +/** Zero-terminated arrays of disk names for each drive ******/ +extern const char *Disks[2][MAXDISKS+1]; + +/** main() ***************************************************/ +/** This is a main() function used in Unix and MSDOS ports. **/ +/** It parses command line arguments, sets emulation **/ +/** parameters, and passes control to the emulation itself. **/ +/*************************************************************/ +int main(int argc,char *argv[]) +{ + int CartCount,TypeCount; + int JoyCount,DiskCount[2]; + char *P; + int N,J; + +#ifdef DEBUG + CPU->Trap = 0xFFFF; + CPU->Trace = 0; +#endif + +#if defined(UNIX) || defined(MAEMO) + ARGC = argc; + ARGV = argv; +#endif + +#if defined(MSDOS) || defined(WINDOWS) + /* No separate console, so no messages */ + Verbose=0; + /* Figure out program directory name */ + ProgDir=GetFilePath(argv[0]); +#else + Verbose=1; +#endif + + /* Clear everything */ + CartCount=TypeCount=JoyCount=0; + DiskCount[0]=DiskCount[1]=0; + + /* Default disk images */ + Disks[0][1]=Disks[1][1]=0; + Disks[0][0]=DSKName[0]; + Disks[1][0]=DSKName[1]; + +#if defined(UNIX) || defined(MAEMO) || defined(MSDOS) + /* Extract visual effects arguments */ + UseEffects = ParseEffects(argv,UseEffects); +#endif + + for(N=1;(N=argc) + printf("%s: No skipped frames percentage supplied\n",argv[0]); + else + { + J=atoi(argv[N]); + if((J>=0)&&(J<=99)) UPeriod=100-J; + } + break; + case 2: Mode=(Mode&~MSX_VIDEO)|MSX_PAL;break; + case 3: Mode=(Mode&~MSX_VIDEO)|MSX_NTSC;break; + case 4: printf + ("%s by Marat Fayzullin (C)1994-2021\n",Title); + for(J=0;HelpText[J];J++) puts(HelpText[J]); + return(0); + case 5: N++; + if(N=argc) + printf("%s: No file for drive A\n",argv[0]); + else + if(DiskCount[0]>=MAXDISKS) + printf("%s: Too many disks for drive A\n",argv[0]); + else + Disks[0][DiskCount[0]++]=argv[N]; + break; + case 8: N++; + if(N>=argc) + printf("%s: No file for drive B\n",argv[0]); + else + if(DiskCount[1]>=MAXDISKS) + printf("%s: Too many disks for drive B\n",argv[0]); + else + Disks[1][DiskCount[1]++]=argv[N]; + break; + case 9: N++; + if(N=argc) + printf("%s: No number of RAM pages supplied\n",argv[0]); + else + RAMPages=atoi(argv[N]); + break; + case 14: N++; + if(N>=argc) + printf("%s: No number of VRAM pages supplied\n",argv[0]); + else + VRAMPages=atoi(argv[N]); + break; + case 15: N++; + if(N>=argc) + printf("%s: No ROM mapper type supplied\n",argv[0]); + else + if(TypeCount>=MAXCARTS) + printf("%s: Excessive -rom option\n",argv[0]); + else + { + J=atoi(argv[N]); + if(J>=MAP_GUESS) Mode|=(MSX_GUESSA<=argc) + printf("%s: No joystick type supplied\n",argv[0]); + else + switch(JoyCount++) + { + case 0: SETJOYTYPE(0,atoi(argv[N])&0x03);break; + case 1: SETJOYTYPE(1,atoi(argv[N])&0x03);break; + default: printf("%s: Excessive -joy option\n",argv[0]); + } + break; + case 22: N++; + if(N>=argc) + printf("%s: No home directory name supplied\n",argv[0]); + else + ProgDir=argv[N]; + break; + case 23: Mode|=MSX_PATCHBDOS;break; + case 24: Mode&=~MSX_PATCHBDOS;break; + case 25: N++; + if(N>=argc) { UseSound=1;N--; } + else if(sscanf(argv[N],"%d",&UseSound)!=1) + { UseSound=1;N--; } + break; + case 26: UseSound=0;break; + +#if defined(DEBUG) + case 27: N++; + if(N>=argc) + printf("%s: No trap address supplied\n",argv[0]); + else + if(!strcmp(argv[N],"now")) CPU->Trace=1; + else sscanf(argv[N],"%hX",&(CPU->Trap)); + break; +#endif /* DEBUG */ + +#if defined(MSDOS) || defined(UNIX) || defined(MAEMO) + case 28: N++; + if(N + +fMSX Documentation + + + + +
+

+fMSX +
+version 6.0 +

+

+MSX Home Computer Emulator +
+by Marat Fayzullin +

+
+

+ +

Contents

+ +
+ + +

New in This Version

+
+
    +
  • Refactored Windows EMULib code. +
  • Most command line options should now work in Windows version. +
  • Added "Hardware | Paste Clipboard" option to fMSX-Windows. +
  • Added "Video | Show Framerate" menu option to fMSX-Windows. +
  • Increased synchronization timer precision in Windows version. +
  • Fixed clearing file associations in fMSX-Windows. +
+ + +

Introduction

+
+ +

+fMSX is a program that emulates MSX, MSX2, and MSX2+ home +computers. It runs the majority of MSX software and mimics most popular +hardware extensions, such as SCC, OPLL, etc. You can always get the latest +fMSX source code, binaries, and support files from +

+ +

+http://fms.komkon.org/fMSX/ +

+ +

+fMSX has a very long history for a piece of software. It has +been in continuous development since 1993, when I wrote the first version +to run on Unix-based DEC Alpha workstations. Because fMSX is written in C, +it is a very portable program. It can run on any sufficiently fast 32bit +hardware platform, be it personal computer, PDA, videogame console, cell +phone, set-top box, or a DVD player. There are fMSX versions for Unix, +Windows, Macintosh, Symbian, MSDOS, Amiga, and many other platforms. +

+ + + +
+

+fMSX source code is open for everyone to see but it is not in public +domain. You can look and learn from it, but you cannot change +it or copy it without giving a credit to the original author and a few other +conditions. If you would like to port fMSX to another platform or make +changes to the code, please, contact me by email or some other means. +

+

+You cannot use fMSX source code for commercial purposes +unless you contact me to arrange the conditions of such usage. If your +company intends to use MSX software in its products and you are +considering using fMSX source code, please, email me about licensing. +

+
+ +

+MSX is an old 8bit family of home computers created in +1982 as an attempt to establish a single standard in home computing similar +to VHS in video. MSX computers have been popular in Asia (Korea, Japan) and +South America (Brazil, Chile) as well as in Europe (Netherlands, France, +Spain) and former Soviet Union, although they are virtually unknown in the +USA. Although the MSX platform quietly died around 1988, the world got to +see MSX2, MSX2+, and TurboR extensions of the MSX platform. +

+ +

+The MSX has been mainly designed by a Japanese company called +ASCII in cooperation with +Microsoft, who provided the +firmware BASIC for the machine. There is a widespread rumor that "MSX" +stands for "MicroSoft eXtended". MSX machines were produced by such giants +as Sony, Yamaha, Panasonic, Toshiba, Daewoo, and Philips. The only MSX +model ever sold in the US appears to be an early SpectraVideo machine +though. +

+ +

+In spite of its short history, MSX is a very nice computer, especially +good for education, as seen in an example of the Soviet Union. Soviet +Ministry of Education bought hundreds of MSXes (and later MSX2s) grouped +into "computerized classroom systems" of 10-16 machines connected with +a simple network. A whole generation of programmers has grown up using +these computers. +

+ +

+Hardware-wise, MSX represents a hybride of a videogame console and a +generic CP/M-80 machine. Its heart is a Z80 CPU running at 3.58MHz in the +base model. The clock frequency has been doubled in the TurboR. The video +subsystem is built around a TI9918 or TI9928 VDP chip also used in Texas +Instruments' TI-99/4 computers, ColecoVision, and Coleco Adam. In the +late MSX models, this chip has been upgraded to V9938 (MSX2) and then to +V9958 (MSX2+ and TurboR). The latest version of this chip is known as V9990. +The audio system is handled by an AY-3-8910 chip from General Instruments, +same as the one used in Sinclair ZX Spectrum 128. AY-3-8910 provides three +channels of melodic sound, a noise channel, volume envelopes, and two +general purpose parallel IO ports, which MSX uses for joysticks and a few +other things. Due to their hardware architecture, MSX machines were +perfectly suitable for games and there was a lot of good games either +written for or ported to them. +

+ + +

fMSX Ports

+
+

+Because fMSX is a very portable program, it can run on many different +platforms: Unix, Macintosh, MSDOS, Windows, Symbian, PocketPC, Amiga, etc. +The complete up-to-date list of fMSX ports is available at the fMSX +distribution site. Following +are the major ports: +

+
+

fMSX-Windows

+
+

+Starting December 2013, fMSX-Windows is free for everyone to use, +in binary form. You can download it from the fMSX +distribution site. +Also see +my other emulators for Windows. +

+

fMSX-Android

+
+

+fMSX-Android is available from the +Google Play Store. +You can download the +full version +or the feature-limited +free demo. +Also see +my other emulators for Android. +

+

fMSX-Unix

+
+

+ fMSX-Unix is available freely in the source code form from the +fMSX distribution site. +

+

fMSX-Linux

+
+

+ fMSX-Linux (ELF, GLIBC, X11) is compiled from the same source +code as all other Unix versions. You can get it for free from the fMSX +distribution site. +

+
+ + +

Registered Users

+

+ If you've registered fMSX-Windows, please do not give your copy to +anybody. And I do mean anybody. There were cases when registered +users gave fMSX away to their friends, relatives, or just some shady +characters on the Net, and then I found it pirated, put onto the Web, and +even posted to USENET newsgroups. If I find your personalized copy +of fMSX being spread around, your registration gets automatically cancelled +which means no support and no more updates. +

+

+ I understand that the previous paragraph may sound threatening to some +people, but this kind of piracy really hurts my profits and feelings. I've +put a lot of effort into fMSX, and can only continue working on it if +people do not try to cheat on me. +

+ +
+

Keyboard Assignments

+
+ +
+  [CONTROL]       - CONTROL (also: joystick FIRE-A button)
+  [SHIFT]         - SHIFT (also: joystick FIRE-B button)
+  [ALT]           - GRAPH (also: swap joysticks)
+  [INSERT]        - INSERT
+  [DELETE]        - DELETE
+  [HOME]          - HOME/CLS
+  [END]           - SELECT
+  [PGUP]          - STOP/BREAK
+  [PGDOWN]        - COUNTRY
+  [F6]            - Load emulation state from .STA file
+  [F7]            - Save emulation state to .STA file
+  [F8]            - Rewind emulation back in time
+  [F9]            - Fast-forward emulation
+  [F10]           - Invoke built-in configuration menu
+  [F11]           - Reset hardware
+  [F12]           - Quit emulation
+  [CONTROL]+[F8]  - Toggle scanlines on/off
+  [ALT]+[F8]      - Toggle screen softening on/off
+  [CONTROL]+[F10] - Go to the built-in debugger
+
+
+ + +

Command Line Options

+
+ +
+Usage: fmsx [-option1 [-option2...]] [filename1] [filename2]
+
+  [filename1] = name of file to load as cartridge A
+  [filename2] = name of file to load as cartridge B
+
+  When compiled with #define ZLIB, fMSX will transparently
+  uncompress singular GZIPped and PKZIPped files.
+
+  [-option] =
+  -verbose <level>    - Select debugging messages [1]
+                        0 - Silent       1 - Startup messages
+                        2 - V9938 ops    4 - Disk/Tape
+                        8 - Memory      16 - Illegal Z80 ops 
+  -skip <percent>     - Percentage of frames to skip [25]
+  -pal/-ntsc          - Set PAL/NTSC HBlank/VBlank periods [NTSC]
+  -help               - Print this help page
+  -home <dirname>     - Set directory with system ROM files [off]
+  -printer <filename> - Redirect printer output to file [stdout]
+  -serial <filename>  - Redirect serial I/O to a file [stdin/stdout]
+  -diska <filename>   - Set disk image used for drive A: [DRIVEA.DSK]
+                        (multiple -diska options accepted)
+  -diskb <filename>   - Set disk image used for drive B: [DRIVEB.DSK]
+                        (multiple -diskb options accepted)
+  -tape <filename>    - Set tape image file [off]
+  -font <filename>    - Set fixed font for text modes [DEFAULT.FNT]
+  -logsnd <filename>  - Set soundtrack log file [LOG.MID]
+  -state <filename>   - Set emulation state save file [automatic]
+  -auto/-noauto             - Use autofire on SPACE [off]
+  -ram <pages>        - Number of 16kB RAM pages [4/8/8]
+  -vram <pages>       - Number of 16kB VRAM pages [2/8/8]
+  -rom <type>         - Select MegaROM mapper types [8,8]
+                        (two -rom options accepted)
+                        0 - Generic 8kB   1 - Generic 16kB (MSXDOS2)
+                        2 - Konami5 8kB   3 - Konami4 8kB
+                        4 - ASCII 8kB     5 - ASCII 16kB
+                        6 - GameMaster2   7 - FMPAC
+                        >7 - try guessing mapper type
+  -msx1/-msx2/-msx2+  - Select MSX model [-msx2]
+  -joy <type>         - Select joystick types [0,0]
+                        (two -joy options accepted)
+                        0 - No joystick
+                        1 - Normal joystick
+                        2 - Mouse in joystick mode
+                        3 - Mouse in real mode
+  -simbdos/-wd1793    - Simulate DiskROM disk access calls [-wd1793]
+  -sound [<quality>]  - Sound emulation quality (Hz) [44100]
+  -nosound            - Same as '-sound 0'
+  -sync <frequency>   - Sync screen updates to <frequency> [60]
+  -nosync             - Do not sync screen updates [-nosync]
+  -static/-nostatic   - Use static color palette [-nostatic]
+  -tv/-lcd/-raster    - Simulate TV scanlines or LCD raster [off]
+  -linear             - Scale display with linear interpolation [off]
+  -soft/-eagle        - Scale display with 2xSaI or EAGLE [off]
+  -epx/-scale2x       - Scale display with EPX or Scale2X [off]
+  -cmy/-rgb           - Simulate CMY/RGB pixel raster [off]
+  -mono/-sepia        - Simulate monochrome or sepia CRT [off]
+  -green/-amber       - Simulate green or amber CRT [off]
+  -4x3                - Force 4:3 television screen ratio [off]
+
+  With #define DEBUG:
+  -trap <address>     - Trap execution when PC reaches address [FFFFh]
+                        (when keyword 'now' is used in place of the
+                        <address>, execution will trap immediately)
+
+  With #define MITSHM:
+  -shm/-noshm         - Use MIT SHM extensions for X [-shm]
+
+  With #define UNIX:
+  -saver/-nosaver     - Save/don't save CPU when inactive [-saver]
+  -scale <factor>     - Scale window by <factor> [2]
+
+  With #define MSDOS:
+  -vsync              - Sync screen updates to VBlank [-vsync]
+  -480/-200           - Use 640x480 or 320x200 VGA mode [-200]
+
+
+ + +

Frequently Asked Questions

+
+
    +
  1. Where do I get MSX software? +

    + Go to http://fms.komkon.org/MSX/ +and follow links from there. +

    +
  2. What do I do with .BAS,.GMB,.CRC,.LDR files? +

    + These are BASIC programs. You run them from MSX BASIC with +

    +

    RUN "filename"

    +

    +

  3. What do I do with .BIN,.OBJ,.GM files? +

    + These are binary files with programs, also known as BLOADable files. You +can run them from MSX BASIC with +

    +

    BLOAD "filename",R

    +

    +

  4. What do I do with .COM files? +

    + These are MSXDOS command files. You can run them from MSXDOS by typing +their names sans the .COM extension. +

    +
  5. What are the .ROM files? +

    + These are binary images of cartridge ROMs that you can load into fMSX. +There are "small" cartridge ROMs of 8kB, 16kB, or 32kB, and the MegaROMs, +which can be 128kB, 256kB and even 512kB. +

    +
  6. What are the .ROM files included with fMSX? +

    + Following .ROM files may not be normal cartridges, but they are used by +fMSX: + +

    +MSX.ROM      - Standard MSX BIOS and BASIC code
    +MSX2.ROM     - MSX2 BIOS and BASIC code
    +MSX2EXT.ROM  - MSX2 ExtROM containing system extensions
    +MSX2P.ROM    - MSX2+ BIOS and BASIC code
    +MSX2PEXT.ROM - MSX2+ ExtROM containing system extensions
    +DISK.ROM     - MSX DiskROM containing BDOS and Disk BASIC (optional)
    +RS232.ROM    - RS232 BIOS and BASIC extensions (optional)
    +FMPAC.ROM    - FM-PAC BIOS and BASIC extensions (optional)
    +MSXDOS2.ROM  - MSXDOS2 system core (optional)
    +PAINTER.ROM  - Yamaha Painter, graphical editor found in Russian MSX
    +               machines from Yamaha (optional)
    +GMASTER.ROM  - Konami GameMaster, a game cheating tool (optional).
    +GMASTER2.ROM - Konami GameMaster2, a game cheating tool (optional).
    +KANJI.ROM    - ROM with Kanji character images (optional)
    +CMOS.ROM     - Non-volatile memory used in MSX2 and MSX2+. This file gets 
    +               overwritten on exit if non-volatile memory has been changed.
    +
    + +Please note that not all of these may be included with your fMSX copy. +

    +
  7. How do I use disks with fMSX? +

    + fMSX includes disk support starting with version 0.9. If you have an +earlier version, you cannot use disks. The following instructions assume +that you have a version supporting disks. +

    +

    + First, make sure that the DISK.ROM file containing MSX DiskROM is in +the current directory. Then, use an MSDOS program called DCOPY.EXE to +create disk images of your MSX disks: +

    +

    DCOPY <drive>: <filename>.DSK

    +

    + These images are just raw files with all disk blocks written in a +sequence. They can also be created on a Unix machine with +

    +

    cp /dev/rfd0 <filename>.DSK

    +

    +or a similar command. If you have a 1.44MB HD floppy formatted on MSX for +720kB, don't forget to stick a piece of tape on the HD/DD indicator hole. +

    +

    + After you have created disk image files, run fMSX in the following way: +

    +

    +fmsx -diska <filename1>.DSK -diskb <filename2>.DSK +

    +

    +where two image filenames will become your drives A: and B:. You can also +have default disk images called DRIVEA.DSK and DRIVEB.DSK and located in +the current directory. +

    +
  8. Is there an easier way to work with disk images? +

    + In fact, there is. Look at two programs that come with fMSX, called +wrdsk and rddsk. The wrdsk program +allows you to create a disk image and add files to it: +

    +

    +wrdsk <filename>.DSK <file> <file> ... +

    +

    + The rddsk program will read files from a given disk image: +

    +

    + +rddsk <filename>.DSK [-d <dir>] [<file> <file> ...] + +

    +
  9. I can't get some BASIC programs to work with the emulator. +

    + Many MSX programs have BASIC loaders written for machines not quite +compatible with each other. Other loaders expect the machine to have +only one floppy disk drive and use the memory dedicated to the second +drive. To make such programs work, perform two "magic passes" on fMSX +before running a loader: +

      +
    • When booting MSX, press [CTRL]+[DEL] keys to switch off + the second disk drive. +
    • Before running a program, type POKE &hFFFF,&hAA + to set MSX memory manager into the mode expected by most loaders. +
    +

    +
  10. I can't get some ROM images to work with the emulator. +

    + If the ROM image is bigger than 32kB, try using -rom <N> +parameter with different <N>s (see fmsx -help +for a complete list). If you still can't get ROM image to work, send it to me +for analysis. +

    +
  11. Is it legal to spread cartridge ROMs? +

    + NO. Nobody seems to care though, mainly because there is no profit +to be made from MSX software any longer. Nevertheless, be aware of the fact +that by using commercial software you haven't bought you are commencing an +act of piracy. +

    +
  12. When compiling fMSX-Unix, I get "undefined name" errors. +

    + This means that your linker cannot find the libraries necessary for the +emulator (namely, libX11.a and libXext.a) or some additional libraries +(like libsocket.a and libnsl.a) are required. Find these libraries in your +system and modify the Makefile so that the final invocation of the C +compiler has -L<path_to_libs> options. If you have no +libXext.a library, try #undefining MITSHM option. +

    +
  13. When starting fMSX-Unix, I get X_ShmAttach error. +

    + You are probably trying to run the emulator on a remote Xterminal while +it attempts to use shared memory for interfacing with X. Use the +-noshm option to disable the shared memory usage. +

    +
  14. The emulation starts under Unix, but then I get X_PutImage error. +

    + Unix/X version of fMSX can currently be compiled for 8bit, 16bit, or +32bit Xterminals. Neither 1bit nor 4bit Xterminals will work with the +drivers included into official fMSX distribution. Arnold Metselaar +developed the drivers supporting any Xterminal. These drivers are +available from the fMSX +distribution site. +

    +
+ + +

History

+
+

New in fMSX 5.9

+
    +
  • Added simple CPU-based linear scaling algorithm. +
  • Added -linear command line option. +
  • Added drag'n'drop files functionality to fMSX-Windows. +
  • Fixed fMSX-Windows loading disk and tape files on click. +
+

New in fMSX 5.8

+
    +
  • Will try opening tapes as read-only, if read-and-append fails. +
  • System ROM files location now defaults to where executable is. +
  • Fixed SHA1 computation when guessing MegaROM mapper type. +
  • ASCII16 mapper now preferred over ASCII8 when guess MegaROM type. +
  • Changed R-Type mapper type to ASCII16. +
  • Changed DragonQuest 1 mapper type to GENERIC8. +
  • Changed Animal mapper type to GENERIC8. +
  • Changed T&E Soft Ashguine mapper type to GENERIC8. +
  • Changed Royal Blood mapper type to ASCII8. +
  • Refactored scaling and special effects framework. +
  • Optimized scaling for large screens and windows. +
  • Fixed fMSX-Windows behavior with multiple displays. +
  • Added tape option to "File | Open" dialog in fMSX-Windows. +
  • Added "Video | Interpolate Video | Linear Scaling" option to fMSX-Windows. +
  • Enabled "Video | Stretch Full Screen" option with effects enabled. +
  • Enabled "Video | Force 4:3 Screen" option with effects enabled. +
+

New in fMSX 5.7

+
    +
  • Fixed noise in Eggerland Mystery and other games. +
  • Moved PSG sound computation to Sync8910(). +
  • Fixed several issues in MIDI recording. +
  • Fixed compatibility with 64bit CPU architectures. +
  • Added "File | Clear Settings and Quit" option to fMSX-Windows. +
  • Fixed small windows behavior in fMSX-Windows. +
  • Fixed window position behavior in fMSX-Windows. +
  • Saving physical joystick/gamepad selection in fMSX-Windows. +
+

New in fMSX 5.3

+
    +
  • Made replay save states approximately every 170ms. +
  • Added ability to browse through paused replay. +
  • During replay, press [UP] to pause or resume. +
  • When replay paused, press [LEFT] and [RIGHT] to browse. +
  • Press any other button to continue playing. +
  • Now cancelling replay if any key or button is pressed. +
  • Made WaitJoystick() exit when window closed. +
  • +

    New in fMSX 5.3

    +
      +
    • Added remaining time display during replay. +
    • Stopping replay when emulation reset or state loaded. +
    • Fixed replay rollover mechanism. +
    • Added shadow underneath the time display. +
    • Compiled fMSX-Unix with -Wall and eliminated warnings. +
    • Replaced -DNO_WAVE_INTERPOLATION with -DWAVE_INTERPOLATION, off by default. +
    • Removed old LoadSTA() and SaveSTA() code. +
    • Finally deprecated -DNEW_STATES. +
    +

    New in fMSX 5.2

    +
      +
    • Fixed DirectInput joysticks support in Speccy-Windows. +
    • Made directional pads work on XBox gamepads. +
    +

    New in fMSX 5.1

    +
      +
    • Added multiple monitor support to fMSX-Windows. +
    • Fixed disappearing window in fMSX-Windows. +
    • Fixed best full-screen mode detection in fMSX-Windows. +
    • Fixed window flicker that appeared after recent Win10 updates. +
    • Moved disk and cartridge options to "Hardware" menu in fMSX-Windows. +
    • Switched fMSX-Linux to using PulseAudio sound. +
    • Updated old PulseAudio driver for 64bit Linux. +
    +

    New in fMSX 5.0

    +
      +
    • Switched to microseconds in the AY8910 PSG emulation. +
    • Now updating, rendering, and playing sound every 8 scanlines. +
    • The PLAY "S8M90O6G" MML test works now. +
    • Added support for DirectInput joysticks to fMSX-Windows. +
    • Fixed crash when changing audio sampling rate in fMSX-Windows. +
    +

    New in fMSX 4.9

    +
      +
    • Moved input settings to the "Input" menu in fMSX-Windows. +
    • Added "Draw 65% Frames" option to fMSX-Windows. +
    • Fixed screen updates after changing scaling algorithm in fMSX-Windows. +
    • Fixed "Force 4:3 Screen" option in TEXT80 screen mode. +
    • Fixed "Load MSXDOS2 ROM" option in fMSX-Windows. +
    • Fixed "Hit MIDI Drums" option in fMSX-Windows. +
    • Now computing MIDI volume by waveform analysis. +
    • Corrected MIDI drums volume in AY8910 emulation. +
    • Fixed MIDI keyboard click in fMSX-Windows. +
    +

    New in fMSX 4.8

    +
      +
    • Fixed loading of BASIC-only cartridges (Danger X4, Crazy Bullet, etc). +
    • Added a hack to ignore bad writes to ASCII16 MegaROM mapper (Vaxol). +
    • Added "Interpolate Video | Nearest Neighbor" option to fMSX-Windows. +
    • fMSX-Windows will not overwrite last loaded file name with other names. +
    • Added "Force 4:3 Screen" option to fMSX-Windows. +
    • Added -4x3 command line option to fMSX-Unix. +
    • Fixed file associations on Windows 8+. +
    • Slightly rearranged Windows menus. +
    +

    New in fMSX 4.7

    +
      +
    • Added Scale2X scaling algorithm. +
    • Added Monochrome, Green, Amber, and Sepia CRT emulation. +
    • Added -scale2x and -raster command line options. +
    • Added -mono, -sepia, -green, + and -amber command line options. +
    • EMULib-specific command line options now processed inside EMULib. +
    • Added "Scale2x Algorithm" and "LCD Raster" options on Windows. +
    • Renamed "Color Raster" menu to "Color Filter" on Windows. +
    • Deprecated -notv and -nolcd command line options. +
    +

    New in fMSX 4.6

    +
      +
    • Enabled magnified sprites (Jawbreaker, Stray Cat, F-Zeru, Triumph). +
    • Fixed sprite collision detection, especially in BASIC games. +
    • Added collision detection even on transparent sprites. +
    • Now stopping collision detection when Y=216 in SCREEN 4-8. +
    • Now setting lower VDPStatus bits to the last tested sprite. +
    +

    New in fMSX 4.5

    +
      +
    • Changed VDP VRAM access logic to comply with TMS9918 datasheet. +
    +

    New in fMSX 4.4

    +
      +
    • Added special effects simulating individual pixel components. +
    • Added EPX and EAGLE image scaling algorithms. +
    • Merged scanline options into "Simulate Scanlines" in fMSX-Windows. +
    • Merged scaling options into "Interpolate Video" in fMSX-Windows. +
    • Added "Clear File Associations" option to fMSX-Windows. +
    • Added "Play Sound When Inactive" option to fMSX-Windows. +
    • Added "Apply Color Raster" submenu to fMSX-Windows. +
    • Added -rgb and -cmy command line options. +
    • Added -epx and -eagle command line options. +
    +

    New in fMSX 4.3

    +
      +
    • Added instant replay function (press [F8]) that "rewinds" gameplay. +
    • Switched to the new state saving code. +
    • Built-in menu now available when pressing [F10]. +
    • Built-in debugger now available when pressing [CTRL]+[F10]. +
    • Fixed a memory corruption bug in the instant replay recorder. +
    • Fixed instant replay not recording the first state. +
    +

    New in fMSX 4.2

    +
      +
    • Added Cheat Hunter tool, press F8 and select "Search Cheats". +
    • Removed waveform interpolation for more realistic sound. +
    • Changed PSG noise to be more realistic. +
    • Switched fMSX-Windows to Direct3D textures-based rendering. +
    • Added -lcd and -nolcd command line options. +
    • Added "Video | Stretch Full Screen" option to fMSX-Windows. +
    • Added "Hardware | Debugger" option to fMSX-Windows. +
    • Added "Help | MSX.ORG Site" option to fMSX-Windows. +
    • Added "Help | MSX.ORG Forums" option to fMSX-Windows. +
    +

    New in fMSX 4.0

    +
      +
    • Added custom palettes. To make a custom palette for Game.rom, + create Game.pal containing 16 #RRGGBB values, one + per line. This palette file will be loaded automatically. +
    • Added cheat codes. To make cheat codes for Game.rom, + create Game.cht containing codes in 00AAAAAA-DD and + 00AAAAAA-DDDD formats, one per line. The cheat file will be + loaded automatically. +
    • AAAAAA is the ROM address and DD is the value to write + there. For 16bit values, use DDDD. +
    • To enable cheat codes, select "Hardware | Cheats" in fMSX-Windows, + or press F8 and select "Cheats | Enable". +
    • To edit cheat codes, press F8 and select "Cheats". +
    +

    New in fMSX 3.9

    +
      +
    • Majorly improved AY8910 PSG emulation. +
    • Added support for .MX1/.MX2 ROM files. +
    • Set precise CPU, VDP, and PSG frequences. +
    • Switched to table-based PSG envelopes. +
    • Switched to logarithmic PSG volumes. +
    • Fixed white noise frequency computation. +
    • Adjusted noise to be 50% of the melodic volume. +
    • Adjusted PSG envelopes length. +
    • Fixed recognition of multiple file extensions. +
    +

    New in fMSX 3.8

    +
      +
    • Added support for tape images, supplied in *.CAS files. +
    • Implemented 5th/9th sprite fields in the VDP status register. +
    • No longer clearing VRAM address latch when reading VDP status. +
    • Added tape operations to the built-in menu. +
    • Added "Simulate LCD Scanlines" menu option to fMSX-Windows. +
    • Added "Load Tape Image" menu option to fMSX-Windows. +
    • Added "Rewind Tape" menu option to fMSX-Windows. +
    • Fixed "Documentation" menu option to fMSX-Windows. +
    +

    New in fMSX 3.7

    +
      +
    • fMSX-Windows is now free! +
    • Added accurate R register emulation, many protected boot loaders run now. +
    • Added accurate LDIR/LDDR emulation, more protected boot loaders run now. +
    • Added undocumented opcode 71h to the Z80 emulation. +
    • Added more realistic "LD R,A" opcode to the Z80 emulation. +
    • Added cartridge type lookup by its SHA1 checksum, as listed in the + CARTS.SHA file. +
    • Updated EMULib to a newer, more stable version. +
    • Switched fMSX-Windows version to use Direct3D in full-screen mode. +
    • Added proper Windows joystick support. +
    • Now properly restoring full-screen mode in fMSX-Windows. +
    • Fixed a bug in fMSX-Unix that corrupted memory on most platforms + but, for some reason, not on Ubuntu Linux. +
    • Added a separate Rules.Solaris make rules file. Include + this file into your Makefile instead of + Rules.Unix to compile for Solaris and don't forget about + removing -DLSB_FIRST on SPARC hardware. +
    • Fixed compilation warnings when compiling fMSX with CLANG. +
    +

    New in fMSX 3.5

    +
      +
    • After a long pause, finally updated fMSX-Unix port. + fMSX-Unix source code is once again available as part of the core + source code distribution. It has been debugged on the Ubuntu Linux. + Let me know of any incompatibilities with other Unix flavors. +
    • Extended fMSX-Symbian port to support UIQ3 + phones from Sony Ericsson and Motorola. As I only have key-operated + Motorola Z8, testers with pen-based SE phones are badly needed. +
    • Added run-time compatibility with 8bpp, 16bpp, 24bpp (32bit pixels) + and 32bpp screen depths to fMSX-Unix. IMPORTANT: At this moment, + built-in config menu, debugger, and NetPlay dialog will only work at + X11 screen depth that matches your compile-time setting (i.e. + -DBPPxx) in Makefile. Make sure you always compile + fMSX-Unix for your default screen depth. +
    • Disabled secondary slots for primary slot #0 in MSX1 mode. DiskROM + boots in MSX1 mode again. +
    • Now syncing all sound channels after loading state, sound does not + get "stuck" any more. +
    • Renamed command line option -zoom/-nozoom into + -soft/-nosoft. +
    • Moved a few key definitions, so that "!@#$%^..." characters + can now be used as indices into Keys[]. This is done + for compatibility with GDK key events. +
    • Renamed KeyMap[] to KeyState[] to avoid + name clash with MacOSX system API. +
    • ALT swaps joysticks 1/2 on all platforms where applicable. +
    • CONTROL/SHIFT keys now act as FIRE-A/FIRE-B joystick buttons + on all platforms. +
    • Improved fMSX-Symbian sound code, although sound problems still + persist on some UIQ3 phones. +
    • Split fMSX-Symbian/S60 configuration menu into four tabs, making + "Video" and "Audio" settings separate. +
    • Added "Audio Latency", "Skip Frames", and "Sync Updates" settings + to fMSX-Symbian. +
    • Added to fMSX-Symbian ability to use Nokia E61/E62 [FUNC] key + in the built-in menu. +
    • Fixed an fMSX-Symbian bug where pieces of virtual keyboard remained + on screen after closing it. +
    • Pressing [8] key on fMSX-Symbian now invokes virtual keyboard, if + enabled, for the phones which do not have the [EDIT] key. +
    +

    New in fMSX 3.4

    +
      +
    • Added network play option to fMSX-Windows and fMSX-Symbian! +
    • Now not opening printer file until the first character is printed. +
    • Fixed OUTD/OUTDR opcodes to modify B register before the OUT operation. +
    • No longer trying to load state files from LoadFile(). +
    • Defined "INLINE" properly, depending on the C standard + supported by the compiler. +
    • Finally retired #ifdef SOUND as sound is always + compiled in anyway. +
    • Added new, much better, scanline simulation and video softening + effects to fMSX-Windows. +
    • fMSX-Windows full-screen mode now runs in 640x480 resolution. +
    • Fixed problem with full-screen SCREEN 6/7 in fMSX-Windows. +
    • fMSX-MSDOS has got complete (non-truncated) emulation for + SCREEN 6/7 and TEXT80 screen modes. +
    • fMSX-MSDOS now always runs in 640x480x15bpp VESA screen mode. +
    • Added options to simulate TV scanlines (-tv/-notv) + and zoom display (-zoom/-nozoom) to fMSX-MSDOS. +
    • Options -sync/-nosync/-vsync work properly again in + fMSX-MSDOS. +
    • Added new "Fill & Soften" zoom mode to fMSX-Symbian. It is + rather slow though. +
    • Added ability to use virtual keyboard in fMSX-Symbian to enter + filename when saving files from the built-in menu. +
    • Fixed problem with gameplay being too fast in fMSX-Symbian. +
    • Fixed possible source of instabilities in the S60 open file dialog + in fMSX-Symbian. +
    • Fixed exit via Symbian-specific menu. +
    • Fixed premature termination of the emulation thread when exiting + fMSX-Symbian, soundtrack recording and config saving work again. +
    • The [C] ("Clear") key is no longer used to toggle sound in + fMSX-Symbian. It acts as [BACKSPACE] instead. +
    +

    New in fMSX 3.3

    +
      +
    • Save state format has changed due to serious changes to the + emulator architecture! +
    • Added OpZ80() function and the FAST_RDOP macro to the Z80 emulation + to optimize code access in architectures with memory-mapped I/O. +
    • Added subslot support to all slots. +
    • Moved PAINTER.ROM to slot 0:1, FMPAC to slot 0:2, MSXDOS2.ROM to + slot 3:0. +
    • Reduced the number of user cartridges back to two (A/B). All the + other slots are taken by system cartridges (MSXDOS2, FMPAC, etc.). +
    • Locked user cartridges in slots 1/2 to subslot 0, as external + cartridges often do not use subslots. +
    • If Konami's GMASTER2.ROM or GMASTER.ROM is present in the program + directory, fMSX will now load it as a system cartridge. +
    • Fixed state file name autogeneration. +
    • Fixed -rom option behaviour. +
    • Fixed hangup when switching from MSX1 to MSX2 mode. +
    • Fixed MSXDOS2.ROM and FMPAC.ROM support. +
    • Fixed StateID() function to take all cartridges and system ROMs into + account. +
    • Fixed non-working joystick autofire. +
    • Now checking joysticks and mice once per frame. +
    • Now accelerating horizontal mouse movement in 512-wide screen modes. +
    • Mouse buttons are now merged with joystick buttons. +
    • Fixed mouse-as-joystick option. +
    • Improved file type detection, now automatically detecting saved + states, disk images, cartridge ROMs, fixed font files. +
    • Readded proper support for fixed text mode fonts with LoadFNT(). +
    • Added proper emulation for the WD1793 FDC (WD1793.c). +
    • Added unified .FDI disk image support (FDIDisk.c). +
    • Added support for standard, SV738, Arabic, and Brazilian WD1793-based + disk interfaces. +
    • Added support for GZIPped disk images. +
    • Made disk geometry discovery smarter (CP/M-80 v2.2 now boots). +
    • Moved 720kB floppy utilities (Floppy.c) and SCC sound chip emulation + (SCC.c) into EMULib. +
    • Added command line option to patch DiskROM calls (-simbdos). + With this option on, fMSX takes over DiskROM disk access routines. When + -wd1793 is used, fMSX will simulate real WD1793 FDC instead. +
    • Rehashed built-in menu to make screen space for more options. +
    • Added menu options to select RAM and VRAM sizes, create new disk images, + save disk images, patch DiskROM calls, load and use fixed text mode + fonts. +
    • Added virtual keyboard to fMSX-Symbian, invoked by pressing + [EDIT] or left [SHIFT] key. The keyboard + can work in three modes: "Off", "Single Key Entry", and "Multiple + Keys Entry". +
    • Added display orientation settings to fMSX-Symbian ("Landscape", + "Portrait", and "Auto"). +
    • Finally fixed opendir() problem in EMULib-Symbian. All built-in + menu file dialogs work now! +
    • Further extended and optimized Symbian screen rendering routines. +
    • Fixed and optimized TV scanline simulation in fMSX-Symbian, made + scanline effect stronger. +
    • Now saving RAM and VRAM size settings in fMSX-Symbian. +
    • fMSX-Symbian now uses true 512-wide display drivers for SCREEN6, + SCREEN7, and TEXT80. +
    • fMSX-Symbian will now save printer output to + E:\Others\fMSX\PRINTER.OUT. +
    • Fixed system warnings in when flipping Nokia E70 open/closed. +
    • Fixed possible crash when selecting an initial file in fMSX-Symbian. +
    • Improved "About" box in fMSX-Symbian. +
    • Moved fMSX-Symbian file selection root to E:\. +
    • When initial file selection cancelled, fMSX-Symbian will now boot + into plain BASIC (i.e. no cartridges and no disks). +
    • Renamed fMSX-Symbian binary and resource files to avoid name clash + with fMSX/S60 port by Juha Riihimaki. +
    • Fixed mouse support in fMSX-MSDOS. +
    +

    New in fMSX 3.2

    +
      +
    • Added ChangePrinter() function to set printer redirection file. +
    • Moved disk operations to a separate submenu of the built-in menu + and added "Eject Disk" options. +
    • Added an easy "Load File" option to the built-in menu and the + fMSX-Windows. +
    • Added options to redirect printer and sound logging output to the + built-in menu and the fMSX-Windows. +
    • Added error messages to fMSX-Windows and fMSX-Symbian. +
    • Added frame rate display option to fMSX-Symbian. +
    • Added "fill screen" zoom option to fMSX-Symbian. +
    • Added MIDI soundtrack logging to fMSX-Symbian (saved into + E:\Sounds and can be used as ringtones). +
    • Added configurable button and key mappings to fMSX-Symbian. +
    • fMSX-Symbian application menu is now split into three pages. +
    +

    New in fMSX 3.1

    +
      +
    • fMSX-Windows and fMSX-DOS are now compiled with OpenWatcom C++ and + use the new EMULib framework. Note that keyboard assignments have + changed! +
    • Added hardware reset key ([F11]) to reboot MSX. +
    • Added universal screen buffer based Z80 debugger ([F10]) that works on + all platforms. +
    • Added universal configuration menu ([F8]) that works on all platforms. +
    • Added ResetMSX() function to restart MSX, possibly in a new operating + mode. +
    • Extended LoadCart() function to load or eject cartridges at runtime. +
    • Added Mode variable to store all configuration options. +
    • Replaced -vperiod/-hperiod settings with easier to understand + -pal/-ntsc settings. +
    • Replaced -uperiod setting with a finer -skip setting that controls + the percentage of skipped frames. +
    • Fixed Z80 NMI handling to comply with the standard. +
    • Fixed SRAM file saving. +
    • Fixed state file name generation. +
    • Added autofire options for joystick buttons. +
    • fMSX-Windows has got a new menu-based GUI, improved sound, and faster + graphics updates. +
    • Fixed USB joypad support in fMSX-Windows. +
    • fMSX-MSDOS now uses 16BPP screen mode. +
    • Updated the documentation. +
    +

    New in fMSX 3.0

    +
      +
    • Added transparent support for GZIPped disk images as disks. You can now + pass .GZ file names in -diska/-diskb command line options. +
    • Added transparent support for directories as disks (thanks to Miguel + Colom). You can now pass directory names in -diska/-diskb + command line options. Changes to such disks will not be saved for + safety reasons. +
    • Added DirectDraw-based full screen mode to fMSX-Windows. + Press [ALT]+[ENTER] to switch in and out of the full screen mode. + Use it with caution, as DirectDraw is notoriously unstable. +
    • Added a full-fledged debugger to fMSX-Windows. Press [F11] to invoke + the debugger. +
    • Fixed several bugs in MIDI implementations that made some games silent + when logging soundtrack or using MIDI output in fMSX-Windows. +
    • Better adapted fMSX-MSDOS to 320x200 VGA screen. +
    • "Sync to VBlanks" option is now default in fMSX-MSDOS. +
    +

    New in fMSX 2.8

    +
      +
    • Fixed initial envelope volume in AY8910.c, thanks to + Koichi Nishida. +
    • Fixed a problem with 8kB/16kB cartridges failing to execute in + the MSX1 mode. +
    • When guessing a MegaROM mapper type, fMSX now tries to look at a + data file CARTS.CRC containing pairs of cartridge CRCs + and mapper types. +
    • Added support for the third cartridge slot. You can now give up to + three cartridge names on the command line and three + -rom options. +
    • Cartridges are now inserted into slots 1:X (A), 2:X (B), and 3:0 (C). + The RS232.ROM has been moved to slot 3:3. +
    • Added support for the 64kB Panasonic FM-PAC cartridge with 8kB SRAM + (-rom 7). +
    • fMSX loads files FMPAC.ROM, MSXDOS2.ROM, + and PAINTER.ROM automatically, as long as they are in + the program directory and there is an empty cartridge slot available. +
    • Added support for ASCII/8kB cartridges with 8kB SRAM (Xanadu, + Royal Blood). Please, inform me + if this has broken support for any "normal" ASCII/8kB cartridges. +
    • Added support for ASCII/16kB cartridges with 2kB SRAM (Hydlide 2). + Please, inform me if this has + broken support for any "normal" ASCII/16kB cartridges. +
    • All battery-backed SRAM save files have now got .SAV + extension. For example, GameMaster2.rom will produce + a SRAM file named GameMaster2.sav. +
    • Added reporting of bad memory writes with -verbose 8. +
    • The R-Type cartridge and its variants are supported by + the ASCII/16kB MegaROM mapper (-rom 5). +
    • HAL's Hole In One Special cartridge is supported by the + GENERIC/16kB MegaROM mapper (-rom 1). +
    • Konami's Majutsushi (aka Mah Jong 2 or RC765) + cartridge is supported by the GENERIC/8kB MegaROM mapper + (-rom 0), although the D/A audio circuit emulation + is not implemented. +
    • Added fast-forwarding option ([F9]) and moved fixed + font switch to a different key ([CONTROL]-[F9]). +
    • Changed [F6],[F9],[F10] assignments in fMSX-Windows + to comply with other fMSX ports. +
    • Added Options menu to fMSX-Windows. +
    • fMSX-FreeBSD now compiles without changes on FreeBSD 5.x (FreeBSD + 4.x users may have to change the location of the + soundcart.h). +
    • Fixed sound in fMSX-FreeBSD by increasing the number of sound + fragments from 8 to 64. If any of FreeBSD developers are reading + this, could you explain why this is needed? +
    +

    New in fMSX 2.7

    +
      +
    • Switched to the latest version of the OpenWatcom C/C++ compiler. +
    • The infamous Windows problem with spaces in directory names seems + to be fixed by using a newer compiler. +
    • Made some arrays "const" to facilitate porting to run-from-ROM + platforms (cellphones, PDAs, etc.). +
    • Joystick problems in fMSX-Windows are fixed. +
    • Descreased the default sound volume in fMSX-Unix. +
    • Increased the default wave synthesis frequency to 44.1kHz. +
    • Minor fixes to the Unix sound drivers. +
    • Removed MIDI volume control from Windows sound drivers. +
    • Added TV raster effect to fMSX-Windows. +
    +

    New in fMSX 2.6

    +
      +
    • Added separate (and correct) emulation for the i8255 PPI chip. +
    • Added masking to lower bits of video table addresses. +
    • Added timing and other fixes to the Z80 core (courtesy of Omar Cornut). +
    • Fixed line coincidence and VBlank handling (thanks to Vincent van Dam). +
    • Save state (.STA) file format has changed. +
    • Fixed a bug that messed up screen table addresses when restoring state. +
    • Fixed a bug that left dummy memory page dirty when loading GZIPped ROMs. +
    • Fixed a -home option bug on Solaris (thanks to Eric Boon). +
    • Fixed fMSX-Unix to allow it to have an application icon under + WindowMaker. +
    +

    New in fMSX 2.5

    +
      +
    • Added emulation state saving and loading. +
    • Added support for GZIPped ROM files and state file. +
    • Changed keyboard handler in fMSX-Unix. Hopefully, the key assignments + should now be more natural. +
    • Added sound on/off switch to fMSX-Unix and fMSX-MSDOS. +
    • Keyboard assignments changed everywhere. +
    +

    New in fMSX 2.4

    +
      +
    • Small fix to the DI instruction. +
    • Small fix to the HALT instruction. +
    • Added -sync option to fMSX-MSDOS on public request. +
    • Fixed PHYDIO DiskROM call (4010h) to return the number of remaining + sectors in register B (thanks, Maarten!). +
    • Fixed ASCII mappers to prevent memory corruption when switching + ROM pages in disconnected slots. +
    • Fixed palette to span to true white. +
    • Added SetWave() call to the sound API. +
    • SCC emulation now makes use of waveforms via SetWave(). +
    • SCC gets silenced when 0 frequency is written (thanks, Maarten!). +
    • Fixed SCC+ emulation. +
    • Vertical screen adjustment is only computed once, when refresh starts. +
    • Moved picture 10 pixels down when the screen is 192 pixels high. +
    • Removed unused SprCol variable. +
    • Added wave synthesis sound to fMSX-Windows. +
    • Fixed fMSX-MSDOS to restore palette when exiting the debugger. +
    +

    New in fMSX 2.3

    +
      +
    • Now, when you buy fMSX-Windows, you also fMSX-MSDOS. +
    • Added modular FM-PAC (aka YM2413 aka OPLL) emulation. +
    • FM-PAC soundtrack can be logged into a MIDI file. +
    • Sound and MIDI logging APIs have been integrated and rewritten to + accomodate MIDI instruments, dynamic instrument changes, drum + usage, etc. +
    • Added PAL/NTSC switch on a bit in VDP[9] (thanks, Alex!). +
    • Added support for the overscan (Mantra demos and games run now). +
    • Added sprite support to SCREEN3 (no idea why it was not there). +
    • Added support for YJK (SCREEN12) and YAE (SCREEN10/11) screens. +
    • Added separate handling for IE0 and IE1 IRQs (Xevious, etc.). +
    • Added masking of Character Table address (SourceOfPower demo runs now). +
    • Fixed Z80 interrupt handling after EI (thanks, Maarten!). +
    • IE0 and IE1 IRQs are now reset when disabled. +
    • IE0 IRQ is now set when enabled. +
    • Palette sequencer now resets when writing to VDP[16]. +
    • Fixed mapper #1 to support generic 16kB-paged MegaROMs (HoleInOneSpecial). +
    • Fixed cartridge loader to correctly load MegaROMs with "AB" signature + in the last 16kB page (R-Type). +
    • Mappers #0 and #1 are now renamed to Generic/8kB and Generic/16kB. +
    • Fixed mapper #3 to have hardwired ROM at addresses 4000h-5FFFh (thanks, + Sean!). +
    • Added mapper #6 that corresponds to the Konami GameMaster2 cartridge + with SRAM (thanks again, Sean!). +
    • Added key click and motor relay click emulation via Drum() call. +
    • Major rewrite of the sound drivers and sound logging system. +
    • Added mouse emulation to fMSX-MSDOS. +
    • Added border emulation via VGA border to fMSX-MSDOS. +
    • Added built-in debugger to fMSX-MSDOS (F11). +
    • Fixed static palette in fMSX-MSDOS. +
    • Fixed fMSX-Unix to work in 24BPP and 32BPP modes. +
    • Added static palette to fMSX-Unix. +
    • Minor fixes to fMSX-Unix makefile. +
    • fMSX-Windows now runs in 16BPP HiColor mode. +
    • Changed Setup Panel in fMSX-Windows. +
    • HPeriod and VPeriod scrollbars are gone from fMSX-Windows as + timing control is done automatically. +
    • PAL and NTSC buttons are gone from fMSX-Windows as PAL/NTSC + switch is done automatically. +
    +

    New in fMSX 2.2

    +
      +
    • Interrupt system rewritten. +
    • Mapper management rewritten. +
    • Keyboard assignments changed again. Check the documentation! +
    • Made a new MSDOS port of fMSX (still in development). +
    • Integrated modular AY8910 emulation. +
    • Integrated modular SCC emulation. +
    • Integrated SndUnix/SndWin/SndMSDOS sound drivers. +
    • Sound track is now logged to MIDI files. +
    • Added a table of I/O handler functions to Z80 core for easy expansion. +
    • Added ability to load flat (no mapper) 64kB ROMs. +
    • Added automatic MegaROM mapper guessing (-rom 6). +
    • Added standard PAL/NTSC timings. +
    • Added V9958 version flag in MSX2+ mode (VDP S1). +
    • Added correct emulation of HR/VR flags (VDP S2). +
    • Fixed border color in SCREEN8 (VDP R7). +
    • Fixed blinking frequency in TEXT80 (VDP R13). +
    • Added ADJUST register emulation (VDP R18). +
    • Added V9938 character scrolling to SCREEN0 and TEXT80 (VDP R23). +
    • Added scrolling to SCREEN1, SCREEN2, and SCREEN3 (VDP R23). +
    • Fixed scrolling bug in SCREEN6 and SCREEN7 (VDP R23). +
    • Made left/right borders 9/7 pixels in SCREEN0 and TEXT80. +
    • Added -home option to tell fMSX where to find system ROMs. +
    • Added -auto option for auto fire on pressing SPACE. +
    • Fixed information printed on -help. +
    • Fixed crash in fMSX-Windows when switching from 8kB to 16kB MegaROMs. +
    • Fixed trash in MSX2 logo in fMSX-Windows that appeared after reset. +
    • Better resizing in fMSX-Windows Setup panel. +
    +

    New in fMSX 2.1

    +
      +
    • New fMSX-Windows available. Buy it + + now + ! +
    • Rewritten V9938 graphical operations support. +
    • Rewritten initialization sequence. +
    • Improved real-time clock emulation. +
    • Fixed a problem with color sprites (Zanac-Ex, Aleste). +
    • Fixed problems with memory allocation. +
    • Fixed mouse offset in fMSX-Unix/X. +
    • Extended documentation, added more questions to FAQ. +
    • Reduced window size in Unix/X to speed up updates. +
    • fMSX-Unix now supports 16bpp and 32bpp Xservers for sure. +
    • Added speed throttling to fMSX-Unix (see -sync). +
    • fMSX-Unix runs in a resizable window now. +
    • Fixed IPC key allocation bug in fMSX-Unix. +
    • Many small fixes to the code. +
    +

    New in fMSX 2.0b

    +
      +
    • Major code rewrite! +
    • Line-by-line screen updates implemented +
    • Standard modular Z80 emulation +
    • Support for 16bit and 32bit X11 screens (possibly 24bit) +
    • Support for KANJI.ROM +
    • Mouse support +
    • Joystick support +
    • Multiple bug fixes +
    • Multiple new bugs (look for them!) +
    +

    New in fMSX 1.5

    +
      +
    • More stable split screen (Zanac-Ex no longer blinks) +
    • RAM mapper fixed again (Designer+ works now) +
    • V9938 SRCH command fixed +
    • MSXDOS2.ROM support +
    • PAINTER.ROM support +
    • FMPAC.ROM support +
    • .PSG file format is updated +
    • Serial port support is not yet implemented +
    + + +

    Thanks to...

    +
    +
    +
    Hans Guijt [.NL] +
    +Working on Amiga port, Hans has done a very good job in getting fMSX run +fast on an Amiga. He rewrote the CPU emulation and the screen drivers in +assembler, optimized the VDP emulation, wrote sound drivers, and added +many more things making fMSX-Amiga the best, if not the fastest MSX +emulator for this platform. Thanks, Hans (and yes, I do remember those +.ROM files too ;)). +

    +

    Igor Sharfmesser [.KZ] and Alex Krasivsky [.RU] +
    +Igor and Alex have brought to life fMSX-MSDOS. The first version, +including screen and keyboard drivers, was done by Igor. AdLib +sound was added by Alex. +

    +

    Teturo Tokuyama [.JP] +
    +Teturo has done an excellent port of fMSX to Windows. As much as +I hate Windows, I must admit that Teturo's fMSX-Win32 rules. +Period. +

    +

    Murakami Reki [.JP] +
    +Murakami has written PC9801 port of fMSX. He is the guy whom you +have to thank for the disk support in fMSX. If not for him, I +would have never started implementing it :). +

    +

    Marcel de Kogel [.NL] +
    +Marcel wrote an excellent port of fMSX to MSDOS. His MSDOS version +of fMSX was actually the first one that supported all fMSX features +correctly. Marcel also provided invaluable help debugging and +improving fMSX and other emulators. +

    +

    Ville Hallik [.EE] +
    +AY8910 and SCC chips emulation, written by Ville for Linux/FreeBSD +/dev/dsp device, and SunOS /dev/audio, was the best fMSX sound +emulation I have seen at the time. And, yes, it lets you play +MSX games with sound on a Unix system! =:) +

    +

    John Stiles [.US] +
    +John took over the development of the Macintosh version of fMSX +and managed to speed up the common part of display drivers used +in the Unix/X, MSDOS, Windows, and Macintosh versions. +

    +

    Paul Chapman [.CA] +
    +Paul Chapman is the original author of fMSX-Macintosh. Although his +port had bugs, it looked great and ran faster than the original MSX +computer on an average PPC-based Macintosh of that time. As Paul was +not able to continue developing fMSX-Macintosh, the development has +been taken over by John Stiles. +

    +

    Arnold Metselaar [.NL] +
    +If you happen to have a non-standard X11 display, get Arnold's X11 +drivers fixed to work for any screen depth. Arnold has also added +to his drivers a lot of improvements that are not in the standard +fMSX distribution. Make sure to check out his work. +

    +

    Guenter Woigk [.DE] +
    +Found a bug with offsets in Z80 commands using IX/IY registers. +

    +

    Martial Benoit [.FR] +
    +Explained how VDP deals with sprites and sent me copies of V9938 +and WD2793 databooks (WOW! Thanks, Martial :)). +

    +

    Sean Young [.NL] +
    +For finally making it clear to me how GameMaster2 SRAM works and also for +finding a bug in my Konami4 mapper emulation. +

    +

    Alex Wulms [.NL] +
    +Alex has written the most accurate emulation of the V9938 graphical +operations to date. This emulation is now a part of fMSX. Also, Alex +explained to me a lot of technical details of VDP operation. And he +sent me a copy of the WD1793 databook :). +

    +

    Maarten ter Huurne [.NL] +
    +Maarten has done extensive bug-hunting on things that I would never +consider to run (that is, demos :)). He also looks for glitches in games +and doesn't rest until he gets to the source of a problem. Additionally, +he has implemented MSX Serial I/O emulation that is slowly making its +way into fMSX. And finally (as the stuff above were not enough) you can +thank Maarten and Takamichi Suzukawa for the English translation of the +Konami's Solid Snake cartridge! +

    +

    Miguel Colom [.ES] +
    +Miguel has come up with an idea and the code to use directories as if +they were MSX disks. +
    +

    +...and to all other people who helped me with advice, information, and +encouragment. +

    +

    + +ENJOY THE EMULATOR AND LET US KEEP MSX ALIVE :) + +
    + +
    + +© Copyright by +Marat Fayzullin +(marat [AT] komkon <DOT> org) + + + diff --git a/components/msx/include/msx.hpp b/components/msx/include/msx.hpp new file mode 100644 index 0000000..d522ccb --- /dev/null +++ b/components/msx/include/msx.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include + +void reset_msx(); +void init_msx(const std::string& rom_filename, uint8_t *romdata, size_t rom_data_size); +void load_msx(std::string_view save_path); +void save_msx(std::string_view save_path); +void start_msx_tasks(); +void stop_msx_tasks(); +void run_msx_rom(); +void deinit_msx(); +void set_msx_video_original(); +void set_msx_video_fit(); +void set_msx_video_fill(); +std::vector get_msx_video_buffer(); diff --git a/components/msx/src/msx.cpp b/components/msx/src/msx.cpp new file mode 100644 index 0000000..48e3205 --- /dev/null +++ b/components/msx/src/msx.cpp @@ -0,0 +1,519 @@ +#include "msx.hpp" + +#include "shared_memory.h" + +#include + +#include "box-emu.hpp" +#include "statistics.hpp" + +static const size_t MSX_SCREEN_WIDTH = 256; +static const size_t MSX_SCREEN_HEIGHT = 228; +static const int MSX_AUDIO_SAMPLE_RATE = 32000; + +extern "C" { + +#define AUDIO_SAMPLE_RATE (32000) +#define AUDIO_BUFFER_LENGTH (AUDIO_SAMPLE_RATE / 60 + 1) + +static uint16_t* displayBuffer[2]; +static uint8_t currentBuffer = 0; +static uint16_t* framebuffer = nullptr; +static uint16_t* audio_buffers[2]; +static uint8_t currentAudioBuffer = 0; +static uint16_t* audio_buffer = nullptr; +static std::atomic frame_complete = false; + +static int JoyState, LastKey, InMenu, InKeyboard; +static int KeyboardCol, KeyboardRow, KeyboardKey; +static int64_t KeyboardDebounce = 0; +static int KeyboardEmulation, CropPicture; +static char *PendingLoadSTA = NULL; + +// #define BPS16 +#define BPP16 +// #define UNIX +#define GenericSetVideo SetVideo +// #define LSB_FIRST +// #define NARROW +#define WIDTH 256 +#define HEIGHT 228 +#define XKEYS 12 +#define YKEYS 6 + +void PutImage(void); + +uint16_t BPal[256]; +uint16_t XPal[80]; +uint16_t XPal0; +uint16_t *XBuf; + + +#include "MSX.h" +#include "Console.h" +#include "EMULib.h" +#include "Sound.h" +#include "Record.h" +#include "Touch.h" +#include "CommonMux.h" +#include "msxfix.h" + +#include "Floppy.h" +#include "MCF.h" +#include "Hunt.h" +}; + +static Image NormScreen; +const char *Title = "fMSX 6.0"; +const char *Disks[2][MAXDISKS + 1]; + +static const unsigned char KBDKeys[YKEYS][XKEYS] = { + {0x1B, CON_F1, CON_F2, CON_F3, CON_F4, CON_F5, CON_F6, CON_F7, CON_F8, CON_INSERT, CON_DELETE, CON_STOP}, + {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '='}, + {CON_TAB, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', CON_BS}, + {'^', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', CON_ENTER}, + {'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', 0, 0}, + {'[', ']', ' ', ' ', ' ', ' ', ' ', '\\', '\'', 0, 0, 0}}; + +static const char *BiosFolder = "/sdcard/msx/bios/"; +// We only check for absolutely essential files to avoid slowing down boot too much! +static const char *BiosFiles[] = { + "MSX.ROM", + "MSX2.ROM", + "MSX2EXT.ROM", + // "MSX2P.ROM", + // "MSX2PEXT.ROM", + // "FMPAC.ROM", + "DISK.ROM", + "MSXDOS2.ROM", + // "PAINTER.ROM", + // "KANJI.ROM", +}; + +int ProcessEvents(int Wait) +{ + for (int i = 0; i < 16; ++i) + KeyState[i] = 0xFF; + JoyState = 0; + + static auto& box = BoxEmu::get(); + auto state = box.gamepad_state(); + + if (state.select) + { + InKeyboard = !InKeyboard; + // TODO: what do we do with this? + // + // rg_input_wait_for_key(RG_KEY_ANY, false, 500); + } + else if (state.start) + { + // I think this key could be better used for something else + // but for now the feedback is to keep a key for fMSX menu... + InMenu = 2; + return 0; + } + + if (InMenu == 2) + { + InMenu = 1; + // TODO: mute audio + // rg_audio_set_mute(true); + MenuMSX(); + // TODO: unmute audio + // rg_audio_set_mute(false); + // TODO: figure out what to do with this + // rg_input_wait_for_key(RG_KEY_ANY, false, 500); + InMenu = 0; + } + else if (InMenu) + { + if (state.left) + LastKey = CON_LEFT; + if (state.right) + LastKey = CON_RIGHT; + if (state.up) + LastKey = CON_UP; + if (state.down) + LastKey = CON_DOWN; + if (state.a) + LastKey = CON_OK; + if (state.b) + LastKey = CON_EXIT; + } + else if (InKeyboard) + { + if (state.up || state.down || state.left || state.right) + { + if (esp_timer_get_time() > KeyboardDebounce) + { + if (state.left) + KeyboardCol--; + if (state.right) + KeyboardCol++; + if (state.up) + KeyboardRow--; + if (state.down) + KeyboardRow++; + + KeyboardCol = std::clamp(KeyboardCol, 0, XKEYS - 1); + KeyboardRow = std::clamp(KeyboardRow, 0, YKEYS - 1); + PutImage(); + KeyboardDebounce = esp_timer_get_time() + 250'000; // 250ms + } + } + else if (state.a) + { + KeyboardKey = KBDKeys[KeyboardRow][KeyboardCol]; + KBD_SET(KeyboardKey); + } + else if (state.b) + { + // TODO: figure out what to do with this + // rg_input_wait_for_key(RG_KEY_ANY, false, 500); + InKeyboard = false; + } + } + else if (KeyboardEmulation) + { + if (state.left) + KBD_SET(KBD_LEFT); + if (state.right) + KBD_SET(KBD_RIGHT); + if (state.up) + KBD_SET(KBD_UP); + if (state.down) + KBD_SET(KBD_DOWN); + if (state.a) + KBD_SET(KBD_SPACE); + if (state.b) + KBD_SET(KBD_ENTER); + } + else + { + if (state.left) + JoyState |= JST_LEFT; + if (state.right) + JoyState |= JST_RIGHT; + if (state.up) + JoyState |= JST_UP; + if (state.down) + JoyState |= JST_DOWN; + if (state.a) + JoyState |= JST_FIREA; + if (state.b) + JoyState |= JST_FIREB; + } + + return 0; +} + +int InitMachine(void) +{ + NormScreen = (Image){ + .Data = framebuffer, + .W = WIDTH, + .H = HEIGHT, + .L = WIDTH, + .D = 16, + }; + + XBuf = NormScreen.Data; + SetScreenDepth(NormScreen.D); + SetVideo(&NormScreen, 0, 0, WIDTH, HEIGHT); + + for (int J = 0; J < 80; J++) + SetColor(J, 0, 0, 0); + + for (int J = 0; J < 256; J++) + { + uint16_t color = make_color(((J >> 2) & 0x07) * 255 / 7, ((J >> 5) & 0x07) * 255 / 7, (J & 0x03) * 255 / 3); + // BPal[J] = ((color >> 8) | (color << 8)) & 0xFFFF; + BPal[J] = color; + } + + InitSound(AUDIO_SAMPLE_RATE, 150); + SetChannels(64, 0xFFFFFFFF); + + RPLInit(SaveState, LoadState, MAX_STASIZE); + RPLRecord(RPL_RESET); + return 1; +} + +void TrashMachine(void) +{ + RPLTrash(); + TrashSound(); +} + +void SetColor(byte N, byte R, byte G, byte B) +{ + uint16_t color = make_color(R, G, B); + // color = (color >> 8) | (color << 8); + color = color; + if (N) + XPal[N] = color; + else + XPal0 = color; +} + +static inline void SubmitFrame(void) +{ + static auto& box = BoxEmu::get(); + box.push_frame(framebuffer); + + // swap buffers + currentBuffer = currentBuffer ? 0 : 1; + framebuffer = displayBuffer[currentBuffer]; + NormScreen.Data = framebuffer; + XBuf = NormScreen.Data; + + frame_complete = true; +} + +void PutImage(void) +{ + if (InKeyboard) + DrawKeyboard(&NormScreen, KBDKeys[KeyboardRow][KeyboardCol]); + + SubmitFrame(); + + XBuf = NormScreen.Data; +} + +unsigned int Joystick(void) +{ + ProcessEvents(0); + return JoyState; +} + +void Keyboard(void) +{ + // Keyboard() is a convenient place to do our vsync stuff :) + if (PendingLoadSTA) + { + LoadSTA(PendingLoadSTA); + free(PendingLoadSTA); + PendingLoadSTA = NULL; + } +} + +unsigned int Mouse(byte N) +{ + return 0; +} + +int ShowVideo(void) +{ + SubmitFrame(); + return 1; +} + +unsigned int GetJoystick(void) +{ + ProcessEvents(0); + return 0; +} + +unsigned int GetMouse(void) +{ + return 0; +} + +unsigned int GetKey(void) +{ + unsigned int J; + ProcessEvents(0); + J = LastKey; + LastKey = 0; + return J; +} + +unsigned int WaitKey(void) +{ + GetKey(); + // TODO: figure out what to do with this + // rg_input_wait_for_key(RG_KEY_ANY, false, 200); + // TODO: figure out what to do with this + // while (!rg_input_wait_for_key(RG_KEY_ANY, true, 100)) + // continue; + return GetKey(); +} + +unsigned int WaitKeyOrMouse(void) +{ + LastKey = WaitKey(); + return 0; +} + +static size_t audio_buffer_offset = 0; +unsigned int InitAudio(unsigned int Rate, unsigned int Latency) +{ + BoxEmu::get().audio_sample_rate(Rate); + return AUDIO_SAMPLE_RATE; +} + +void TrashAudio(void) +{ + // +} + +unsigned int GetFreeAudio(void) +{ + return 1024; +} + +void PlayAllSound(int uSec) { + // unsigned int samples = 2 * uSec * AUDIO_SAMPLE_RATE / 1000000; + // rg_queue_send(audioQueue, &samples, 100); + static auto &box = BoxEmu::get(); + unsigned int samples = 2 * uSec * AUDIO_SAMPLE_RATE / 1'000'000; + RenderAndPlayAudio(samples); +} + +unsigned int WriteAudio(sample *Data, unsigned int Length) { + // rg_audio_submit((void *)Data, Length >> 1); + static auto &box = BoxEmu::get(); + bool sound_enabled = !box.is_muted(); + // fmt::print("WriteAudio: Length: {}\n", Length); + if (sound_enabled) { + if (audio_buffer_offset + Length > AUDIO_BUFFER_LENGTH) { + box.play_audio((uint8_t*)audio_buffer, audio_buffer_offset * sizeof(int16_t)); + audio_buffer_offset = 0; + currentAudioBuffer = currentAudioBuffer ? 0 : 1; + audio_buffer = audio_buffers[currentAudioBuffer]; + } + memcpy(audio_buffer + audio_buffer_offset, Data, Length * sizeof(int16_t)); + audio_buffer_offset += Length; + } + return Length; +} + +void reset_msx() { + ResetMSX(Mode,RAMPages,VRAMPages); +} + +extern I8255* PPI; +extern Z80* CPU; +extern void **Chunks; // MAXCHUNKS +extern WD1793* FDC; +extern FDIDisk* FDD; // 4 +extern AY8910* PSG; +extern YM2413* OPLL; +extern SCC* SCChip; +extern I8251* SIO; +extern MCFEntry *MCFEntries; // MAXCHEATS +extern CheatCode *CheatCodes; // MAXCHEATS +extern HUNTEntry *Buf; +extern RPLState *RPLData; // [RPL_BUFSIZE] = {{0}}; + +void init_msx(const std::string& rom_filename, uint8_t *romdata, size_t rom_data_size) { + // init shared memory + CPU = (Z80*)shared_malloc(sizeof(Z80)); + Chunks = (void**)shared_malloc(sizeof(void*) * MAXCHUNKS); + PPI = (I8255*)shared_malloc(sizeof(I8255)); + FDC = (WD1793*)shared_malloc(sizeof(WD1793)); + FDD = (FDIDisk*)shared_malloc(sizeof(FDIDisk) * 4); + PSG = (AY8910*)shared_malloc(sizeof(AY8910)); + OPLL = (YM2413*)shared_malloc(sizeof(YM2413)); + SCChip= (SCC*)shared_malloc(sizeof(SCC)); + SIO = (I8251*)shared_malloc(sizeof(I8251)); + MCFEntries = (MCFEntry*)shared_malloc(sizeof(MCFEntry) * MAXCHEATS); + CheatCodes = (CheatCode*)shared_malloc(sizeof(CheatCode) * MAXCHEATS); + Buf = (HUNTEntry*)shared_malloc(sizeof(HUNTEntry) * HUNT_BUFSIZE); + RPLData = (RPLState*)shared_malloc(sizeof(RPLState) * RPL_BUFSIZE); + + // now init the state + displayBuffer[0] = (uint16_t*)BoxEmu::get().frame_buffer0(); + displayBuffer[1] = (uint16_t*)BoxEmu::get().frame_buffer1(); + currentBuffer = 0; + framebuffer = displayBuffer[0]; + + audio_buffers[0] = (uint16_t*)shared_malloc(AUDIO_BUFFER_LENGTH * sizeof(int16_t)); + audio_buffers[1] = (uint16_t*)shared_malloc(AUDIO_BUFFER_LENGTH * sizeof(int16_t)); + currentAudioBuffer = 0; + audio_buffer = audio_buffers[currentAudioBuffer]; + + BoxEmu::get().native_size(MSX_SCREEN_WIDTH, MSX_SCREEN_HEIGHT); + BoxEmu::get().palette(nullptr); + + reset_frame_time(); + + const char *argv[] = { + "fmsx", + "-ram", "2", + "-vram", "2", + "-skip", "50", + "-home", BiosFolder, + "-joy", "1", + NULL, NULL, NULL, + }; + int argc = std::size(argv) - 3; + + // get the file extension from the rom_filename + const char *ext = strrchr(rom_filename.c_str(), '.'); + if (ext != NULL) { + ext++; + if (strcasecmp(ext, "dsk") == 0) { + argv[argc++] = "-diska"; + } + } + + argv[argc++] = rom_filename.c_str(); + + // started + fmsx_main(argc, (char **)argv); + +} + +static bool load_save = false; +static std::string save_path_to_load; +void IRAM_ATTR run_msx_rom() { + auto start = esp_timer_get_time(); + + // static constexpr size_t num_cycles_per_frame = 2000; + // for (int i = 0; i < num_cycles_per_frame; i++) { + while (!frame_complete) { + RunZ80(CPU); + } + frame_complete = false; + + auto end = esp_timer_get_time(); + auto elapsed = end - start; + update_frame_time(elapsed); + + // // frame rate should be 60 FPS, so 1/60th second is what we want to sleep for + // static constexpr auto delay = std::chrono::duration(1.0f/60.0f); + // std::this_thread::sleep_until(start + delay); +} + +void load_msx(std::string_view save_path) { + save_path_to_load = save_path; + load_save = true; + if (PendingLoadSTA) + free(PendingLoadSTA); + PendingLoadSTA = strdup(save_path.data()); +} + +void save_msx(std::string_view save_path) { + // save_sram((char *)save_path.data(), console_msx); + SaveSTA((char *)save_path.data()); +} + +std::vector get_msx_video_buffer() { + // copy the frame buffer to a new buffer + static constexpr auto width = MSX_SCREEN_WIDTH; + static constexpr auto height = MSX_SCREEN_HEIGHT; + std::vector new_frame_buffer(width * 2 * height); + memcpy(new_frame_buffer.data(), framebuffer, width * 2 * height); + return new_frame_buffer; +} + +void deinit_msx() { + TrashMSX(); + TrashMachine(); + RPLTrash(); + TrashSound(); + shared_mem_clear(); + BoxEmu::get().audio_sample_rate(48000); +} diff --git a/main/carts.hpp b/main/carts.hpp index a5c9c26..94930d7 100644 --- a/main/carts.hpp +++ b/main/carts.hpp @@ -5,8 +5,9 @@ #include "display.hpp" #include "gbc_cart.hpp" -#include "nes_cart.hpp" #include "genesis_cart.hpp" +#include "msx_cart.hpp" +#include "nes_cart.hpp" #include "sms_cart.hpp" std::unique_ptr make_cart(const RomInfo& info, std::shared_ptr> display) { @@ -39,6 +40,12 @@ std::unique_ptr make_cart(const RomInfo& info, std::shared_ptr(Cart::Config{ + .info = info, + .display = display, + .verbosity = espp::Logger::Verbosity::WARN + }); default: return nullptr; } diff --git a/main/doom_cart.hpp b/main/doom_cart.hpp new file mode 100644 index 0000000..a36b845 --- /dev/null +++ b/main/doom_cart.hpp @@ -0,0 +1,128 @@ +#pragma once + +#include "cart.hpp" +#if defined(ENABLE_DOOM) +#include "doom.hpp" +#endif + +class DoomCart : public Cart { +public: + explicit DoomCart(const Cart::Config& config) + : Cart(config) { + handle_video_setting(); + init(); + } + + virtual ~DoomCart() override { + logger_.info("~DoomCart()"); + deinit(); + } + + // cppcheck-suppress uselessOverride + virtual void reset() override { + Cart::reset(); +#if defined(ENABLE_DOOM) + // Doom doesn't have a reset function, we'll just reinitialize + deinit(); + init(); +#endif + } + + // cppcheck-suppress uselessOverride + virtual void load() override { + Cart::load(); +#if defined(ENABLE_DOOM) + load_doom(get_save_path()); +#endif + } + + // cppcheck-suppress uselessOverride + virtual void save() override { + Cart::save(); +#if defined(ENABLE_DOOM) + save_doom(get_save_path(true)); +#endif + } + + void init() { +#if defined(ENABLE_DOOM) + logger_.info("doom::init()"); + init_doom(get_rom_filename(), romdata_, rom_size_bytes_); +#endif + } + + void deinit() { +#if defined(ENABLE_DOOM) + deinit_doom(); +#endif + } + + // cppcheck-suppress uselessOverride + virtual bool run() override { +#if defined(ENABLE_DOOM) + run_doom_rom(); +#endif + return Cart::run(); + } + +protected: + // DOOM + static constexpr size_t DOOM_WIDTH = 320; + static constexpr size_t DOOM_HEIGHT = 200; + + // cppcheck-suppress uselessOverride + virtual void pre_menu() override { + Cart::pre_menu(); +#if defined(ENABLE_DOOM) + logger_.info("doom::pre_menu()"); + stop_doom_tasks(); +#endif + } + + // cppcheck-suppress uselessOverride + virtual void post_menu() override { + Cart::post_menu(); +#if defined(ENABLE_DOOM) + logger_.info("doom::post_menu()"); + start_doom_tasks(); +#endif + } + + virtual void set_original_video_setting() override { +#if defined(ENABLE_DOOM) + logger_.info("doom::video: original"); + set_doom_video_original(); +#endif + } + + virtual std::pair get_video_size() const override { + return std::make_pair(DOOM_WIDTH, DOOM_HEIGHT); + } + + // cppcheck-suppress uselessOverride + virtual std::vector get_video_buffer() const override { +#if defined(ENABLE_DOOM) + return get_doom_video_buffer(); +#else + return std::vector(); +#endif + } + + virtual void set_fit_video_setting() override { +#if defined(ENABLE_DOOM) + logger_.info("doom::video: fit"); + set_doom_video_fit(); +#endif + } + + virtual void set_fill_video_setting() override { +#if defined(ENABLE_DOOM) + logger_.info("doom::video: fill"); + set_doom_video_fill(); +#endif + } + + virtual std::string get_save_extension() const override { + return "_doom.sav"; + } +}; \ No newline at end of file diff --git a/main/msx_cart.hpp b/main/msx_cart.hpp index 3aa8edf..3f1df49 100644 --- a/main/msx_cart.hpp +++ b/main/msx_cart.hpp @@ -45,15 +45,7 @@ class MsxCart : public Cart { void init() { #if defined(ENABLE_MSX) - switch (info_.platform) { - case Emulator::MSX: - logger_.info("msx::init()"); - init_msx(romdata_, rom_size_bytes_); - break; - default: - logger_.error("unknown platform"); - return; - } + init_msx(get_rom_filename(), romdata_, rom_size_bytes_); #endif } @@ -74,7 +66,7 @@ class MsxCart : public Cart { protected: // MSX static constexpr size_t MSX_WIDTH = 256; - static constexpr size_t MSX_HEIGHT = 192; + static constexpr size_t MSX_HEIGHT = 228; // cppcheck-suppress uselessOverride virtual void pre_menu() override { @@ -95,7 +87,7 @@ class MsxCart : public Cart { virtual void set_original_video_setting() override { #if defined(ENABLE_MSX) logger_.info("msx::video: original"); - set_msx_video_original(); + BoxEmu::get().display_size(MSX_WIDTH, MSX_HEIGHT); #endif } @@ -115,14 +107,16 @@ class MsxCart : public Cart { virtual void set_fit_video_setting() override { #if defined(ENABLE_MSX) logger_.info("msx::video: fit"); - set_msx_video_fit(); + float x_scale = static_cast(SCREEN_HEIGHT) / static_cast(MSX_HEIGHT); + int new_width = static_cast(static_cast(MSX_WIDTH) * x_scale); + BoxEmu::get().display_size(new_width, SCREEN_HEIGHT); #endif } virtual void set_fill_video_setting() override { #if defined(ENABLE_MSX) logger_.info("msx::video: fill"); - set_msx_video_fill(); + BoxEmu::get().display_size(SCREEN_WIDTH, SCREEN_HEIGHT); #endif } diff --git a/sdkconfig.defaults b/sdkconfig.defaults index c8810e4..0c978aa 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -1,12 +1,12 @@ CONFIG_IDF_TARGET="esp32s3" CONFIG_IDF_EXPERIMENTAL_FEATURES=y - +CONFIG_BOOTLOADER_FLASH_DC_AWARE=y CONFIG_SPI_FLASH_UNDER_HIGH_FREQ=y .... CONFIG_SPI_FLASH_HPM_ENA=y -CONFIG_COMPILER_OPTIMIZATION_PERF=y -# CONFIG_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_COMPILER_OPTIMIZATION_PERF=y +CONFIG_COMPILER_OPTIMIZATION_SIZE=y # disable interrupt watchdog CONFIG_ESP_INT_WDT=n @@ -16,6 +16,7 @@ CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y CONFIG_ESPTOOLPY_FLASHSIZE="16MB" # over twice as fast as DIO CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +# CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_FLASHFREQ_120M=y CONFIG_FREERTOS_HZ=1000