├── .idea ├── .name ├── .gitignore ├── codeStyles │ └── codeStyleConfig.xml ├── compiler.xml ├── kotlinc.xml ├── deploymentTargetDropDown.xml ├── migrations.xml ├── deploymentTargetSelector.xml ├── gradle.xml └── inspectionProfiles │ └── Project_Default.xml ├── app ├── .gitignore ├── src │ └── main │ │ ├── cpp │ │ ├── cosmic │ │ │ ├── gpu │ │ │ │ ├── adreno │ │ │ │ │ └── .gitkeep │ │ │ │ ├── filters │ │ │ │ │ └── .gitkeep │ │ │ │ ├── vulcano │ │ │ │ │ ├── vram_allocator.h │ │ │ │ │ └── vram_allocator.cpp │ │ │ │ ├── functions.h │ │ │ │ ├── exhibition_engine.h │ │ │ │ ├── render_driver.h │ │ │ │ ├── renders.h │ │ │ │ ├── exhibition_engine.cpp │ │ │ │ ├── render_driver.cpp │ │ │ │ └── graphics_layer.h │ │ │ ├── cdvd │ │ │ │ ├── cd_header.h │ │ │ │ └── cd_header.cpp │ │ │ ├── input │ │ │ │ ├── memcard.h │ │ │ │ ├── sio2_io.h │ │ │ │ ├── padjoy.cpp │ │ │ │ ├── sio2_io.cpp │ │ │ │ └── memcard.cpp │ │ │ ├── usb │ │ │ │ ├── dev_ir.h │ │ │ │ └── dev_ir.cpp │ │ │ ├── snd │ │ │ │ ├── stream_buffer.h │ │ │ │ └── stream_buffer.cpp │ │ │ ├── gamedb │ │ │ │ ├── title_patches.cpp │ │ │ │ └── title_patches.h │ │ │ ├── cpu │ │ │ │ ├── cyclic32.h │ │ │ │ ├── verify_features.h │ │ │ │ ├── verify_features.cpp │ │ │ │ └── cyclic32.cpp │ │ │ ├── ipu │ │ │ │ ├── decoder_fifo.cpp │ │ │ │ ├── chrome_table.h │ │ │ │ ├── chrome_table.cpp │ │ │ │ ├── decoder_fifo.h │ │ │ │ ├── data_matrix.h │ │ │ │ └── ipu_core.h │ │ │ ├── pshook │ │ │ │ ├── psx_native.cpp │ │ │ │ ├── hk_psx.h │ │ │ │ └── hk_psx.cpp │ │ │ ├── fishron │ │ │ │ ├── jitter_arm64_ee.cpp │ │ │ │ ├── jitter_arm64_ee.h │ │ │ │ └── emitter_common.h │ │ │ ├── ee │ │ │ │ ├── cop_dma.cpp │ │ │ │ ├── cop1_fu.h │ │ │ │ ├── ee_intc.h │ │ │ │ ├── ee_intc.cpp │ │ │ │ ├── ee_timers.h │ │ │ │ └── ee_plus.cpp │ │ │ ├── common │ │ │ │ ├── global.h │ │ │ │ ├── alias.h │ │ │ │ ├── app.h │ │ │ │ ├── except.h │ │ │ │ ├── except.cpp │ │ │ │ ├── logger.cpp │ │ │ │ ├── app.cpp │ │ │ │ ├── logger.h │ │ │ │ └── types.h │ │ │ ├── creeper │ │ │ │ ├── fastmem.h │ │ │ │ ├── fastmem.cpp │ │ │ │ ├── vector_int_lower1.cpp │ │ │ │ ├── mips_fpu.cpp │ │ │ │ ├── psx_interpreter.h │ │ │ │ ├── psx_ep1.cpp │ │ │ │ └── vector_codes.h │ │ │ ├── vu │ │ │ │ ├── vu_info.h │ │ │ │ ├── v01_cop2vu.h │ │ │ │ ├── vu_time.cpp │ │ │ │ ├── vif_fifo.h │ │ │ │ ├── vu1_xgkick.cpp │ │ │ │ ├── vif_fifo.cpp │ │ │ │ ├── v01_cop2vu.cpp │ │ │ │ └── vif10_upload.h │ │ │ ├── console │ │ │ │ ├── intc.h │ │ │ │ ├── backdoor.h │ │ │ │ ├── intc.cpp │ │ │ │ ├── bios_loader.h │ │ │ │ ├── backdoor.cpp │ │ │ │ ├── virt_devices.cpp │ │ │ │ └── virt_devices.h │ │ │ ├── hle │ │ │ │ ├── syscall_gate.h │ │ │ │ ├── bios_patch.h │ │ │ │ ├── group_mgr.h │ │ │ │ ├── bios_info.h │ │ │ │ └── bios_info.cpp │ │ │ ├── spu │ │ │ │ ├── sound_core.cpp │ │ │ │ └── sound_core.h │ │ │ ├── iop │ │ │ │ ├── iop_intc.h │ │ │ │ ├── iop_info.h │ │ │ │ ├── iop_cop.h │ │ │ │ ├── iop_dma.cpp │ │ │ │ ├── iop_timers.h │ │ │ │ ├── iop_dma.h │ │ │ │ ├── iop_intc.cpp │ │ │ │ ├── iop_cop.cpp │ │ │ │ └── iop_core.h │ │ │ ├── gs │ │ │ │ ├── gs_tables.h │ │ │ │ ├── gif_queuev8.cpp │ │ │ │ ├── gs_engine.h │ │ │ │ ├── gs_engine.cpp │ │ │ │ └── gif_packed.cpp │ │ │ ├── vm │ │ │ │ ├── watch.h │ │ │ │ ├── emu_vm.h │ │ │ │ ├── emu_thread.h │ │ │ │ └── step_frameloop.cpp │ │ │ ├── os │ │ │ │ ├── jclasses.h │ │ │ │ ├── env.h │ │ │ │ ├── jclasses.cpp │ │ │ │ ├── mapped.h │ │ │ │ ├── neon_simd.h │ │ │ │ └── system_state.h │ │ │ └── mio │ │ │ │ ├── blocks.h │ │ │ │ ├── mmu_tlb.h │ │ │ │ └── mmu_tlb.cpp │ │ ├── addons │ │ │ └── compile_this.cpp │ │ ├── emu_user.cpp │ │ ├── drivers_glvk_jni.cpp │ │ ├── jvm_comm.cpp │ │ └── bios_jni.cpp │ │ ├── assets │ │ └── countries │ │ │ ├── ch.png │ │ │ ├── eu.png │ │ │ ├── hk.png │ │ │ ├── jp.png │ │ │ └── us.png │ │ ├── res │ │ ├── xml │ │ │ ├── backup_rules.xml │ │ │ ├── data_extraction_rules.xml │ │ │ ├── settings.xml │ │ │ ├── graphics_settings.xml │ │ │ └── global_settings.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── values │ │ │ ├── styles.xml │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── layout │ │ │ ├── preference_switch_material3.xml │ │ │ ├── games_folder_fragment.xml │ │ │ ├── emulation_activity.xml │ │ │ ├── settings_activity.xml │ │ │ ├── about_dialog.xml │ │ │ ├── bios_activity.xml │ │ │ ├── driver_activity.xml │ │ │ └── main_activity.xml │ │ ├── drawable │ │ │ ├── button_round_add_24.xml │ │ │ ├── button_round_save_24.xml │ │ │ ├── button_round_snippet_folder_24.xml │ │ │ ├── button_round_games_24.xml │ │ │ └── button_round_settings_24.xml │ │ ├── menu │ │ │ └── main_menu.xml │ │ ├── values-night │ │ │ └── themes.xml │ │ └── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ ├── java │ │ └── emu │ │ │ └── cosmic │ │ │ ├── data │ │ │ ├── Permissions.kt │ │ │ ├── DriverMeta.kt │ │ │ ├── BiosInfo.kt │ │ │ ├── CosmicKeys.kt │ │ │ ├── DelegateDataStore.kt │ │ │ └── CosmicSettings.kt │ │ │ ├── fragments │ │ │ ├── SettingsFragment.kt │ │ │ ├── GlobalSettingsFragment.kt │ │ │ ├── GamesFolderFragment.kt │ │ │ └── GraphicsSettingsFragment.kt │ │ │ ├── settings │ │ │ └── OpenSAFContract.kt │ │ │ ├── models │ │ │ └── EmulationModel.kt │ │ │ ├── adapters │ │ │ ├── GenericListContainer.kt │ │ │ ├── SelectableViewAdapter.kt │ │ │ └── GenericViewAdapter.kt │ │ │ ├── helpers │ │ │ └── PermissionHelper.kt │ │ │ ├── listeners │ │ │ ├── ActivitySwapperListener.kt │ │ │ └── PickerProviderListeners.kt │ │ │ ├── dialogs │ │ │ └── AboutDialog.kt │ │ │ ├── CosmicApplication.kt │ │ │ └── views │ │ │ ├── DriverViewItem.kt │ │ │ └── BiosViewItem.kt │ │ └── AndroidManifest.xml └── proguard-rules.pro ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .gitignore ├── settings.gradle ├── README.md ├── LICENSE.md └── .gitmodules /.idea/.name: -------------------------------------------------------------------------------- 1 | cosmic-emu -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/adreno/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/filters/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/cdvd/cd_header.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cosmic::cdvd { 4 | 5 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/input/memcard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cosmic::input { 4 | 5 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/input/sio2_io.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cosmic::input { 4 | 5 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/usb/dev_ir.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cosmic::usb { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/snd/stream_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace cosmic::snd { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/usb/dev_ir.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::usb { 3 | 4 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/input/padjoy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::input { 3 | 4 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/input/sio2_io.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::input { 3 | 4 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/cdvd/cd_header.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::cdvd { 3 | 4 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/input/memcard.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::input { 4 | 5 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/snd/stream_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::snd { 3 | 4 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gamedb/title_patches.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::gamedb { 3 | 4 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/assets/countries/ch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/assets/countries/ch.png -------------------------------------------------------------------------------- /app/src/main/assets/countries/eu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/assets/countries/eu.png -------------------------------------------------------------------------------- /app/src/main/assets/countries/hk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/assets/countries/hk.png -------------------------------------------------------------------------------- /app/src/main/assets/countries/jp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/assets/countries/jp.png -------------------------------------------------------------------------------- /app/src/main/assets/countries/us.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/assets/countries/us.png -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shadergz/cosmic-station/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # These classes and methods are called through JNI and should not be obfuscated in the release build 2 | -keep class emu.cosmic.** { *; } 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/cpu/cyclic32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | namespace cosmic::cpu { 7 | u32 check32(std::span chkData); 8 | } 9 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ipu/decoder_fifo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::ipu { 4 | void DecoderFifo::resetDeck() { 5 | isCacheDirty = true; 6 | size = capacity = 0; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 2 | 3 | android.useAndroidX=true 4 | 5 | kotlin.code.style=official 6 | 7 | android.nonTransitiveRClass=true 8 | org.gradle.unsafe.configuration-cache=true 9 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ipu/chrome_table.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | namespace cosmic::ipu { 5 | class [[maybe_unused]] ChTable { 6 | public: 7 | static std::array indexTable; 8 | }; 9 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ipu/chrome_table.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::ipu { 4 | std::array ChTable::indexTable { 5 | 0, 0, 3, 4, 6 | 5, 6, 7, 8, 7 | 9, 10, 8 | }; 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/pshook/psx_native.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::pshook{ 4 | struct PsxRegs r; 5 | 6 | void psxAbs() { 7 | r.v0->s = abs(r.a0->s); 8 | *r.pc = *r.ra; 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gamedb/title_patches.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::gamedb { 6 | struct SwitchPath { 7 | u32 gameCase, rCase; 8 | }; 9 | 10 | using SwitchPatches = std::vector; 11 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jul 18 23:12:32 BRT 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ipu/decoder_fifo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | namespace cosmic::ipu { 5 | class DecoderFifo { 6 | public: 7 | void resetDeck(); 8 | 9 | bool isCacheDirty; 10 | u16 size; 11 | u16 capacity; 12 | }; 13 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/data/Permissions.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.data 2 | 3 | import android.Manifest 4 | 5 | class Permissions(val permissions: Array) { 6 | companion object { 7 | val storageAccess = Permissions(arrayOf(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) 8 | } 9 | } -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/cpp/addons/compile_this.cpp: -------------------------------------------------------------------------------- 1 | // https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/quick_start.html#quick_start_project_setup 2 | #define VMA_IMPLEMENTATION 3 | #define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2 4 | #define VMA_STATIC_VULKAN_FUNCTIONS 0 5 | #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 6 | #include 7 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/data/DriverMeta.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.data 2 | 3 | data class DriverMeta( 4 | val name: String, 5 | val description: String, 6 | val author: String, 7 | val packageVersion: String, 8 | val vendor: String, 9 | val driverVersion: String, 10 | val minApi: String, 11 | val libraryName: String, 12 | ) 13 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/fishron/jitter_arm64_ee.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::fishron { 4 | u32 EeArm64Jitter::executeCode() { 5 | return {}; 6 | } 7 | u32 EeArm64Jitter::fetchPcInst(u32 address) { 8 | return {}; 9 | } 10 | void EeArm64Jitter::performInvalidation(u32 address) { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | app/release 17 | app/release/ 18 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ee/cop_dma.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::ee { 5 | bool CtrlCop::getCondition() { 6 | u32 stat{mio::BitBashing(dmac->performRead(0x1000e10)) & 0x3ff}; 7 | u32 pcr{mio::BitBashing(dmac->performRead(0x1000e020)) & 0x3ff}; 8 | return ((~pcr | stat) & 0x3ff) == 0x3ff; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = 'cosmic-emu' 16 | include ':app' 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/global.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | namespace cosmic { 8 | extern std::unique_ptr states; 9 | extern std::shared_ptr user; 10 | extern std::shared_ptr app; 11 | 12 | extern thread_local os::CosmicEnv cosmicEnv; 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/fragments/SettingsFragment.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.preference.PreferenceFragmentCompat 5 | import emu.cosmic.R 6 | 7 | class SettingsFragment : PreferenceFragmentCompat() { 8 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 9 | addPreferencesFromResource(R.xml.settings) 10 | } 11 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/preference_switch_material3.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/vulcano/vram_allocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::gpu::vulcano { 6 | class GraphicsLayer; 7 | class VramManager { 8 | public: 9 | VramManager(Ref& gpu); 10 | ~VramManager(); 11 | private: 12 | VmaAllocator vma{VK_NULL_HANDLE}; 13 | [[maybe_unused]] Ref graphics; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/vulcano/vram_allocator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::gpu::vulcano { 4 | VramManager::VramManager(Ref& gpu) : graphics(gpu) { 5 | VmaAllocatorCreateInfo allocatorInfo{}; 6 | vmaCreateAllocator(&allocatorInfo, &vma); 7 | } 8 | VramManager::~VramManager() { 9 | if (vma != VK_NULL_HANDLE) 10 | vmaDestroyAllocator(vma); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | namespace cosmic::gpu { 7 | class GraphicsLayer; 8 | class GraphicsFunctionsRef { 9 | public: 10 | std::function notifySurfaceChange; 11 | std::function prepareGraphicsApi; 12 | std::function displayApiVersion; 13 | }; 14 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/creeper/fastmem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | namespace cosmic::creeper { 8 | class CachedFastPc { 9 | public: 10 | CachedFastPc(); 11 | bool isFastMemoryEnb{true}; 12 | 13 | bool checkPc(u32 pc); 14 | void pushVpc(u32 pc, u8* vpc); 15 | std::pair fastFetch(u32 pc); 16 | private: 17 | std::map savedPc; 18 | }; 19 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/fragments/GlobalSettingsFragment.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.preference.PreferenceFragmentCompat 5 | import emu.cosmic.R 6 | 7 | class GlobalSettingsFragment : PreferenceFragmentCompat() { 8 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 9 | addPreferencesFromResource(R.xml.global_settings) 10 | activity?.actionBar?.title = "Global Settings" 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_round_add_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ipu/data_matrix.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | namespace cosmic::ipu { 4 | template 5 | class DataMatrix { 6 | public: 7 | DataMatrix() { 8 | } 9 | std::span operator[](const u64 idX) { 10 | return {&data[4 * idX], 4}; 11 | } 12 | 13 | void clear() { 14 | ranges::fill(data, T{}); 15 | } 16 | 17 | private: 18 | std::array data; 19 | }; 20 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/games_folder_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/fishron/jitter_arm64_ee.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::fishron { 6 | class EeArm64Jitter : public ee::EeExecutor { 7 | public: 8 | EeArm64Jitter(Ref intCpu) : 9 | EeExecutor(intCpu) {} 10 | u32 executeCode() override; 11 | u32 fetchPcInst(u32 address) override; 12 | void performInvalidation(u32 address) override; 13 | 14 | std::unique_ptr emitter; 15 | }; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/exhibition_engine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | namespace cosmic::gpu { 7 | class ExhibitionEngine { 8 | public: 9 | ExhibitionEngine(); 10 | ~ExhibitionEngine(); 11 | void inheritSurface(jobject surface); 12 | private: 13 | jobject globalSurface{}; 14 | ANativeWindow* window{}; 15 | 16 | RenderApi graphics{HardwareOpenGL}; 17 | GraphicsLayer scene{graphics}; 18 | }; 19 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vu/vu_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | namespace cosmic::vu { 5 | class VectorUnit; 6 | class VuMicroExecutor { 7 | public: 8 | VuMicroExecutor(Ref& vu) : vuMicro(vu) { 9 | } 10 | virtual u32 executeCode() = 0; 11 | 12 | virtual void setCurrentProgram(u32 crc) = 0; 13 | 14 | virtual std::pair fetchPcInst() = 0; 15 | virtual ~VuMicroExecutor() = default; 16 | protected: 17 | Ref vuMicro; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/console/intc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::vm { 6 | class EmuVm; 7 | } 8 | namespace cosmic::console { 9 | enum IntControllers { 10 | EeInt, 11 | IopInt, 12 | }; 13 | 14 | class IntCInfra { 15 | public: 16 | IntCInfra(vm::EmuVm& vm); 17 | void checkInt(IntControllers tni); 18 | void trapIrq(IntControllers in, u8 id); 19 | void resetPic(); 20 | 21 | iop::IopIntC iopInt; 22 | ee::EeIntC eeInt; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/render_driver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | namespace cosmic::gpu { 5 | using LinkableObject = void*; 6 | class RenderDriver { 7 | public: 8 | RenderDriver() = default; 9 | ~RenderDriver(); 10 | void operator=(LinkableObject devDriver) { 11 | driver = devDriver; 12 | } 13 | LinkableObject driver{}; 14 | PFN_vkGetInstanceProcAddr vulkanInstanceAddr{}; 15 | 16 | void pickUserRender(const RenderApi api, bool reload = false); 17 | bool loadVulkanDriver(); 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/alias.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic { 6 | using i8 = std::int8_t; 7 | using u8 = std::uint8_t; 8 | using u16 = std::uint16_t; 9 | 10 | using i32 = std::int32_t; 11 | using i16 = std::int16_t; 12 | 13 | using u32 = std::uint32_t; 14 | 15 | using i64 = std::int64_t; 16 | using u64 = std::uint64_t; 17 | 18 | using u128 = uint64x2_t; 19 | using u256 = uint64x1x4_t; 20 | 21 | using f32 = float32_t; 22 | using f64 = float64_t; 23 | using f128 = float64x2_t; 24 | using f512 = float64x2x4_t; 25 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/console/backdoor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | namespace cosmic::vm { 7 | class EmuVm; 8 | } 9 | namespace cosmic::console { 10 | class BackDoor { 11 | public: 12 | BackDoor(vm::EmuVm& aliveVm); 13 | Ref openVm(); 14 | void leaveVm(Ref& lvm); 15 | private: 16 | std::thread::id owner; 17 | std::mutex echo; 18 | Ref vm; 19 | i32 vmRefs; 20 | }; 21 | } 22 | namespace cosmic { 23 | extern std::shared_ptr outside; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_round_save_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vu/v01_cop2vu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::vu { 6 | // Just a communication interface between these two VUs 7 | class MacroModeCop2 { 8 | public: 9 | MacroModeCop2(Ref vus[2]); 10 | void clearInterlock(); 11 | bool checkInterlock(); 12 | bool interlockCheck(bool isCop2); 13 | 14 | u32 cfc2(u32 special); 15 | void ctc2(u32 special, u32 value); 16 | 17 | Ref v0; 18 | Ref v1; 19 | bool cop2il, 20 | vuIl; 21 | }; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/renders.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | namespace cosmic::gpu { 6 | enum RenderApi : u8 { 7 | SoftwareSlow, 8 | HardwareVulkan, 9 | HardwareOpenGL 10 | }; 11 | struct Gl3Render { 12 | EGLSurface surface; 13 | }; 14 | constexpr u32 vkVersion{VK_API_VERSION_1_2}; 15 | constexpr u32 appImplVersion{VK_MAKE_VERSION(1, 1, 4)}; 16 | struct VkRender { 17 | VkRender() {} 18 | std::optional surface; 19 | std::unique_ptr allocator; 20 | }; 21 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_round_snippet_folder_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/cpu/verify_features.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | namespace cosmic::cpu { 9 | struct FeaturesImpl { 10 | bool isArchOptional; 11 | std::string family; 12 | }; 13 | struct ISA64 { 14 | enum { 15 | Crc32 16 | }; 17 | static constexpr auto size{Crc32 + 1}; 18 | }; 19 | extern const std::array features; 20 | 21 | class HostFeatures { 22 | public: 23 | HostFeatures(); 24 | bool haveCrc32C(); 25 | private: 26 | std::bitset mrsA64; 27 | }; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/pshook/hk_psx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace cosmic::iop { 6 | class IoMipsCore; 7 | } 8 | namespace cosmic::pshook { 9 | union PsxR { u32 i; i32 s; }; 10 | struct PsxRegs { 11 | PsxR* a0; 12 | PsxR* v0; 13 | PsxR* ra; 14 | PsxR* pc; 15 | }; 16 | extern struct PsxRegs r; 17 | 18 | void psxAbs(); 19 | 20 | extern struct PsxRegs eeSavedCtx; 21 | extern struct PsxRegs iopSavedCtx; 22 | 23 | class EstablishHook { 24 | public: 25 | static void hookIoPsx(u32 base, iop::IoMipsCore& psx); 26 | private: 27 | static std::mutex owner; 28 | static u8 isEe; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 13 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/hle/syscall_gate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | namespace cosmic::hle { 7 | enum CallParams { 8 | Return = ee::$v0, 9 | Param0 = ee::$a0, 10 | Param1 = ee::$a1, 11 | Param2 = ee::$a2, 12 | Param3 = ee::$a3, 13 | Param4 = ee::$t0, 14 | Param5 = ee::$t1 15 | }; 16 | 17 | enum SyscallOrigin { 18 | SysEmotionEngine, 19 | SysIop 20 | }; 21 | class SyscallDealer { 22 | public: 23 | SyscallDealer(); 24 | void doSyscall(SyscallOrigin origin, i16 sys); 25 | private: 26 | void resetEe(); 27 | Ref vm; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/settings/OpenSAFContract.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.settings 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.net.Uri 7 | import androidx.activity.result.contract.ActivityResultContract 8 | 9 | class OpenASFContract : ActivityResultContract() { 10 | override fun createIntent(context: Context, input: String): Intent = 11 | Intent(Intent.ACTION_OPEN_DOCUMENT).apply { 12 | type = input 13 | } 14 | override fun parseResult(resultCode: Int, intent: Intent?): Uri? { 15 | if (resultCode != Activity.RESULT_OK || intent == null) 16 | return null 17 | return intent.data!! 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/models/EmulationModel.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.models 2 | 3 | import androidx.lifecycle.ViewModel 4 | import kotlinx.coroutines.flow.MutableStateFlow 5 | import kotlinx.coroutines.flow.asStateFlow 6 | import java.util.concurrent.atomic.AtomicBoolean 7 | 8 | data class EmulationStatus( 9 | var running: AtomicBoolean = AtomicBoolean(false) 10 | ) 11 | 12 | class EmulationModel : ViewModel() { 13 | private val status = MutableStateFlow(EmulationStatus()) 14 | private val liveStatus = status.asStateFlow() 15 | fun checkRunning(state: Boolean) { 16 | liveStatus.value.apply { 17 | running.set(state) 18 | } 19 | } 20 | fun isRunning(): Boolean { 21 | return liveStatus.value.running.get() 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/adapters/GenericListContainer.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.adapters 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.viewbinding.ViewBinding 7 | 8 | interface ViewBindingFactory { 9 | fun create(parent: ViewGroup): ViewBinding 10 | } 11 | fun View.inflater() = LayoutInflater.from(context)!! 12 | 13 | abstract class GenericListContainer { 14 | var adaptedBy: GenericViewAdapter? = null 15 | 16 | abstract fun getFactory(): ViewBindingFactory 17 | abstract fun bind(holder: GenericViewHolder, position: Int) 18 | 19 | abstract fun compareItem(prob: GenericListContainer): Boolean 20 | abstract fun isTheSame(prob: GenericListContainer): Boolean 21 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/creeper/fastmem.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::creeper { 4 | CachedFastPc::CachedFastPc() { 5 | savedPc.clear(); 6 | } 7 | void CachedFastPc::pushVpc(u32 pc, u8* vpc) { 8 | if (checkPc(pc)) 9 | return; 10 | else 11 | savedPc[pc & 0xfffff000] = vpc; 12 | } 13 | bool CachedFastPc::checkPc(u32 pc) { 14 | return savedPc.contains(pc & 0xfffff000); 15 | } 16 | std::pair CachedFastPc::fastFetch(u32 pc) { 17 | if (!checkPc(pc)) 18 | return std::make_pair(0, false); 19 | auto roMem{savedPc[pc & 0xfffff000]}; 20 | auto pcVal{reinterpret_cast(roMem + (pc & 0xfff))}; 21 | return std::make_pair(*pcVal, true); 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vu/vu_time.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::vu { 3 | // We will halt the execution of instructions and advance the pipeline until the div/efu event concludes 4 | void VectorUnit::finishStallPipeTask(bool isDiv) { 5 | bool isWaiting; 6 | i64 after{isDiv ? status.div.finishAfter : status.efu.finishAfter}; 7 | if (!isDiv) 8 | after -= 0x1; 9 | for ( ;; ) { 10 | isWaiting = clock.count < after; 11 | if (!isWaiting) 12 | break; 13 | // Simulating a wait, clock synchronization needs to be perfect here 14 | clock.count++; 15 | 16 | updateMacPipeline(); 17 | intPipeline.update(); 18 | } 19 | updateDivEfuPipes(); 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/fragments/GamesFolderFragment.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.fragments 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import emu.cosmic.databinding.GamesFolderFragmentBinding 9 | 10 | class GamesFolderFragment : Fragment() { 11 | private val binding by lazy { GamesFolderFragmentBinding.inflate(layoutInflater) } 12 | override fun onCreateView( 13 | inflater: LayoutInflater, 14 | container: ViewGroup?, 15 | savedInstanceState: Bundle? 16 | ): View { 17 | // val layout = LinearLayoutManager(context) 18 | // binding.gamesFolders.layoutManager = layout 19 | return binding.root.rootView 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/creeper/vector_int_lower1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::creeper { 5 | void VuMicroInterpreter::iddai(VuMicroOperands& ops) { 6 | vu->pushIntPipe(ops.ft & 0xf, ops.fs & 0xf); 7 | auto imm{static_cast((ops.inst >> 6) & 0x1f)}; 8 | imm = static_cast((imm & 0x10 ? 0xfff0 : 0) | (imm & 0xf)); 9 | 10 | auto vui{vu->intsRegs[ops.fs].sig + imm}; 11 | vu->intsRegs[ops.ft] = vui; 12 | } 13 | void VuMicroInterpreter::mtir(VuMicroOperands& ops) { 14 | vu->pushIntPipe(ops.ft & 0xf, 0); 15 | u32 id{(ops.inst >> 21) & 0x3}; 16 | if (id > 3) { 17 | } 18 | vu->intsRegs[ops.ft & 0xf].uns = static_cast(vu->vuGPRs[ops.fs].uns[id]); 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ee/cop1_fu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | namespace cosmic::ee { 5 | union FpuReg { 6 | f32 decimal; 7 | u32 un; 8 | i32 si; 9 | }; 10 | struct Cop1Control { 11 | bool overflow; 12 | bool underflow; 13 | }; 14 | 15 | class FpuCop { 16 | public: 17 | FpuCop(); 18 | void resetFlu(); 19 | f32 sony754con(u32 value); 20 | u32 c1cfc(u8 index); 21 | 22 | void checkOverflow(u8 reg); 23 | void checkUnderflow(u8 reg); 24 | 25 | union { 26 | std::array fprRegs; 27 | }; 28 | FpuReg acc; 29 | FpuReg fpuId; 30 | Cop1Control status; 31 | private: 32 | const std::string fpuGpr2String(u8 id) const; 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace cosmic { 12 | class CoreApplication { 13 | public: 14 | CoreApplication(); 15 | std::shared_ptr getBiosMgr(); 16 | const std::string& getDeviceName(); 17 | 18 | std::string lastSetSync; 19 | std::unique_ptr vm; 20 | std::shared_ptr scene; 21 | private: 22 | std::shared_ptr simulated; 23 | 24 | cpu::HostFeatures riscFeatures{}; 25 | 26 | i32 apiLevel{-1}; 27 | std::string artDeviceName{}; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/console/intc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | namespace cosmic::console { 4 | IntCInfra::IntCInfra(vm::EmuVm& vm) : 5 | iopInt(vm.iop), 6 | eeInt(vm.mips, vm.scheduler) 7 | { 8 | } 9 | void IntCInfra::resetPic() { 10 | // iopInt.stat = 0; 11 | // iopInt.mask = 0; 12 | // iopInt.ctrl = 0; 13 | eeInt.resetEeInterrupt(); 14 | iopInt.resetInterrupt(); 15 | } 16 | void IntCInfra::checkInt(IntControllers tni) { 17 | switch (tni) { 18 | case IopInt: 19 | iopInt.iopCheck(); break; 20 | case EeInt: 21 | break; 22 | } 23 | } 24 | void IntCInfra::trapIrq(IntControllers in, u8 id) { 25 | if (in == EeInt) { 26 | eeInt.raiseIrq(id); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/spu/sound_core.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::spu { 3 | void Spu2::resetSound() { 4 | status = {}; 5 | transferAddr = 0; 6 | currentAddr = 0; 7 | 8 | } 9 | void Spu2::writeDmaData(u32 data) { 10 | if (!status.dmaReady) { 11 | } 12 | spuWrite(data & 0xffff); 13 | 14 | status.dmaBusy = true; 15 | status.dmaReady = false; 16 | } 17 | u32 Spu2::requestDmaData() { 18 | return 0; 19 | } 20 | 21 | u16 Spu2::spuRead(u32 address) { 22 | return 0; 23 | } 24 | void Spu2::spuWrite(u32 address, u16 value) { 25 | *PipeCraftPtr(spuRam, address, mio::Spu2Dev) = value; 26 | } 27 | void Spu2::spuWrite(u16 value) { 28 | *PipeCraftPtr(spuRam, currentAddr, mio::Spu2Dev) = value; 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/hle/bios_patch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | namespace cosmic::hle { 10 | class BiosPatcher { 11 | public: 12 | BiosPatcher(std::shared_ptr& core) : 13 | group(std::make_shared()), 14 | mips(core) { 15 | } 16 | void resetBios(); 17 | void emit(u32 address); 18 | 19 | std::shared_ptr group; 20 | private: 21 | u32 prodAsmIntHandler(); 22 | void andIntCStatToT2(u32& range); 23 | void regsFromKernel0(u32& range, bool save); 24 | void intCAndJump(u32& range); 25 | 26 | std::shared_ptr mips; 27 | std::array intCodeAsm; 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cosmic Project 2 | 3 | ### Project Progress: 4 | - Progression: ```19%``` 5 | - Current version: 0.0.20 (Pre-Boot stage) 6 | - Target for the first demo release: 2025 7 | - Top priority: Test everything already done 8 | 9 | ### Special thanks 10 | - [DobieStation](https://github.com/PSI-Rockin/DobieStation.git) Used as the main reference for the project 11 | - [libadrenotools](https://github.com/bylaws/libadrenotools.git) The custom driver management system was implemented following the steps of this project 12 | 13 | ### Reference to third-party resources and assets used 14 | - ```assets/countries/*``` [flag-icons](https://github.com/lipis/flag-icons.git) 15 | 16 | ### References, similar projects and researches 17 | - [Attacking the Qualcomm Adreno GPU](https://googleprojectzero.blogspot.com/2020/09/attacking-qualcomm-adreno-gpu.html) by Ben Hawkes, Project Zero 18 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ee/ee_intc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace cosmic::ee { 4 | class EeMipsCore; 5 | 6 | enum EmotionTrapCode { 7 | Gs, 8 | SBus, 9 | VBlankStart, 10 | VBlankEnd, 11 | Vif0, 12 | Vif1, 13 | Vu0, 14 | Vu1, 15 | T0, T1, T2, T3, 16 | SFifo, 17 | VuoWatchDog 18 | }; 19 | class EeIntC { 20 | public: 21 | EeIntC(std::shared_ptr& mips, 22 | std::shared_ptr& sq); 23 | 24 | void raiseIrq(u8 id); 25 | void int0Check(); 26 | 27 | void resetEeInterrupt(); 28 | private: 29 | u32 intcStat, 30 | intcMask; 31 | vm::CallBackId check0Id{}; 32 | std::shared_ptr ee; 33 | std::shared_ptr sched; 34 | }; 35 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ee/ee_intc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::ee { 5 | EeIntC::EeIntC(std::shared_ptr& mips, std::shared_ptr& sq) : 6 | ee(mips), sched(sq) { 7 | intcStat = {}; 8 | intcMask = {}; 9 | } 10 | void EeIntC::resetEeInterrupt() { 11 | intcStat = {}; 12 | intcMask = {}; 13 | check0Id = sched->createSchedTick(true, [this](u64 unused0, bool unused1) { 14 | int0Check(); 15 | }); 16 | } 17 | 18 | void EeIntC::raiseIrq(u8 id) { 19 | intcStat |= (1 << id); 20 | 21 | // Some games utilize a wait-for-VBLANK loop in which they continuously check the INTC_STAT 22 | // while a VBLANK interrupt handler is active 23 | sched->placeTickedTask(check0Id, 0x8, {}, true); 24 | } 25 | void EeIntC::int0Check() { 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/hle/group_mgr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | namespace cosmic::hle { 8 | class HleBiosGroup { 9 | public: 10 | HleBiosGroup(); 11 | 12 | bool storeAndFill(jobject model, BiosInfo&& bios); 13 | bool isAlreadyAdded(std::array& is, bool usePos = false); 14 | bool rmFromStorage(std::array& rmBy, bool usePos = true); 15 | void discardAll(); 16 | i32 choice(std::array& chBy, bool usePos = false); 17 | 18 | bool loadBiosBy(jobject model, std::array& ldBy, bool usePos = false); 19 | void readBios(std::span loadHere); 20 | 21 | std::unique_ptr slotBios; 22 | private: 23 | bool isCrucial{}; 24 | std::list biosList; 25 | 26 | console::BiosLoader loader{}; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_intc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | // 1F801070h I_STAT - Interrupt status register (R=Status, W=Acknowledge) 5 | // 1F801074h I_MASK - Interrupt mask register (R/W) 6 | 7 | namespace cosmic::iop { 8 | class IoMipsCore; 9 | class IopIntC { 10 | public: 11 | IopIntC(std::shared_ptr& mips) : 12 | iop(mips) {} 13 | void iopCheck(); 14 | void resetInterrupt(); 15 | 16 | void assertIrq(i32 id); 17 | u32 readStat(); 18 | u32 readMask(); 19 | u32 readICtrl(); 20 | 21 | void wrStat(u32 st); 22 | void wrMask(u32 overMask); 23 | void wrCtrl(u32 ic); 24 | // Status: Read I_STAT (1=IRQ raised) 25 | // Mask: Read/Write I_MASK (0=Disabled 1=Enabled) 26 | u32 stat, mask, ctrl; 27 | private: 28 | std::shared_ptr iop; 29 | }; 30 | } -------------------------------------------------------------------------------- /app/src/main/cpp/emu_user.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern "C" 6 | JNIEXPORT void JNICALL 7 | Java_emu_cosmic_EmulationActivity_swtSurfaceContext(JNIEnv* env, jobject thiz, jobject surface) { 8 | cosmic::app->scene->inheritSurface(surface); 9 | } 10 | 11 | std::atomic is{false}; 12 | extern "C" 13 | JNIEXPORT void JNICALL 14 | Java_emu_cosmic_EmulationActivity_startEmulator(JNIEnv* env, jobject thiz) { 15 | cosmic::CosmicException::setExceptionClass(thiz); 16 | 17 | cosmic::app->vm->resetVm(); 18 | is = 1; 19 | cosmic::app->vm->startVm(); 20 | 21 | for (; is ;) 22 | std::this_thread::sleep_for(std::chrono::nanoseconds(10'000)); 23 | cosmic::app->vm->stopVm(); 24 | } 25 | extern "C" 26 | JNIEXPORT void JNICALL 27 | Java_emu_cosmic_EmulationActivity_stopEmulator(JNIEnv* env, jobject thiz) { 28 | if (is != 0) 29 | is = 0; 30 | } -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/adapters/SelectableViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.adapters 2 | 3 | class SelectableViewAdapter(private val defaultPos: Int) : GenericViewAdapter() { 4 | var selectedPos = defaultPos 5 | fun selectItem(position: Int) { 6 | if (selectedPos != position) { 7 | notifyItemChanged(selectedPos) 8 | notifyItemChanged(position) 9 | } else { 10 | notifyItemChanged(position) 11 | } 12 | selectedPos = position 13 | } 14 | 15 | fun popItem(position: Int) { 16 | dropItem(position) 17 | if (position < selectedPos) 18 | selectedPos-- 19 | else if (position == selectedPos) 20 | selectItem(defaultPos) 21 | notifyItemRemoved(position) 22 | } 23 | 24 | fun insertItem(position: Int, item: GenericListContainer<*>) { 25 | addItem(item, position) 26 | notifyItemInserted(position) 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/cpu/verify_features.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::cpu { 4 | const std::array features{ 5 | {{true, "crc32cb,crc32ch,crc32cw,crc32cx"}} 6 | }; 7 | 8 | HostFeatures::HostFeatures() { 9 | u64 el1; 10 | asm volatile("mrs %0, ID_AA64ISAR0_EL1" : "=r" (el1)); 11 | 12 | for (u32 feat{}; feat < features.size(); feat++) { 13 | u8 have{}; 14 | switch (feat) { 15 | case ISA64::Crc32: 16 | have = ((el1 >> 4) & 0xf) != 0; 17 | break; 18 | } 19 | if (!features[feat].isArchOptional && !have) { 20 | throw AppErr("Your CPU SoC doesn't support the required family of instructions {}", features[feat].family); 21 | } 22 | mrsA64.set(feat, have); 23 | } 24 | } 25 | 26 | bool HostFeatures::haveCrc32C() { 27 | return mrsA64[ISA64::Crc32]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_round_games_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/xml/graphics_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 16 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright <2024> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/console/bios_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace cosmic::console { 10 | #pragma pack(push, 1) 11 | struct RomEntry { 12 | std::array entity; 13 | [[maybe_unused]] u8 ext[2]; 14 | u32 value; 15 | }; 16 | #pragma pack(pop) 17 | 18 | class BiosLoader { 19 | public: 20 | static constexpr u16 hdrSize{0x3000}; 21 | BiosLoader() = default; 22 | 23 | bool fetchBiosInfo(hle::BiosInfo& bios); 24 | void placeBios(std::span here); 25 | void triggerBios(hle::BiosInfo& info); 26 | private: 27 | bool isABios(); 28 | 29 | Ref getModule(const std::string model); 30 | bool loadVersionInfo(std::span info); 31 | void fillVersion(hle::BiosInfo& bios, std::span info); 32 | 33 | DescriptorRaii biosf{}; 34 | std::unique_ptr> romHeader; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/res/xml/global_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 14 | 15 | 16 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/cpp/drivers_glvk_jni.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | struct kgslDeviceGetProperty { 9 | cosmic::u32 type; 10 | void __user* value; 11 | __kernel_size_t sizeBytes; 12 | }; 13 | 14 | static constexpr cosmic::u8 kgslPropPWRCTRL{0xe}; 15 | static constexpr auto ioctlKgslSetProperty{_IOW(0x09, 0x32, struct kgslDeviceGetProperty)}; 16 | void driverSetTurbo(bool mode) { 17 | cosmic::u32 enable{mode ? 0U : 1U}; 18 | kgslDeviceGetProperty prop{ 19 | .type = kgslPropPWRCTRL, 20 | .value = cosmic::BitCast(&enable), 21 | .sizeBytes = sizeof(enable)}; 22 | 23 | cosmic::i32 kgslFd{open("/dev/kgsl-3d0", O_RDWR)}; 24 | if (kgslFd < 0) 25 | return; 26 | ioctl(kgslFd, ioctlKgslSetProperty, &prop); 27 | close(kgslFd); 28 | } 29 | 30 | extern "C" 31 | JNIEXPORT void JNICALL 32 | Java_emu_cosmic_helpers_DriverHelper_00024Companion_switchTurboMode(JNIEnv* env, jobject thiz, jboolean enable) { 33 | driverSetTurbo(enable); 34 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "app/src/main/cpp/addons/fmt"] 2 | path = app/src/main/cpp/addons/fmt 3 | url = https://github.com/fmtlib/fmt.git 4 | [submodule "app/src/main/cpp/addons/perfetto"] 5 | path = app/src/main/cpp/addons/perfetto 6 | url = https://android.googlesource.com/platform/external/perfetto 7 | [submodule "app/src/main/cpp/addons/range-v3"] 8 | path = app/src/main/cpp/addons/range-v3 9 | url = https://github.com/ericniebler/range-v3.git 10 | [submodule "app/src/main/cpp/addons/Vulkan-Hpp"] 11 | path = app/src/main/cpp/addons/Vulkan-Hpp 12 | url = https://github.com/KhronosGroup/Vulkan-Hpp.git 13 | [submodule "app/src/main/cpp/addons/VulkanMemoryAllocator"] 14 | path = app/src/main/cpp/addons/VulkanMemoryAllocator 15 | url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git 16 | [submodule "app/src/main/cpp/addons/imgui"] 17 | path = app/src/main/cpp/addons/imgui 18 | url = https://github.com/ocornut/imgui.git 19 | branch = v1.90.1 20 | 21 | [submodule "app/src/main/cpp/addons/boost"] 22 | path = app/src/main/cpp/addons/boost 23 | url = https://github.com/shadergz/boost.git 24 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/fragments/GraphicsSettingsFragment.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.fragments 2 | 3 | import android.os.Bundle 4 | import androidx.preference.PreferenceFragmentCompat 5 | import androidx.preference.TwoStatePreference 6 | import emu.cosmic.R 7 | import emu.cosmic.data.CosmicSettings 8 | import emu.cosmic.helpers.DriverHelper 9 | 10 | class GraphicsSettingsFragment : PreferenceFragmentCompat() { 11 | override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 12 | addPreferencesFromResource(R.xml.graphics_settings) 13 | activity?.actionBar?.title = "Graphics Settings" 14 | 15 | val gpuTurboMode = findPreference("gpuTurboMode") 16 | val settings = CosmicSettings.globalSettings 17 | 18 | gpuTurboMode?.setOnPreferenceChangeListener { _, value -> 19 | if (value is Boolean) { 20 | DriverHelper.switchTurboMode(value) 21 | settings.gpuTurboMode = value 22 | } 23 | true 24 | } 25 | gpuTurboMode?.isChecked = settings.gpuTurboMode 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/exhibition_engine.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | namespace cosmic::gpu { 6 | ExhibitionEngine::ExhibitionEngine() { 7 | } 8 | ExhibitionEngine::~ExhibitionEngine() { 9 | if (globalSurface) 10 | cosmicEnv->DeleteGlobalRef(globalSurface); 11 | } 12 | void ExhibitionEngine::inheritSurface(jobject surface) { 13 | if (cosmicEnv->IsSameObject(surface, nullptr)) 14 | return; 15 | if (globalSurface) 16 | cosmicEnv->DeleteGlobalRef(globalSurface); 17 | 18 | globalSurface = cosmicEnv->NewGlobalRef(surface); 19 | if (!globalSurface) { 20 | throw GpuErr("A Surface is required for us to control and inherit to the screen"); 21 | } 22 | window = ANativeWindow_fromSurface(*cosmicEnv, globalSurface); 23 | ANativeWindow_acquire(window); 24 | if (scene.notifySurfaceChange) 25 | scene.notifySurfaceChange(scene, surface); 26 | ANativeWindow_release(window); 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/data/BiosInfo.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.data 2 | 3 | import androidx.annotation.Keep 4 | import java.io.FileInputStream 5 | 6 | // For some reason, proguard-rules.pro can't keep these fields from KernelModel in release packages 7 | @Keep 8 | data class BiosInfo( 9 | var position: Int, 10 | 11 | var fileAlive: FileInputStream, 12 | var selected: Boolean, 13 | 14 | var biosPath: String, 15 | var biosName: String, 16 | var biosDetails: String 17 | ) { 18 | override fun equals(other: Any?): Boolean { 19 | val info = other as BiosInfo 20 | return (biosPath == info.biosPath) && 21 | (biosName == info.biosName) && 22 | (biosDetails == info.biosDetails) 23 | } 24 | override fun hashCode(): Int { 25 | var result = position 26 | result = 31 * result + fileAlive.hashCode() 27 | result = 31 * result + selected.hashCode() 28 | result = 31 * result + biosPath.hashCode() 29 | result = 31 * result + biosName.hashCode() 30 | result = 31 * result + biosDetails.hashCode() 31 | return result 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/helpers/PermissionHelper.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.helpers 2 | 3 | import android.Manifest 4 | import android.content.Context 5 | import android.os.Environment 6 | import androidx.core.content.ContextCompat 7 | import androidx.core.content.PermissionChecker 8 | import emu.cosmic.data.Permissions 9 | 10 | class PermissionHelper( 11 | val context: Context, 12 | private val perm: Permissions, 13 | private val request: (permission : Array) -> Unit) { 14 | 15 | fun checkForPermission() { 16 | val perms = perm.permissions 17 | perms.forEach { 18 | when (it) { 19 | Manifest.permission.MANAGE_EXTERNAL_STORAGE -> { 20 | if (!Environment.isExternalStorageManager()) 21 | return request(perms) 22 | } 23 | else -> { 24 | if (ContextCompat.checkSelfPermission(context, it) != 25 | PermissionChecker.PERMISSION_GRANTED) { 26 | return request(perms) 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/listeners/ActivitySwapperListener.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.listeners 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.util.AttributeSet 6 | import androidx.preference.Preference 7 | import emu.cosmic.settings.BiosActivity 8 | import emu.cosmic.settings.CustomDriverActivity 9 | 10 | class ActivitySwapperListener @JvmOverloads 11 | constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr : Int = androidx.preference.R.attr.preferenceStyle) : 12 | Preference(context, attrs, defStyleAttr) { 13 | private fun launchActivity() { 14 | var swapTo: Intent? = null 15 | when (title) { 16 | "BIOS Selector" -> { 17 | swapTo = Intent(context, BiosActivity::class.java) 18 | } 19 | "Custom Drivers" -> { 20 | swapTo = Intent(context, CustomDriverActivity::class.java) 21 | } 22 | } 23 | swapTo?.let { 24 | it.putExtra("Activity Provider", "App") 25 | context.startActivity(it) 26 | } 27 | } 28 | override fun onClick() = launchActivity() 29 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace cosmic::iop { 4 | class IoMipsCore; 5 | 6 | class IopExecVe { 7 | public: 8 | IopExecVe(Ref& mips) : 9 | cpu(mips) {} 10 | 11 | virtual u32 executeCode() = 0; 12 | virtual u32 fetchPcInst() = 0; 13 | virtual ~IopExecVe() = default; 14 | protected: 15 | Ref cpu; 16 | }; 17 | 18 | enum IopSpecial { 19 | SpecialJr = 0x8, 20 | SpecialSyscall = 0xc, 21 | SpecialMfhi = 0x10, 22 | SpecialMthi = 0x11, 23 | SpecialOr = 0x25, 24 | SpecialNor = 0x27, 25 | SpecialXor = 0x26, 26 | }; 27 | enum IopCops { 28 | CopMfc = 0x000, 29 | CopMtc = 0x004, 30 | CopRfe = 0x010, 31 | }; 32 | enum IopOpcodes { 33 | SpecialOp = 0x0, 34 | Beq = 0x4, 35 | Bne = 0x5, 36 | Blez = 0x6, 37 | Addi = 0x8, 38 | Addiu = 0x9, 39 | Slti = 0x0a, 40 | Sltiu = 0x0b, 41 | Andi = 0xc, 42 | Ori = 0xd, 43 | Lui = 0xf, 44 | Lw = 0x23, 45 | Sw = 0x2b, 46 | }; 47 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vu/vif_fifo.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | namespace cosmic::vu { 7 | enum FifoMethodVif { 8 | // Clear the valid bit and delete the stored value at the specified index 9 | FifoClean, 10 | // Set a valid value 11 | FifoSet, 12 | // Return the value at the index 13 | FifoLoad 14 | }; 15 | 16 | // A vector-based FIFO - We will not delete our FIFO data, just mark it as trash 17 | // This will satisfy the need to not reallocate queue data during execution 18 | struct VifDataPack { 19 | u32 gsData; 20 | bool isValid; 21 | }; 22 | class VifFifo { 23 | public: 24 | VifFifo() = default; 25 | VifFifo(u32 queueSize); 26 | void resetVifFifo(); 27 | u32 size() const { 28 | return fifoIndex; 29 | } 30 | void pushQuad(os::vec& gsd); 31 | void push(u32 gsValue); 32 | u32 consume(); 33 | 34 | u32 accessData(u32 index, u32 gsValue, FifoMethodVif fifoMode); 35 | u32 fifoIndex; 36 | std::vector dataPack; 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/dialogs/AboutDialog.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.dialogs 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 8 | import emu.cosmic.R 9 | import emu.cosmic.databinding.AboutDialogBinding 10 | 11 | class AboutDialog : BottomSheetDialogFragment() { 12 | private var binding: AboutDialogBinding? = null 13 | private val aboutBinding get() = binding!! 14 | 15 | companion object { 16 | const val DIALOG_TAG = "aboutDialog" 17 | } 18 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, 19 | savedInstanceState: Bundle?): View { 20 | binding = AboutDialogBinding.inflate(inflater) 21 | return aboutBinding.root 22 | } 23 | 24 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 25 | super.onViewCreated(view, savedInstanceState) 26 | aboutBinding.aboutCommitVersion.apply { 27 | val commitHash = String.format(resources.getString(R.string.about_commit)) 28 | text = commitHash 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/cpu/cyclic32.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | namespace cosmic::cpu { 5 | u32 check32(std::span chkData) { 6 | u32 crc{0xffffffff}; 7 | u64 last{chkData.size()}; 8 | 9 | u32* chucks{BitCast(chkData.data())}; 10 | 11 | for (; last >= sizeof(u32) * 8; ) { 12 | crc = __crc32cd(crc, *chucks++); 13 | crc = __crc32cd(crc, *chucks++); 14 | 15 | crc = __crc32cd(crc, *chucks++); 16 | crc = __crc32cd(crc, *chucks++); 17 | 18 | crc = __crc32cd(crc, *chucks++); 19 | crc = __crc32cd(crc, *chucks++); 20 | 21 | crc = __crc32cd(crc, *chucks++); 22 | crc = __crc32cd(crc, *chucks++); 23 | 24 | last -= sizeof(u32) * 8; 25 | } 26 | 27 | for (; last >= sizeof(u32); ) { 28 | crc = __crc32cd(crc, *chucks++); 29 | last -= sizeof(u32); 30 | } 31 | 32 | u8* pieces{BitCast(chucks)}; 33 | 34 | for (; last; ) { 35 | crc = __crc32b(crc, *pieces++); 36 | last -= sizeof(u8); 37 | } 38 | return ~crc; 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gs/gs_tables.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | namespace cosmic::gs { 4 | // After some years of development, the Z mapping tables have been removed, 5 | // with the commit message stating, "They aren't just an offset of the base value 6 | // like the color formats, but instead an XOR of the associated color format" 7 | 8 | template 9 | using ConstByteArray = const std::array; 10 | template 11 | using ConstWordArray = const std::array; 12 | 13 | extern ConstByteArray<8> blockTable32[4]; 14 | extern ConstByteArray<4> blockTable16[8]; 15 | extern ConstByteArray<4> blockTable16S[8]; 16 | extern ConstByteArray<8> blockTable8[4]; 17 | extern ConstByteArray<4> blockTable4[8]; 18 | 19 | extern ConstByteArray<8> columnTable32[8]; 20 | extern ConstByteArray<16> columnTable16[8]; 21 | extern ConstByteArray<16> columnTable8[16]; 22 | extern ConstWordArray<32> columnTable4[16]; 23 | 24 | extern ConstByteArray<128> clutTableT32I8; 25 | extern ConstByteArray<16> clutTableT32I4; 26 | extern ConstByteArray<32> clutTableT16I8; 27 | extern ConstByteArray<16> clutTableT16I4; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/spu/sound_core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | namespace cosmic { 5 | namespace console { 6 | class IntCInfra; 7 | } 8 | namespace iop { 9 | class IopDma; 10 | } 11 | } 12 | 13 | namespace cosmic::spu { 14 | struct SpuStatus { 15 | bool dmaReady; 16 | bool dmaBusy; 17 | }; 18 | class Spu2 { 19 | public: 20 | Spu2(std::shared_ptr& infra, 21 | std::shared_ptr& ioDma, 22 | std::shared_ptr& pipe) : 23 | intc(infra), dmac(ioDma), spuRam(pipe) 24 | { 25 | } 26 | SpuStatus status; 27 | 28 | void resetSound(); 29 | u32 transferAddr; 30 | u32 currentAddr; 31 | 32 | void writeDmaData(u32 data); 33 | u32 requestDmaData(); 34 | private: 35 | u16 spuRead(u32 address); 36 | void spuWrite(u32 address, u16 value); 37 | void spuWrite(u16 value); 38 | 39 | std::shared_ptr intc; 40 | std::shared_ptr dmac; 41 | std::shared_ptr spuRam; 42 | }; 43 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/hle/bios_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::hle { 6 | class BiosInfo : java::JavaClass { 7 | public: 8 | BiosInfo() : java::JavaClass("emu/cosmic/data/BiosInfo") { 9 | } 10 | i32 position; 11 | DescriptorRaii fd; 12 | u32 dataCRC; 13 | bool selected{false}; 14 | 15 | java::JniString dspName; 16 | java::JniString details; 17 | 18 | jobject createInstance() override; 19 | void deleteInstance(jobject kotlinBios) override; 20 | 21 | void fillInstance(jobject kotlin) override; 22 | void chkAndLoad(i32 descriptor); 23 | 24 | bool isSame(std::array& is, bool usePos = false) const { 25 | bool equal; 26 | if (usePos) { 27 | equal = position == is[1]; 28 | } else { 29 | std::array stat{}; 30 | 31 | fstat(static_cast(is[0]), &stat[0]); 32 | fstat(fd.getFd(), &stat[1]); 33 | 34 | equal = stat[0].st_ino == stat[1].st_ino; 35 | } 36 | return equal; 37 | } 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/except.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | namespace cosmic { 9 | class CosmicException : public std::runtime_error { 10 | protected: 11 | CosmicException(const std::string& format); 12 | 13 | public: 14 | static void setExceptionClass(jobject super); 15 | private: 16 | jstring lookupByActivity(); 17 | void alertUser(); 18 | 19 | jstring msg{}; 20 | jstring title{}; 21 | jmethodID alert{}; 22 | }; 23 | 24 | #define DECLARE_EXCEPTION_TYPE(name, tag)\ 25 | class name : public CosmicException {\ 26 | public:\ 27 | template \ 28 | name(fmt::format_string format, Args&&... args) :\ 29 | CosmicException("(" tag ") " + fmt::format(format, std::forward(args)...)) {}\ 30 | } 31 | 32 | DECLARE_EXCEPTION_TYPE(Cop0Err, "Cop0"); 33 | DECLARE_EXCEPTION_TYPE(TimerErr, "EE::Timer"); 34 | DECLARE_EXCEPTION_TYPE(MioErr, "MIO"); 35 | DECLARE_EXCEPTION_TYPE(IoErr, "IO"); 36 | DECLARE_EXCEPTION_TYPE(FsErr, "FS"); 37 | DECLARE_EXCEPTION_TYPE(GpuErr, "GPU"); 38 | DECLARE_EXCEPTION_TYPE(AppErr, "Cosmic"); 39 | #undef DECLARE_EXCEPTION_TYPE 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_round_settings_24.xml: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vm/watch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::vm { 6 | enum CheckStatus { 7 | HasFrame, 8 | IsRunning, 9 | IsMonitoring 10 | }; 11 | class WatchStatus { 12 | public: 13 | WatchStatus(); 14 | void checkStatus(); 15 | void markStarts(); 16 | void markStepsDone(); 17 | 18 | void setDesiredFrames(u8 fps); 19 | void frameFinished(u64 eeCycles, u64 iopCycles, u64 busCycles); 20 | auto getExecutionCount() const { 21 | return executionCount.load(); 22 | } 23 | 24 | bool get(CheckStatus status) const; 25 | void set(CheckStatus status, bool value); 26 | 27 | void clearStatus(); 28 | std::atomic isFrameCompleted{false}; 29 | std::atomic running{false}; 30 | std::atomic monitor{false}; 31 | 32 | std::atomic executionCount{}; 33 | private: 34 | u8 desiredFps{}; 35 | struct { 36 | u64 desired; 37 | u64 acc; 38 | } eeVu, psx, bus; 39 | 40 | std::chrono::time_point starts; 41 | std::chrono::time_point finish; 42 | }; 43 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/emulation_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 19 | 20 | 21 | 25 | 26 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/creeper/mips_fpu.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-short-identifier: MIT, Version N/A 2 | // This file is protected by the MIT license (please refer to LICENSE.md before making any changes, copying, or redistributing this software) 3 | #include 4 | #include 5 | 6 | #define DSP_MATH_PARAMS 1 7 | namespace cosmic::creeper { 8 | std::array fpList; 9 | u8 dest; 10 | 11 | void MipsIvInterpreter::fpuMadd(Operands ops) { 12 | fpList[0] = fpu->sony754con(fpu->fprRegs[ops.rd].un); 13 | fpList[1] = fpu->sony754con(fpu->fprRegs[ops.rt].un); 14 | fpList[2] = fpu->sony754con(fpu->acc.un); 15 | #if DSP_MATH_PARAMS 16 | user->info("(Creeper, FPU): Converted result to Sony 754 values: {}", fmt::join(fpList, "/ ")); 17 | #endif 18 | dest = static_cast((ops.inst >> 6) & 0x1f); 19 | 20 | fpu->fprRegs[dest].decimal = fpList[2] + (fpList[0] * fpList[1]); 21 | fpu->checkOverflow(dest); 22 | fpu->checkUnderflow(dest); 23 | } 24 | void MipsIvInterpreter::fpuAdda(Operands ops) { 25 | fpList[0] = fpu->sony754con(fpu->fprRegs[ops.rd].un); 26 | fpList[1] = fpu->sony754con(fpu->fprRegs[ops.rt].un); 27 | 28 | fpu->acc.decimal = fpList[0] + fpList[1]; 29 | fpu->checkOverflow(32); 30 | fpu->checkUnderflow(32); 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/os/jclasses.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | namespace cosmic::java { 8 | using JniInteger = jint; 9 | using JniBool = jboolean; 10 | 11 | class JniString { 12 | public: 13 | JniString() { 14 | } 15 | JniString(const char* str); 16 | JniString(const std::string str); 17 | JniString(jstring validJniString); 18 | ~JniString(); 19 | 20 | auto operator =(const JniString& str) -> JniString&; 21 | JniString(JniString&& str); 22 | auto operator *() { 23 | return utfSide; 24 | } 25 | auto operator !=(JniString& differ) { 26 | return utfSide != differ.utfSide; 27 | } 28 | auto get() { 29 | return utfSide; 30 | } 31 | jobject javaRef{}; 32 | private: 33 | std::string utfSide{""}; 34 | }; 35 | 36 | class JavaClass { 37 | protected: 38 | JavaClass(const char* className) : 39 | modelName(className) {} 40 | virtual ~JavaClass() = default; 41 | 42 | virtual jobject createInstance() = 0; 43 | virtual void fillInstance(jobject kotlin) = 0; 44 | virtual void deleteInstance(jobject kotlinBios) = 0; 45 | 46 | jclass findClass(); 47 | 48 | const char* modelName; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/hle/bios_info.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | namespace cosmic::hle { 5 | jobject BiosInfo::createInstance() { 6 | auto ioClass{findClass()}; 7 | 8 | auto modelInit{cosmicEnv->GetMethodID(ioClass, "", "()V")}; 9 | auto kotlinBios{cosmicEnv->NewObject(ioClass, modelInit)}; 10 | 11 | return kotlinBios; 12 | } 13 | void BiosInfo::deleteInstance(jobject kotlinBios) { 14 | cosmicEnv->DeleteLocalRef(kotlinBios); 15 | } 16 | 17 | void BiosInfo::fillInstance(jobject kotlin) { 18 | auto kotlinModel{findClass()}; 19 | 20 | auto posBrains{cosmicEnv->GetFieldID(kotlinModel, "position", "I")}; 21 | auto selectedBrains{cosmicEnv->GetFieldID(kotlinModel, "selected", "Z")}; 22 | auto biosNameBrains{cosmicEnv->GetFieldID(kotlinModel, "biosName", "Ljava/lang/String;")}; 23 | auto biosDetailsBrains{cosmicEnv->GetFieldID(kotlinModel, "biosDetails", "Ljava/lang/String;")}; 24 | 25 | cosmicEnv->SetIntField(kotlin, posBrains, position); 26 | 27 | cosmicEnv->SetBooleanField(kotlin, selectedBrains, selected); 28 | 29 | cosmicEnv->SetObjectField(kotlin, biosNameBrains, dspName.javaRef); 30 | cosmicEnv->SetObjectField(kotlin, biosDetailsBrains, details.javaRef); 31 | } 32 | void BiosInfo::chkAndLoad(i32 descriptor) { 33 | fd = DescriptorRaii(descriptor, true); 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/CosmicApplication.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import androidx.datastore.core.DataStore 6 | import androidx.datastore.preferences.core.Preferences 7 | import androidx.datastore.preferences.preferencesDataStore 8 | import com.google.android.material.color.DynamicColors 9 | import com.google.android.material.color.DynamicColorsOptions 10 | import dagger.hilt.android.HiltAndroidApp 11 | import javax.inject.Singleton 12 | 13 | @HiltAndroidApp 14 | class CosmicApplication : Application() { 15 | init { 16 | app = this 17 | } 18 | override fun onCreate() { 19 | app = this 20 | 21 | super.onCreate() 22 | // Applies dynamic colors to your application 23 | val colorsOption = DynamicColorsOptions.Builder().setPrecondition { _, _ -> true }.build() 24 | DynamicColors.applyToActivitiesIfAvailable(this, colorsOption) 25 | System.loadLibrary("cosmic") 26 | } 27 | companion object { 28 | lateinit var app: CosmicApplication 29 | private set 30 | val context : Context get() = app.applicationContext 31 | } 32 | } 33 | 34 | // We won't be using Protocol Buffers, but instead, Jetpack DataStore, as SharedPreferences has been 35 | // deprecated in the new Android versions 36 | @Singleton 37 | val Context.dataSettings: DataStore by preferencesDataStore(name = "cosmicSettings") -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/data/CosmicKeys.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.data 2 | import android.content.Context 3 | import android.os.Environment 4 | import androidx.datastore.preferences.core.Preferences 5 | import androidx.datastore.preferences.core.stringPreferencesKey 6 | import emu.cosmic.R 7 | 8 | enum class SettingsKeys(val dsdbKey: Int) { 9 | AppStorage(R.string.datastore_app_storage), 10 | GpuTurboMode(R.string.datastore_gpu_turbo_mode), 11 | CustomDriver(R.string.datastore_gpu_custom_driver), 12 | EEMode(R.string.datastore_ee_mode), 13 | BiosPath(R.string.datastore_bios_path) 14 | } 15 | 16 | @Suppress("unchecked_cast") 17 | class SettingContainer(context: Context, key: SettingsKeys) { 18 | val defaultValue: T 19 | val preferKey: Preferences.Key 20 | 21 | private var keyValue: String = context.getString(key.dsdbKey) 22 | val containerContext = context 23 | init { 24 | preferKey = stringPreferencesKey(keyValue) as Preferences.Key 25 | 26 | defaultValue = when (key) { 27 | SettingsKeys.AppStorage -> { 28 | val envDir = Environment.getExternalStorageDirectory() 29 | envDir.path as T 30 | } 31 | SettingsKeys.GpuTurboMode -> { false as T } 32 | SettingsKeys.CustomDriver -> { "libvulkan.so" as T } 33 | SettingsKeys.EEMode -> { 1 as T } 34 | SettingsKeys.BiosPath -> { "" as T } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_cop.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace cosmic::iop { 5 | // https://psx-spx.consoledev.net/cpuspecifications/ 6 | struct alignas(8) IopCopStatus { 7 | // iec: Current Interrupt Enable 8 | bool iec, 9 | // kuc: Current Kernel/User Mode (0=Kernel, 1=User) 10 | kuc, 11 | // iep: Previous Interrupt Disable 12 | iep, 13 | // kup: Previous Kernel/User Mode 14 | kup, 15 | // ieo: Old Interrupt Disable 16 | ieo, 17 | // kuo Old Kernel/User Mode 18 | kuo; 19 | u8 imm; 20 | bool isC, 21 | // bev: Boot exception vectors in RAM/ROM (0=RAM/KSEG0, 1=ROM/KSEG1) 22 | bev; 23 | // Just for arithmetic reasons 24 | auto to64() { 25 | return *BitCast(this); 26 | } 27 | void st64(u64 value) { 28 | *BitCast(this) = value; 29 | } 30 | }; 31 | struct IopCopCause { 32 | u8 code; 33 | u8 intPending; 34 | bool bd; 35 | }; 36 | 37 | class IopCop { 38 | public: 39 | IopCopStatus status; 40 | IopCopCause cause; 41 | u32 ePc; 42 | u32 c0id; 43 | 44 | u32 mfc(u8 copId) const; 45 | void mtc(u8 copId, u32 regVal); 46 | // Return from exception (COP0) 47 | void rfe(); 48 | 49 | void resetIoCop(); 50 | }; 51 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/os/env.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::os { 6 | struct CosmicEnv { 7 | CosmicEnv(CosmicEnv&) = delete; 8 | CosmicEnv(CosmicEnv&&) = delete; 9 | CosmicEnv() { 10 | if (jvm) { 11 | jvm->GetEnv(BitCast(&gnv), jv); 12 | } 13 | if (!gnv && jvm) { 14 | jvm->AttachCurrentThread(&gnv, nullptr); 15 | attached = true; 16 | } 17 | } 18 | void feedVm(JNIEnv* e) { 19 | if ((jv = e->GetVersion()) != JNI_VERSION_1_6) { 20 | } 21 | if (!jvm) 22 | e->GetJavaVM(&jvm); 23 | gnv = e; 24 | } 25 | void reload() { 26 | jvm->GetEnv(BitCast(&gnv), jv); 27 | } 28 | void reload(JNIEnv* e) { 29 | if (!gnv) 30 | feedVm(e); 31 | else 32 | gnv = e; 33 | } 34 | ~CosmicEnv() { 35 | if (attached) { 36 | jvm->DetachCurrentThread(); 37 | } 38 | } 39 | auto operator*() { 40 | return gnv; 41 | } 42 | auto operator->() { 43 | return gnv; 44 | } 45 | 46 | static inline JavaVM* jvm{}; 47 | static inline i32 jv; 48 | // Means that it was created by a native thread 49 | bool attached{}; 50 | JNIEnv* gnv{}; 51 | }; 52 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/pshook/hk_psx.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | namespace cosmic::pshook { 7 | struct PsxRegs eeSavedCtx{}; 8 | struct PsxRegs iopSavedCtx{}; 9 | 10 | std::mutex EstablishHook::owner{}; 11 | u8 EstablishHook::isEe{1}; 12 | 13 | using psxCall = std::function; 14 | static std::unordered_map calls{ 15 | {0x1a000014, psxAbs} 16 | }; 17 | 18 | void EstablishHook::hookIoPsx(u32 base, iop::IoMipsCore& psx) { 19 | u32 ib{}; 20 | switch (base) { 21 | case 0xa0: ib = 0x1a000000; break; 22 | case 0xb0: ib = 0x1b000000; break; 23 | case 0xc0: ib = 0x1c000000; break; 24 | } 25 | psxCall handler{}; 26 | 27 | if (!ib) 28 | return; 29 | std::scoped_lock lock{owner}; 30 | if (isEe) { 31 | std::memcpy(&eeSavedCtx, &r, sizeof(r)); 32 | isEe = 0; 33 | } 34 | r.a0 = BitCast(&psx.ioGPRs[4]); 35 | r.v0 = BitCast(&psx.ioGPRs[2]); 36 | r.ra = BitCast(&psx.ioGPRs[31]); 37 | r.pc = BitCast(&psx.ioPc); 38 | 39 | if (ib == 0x1a000000) 40 | if (psx.ioGPRs[9] != 0x28 && psx.ioGPRs[9] != 0xe) 41 | if (calls.contains(ib | psx.ioGPRs[9])) 42 | handler = calls[ib | psx.ioGPRs[9]]; 43 | if (handler) 44 | std::invoke(handler); 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vm/emu_vm.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | namespace cosmic::vm { 13 | class EmuVm { 14 | public: 15 | EmuVm( 16 | std::shared_ptr& virtDevs, 17 | std::shared_ptr& dsp); 18 | 19 | void resetVm(); 20 | void startVm(); 21 | void stopVm(); 22 | void dealWithSyscalls(); 23 | 24 | std::shared_ptr biosHigh; 25 | std::shared_ptr mips; 26 | std::shared_ptr iop; 27 | std::shared_ptr ioDma; 28 | std::shared_ptr sound; 29 | 30 | std::shared_ptr mpegDecoder; 31 | std::shared_ptr gsCore; 32 | std::shared_ptr gsGif; 33 | 34 | std::shared_ptr screenEngine; 35 | 36 | std::shared_ptr scheduler; 37 | std::shared_ptr vu01; 38 | std::shared_ptr sharedPipe; 39 | 40 | WatchStatus status{}; 41 | private: 42 | EmuThread emuThread; 43 | std::shared_ptr intc; 44 | bool dumpMemoryAtClash{}; 45 | 46 | std::unique_ptr dealer; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/console/backdoor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic { 4 | std::shared_ptr outside; 5 | } 6 | namespace cosmic::console { 7 | BackDoor::BackDoor(vm::EmuVm& aliveVm) { 8 | vm = Ref(aliveVm); 9 | echo.lock(); 10 | vmRefs = 1; 11 | } 12 | Ref BackDoor::openVm() { 13 | std::thread::id nub{}; 14 | if (owner == nub && owner != std::this_thread::get_id()) { 15 | while (echo.try_lock()) { 16 | std::this_thread::sleep_for( 17 | std::chrono::nanoseconds(2'245)); 18 | std::this_thread::yield(); 19 | } 20 | owner = std::this_thread::get_id(); 21 | 22 | } 23 | if (owner != std::this_thread::get_id()) 24 | throw AppErr("This resource should have the lock held until the object is released"); 25 | Ref vmRef{}; 26 | if (vmRefs) { 27 | vmRef = vm; 28 | vmRefs++; 29 | } 30 | return vmRef; 31 | } 32 | void BackDoor::leaveVm(Ref& lvm) { 33 | if (echo.try_lock()) { 34 | if (owner != std::this_thread::get_id()) 35 | throw AppErr("The program flow is broken, review the usage of BackDoor in the code"); 36 | } 37 | vmRefs--; 38 | if (!vm || vmRefs <= 0) { 39 | vm = {}; 40 | vm = Ref(lvm); 41 | vmRefs = 1; 42 | } 43 | owner = {}; 44 | echo.unlock(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/res/layout/settings_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | 25 | 26 | 27 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vu/vu1_xgkick.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | namespace cosmic::vu { 4 | void VectorUnit::stallByXgKick() { 5 | updateDeltaCycles(clock.runCycles, true); 6 | for (; clock.runCycles > 0; clock.runCycles--) { 7 | updateMacPipeline(); 8 | updateDivEfuPipes(); 9 | intPipeline.update(); 10 | } 11 | path1.stallXgKick = false; 12 | } 13 | void VectorUnit::handleDataTransfer() { 14 | os::vec quad; 15 | u16 addr{gifAddr}; 16 | 17 | gifAddr += 16; 18 | std::memcpy(&quad, &vecRegion.rw[addr], sizeof(quad)); 19 | if (vu1Gif->feedPathWithData(gs::Vu1, quad)) { 20 | if (!path1.stallXgKick) { 21 | // Reactivating the previous interrupted transfer 22 | path1.stallXgKick = {}; 23 | gifAddr = gifStallAddr; 24 | vu1Gif->requestDmac(gs::Vu1, true); 25 | } else { 26 | vu1Gif->deactivatePath(gs::Vu1); 27 | path1.transferringGif = {}; 28 | return; 29 | } 30 | } 31 | } 32 | 33 | void VectorUnit::startXgKick2Gif() { 34 | if (!vu1Gif) 35 | return; 36 | vu1Gif->requestDmac(gs::Vu1, true); 37 | while (path1.cycles >= 0x2) { 38 | if (!vu1Gif->isPathActivated(gs::Vu1, true)) { 39 | path1.cycles = 0; 40 | break; 41 | } 42 | handleDataTransfer(); 43 | path1.cycles -= 0x2; 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/creeper/psx_interpreter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | namespace cosmic::vm { 7 | class EmuVm; 8 | } 9 | namespace cosmic::creeper { 10 | class IopInterpreter : public iop::IopExecVe { 11 | public: 12 | IopInterpreter(Ref core); 13 | u32 executeCode() override; 14 | u32 execPsx(u32 opcode, std::array opeRegs); 15 | u32 execCop(u32 opcode, std::array opeRegs); 16 | u32 execSpecial(u32 opcode, std::array opeRegs); 17 | private: 18 | void ioFuncHook(u32 pc); 19 | CachedFastPc fastPc; 20 | 21 | u32 fetchPcInst() override; 22 | Ref vm; 23 | void issueInterruptSignal(); 24 | 25 | void sltiu(Operands ops); 26 | void ioSyscall(Operands ops); 27 | 28 | void mfhi(Operands ops); 29 | void mthi(Operands ops); 30 | void orMips(Operands ops); 31 | void xorMips(Operands ops); 32 | void nor(Operands ops); 33 | void lui(Operands ops); 34 | void ori(Operands ops); 35 | void jr(Operands ops); 36 | void beq(Operands ops); 37 | void lw(Operands ops); 38 | void andi(Operands ops); 39 | void addi(Operands ops); void addiu(Operands ops); 40 | void sw(Operands ops); 41 | 42 | void mfc(Operands ops); 43 | void mtc(Operands ops); 44 | void rfe(Operands ops); 45 | 46 | void bne(Operands ops); void blez(Operands ops); 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/except.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic { 5 | jclass exceptionActivity{}; 6 | CosmicException::CosmicException(const std::string& format) : 7 | std::runtime_error(format) { 8 | user->error("An exception of type CosmicException was raised due to: {}", format); 9 | 10 | msg = cosmicEnv->NewStringUTF(format.c_str()); 11 | title = lookupByActivity(); 12 | alertUser(); 13 | } 14 | jstring CosmicException::lookupByActivity() { 15 | const jclass emulation{cosmicEnv->FindClass("emu/cosmic/EmulationActivity")}; 16 | if (cosmicEnv->IsSameObject(exceptionActivity, emulation)) { 17 | return cosmicEnv->NewStringUTF("Emulation Scene"); 18 | } 19 | return cosmicEnv->NewStringUTF("General Exception"); 20 | } 21 | 22 | void CosmicException::alertUser() { 23 | alert = cosmicEnv->GetStaticMethodID(exceptionActivity, 24 | "displayAlert", "(Ljava/lang/String;Ljava/lang/String;)V"); 25 | if (alert) { 26 | cosmicEnv->CallStaticVoidMethod(exceptionActivity, alert, title, msg); 27 | } 28 | cosmicEnv->DeleteLocalRef(title); 29 | cosmicEnv->DeleteLocalRef(msg); 30 | } 31 | 32 | void CosmicException::setExceptionClass(jobject super) { 33 | const jclass emuClass{cosmicEnv->FindClass("emu/cosmic/EmulationActivity")}; 34 | if (cosmicEnv->IsInstanceOf(super, emuClass)) { 35 | exceptionActivity = cosmicEnv->FindClass("emu/cosmic/EmulationActivity"); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/os/jclasses.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::java { 5 | JniString::JniString(const char* str) { 6 | utfSide = std::string(str); 7 | auto kotlinStr{cosmicEnv->NewStringUTF(str)}; 8 | javaRef = cosmicEnv->NewGlobalRef(kotlinStr); 9 | } 10 | 11 | JniString::JniString(jstring validJniString) { 12 | auto rawStr{ 13 | cosmicEnv->GetStringUTFChars(validJniString, nullptr)}; 14 | utfSide = std::string(BitCast(rawStr)); 15 | 16 | cosmicEnv->ReleaseStringUTFChars(validJniString, rawStr); 17 | } 18 | JniString::~JniString() { 19 | if (javaRef) 20 | cosmicEnv->DeleteGlobalRef(javaRef); 21 | javaRef = {}; 22 | utfSide = {}; 23 | } 24 | JniString::JniString(JniString&& str) { 25 | javaRef = std::exchange(str.javaRef, nullptr); 26 | utfSide = std::move(str.utfSide); 27 | } 28 | 29 | JniString::JniString(const std::string str) : 30 | utfSide(str) { 31 | javaRef = cosmicEnv->NewGlobalRef(cosmicEnv->NewStringUTF(str.c_str())); 32 | } 33 | auto JniString::operator=(const JniString& str) -> JniString& { 34 | if (javaRef) { 35 | if (!cosmicEnv->IsSameObject(javaRef, nullptr)) 36 | cosmicEnv->DeleteGlobalRef(javaRef); 37 | } 38 | javaRef = cosmicEnv->NewGlobalRef(str.javaRef); 39 | utfSide = str.utfSide; 40 | 41 | return *this; 42 | } 43 | jclass JavaClass::findClass() { 44 | return cosmicEnv->FindClass(modelName); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vu/vif_fifo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | namespace cosmic::vu { 4 | inline u32 VifFifo::accessData(u32 index, u32 gsValue, FifoMethodVif fifoMode) { 5 | if (index >= dataPack.size()) { 6 | return {}; 7 | } 8 | auto& dataAtRange{dataPack.at(index)}; 9 | if (fifoMode != FifoLoad) 10 | dataAtRange.gsData = gsValue; 11 | 12 | if (fifoMode == FifoClean) 13 | dataAtRange.isValid = {}; 14 | if (fifoMode == FifoSet) 15 | dataAtRange.isValid = true; 16 | 17 | if (fifoMode == FifoLoad) 18 | return dataAtRange.gsData; 19 | return {}; 20 | } 21 | void VifFifo::push(u32 gsValue) { 22 | accessData(fifoIndex++, gsValue, FifoSet); 23 | } 24 | u32 VifFifo::consume() { 25 | u32 front{}; 26 | front = accessData(static_cast(--fifoIndex), 0, FifoLoad); 27 | if (dataPack.size()) { 28 | dataPack.at(fifoIndex).isValid = {}; 29 | } 30 | return front; 31 | } 32 | void VifFifo::resetVifFifo() { 33 | if (!dataPack.size()) 34 | dataPack.resize(64); 35 | 36 | for (auto& packet: dataPack) { 37 | packet.isValid = {}; 38 | } 39 | fifoIndex = {}; 40 | } 41 | void VifFifo::pushQuad(os::vec& gsd) { 42 | for (u8 vl{}; vl < 3; vl++) { 43 | push(gsd.to32(vl)); 44 | } 45 | } 46 | VifFifo::VifFifo(u32 queueSize) { 47 | dataPack.resize(queueSize); 48 | // resetVifFifo(); 49 | fifoIndex = {}; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_dma.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::iop { 5 | void IopDma::resetIoDma() { 6 | activeChannel.reset(); 7 | for (u64 chan{}; chan < channels.size(); chan++) { 8 | channels[chan] = {}; 9 | channels[chan].index = static_cast(chan); 10 | } 11 | } 12 | void IopDma::pulse(u32 cycles) { 13 | if (!activeChannel) 14 | return; 15 | 16 | for (; cycles ; cycles--) { 17 | switch (activeChannel.channel) { 18 | case IopSpu2: 19 | pulseSpu2Chain(); 20 | break; 21 | } 22 | } 23 | } 24 | void IopDma::pulseSpu2Chain() { 25 | std::array packet; 26 | auto& spu2ch{channels[IopSpu2]}; 27 | // When true, it means that we will write into the SPU2 device 28 | auto write2Spu{spu2ch.status.isFrom2Device}; 29 | 30 | if (spu2ch.cyclesDelay) { 31 | spu2ch.cyclesDelay--; 32 | return; 33 | } 34 | if (spu2ch.cyclesDelay <= 0) { 35 | if (write2Spu) { 36 | packet[0] = ioDmaRead(spu2ch.addr); 37 | spu2->writeDmaData(packet[0]); 38 | } else { 39 | packet[1] = spu2->requestDmaData(); 40 | ioDmaWrite(spu2ch.addr, packet[1]); 41 | } 42 | spu2ch.size--; 43 | spu2ch.addr += sizeof(u32); 44 | spu2ch.cyclesDelay = 3; 45 | } 46 | if (!spu2ch.size) { 47 | spu2ch.wordCount = 0; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/mio/blocks.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::mio { 6 | enum RealAddressFrom { 7 | MainMemory, 8 | BiosMemory, 9 | IopMemory, 10 | Spu2Ram, 11 | }; 12 | 13 | class GlobalMemory { 14 | public: 15 | GlobalMemory(); 16 | 17 | u8* mapVirtAddress(u32 address, RealAddressFrom mkFrom = MainMemory); 18 | u64 biosSize() { 19 | return static_cast(&ramBlock[0x3fffff] - &ramBlock[0]) + 1; 20 | } 21 | u8* iopUnaligned(u32 address); 22 | u8* spu2Unaligned(u32 address); 23 | 24 | [[maybe_unused]] void iopSoftClean() { 25 | memset(*iopBlock, 0, iopBlock.getBlockSize()); 26 | } 27 | [[maybe_unused]] void sndSoftClean() { 28 | memset(*sndBlock, 0, sndBlock.getBlockSize()); 29 | } 30 | [[maybe_unused]] void ramSoftClean() { 31 | memset(*ramBlock, 0, ramBlock.getBlockSize()); 32 | } 33 | void printMemoryImage(boost::filesystem::path& storage); 34 | private: 35 | void dumpMemoryToDisk( 36 | boost::filesystem::path storage, 37 | boost::filesystem::path& devOutFile, 38 | os::MappedMemory& devBlock); 39 | 40 | u8* access(u32 address, RealAddressFrom from); 41 | // Our unified memory strategy pointer 42 | os::MappedMemory umm; 43 | // Below, all unified memory regions related to console activities 44 | os::MappedMemory iopBlock; 45 | os::MappedMemory sndBlock; 46 | os::MappedMemory ramBlock; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vu/v01_cop2vu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::vu { 4 | MacroModeCop2::MacroModeCop2(Ref vus[2]) 5 | : v0(vus[0]), 6 | v1(vus[1]) { 7 | cop2il = false; 8 | vuIl = false; 9 | } 10 | // CFC2 is a requirement, it is a way for EE to access the V0 special registers 11 | u32 MacroModeCop2::cfc2(u32 special) { 12 | if (special < 0x10) { 13 | if (special != 0) 14 | return v0->intsRegs[special].uns; 15 | } 16 | return 0; 17 | } 18 | void MacroModeCop2::ctc2(u32 special, u32 value) { 19 | switch (special) { 20 | case 0x0 ... 0xf: 21 | if (special < 0x10) 22 | if (special != 0) 23 | v0->intsRegs[special].uns = static_cast(value); 24 | break; 25 | case 0x16: 26 | v0->setSpecialReg(vu::Q, value); break; 27 | case 0x17: 28 | v0->setSpecialReg(vu::P, value); 29 | default: 30 | throw AppErr("Invalid VU special register index {} used with CTC2 instruction, value: {}", special, value); 31 | } 32 | } 33 | void MacroModeCop2::clearInterlock() { 34 | cop2il = vuIl = false; 35 | } 36 | bool MacroModeCop2::checkInterlock() { 37 | return vuIl; 38 | } 39 | bool MacroModeCop2::interlockCheck(bool isCop2) { 40 | if (isCop2) { 41 | cop2il = true; 42 | if (!vuIl) 43 | return true; 44 | } else { 45 | vuIl = true; 46 | if (!cop2il) 47 | return true; 48 | } 49 | return false; 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/render_driver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | namespace cosmic::gpu { 8 | void RenderDriver::pickUserRender(const RenderApi api, bool reload) { 9 | if (driver && !reload) 10 | return; 11 | switch (api) { 12 | case HardwareVulkan: 13 | if (!loadVulkanDriver()) { 14 | throw GpuErr("No instance of the Vulkan driver was found"); 15 | } 16 | break; 17 | case SoftwareSlow: 18 | case HardwareOpenGL: 19 | break; 20 | } 21 | } 22 | bool RenderDriver::loadVulkanDriver() { 23 | auto serviceDriver{*(states->customDriver)}; 24 | auto appStorage{*(states->appStorage)}; 25 | if (driver) 26 | dlclose(std::exchange(driver, nullptr)); 27 | if (serviceDriver.starts_with(appStorage)) {} 28 | if (!driver) { 29 | // Rolling back to the driver installed on the device 30 | driver = dlopen(serviceDriver.c_str(), RTLD_LAZY); 31 | if (!driver) 32 | driver = dlopen("libvulkan.so", RTLD_LAZY); 33 | if (!driver) 34 | throw GpuErr("No valid Vulkan driver was found on the host device"); 35 | } 36 | vulkanInstanceAddr = BitCast(dlsym(driver, "vkGetInstanceProcAddr")); 37 | return driver && vulkanInstanceAddr; 38 | } 39 | RenderDriver::~RenderDriver() { 40 | if (driver) 41 | dlclose(driver); 42 | driver = {}; 43 | vulkanInstanceAddr = {}; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/logger.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-short-identifier: MIT, Version N/A 2 | // This file is protected by the MIT license (please refer to LICENSE.md before making any changes, copying, or redistributing this software) 3 | #include 4 | 5 | #include 6 | PERFETTO_DEFINE_CATEGORIES( 7 | perfetto::Category("launch") 8 | .SetDescription("Metrics obtained through the launch of the application")); 9 | 10 | // It will expand into a structure containing all possible events, which need to be 11 | // static in the binary sections 12 | PERFETTO_TRACK_EVENT_STATIC_STORAGE(); 13 | 14 | namespace cosmic { 15 | thread_local fmt::memory_buffer out; 16 | std::string GlobalLogger::prodPrefix(const LoggerLevel ml) { 17 | // Level (Thread:Core:ID) 18 | static thread_local i64 msgCount{}; 19 | i32 coreNum{sched_getcpu()}; 20 | std::array thread; 21 | 22 | pthread_getname_np(pthread_self(), thread.data(), thread.size()); 23 | std::string level{}; 24 | switch (ml) { 25 | case Verbose: 26 | level = "Success"; break; 27 | case Info: 28 | level = "Info"; break; 29 | case Error: 30 | level = "Error"; break; 31 | default: 32 | level = "Unk"; break; 33 | } 34 | return fmt::format("{} ({}:{}:{}) -> ", level, thread.data(), coreNum, msgCount++); 35 | } 36 | 37 | GlobalLogger::GlobalLogger() { 38 | #ifndef NDEBUG 39 | perfetto::TracingInitArgs track; 40 | track.backends |= perfetto::kSystemBackend; 41 | 42 | perfetto::Tracing::Initialize(track); 43 | perfetto::TrackEvent::Register(); 44 | #endif 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_timers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | namespace cosmic::iop { 9 | struct TimerControl { 10 | bool useGate; 11 | u8 gateMode; 12 | bool zeroReturn; 13 | bool cmpIntEnb; 14 | bool overIntEnb; 15 | bool repeatInt; 16 | // toggle bit 10 (intEnable) on IRQs if bit 6 (repeatInt) is set 17 | bool toggleInt; 18 | bool intEnable; 19 | bool externSignal; 20 | u8 preScale; 21 | bool cmpInterrupt; 22 | bool overInterrupt; 23 | bool started; 24 | }; 25 | 26 | struct IopTimer { 27 | u64 counter; 28 | TimerControl control; 29 | u64 target; 30 | }; 31 | 32 | class IopTimers { 33 | public: 34 | IopTimers(std::shared_ptr& source, 35 | std::shared_ptr& infra); 36 | 37 | void resetIoTimers(); 38 | u16 readCtrl(u64 index); 39 | void writeCounter(u64 index, u32 value); 40 | void writeCtrl(u64 index, u16 value); 41 | void writeTarget(u64 index, u32 value); 42 | 43 | os::vec performTimerAccess(u32 address, u32 value, bool write); 44 | private: 45 | void timerIrqTest(u64 index, bool overflow); 46 | [[clang::always_inline]] void clearTimerCounter(u64 index); 47 | 48 | std::array ioTimers; 49 | std::array intEvents; 50 | vm::CallBackId timerIntEnbId; 51 | 52 | std::shared_ptr ioSched; 53 | std::shared_ptr infra; 54 | }; 55 | } 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/data/DelegateDataStore.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.data 2 | 3 | import androidx.datastore.preferences.core.edit 4 | import kotlin.properties.ReadWriteProperty 5 | import kotlin.reflect.KProperty 6 | import androidx.annotation.WorkerThread 7 | import emu.cosmic.dataSettings 8 | import kotlinx.coroutines.Dispatchers 9 | import kotlinx.coroutines.flow.Flow 10 | import kotlinx.coroutines.flow.first 11 | import kotlinx.coroutines.flow.map 12 | import kotlinx.coroutines.runBlocking 13 | 14 | class DelegateDataStore( 15 | card: SettingContainer, 16 | ) : ReadWriteProperty { 17 | private val context = card.containerContext 18 | private val globalStorage = context.dataSettings 19 | private val key = card.preferKey 20 | private val defaultValue = card.defaultValue 21 | 22 | @WorkerThread 23 | override operator fun getValue(thisRef: Any, property: KProperty<*>): T { 24 | var value = defaultValue 25 | runBlocking(Dispatchers.IO) { 26 | val dsValueFlow: Flow = globalStorage.data.map { dsReadable -> 27 | dsReadable[key] ?: defaultValue!! 28 | } 29 | value = dsValueFlow.first() 30 | } 31 | return value!! 32 | } 33 | override operator fun setValue(thisRef: Any, property: KProperty<*>, value: T) { 34 | val savedValue: T = value ?: defaultValue 35 | var lastValue: T? = defaultValue 36 | runBlocking { 37 | globalStorage.edit { dsEditor -> 38 | lastValue = dsEditor[key] 39 | dsEditor[key] = savedValue 40 | } 41 | } 42 | if (savedValue != lastValue) { 43 | CosmicSettings.updateSettings = true 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/console/virt_devices.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cosmic::console { 4 | VirtDevices::VirtDevices() { 5 | virtBlocks = std::make_shared(); 6 | dma = std::make_shared(); 7 | 8 | VUs = std::make_shared(gif); 9 | } 10 | void VirtDevices::level2devsInit(std::shared_ptr& pipe) { 11 | pipe->controller = dma; 12 | pipe->controller->mapped = virtBlocks; 13 | 14 | eeR5900 = std::make_shared(pipe); 15 | mipsIop = std::make_shared(pipe); 16 | gs = std::make_shared(pipe); 17 | iopDma = std::make_shared(pipe); 18 | 19 | decoderMpeg12 = std::make_shared(pipe->controller); 20 | } 21 | void VirtDevices::level3devsInit( 22 | std::shared_ptr& pipe, 23 | std::shared_ptr &infra) { 24 | soundPu = std::make_shared(infra, iopDma, pipe); 25 | 26 | iop::IopDmaDevices ioDma{ 27 | .sound = soundPu 28 | }; 29 | iopDma->connectDevices(ioDma); 30 | gif = std::make_shared(gs); 31 | 32 | mio::HardWithDmaCap caps{}; 33 | caps.vif0 = Ref(VUs->vifs[0]); 34 | caps.vif1 = Ref(VUs->vifs[1]); 35 | caps.ee = eeR5900; 36 | 37 | pipe->controller->connectDevices(caps); 38 | } 39 | void Vu01Package::populate(std::shared_ptr infra, 40 | std::shared_ptr dma) { 41 | 42 | vifs[0].dmac = dma; 43 | vifs[1].dmac = dma; 44 | vifs[0].interrupts = infra; 45 | vifs[1].interrupts = infra; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/mio/mmu_tlb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | // kuseg | 00000000h-7fffffffh | User, TLB-mapped 8 | 9 | // kseg0 | 80000000h-9fffffffh | Kernel, directly-mapped, cached 10 | 11 | // kseg1 | a0000000h-bfffffffh | Kernel, directly-mapped, uncached 12 | namespace cosmic::mio { 13 | enum TlbCacheMode : u32 { 14 | Invalid = 0b00, 15 | Uncached = 0b10, 16 | Cached = 0b11, 17 | UncachedAccelerated = 0b111 18 | }; 19 | 20 | struct TlbPageEntry { 21 | std::array cacheMode; 22 | // Scratchpad: When set, the virtual mapping goes to scratchpad instead of main memory 23 | bool isSPad; 24 | std::array pfn; 25 | std::array dirty; 26 | std::array valid; 27 | u32 asid; 28 | u32 vpn2; 29 | u32 pageMask; 30 | u32 pageSize; 31 | u32 pageShift; 32 | 33 | bool isGlobal; 34 | bool modified; 35 | }; 36 | struct TlbInfo { 37 | u32 cacheMode; 38 | bool isModified; 39 | }; 40 | 41 | class TlbCache { 42 | public: 43 | TlbCache(std::shared_ptr& global); 44 | ~TlbCache(); 45 | u8** userVirt{}; 46 | u8** supervisorVirt{}; 47 | u8** kernelVirt{}; 48 | 49 | std::array entries; 50 | std::vector virtArea; 51 | std::vector tlbInfo; 52 | 53 | u8* choiceMemSrc(u32 logicalA); 54 | 55 | bool isCached(u32 address); 56 | void tlbChangeModified(u32 page, bool value); 57 | 58 | void mapTlb(TlbPageEntry& entry); 59 | void unmapTlb(TlbPageEntry& entry); 60 | private: 61 | std::shared_ptr blocks; 62 | }; 63 | 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/app.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-short-identifier: MIT, Version N/A 2 | // This file is protected by the MIT license (please refer to LICENSE.md before making any changes, copying, or redistributing this software) 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | namespace cosmic { 10 | std::unique_ptr states; 11 | std::shared_ptr user; 12 | std::shared_ptr app; 13 | 14 | thread_local os::CosmicEnv cosmicEnv; 15 | 16 | CoreApplication::CoreApplication() : 17 | simulated(std::make_shared()) { 18 | 19 | apiLevel = android_get_device_api_level(); 20 | std::array feats{ 21 | riscFeatures.haveCrc32C() 22 | }; 23 | auto failed = ranges::find_if(feats, [](auto test) { return !test; }); 24 | if (failed != feats.end()) { 25 | throw AppErr("Some of the required ARM ISA sets aren't available on your host processor"); 26 | } 27 | 28 | scene = std::make_shared(); 29 | user->success("Device {} accepted as the host device, Android API {}", getDeviceName(), apiLevel); 30 | vm = std::make_unique(simulated, scene); 31 | } 32 | std::shared_ptr CoreApplication::getBiosMgr() { 33 | auto group{vm->biosHigh->group}; 34 | return group; 35 | } 36 | const std::string& CoreApplication::getDeviceName() { 37 | if (artDeviceName.empty()) { 38 | std::array model; 39 | __system_property_get("ro.product.model", model.data()); 40 | artDeviceName = std::string(model.data()); 41 | } 42 | return artDeviceName; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/cpp/jvm_comm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | static std::array signals; 7 | void catchSystemSignals(cosmic::i32 sig, siginfo_t* ino, void* context) { 8 | cosmic::u8 sid{}; 9 | if (sig == SIGABRT || sig == SIGTRAP) { 10 | if (sig == SIGTRAP) 11 | sid = 1; 12 | } 13 | if (sig == SIGSEGV) { 14 | sid = 2; 15 | } 16 | sigaction(sig, &signals[sid], nullptr); 17 | } 18 | static struct sigaction trap{ 19 | .sa_flags = SA_SIGINFO, 20 | .sa_sigaction = catchSystemSignals, 21 | }; 22 | 23 | // JNI_OnLoad function is called when the JVM has loaded our native code in the heap, this process 24 | // is started by Java Runtime using System.loadLibrary("cosmic") 25 | extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) { 26 | void* env{}; 27 | if (vm) 28 | vm->GetEnv(&env, JNI_VERSION_1_6); 29 | cosmic::cosmicEnv.feedVm(cosmic::BitCast(env)); 30 | 31 | // Kickstart the user readable log system also called as, GlobalLogger 32 | cosmic::user = std::make_shared(); 33 | cosmic::states = std::make_unique(); 34 | 35 | sigaction(SIGABRT, &trap, &signals[0]); 36 | sigaction(SIGTRAP, &trap, &signals[1]); 37 | // sigaction(SIGSEGV, &trap, &signals[2]); 38 | 39 | cosmic::app = std::make_shared(); 40 | return JNI_VERSION_1_6; 41 | } 42 | extern "C" 43 | JNIEXPORT void JNICALL 44 | Java_emu_cosmic_MainActivity_syncSettings(JNIEnv* env, jobject thiz, jstring dateTime) { 45 | cosmic::app->lastSetSync = cosmic::java::JniString(dateTime).get(); 46 | cosmic::states->syncAllSettings(); 47 | 48 | cosmic::user->success("Time of the last synchronization of global settings: {}", cosmic::app->lastSetSync); 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vm/emu_thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::vm { 6 | class EmuVm; 7 | enum MonitorMode { 8 | SvrNone = 0, 9 | SvrNeedsCheck, 10 | SvrRunAnUpdate, 11 | SvrNewState 12 | }; 13 | struct SharedVm { 14 | SharedVm(EmuVm& svm) { 15 | vm = Ref(svm); 16 | running.store(false); 17 | monitor.store(SvrNone); 18 | } 19 | auto setMode(MonitorMode mood) { 20 | auto before{monitor.load()}; 21 | monitor.store(mood); 22 | return before; 23 | } 24 | auto getMode() const { 25 | return monitor.load(); 26 | } 27 | auto setRunning(bool is) -> bool { 28 | auto was{running.load()}; 29 | running.store(is); 30 | return was; 31 | } 32 | auto isRunning() const { 33 | return running.load(); 34 | } 35 | Ref vm; 36 | 37 | std::atomic running; 38 | std::atomic monitor; 39 | }; 40 | class EmuThread { 41 | public: 42 | EmuThread(EmuVm& vm); 43 | void runVm(); 44 | void haltVm(); 45 | void switchVmPower(bool is); 46 | private: 47 | void updateValues(std::shared_ptr& svm, bool running, u8 isSuper); 48 | static void vmMain(std::shared_ptr& svm); 49 | static void vmSupervisor(std::shared_ptr svm); 50 | static void runFrameLoop(Ref& vm); 51 | static void stepMips(Ref& vm, u32 mips, u32 iop, u32 bus); 52 | static void stepVus(Ref& vm, u32 mips, u32 bus); 53 | static void stepGs(Ref& vm, u32 bus); 54 | 55 | std::thread vmThread; 56 | std::shared_ptr vmSharedPtr; 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/about_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 15 | 22 | 23 | 33 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gs/gif_queuev8.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::gs { 5 | void GifBridge::queueReset() { 6 | fifoSize = {}; 7 | #if !NDEBUG 8 | memset(&gifFifo[0], 0xff, sizeof(gifFifo)); 9 | #endif 10 | __asm("eor v0.16b, v0.16b, v0.16b"); 11 | #define STORE_PACKED_16B(addr)\ 12 | __asm("st1 {v0.16b}, [%0]" :: "r" (addr)) 13 | 14 | for (u32 gifData{}; gifData < gifFifo.size(); ) { 15 | STORE_PACKED_16B(&gifFifo[gifData++]); 16 | STORE_PACKED_16B(&gifFifo[gifData++]); 17 | STORE_PACKED_16B(&gifFifo[gifData++]); 18 | STORE_PACKED_16B(&gifFifo[gifData++]); 19 | } 20 | fifoFront = std::begin(gifFifo); 21 | ranges::fill(fifoArr, 0); 22 | } 23 | u64 GifBridge::queueFreePos() { 24 | u64 writable{}; 25 | ranges::for_each(fifoArr, [&](const auto pos){ 26 | if (!pos) 27 | writable++; 28 | }); 29 | return writable; 30 | } 31 | os::vec GifBridge::queueConsume() { 32 | if (fifoFront > std::end(gifFifo)) { 33 | return {}; 34 | } 35 | auto front{*fifoFront}; 36 | fifoSize = static_cast( 37 | std::abs(fifoFront - std::begin(gifFifo))); 38 | fifoArr[fifoSize] = {}; 39 | 40 | fifoFront++; 41 | return front; 42 | } 43 | 44 | u64 GifBridge::queueGetSize() { 45 | if (fifoFront) { 46 | fifoSize = static_cast( 47 | std::abs(fifoFront - std::begin(gifFifo))); 48 | } 49 | // We can pre-load the array values into the L2 cache since we'll be accessing it shortly 50 | for (u64 preload{}; preload < gifFifo.size(); preload++) { 51 | __asm("prfm pldl2keep, [%0]"::"r" (&gifFifo[preload])); 52 | } 53 | 54 | return fifoSize; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ipu/ipu_core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | namespace cosmic::ipu { 8 | enum PictureVDec : u8 { 9 | I = 1, P, B, D 10 | }; 11 | struct IpuStatus { 12 | // When set, it treats the bitstream as MPEG-1 format; otherwise, 13 | // we are dealing with an MPEG-2 image 14 | bool isMpeg1; 15 | bool hasError; 16 | bool startCode; 17 | bool rst; 18 | u8 pictureCode; 19 | bool isBusy; 20 | }; 21 | enum FifoLayout { 22 | In, 23 | Out 24 | }; 25 | enum SliceDecState { 26 | Qsc, 27 | Done 28 | }; 29 | 30 | struct SliceDecCommand { 31 | SliceDecState state; 32 | i32 blocksDecoded; 33 | }; 34 | 35 | class IpuMpeg2 { 36 | public: 37 | IpuMpeg2(std::shared_ptr& direct); 38 | void resetDecoder(); 39 | void update(); 40 | 41 | static std::array inverseScanZz; 42 | static std::array inverseScanAlternate; 43 | // A device or algorithmic function that performs quantization is called a quantizer. 44 | // An analog-to-digital converter is an example of a quantizer 45 | static std::array quantizerLinear; 46 | static std::array quantizerNonLinear; 47 | 48 | bool fifoIsEchoing(FifoLayout fifo); 49 | void issueACmd(u32 cmd); 50 | bool processSliceDecode(); 51 | void clearActionWithInt(); 52 | u8 coreAction; 53 | private: 54 | std::array crCbMap; 55 | DataMatrix ditherMtx; 56 | SliceDecCommand iDec; 57 | 58 | IpuStatus status; 59 | DecoderFifo in, out; 60 | std::shared_ptr dmac; 61 | }; 62 | 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/os/mapped.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | namespace cosmic::os { 6 | template 7 | struct MappedMemory { 8 | MappedMemory() = default; 9 | explicit MappedMemory(T* address, u64 sz, bool unmap = false) : 10 | blockSize(sz), 11 | unmapChunk(unmap), 12 | managedBlock(address) { 13 | } 14 | MappedMemory(MappedMemory&) = delete; 15 | auto operator=(MappedMemory&& mapped) { 16 | blockSize = mapped.blockSize; 17 | unmapChunk = mapped.unmapChunk; 18 | managedBlock = mapped.managedBlock; 19 | 20 | mapped.unmapChunk = {}; 21 | } 22 | MappedMemory(u64 mSize) : 23 | blockSize(mSize * sizeof(T)), 24 | managedBlock(reinterpret_cast(mmap(nullptr, blockSize, 25 | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))) { 26 | madvise(reinterpret_cast(managedBlock), blockSize, MADV_DONTDUMP); 27 | #if !defined(NDEBUG) 28 | enableDump(); 29 | #endif 30 | } 31 | 32 | ~MappedMemory() { 33 | if (unmapChunk) 34 | munmap(reinterpret_cast(managedBlock), blockSize); 35 | } 36 | T& operator[](u32 address) { 37 | return managedBlock[address]; 38 | } 39 | auto operator*() { 40 | return managedBlock; 41 | } 42 | auto getBlockSize() { 43 | return blockSize; 44 | } 45 | void enableDump() { 46 | madvise(reinterpret_cast(managedBlock), blockSize, MADV_DODUMP); 47 | } 48 | operator bool() const { 49 | return managedBlock != nullptr; 50 | } 51 | private: 52 | u64 blockSize{}; 53 | bool unmapChunk{true}; 54 | T* managedBlock{}; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ee/ee_timers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | namespace cosmic::ee { 8 | struct TimerInt { 9 | // Causes interruptions in the CPU when enabled 10 | // TN_COMP 11 | u16 tnComp; 12 | // Only exists for T0 and T1. 13 | u16 tnHold; 14 | u16 sbus; 15 | }; 16 | enum GateMode { 17 | ActivateGate, 18 | ResetGateWhenHigh, 19 | ResetGateWhenLow, 20 | ResetGateWithDiffer, 21 | }; 22 | 23 | struct HwTimer { 24 | u32 clocks; 25 | // Count while gate not active 26 | u32 count; 27 | // All the EE timers could be deactivated by a locked gate 28 | bool gated; 29 | bool isGateEnabled; 30 | GateMode gateMode; 31 | bool withVbSync; 32 | bool clearCountWithDiff; 33 | 34 | operator bool() const { 35 | return gated || !isEnabled; 36 | } 37 | vm::CallBackId callId; 38 | struct { 39 | TimerInt values; 40 | bool overflow; 41 | bool isOfEnabled; 42 | bool compare; 43 | bool cmpIsEnabled; 44 | bool isEnabled; 45 | }; 46 | }; 47 | 48 | class EeTimers { 49 | public: 50 | EeTimers(std::shared_ptr& solver, 51 | std::shared_ptr& inte); 52 | void resetTimers(); 53 | private: 54 | std::shared_ptr scheduler; 55 | std::shared_ptr intc; 56 | void resetTimerCounter(HwTimer& timer); 57 | bool isTimerEnabled(HwTimer& timer); 58 | 59 | void raiseClkTrigger(u8 raised, bool overflow); 60 | void sysCtrlGate(bool hasVSync, bool high); 61 | vm::CallBackId raiseId{}; 62 | std::array timers; 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/os/neon_simd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | namespace cosmic::os { 5 | struct vec { 6 | vec() = default; 7 | vec(u64 qWord0, u64 qWord1 = 0) { 8 | native = vsetq_lane_u64(qWord0, native, 0); 9 | native = vsetq_lane_u64(qWord1, native, 1); 10 | } 11 | vec(const u128 val) { 12 | native = val; 13 | } 14 | auto get() const { 15 | return native; 16 | } 17 | inline u32 to32(const u64 lane) const { 18 | switch (lane) { 19 | case 0: return vgetq_lane_u32(native, 0); 20 | case 1: return vgetq_lane_u32(native, 1); 21 | case 2: return vgetq_lane_u32(native, 2); 22 | case 3: return vgetq_lane_u32(native, 3); 23 | } 24 | return {}; 25 | } 26 | inline u64 to64(const u64 lane) const { 27 | return lane == 0 ? vgetq_lane_u64(native, 0) : vgetq_lane_u64(native, 1); 28 | } 29 | template 30 | T as() const { 31 | if constexpr (sizeof(T) == 4) { 32 | return static_cast(to32(lane)); 33 | } else if constexpr (sizeof(T) == 8) { 34 | return static_cast(to64(lane)); 35 | } 36 | return {}; 37 | } 38 | u64& operator[](u32 vec) { 39 | return reinterpret_cast(&native)[vec]; 40 | } 41 | u64 operator[](u32 vec) const { 42 | return reinterpret_cast(&native)[vec]; 43 | } 44 | os::vec operator&(const os::vec& vv) const { 45 | return { 46 | vv.to64(0) & to64(0), 47 | vv.to64(1) & to64(1)}; 48 | } 49 | 50 | void operator=(const vec& super) { 51 | native = super.native; 52 | } 53 | private: 54 | u128 native; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/creeper/psx_ep1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::creeper { 5 | void IopInterpreter::orMips(Operands ops) { 6 | cpu->ioGPRs[ops.rd] = cpu->ioGPRs[ops.rs] | cpu->ioGPRs[ops.rt]; 7 | } 8 | void IopInterpreter::xorMips(Operands ops) { 9 | cpu->ioGPRs[ops.rd] = cpu->ioGPRs[ops.rs] ^ cpu->ioGPRs[ops.rt]; 10 | } 11 | 12 | void IopInterpreter::nor(Operands ops) { 13 | orMips(ops); 14 | cpu->ioGPRs[ops.rd] = ~cpu->ioGPRs[ops.rd]; 15 | } 16 | void IopInterpreter::lui(Operands ops) { 17 | cpu->ioGPRs[ops.rt] = (ops.inst & 0xffff) << 16; 18 | } 19 | void IopInterpreter::ori(Operands ops) { 20 | cpu->ioGPRs[ops.rt] = cpu->ioGPRs[ops.rs] | (ops.inst & 0xffff); 21 | } 22 | void IopInterpreter::lw(Operands ops) { 23 | // A_IOP_IRQ_CTRL (0xbf801450) 24 | // 2, Boot ROM, Boot ROM, - -, - 0xBf801010 25 | 26 | u32 effAddr{cpu->ioGPRs[ops.base] + (ops.sins & 0xffff)}; 27 | if (effAddr & 1) { 28 | } 29 | cpu->ioGPRs[ops.rt] = static_cast(cpu->iopRead(effAddr)); 30 | } 31 | void IopInterpreter::andi(Operands ops) { 32 | cpu->ioGPRs[ops.rt] = cpu->ioGPRs[ops.rs] & (ops.sins & 0xffff); 33 | } 34 | void IopInterpreter::addi(Operands ops) { 35 | cpu->ioGPRs[ops.rt] = cpu->ioGPRs[ops.rs] + (ops.sins & 0xffff); 36 | } 37 | void IopInterpreter::addiu(Operands ops) { 38 | cpu->ioGPRs[ops.rt] = cpu->ioGPRs[ops.rs] + (ops.inst & 0xffff); 39 | } 40 | // https://github.com/ps2dev/ps2sdk/blob/4b27a27a71fd684a641f1ab7414ac5ee51598ce6/iop/kernel/include/ssbusc.h#L185 41 | 42 | void IopInterpreter::sw(Operands ops) { 43 | // SSBUSC: (Common Delay register: 0xbf801020) 44 | u32 effective{cpu->ioGPRs[ops.base] + (ops.sins & 0xffff)}; 45 | cpu->iopWrite(effective, cpu->ioGPRs[ops.rt]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vm/step_frameloop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::vm { 5 | void EmuThread::stepMips(Ref& vm, u32 mips, u32 iop, u32 bus) { 6 | vm->mips->pulse(mips); 7 | vm->iop->pulse(iop); 8 | // DMAC runs in parallel, which could be optimized (and will be early next year) 9 | vm->sharedPipe->controller->pulse(bus); 10 | vm->mpegDecoder->update(); 11 | } 12 | void EmuThread::stepVus(Ref& vm, u32 mips, u32 bus) { 13 | // VUs can run in parallel with EE... 14 | for (u8 runVifs{}; runVifs < 2; runVifs++) 15 | vm->vu01->vifs[runVifs].update(bus); 16 | vm->vu01->vpu0Cop2.pulse(mips); 17 | vm->vu01->vpu1Dlo.pulse(mips); 18 | } 19 | void EmuThread::stepGs(Ref& vm, u32 bus) { 20 | vm->gsGif->update(bus); 21 | } 22 | 23 | void EmuThread::runFrameLoop(Ref& vm) { 24 | auto sched{vm->scheduler}; 25 | while (!vm->status.get(HasFrame)) { 26 | u32 mipsCycles{sched->getNextCycles(Scheduler::Mips)}; 27 | u32 busCycles{sched->getNextCycles(Scheduler::Bus)}; 28 | u32 iopCycles{sched->getNextCycles(Scheduler::IOP)}; 29 | sched->updateCyclesCount(); 30 | 31 | for (u8 shift{}; shift < 3; shift++) { 32 | switch (sched->affinity >> (shift * 4) & 0xf) { 33 | case EmotionEngine: 34 | stepMips(vm, mipsCycles, iopCycles, busCycles); 35 | break; 36 | case GS: 37 | stepGs(vm, busCycles); 38 | break; 39 | case VUs: 40 | stepVus(vm, mipsCycles, busCycles); 41 | break; 42 | } 43 | } 44 | sched->runTasks(); 45 | // Todo: Just for testing purposes 46 | vm->status.markStepsDone(); 47 | vm->status.set(HasFrame, true); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_dma.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | namespace cosmic { 6 | namespace spu { 7 | class Spu2; 8 | } 9 | } 10 | 11 | namespace cosmic::iop { 12 | struct IopDmaDevices { 13 | std::shared_ptr sound; 14 | }; 15 | // Chan is used here, but we can also refer to it as Channels 16 | struct IopChanControl { 17 | bool isFrom2Device; 18 | u8 syncMode; 19 | bool isBusy; 20 | }; 21 | struct IopChan { 22 | u32 addr; 23 | u32 wordCount; 24 | u32 size; 25 | u16 blockSize; 26 | u8 index; 27 | u32 cyclesDelay; 28 | 29 | IopChanControl status; 30 | }; 31 | enum DmaChannels { 32 | IopSpu2 = 0x8 33 | }; 34 | class IopDma { 35 | static constexpr u8 invalidChannel{0x77}; 36 | public: 37 | IopDma(std::shared_ptr& pipe) : ram(pipe) { 38 | } 39 | void resetIoDma(); 40 | void pulse(u32 cycles); 41 | void connectDevices(IopDmaDevices& devices) { 42 | spu2 = devices.sound; 43 | } 44 | private: 45 | std::array channels; 46 | std::shared_ptr ram; 47 | std::shared_ptr spu2; 48 | 49 | template 50 | T ioDmaRead(u32 iopAddr) { 51 | return *PipeCraftPtr(ram, iopAddr, mio::IopDev); 52 | } 53 | template 54 | void ioDmaWrite(u32 iopAddr, u32 value) { 55 | *PipeCraftPtr(ram, iopAddr, mio::IopDev) = static_cast(value); 56 | } 57 | 58 | struct ActiveChannel { 59 | u16 channel; 60 | operator bool() const { 61 | return channel != invalidChannel; 62 | } 63 | void reset() { 64 | channel = invalidChannel; 65 | } 66 | } activeChannel; 67 | void pulseSpu2Chain(); 68 | }; 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 9 | 10 | 23 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 37 | 42 | 46 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/listeners/PickerProviderListeners.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.listeners 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.os.Environment 6 | import android.util.AttributeSet 7 | import androidx.preference.Preference 8 | import emu.cosmic.SettingsActivity 9 | import emu.cosmic.data.CosmicSettings 10 | import emu.cosmic.helpers.BiosHelper 11 | import emu.cosmic.helpers.DriverHelper 12 | 13 | class FolderPickerListener @JvmOverloads 14 | constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr : Int = androidx.preference.R.attr.preferenceStyle) : 15 | Preference(context, attrs, defStyleAttr) { 16 | 17 | private val settings = CosmicSettings.globalSettings 18 | private val picker = SettingsActivity.filePicker 19 | private val callback = { pair: Pair -> 20 | if (pair.second == "App Storage Directory") 21 | modifyAppDir(pair.first!!) 22 | } 23 | 24 | private fun modifyAppDir(dirPath: Uri) { 25 | settings.appStorage = context.pathSolver(dirPath) 26 | // Resetting all static data resources loaded from the last storage directory 27 | BiosHelper.toDefault() 28 | DriverHelper.toDefault() 29 | } 30 | 31 | override fun onClick() { 32 | SettingsActivity.threatPickerEvent = callback 33 | picker?.launch(title) 34 | } 35 | } 36 | 37 | @Suppress("UnusedReceiverParameter") 38 | fun Context.pathSolver(original: Uri, with: String? = null): String { 39 | val schemes = arrayOf("/document/primary", "/document", "/tree/primary") 40 | var realFile = "" 41 | val target = original.path!! 42 | for (scheme in schemes) { 43 | if (!target.startsWith(scheme)) 44 | continue 45 | realFile = target.substringAfter(scheme).replace(':', '/') 46 | break 47 | } 48 | if (with != null) 49 | return "$with${realFile}" 50 | val canonical = if (target.contains("primary")) { 51 | Environment.getExternalStorageDirectory().path 52 | } else { 53 | Environment.getStorageDirectory().path 54 | } 55 | return "$canonical${realFile}" 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/ee/ee_plus.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | namespace cosmic::ee { 3 | template 4 | struct RangedAddress { 5 | u32 base; 6 | RangedAddress(u32 addr) : base(addr) { 7 | } 8 | u32 operator++(i32 inc) { 9 | auto before{base}; 10 | base += sizeof(T); 11 | return before; 12 | } 13 | operator u32() const { 14 | return base; 15 | } 16 | }; 17 | u64 EeMipsCore::writeArr(u32 address, std::span dataBlk) { 18 | u64 count{}; 19 | RangedAddress addrW{address}; 20 | 21 | for (; (dataBlk.size() - count) >= 8; count += 8) { 22 | mipsWrite(addrW++, dataBlk[count + 0]); 23 | mipsWrite(addrW++, dataBlk[count + 1]); 24 | mipsWrite(addrW++, dataBlk[count + 2]); 25 | mipsWrite(addrW++, dataBlk[count + 3]); 26 | mipsWrite(addrW++, dataBlk[count + 4]); 27 | mipsWrite(addrW++, dataBlk[count + 5]); 28 | mipsWrite(addrW++, dataBlk[count + 6]); 29 | mipsWrite(addrW++, dataBlk[count + 7]); 30 | 31 | } 32 | const std::span remain{ 33 | &dataBlk[count], 34 | dataBlk.size() - count}; 35 | for (const auto value : remain) { 36 | mipsWrite(addrW++, value); 37 | } 38 | return count + remain.size(); 39 | } 40 | void EeMipsCore::setLoHi(i64 lo, i64 hi) { 41 | mulDivStorage[0] = lo & 0xffffffff; 42 | mulDivStorage[1] = hi & 0xffffffff; 43 | } 44 | void EeMipsCore::setLoHi(u64 split) { 45 | i64 val{BitCast(split)}; 46 | mulDivStorage[0] = val & 0xffffffff; 47 | mulDivStorage[1] = (val >> 32) & 0xffffffff; 48 | } 49 | const std::array eeAllGprIdentifier{ 50 | "zero", 51 | "at", "v0", "v1", "a0", "a1", "a2", "a3", 52 | "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", 53 | "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", 54 | "t8", "t9", 55 | "k0", "k1", 56 | "gp", "sp", "fp", "ra" 57 | }; 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/fishron/emitter_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | namespace cosmic::fishron { 6 | enum ArmV8aRegister : u8 { 7 | IntRegister, 8 | LongRegister, 9 | SingleRegister, 10 | DoubleRegister, 11 | NeonRegister 12 | }; 13 | enum ArmV8aAbiGprs : u8 { 14 | w0 = 0, 15 | w8 = 8, 16 | w18 = 18, 17 | }; 18 | enum ArmV8aAbiVec : u8 {}; 19 | struct Arm64v8Gpr { 20 | u16 registerSet; 21 | u32 referCount; 22 | 23 | auto operator <(Arm64v8Gpr& gpr) { 24 | return referCount < gpr.referCount; 25 | } 26 | }; 27 | 28 | class JitBlockRegisters { 29 | public: 30 | JitBlockRegisters(); 31 | std::array intRegisters; 32 | std::array longRegisters; 33 | 34 | std::array singleRegisters; 35 | std::array doubleRegisters; 36 | std::array neonRegisters; 37 | 38 | std::array allocate(ArmV8aRegister set, u32 regsCount); 39 | void deallocate(std::span regs); 40 | }; 41 | class Emitter; 42 | class TextSegment { 43 | public: 44 | TextSegment(std::span segment) : exe(segment) { 45 | pos = 0; 46 | } 47 | void write(u32 instruction) { 48 | exe[pos++] = instruction; 49 | } 50 | u64 pos; 51 | std::span exe; 52 | }; 53 | 54 | class Emitter; 55 | class JitBlock { 56 | public: 57 | JitBlock() : text(executable) {} 58 | JitBlockRegisters regFiles{}; 59 | u32 executable[2]; 60 | TextSegment text; 61 | u32 pc; 62 | u32 page; 63 | 64 | u8 saveRegisters(std::span sets); 65 | void restoreRegisters(std::span sets); 66 | 67 | std::shared_ptr jitter; 68 | }; 69 | 70 | class Emitter { 71 | public: 72 | void stackPushGpr(JitBlock& block, std::span regs); 73 | void stackPopGpr(JitBlock& block, std::span regs); 74 | 75 | std::vector blocks; 76 | }; 77 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/os/system_state.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace cosmic { 13 | extern thread_local os::CosmicEnv cosmicEnv; 14 | } 15 | namespace cosmic::os { 16 | enum StateId { 17 | AppStorage, 18 | GpuCustomDriver, 19 | BiosPath, 20 | 21 | GpuTurboMode, 22 | DumpImageAtClash, 23 | 24 | SchedulerAffinity, 25 | EeMode, 26 | }; 27 | extern const std::unordered_map dsKeys; 28 | using ListenFunc = std::function; 29 | 30 | template 31 | class OsVariable { 32 | public: 33 | OsVariable(const std::string& stateName) { 34 | auto state{cosmicEnv->NewStringUTF(stateName.data())}; 35 | varName = BitCast(cosmicEnv->NewGlobalRef(state)); 36 | if (varName) { 37 | cosmicEnv->DeleteLocalRef(state); 38 | } 39 | } 40 | ~OsVariable() { 41 | cosmicEnv->DeleteGlobalRef(varName); 42 | } 43 | void updateValue(); 44 | 45 | void operator=(const T&& variable) { 46 | cachedState = std::move(variable); 47 | } 48 | auto operator*() { 49 | if constexpr (std::is_same::value) 50 | return *(cachedState.value()); 51 | else 52 | return cachedState.value(); 53 | } 54 | std::vector listeners{2}; 55 | void addListener(ListenFunc&& listen) { 56 | listeners.push_back(listen); 57 | } 58 | private: 59 | std::optional cachedState{}; 60 | jstring varName{}; 61 | }; 62 | 63 | class OsMachState { 64 | public: 65 | OsMachState(); 66 | void syncAllSettings(); 67 | 68 | OsVariable appStorage, 69 | customDriver, 70 | biosPath; 71 | 72 | OsVariable turboMode, dumpImage; 73 | OsVariable schedAffinity, eeMode; 74 | private: 75 | [[maybe_unused]] JavaVM* androidRuntime{}; 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_intc.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-short-identifier: MIT, Version N/A 2 | // This file is protected by the MIT license (please refer to LICENSE.md before making any changes, copying, or redistributing this software) 3 | #include 4 | #include 5 | #include 6 | 7 | namespace cosmic::iop { 8 | void IopIntC::iopCheck() { 9 | iop->intByIntC(ctrl && (stat & mask)); 10 | } 11 | void IopIntC::resetInterrupt() { 12 | stat = {}; 13 | mask = {}; 14 | ctrl = {}; 15 | } 16 | 17 | void IopIntC::assertIrq(i32 id) { 18 | stat |= 1 << id; 19 | iopCheck(); 20 | } 21 | u32 IopIntC::readMask() { 22 | return mask; 23 | } 24 | u32 IopIntC::readStat() { 25 | #if !defined(NDEBUG) 26 | static std::array iopIrqStats { 27 | "VblankStart", 28 | "Gpu", // (used in PSX mode) 29 | "CdvdDrive", 30 | "Dma", 31 | "Timer0", // Reached 32 | "Timer1", 33 | "Timer2", 34 | "Sio0", 35 | "Sio1", 36 | "Spu2", 37 | "Pio", 38 | "VblankEnd", 39 | "Dvd", // Again? 40 | "PCMCIA", 41 | "Timer3", 42 | "Timer4", 43 | "Timer5", 44 | "Sio2", 45 | "HTR0", 46 | "HTR1", // All HTRx is Unknown 47 | "HTR2", 48 | "HTR3", 49 | "Usb", 50 | "EXTR", 51 | "FireWire", 52 | "FDma" // FireWire DMA? (unknown) 53 | }; 54 | if (stat < iopIrqStats.size()) { 55 | user->info("Stat flag being read, output value: {}", iopIrqStats[stat]); 56 | } 57 | #endif 58 | return stat; 59 | } 60 | u32 IopIntC::readICtrl() { 61 | // Global interrupt disable 62 | const auto ic{ctrl}; 63 | ctrl ^= ctrl; 64 | iopCheck(); 65 | return ic; 66 | } 67 | void IopIntC::wrStat(u32 st) { 68 | stat &= st; 69 | iopCheck(); 70 | } 71 | void IopIntC::wrMask(u32 overMask) { 72 | mask = overMask; 73 | iopCheck(); 74 | } 75 | void IopIntC::wrCtrl(u32 ic) { 76 | ctrl = ic & 1; 77 | iopCheck(); 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/data/CosmicSettings.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.data 2 | import android.content.Context 3 | import android.content.res.Resources.NotFoundException 4 | import emu.cosmic.CosmicApplication 5 | 6 | class CosmicSettings private constructor(context: Context) { 7 | var appStorage by DelegateDataStore( 8 | SettingContainer(context, SettingsKeys.AppStorage)) 9 | 10 | var gpuTurboMode by DelegateDataStore( 11 | SettingContainer(context, SettingsKeys.GpuTurboMode)) 12 | 13 | var customDriver by DelegateDataStore( 14 | SettingContainer(context, SettingsKeys.CustomDriver)) 15 | 16 | var eeMode by DelegateDataStore( 17 | SettingContainer(context, SettingsKeys.EEMode)) 18 | 19 | var biosPath by DelegateDataStore( 20 | SettingContainer(context, SettingsKeys.BiosPath)) 21 | 22 | // Creating a static object to store all our configurations 23 | // This object will reside in the global heap memory (Accessible to JNI) 24 | companion object { 25 | val globalSettings by lazy { CosmicSettings(CosmicApplication.context) } 26 | var updateSettings: Boolean = false 27 | 28 | private var dsCachedSet: HashMap? = null 29 | private fun updateAllValues() { 30 | if (!updateSettings) 31 | return 32 | dsCachedSet?.clear() 33 | 34 | val dsCached = mapOf( 35 | "dsdb_app_storage" to globalSettings.appStorage, 36 | "dsdb_gpu_turbo_mode" to globalSettings.gpuTurboMode, 37 | "dsdb_gpu_custom_driver" to globalSettings.customDriver, 38 | "dsdb_ee_mode" to globalSettings.eeMode, 39 | "dsdb_bios_path" to globalSettings.biosPath 40 | ) 41 | dsCachedSet = HashMap(dsCached) 42 | updateSettings = false 43 | } 44 | @JvmStatic 45 | fun getDataStoreValue(config: String) : Any { 46 | if (!updateSettings) 47 | updateSettings = dsCachedSet == null 48 | updateAllValues() 49 | dsCachedSet?.let { 50 | if (!it.containsKey(config)) 51 | throw NotFoundException(config) 52 | return it[config]!! 53 | } 54 | return {} 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_cop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::iop { 5 | void IopCop::rfe() { 6 | status.kuc = status.kup; 7 | status.kup = status.kuo; 8 | 9 | status.iec = status.iep; 10 | status.iep = status.ieo; 11 | } 12 | static const u8 mask{0xff}; 13 | u32 IopCop::mfc(u8 copId) const { 14 | u32 mcVar{}; 15 | switch (copId) { 16 | case 12: { 17 | std::bitset<8 * 4> bin{}; 18 | bin[0] = status.iec; 19 | bin[1] = status.kuc; 20 | bin[2] = status.iep; 21 | bin[3] = status.kup; 22 | bin[4] = status.ieo; 23 | bin[5] = status.kuo; 24 | bin[0x10] = status.isC; 25 | bin[0x16] = status.bev; 26 | 27 | // bev: needs to be set externally, as its value is not within the range of 8 bytes 28 | mcVar |= static_cast(status.imm << 8); 29 | mcVar |= bin.to_ulong(); 30 | } 31 | break; 32 | case 13: 33 | mcVar |= static_cast(cause.code << 2); 34 | mcVar |= static_cast(cause.intPending << 8); 35 | mcVar |= static_cast(cause.bd << 31); 36 | break; 37 | case 14: 38 | mcVar = ePc; break; 39 | case 15: 40 | mcVar = c0id; break; 41 | default: 42 | mcVar = {}; 43 | } 44 | return mcVar; 45 | } 46 | void IopCop::mtc(u8 copId, u32 regVal) { 47 | std::bitset<8 * 8> leaf{status.to64()}; 48 | if (copId != 12) { 49 | throw AppErr("Unknown register with index {} being used", copId); 50 | } 51 | leaf[0] = regVal & 1; 52 | leaf[1] = regVal & (1 << 1); 53 | leaf[2] = regVal & (1 << 2); 54 | leaf[3] = regVal & (1 << 3); 55 | leaf[4] = regVal & (1 << 4); 56 | leaf[5] = regVal & (1 << 5); 57 | leaf[6] = (regVal >> 8) & mask; 58 | leaf[7] = regVal & (1 << 16); 59 | status.bev = regVal & (1 << 22); 60 | 61 | status.st64(leaf.to_ulong()); 62 | } 63 | 64 | void IopCop::resetIoCop() { 65 | status = {}; 66 | cause = {}; 67 | 68 | status.bev = true; 69 | cause.intPending = 0; 70 | ePc = 0; 71 | c0id = 0x58; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/layout/bios_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 20 | 21 | 28 | 29 | 30 | 38 | 39 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/views/DriverViewItem.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.views 2 | 3 | import android.annotation.SuppressLint 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import androidx.viewbinding.ViewBinding 7 | import emu.cosmic.adapters.GenericListContainer 8 | import emu.cosmic.adapters.GenericViewHolder 9 | import emu.cosmic.adapters.ViewBindingFactory 10 | import emu.cosmic.adapters.inflater 11 | import emu.cosmic.databinding.DriverItemBinding 12 | import emu.cosmic.helpers.DriverContainer 13 | object DriverBindFactory : ViewBindingFactory { 14 | override fun create(parent: ViewGroup): ViewBinding = DriverItemBinding.inflate(parent.inflater(), parent, false) 15 | } 16 | 17 | class DriverViewItem( 18 | private val driver: DriverContainer, 19 | var onDelete: ((position: Int, used: Boolean) -> Unit)? = null, 20 | var onClick: ((View) -> Unit)? = null) 21 | : GenericListContainer() { 22 | 23 | private lateinit var binding: DriverItemBinding 24 | override fun getFactory(): ViewBindingFactory = DriverBindFactory 25 | @SuppressLint("SetTextI18n") 26 | override fun bind(holder: GenericViewHolder, position: Int) { 27 | binding = holder.binding 28 | val meta = driver.meta 29 | binding.drvName.text = "Name: ${meta.name}" 30 | binding.drvDescription.text = "Desc: ${meta.description}" 31 | binding.drvAuthor.text = "Author: ${meta.author}" 32 | binding.drvVendor.text = "Vendor: ${meta.vendor}" 33 | binding.drvMinApi.text = "MinAPI: ${meta.minApi}" 34 | binding.dpkVulkanLib.text = "Library: ${meta.libraryName}" 35 | 36 | binding.drvChecker.isChecked = driver.selected 37 | 38 | onClick?.let { 39 | binding.drvChecker.setOnClickListener(it) 40 | binding.root.setOnClickListener(it) 41 | } 42 | } 43 | 44 | override fun compareItem(prob: GenericListContainer): Boolean { 45 | val metaProb = (prob as DriverViewItem).let { 46 | prob.driver.meta 47 | } 48 | val metaOrigin = driver.meta 49 | return metaProb.name == metaOrigin.name && metaProb.libraryName == metaOrigin.libraryName 50 | } 51 | 52 | override fun isTheSame(prob: GenericListContainer) = 53 | prob is DriverViewItem && prob.driver == driver 54 | } -------------------------------------------------------------------------------- /app/src/main/cpp/bios_jni.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | extern "C" 8 | JNIEXPORT jobject JNICALL 9 | Java_emu_cosmic_helpers_BiosHelper_00024Companion_addBios(JNIEnv* env, jobject thiz, jobject descriptor, jint position) { 10 | cosmic::hle::BiosInfo info{}; 11 | info.position = position; 12 | 13 | auto biosHld{AFileDescriptor_getFd(env, descriptor)}; 14 | auto biosMgr{cosmic::app->getBiosMgr()}; 15 | std::array find{biosHld, 0}; 16 | 17 | auto object{info.createInstance()}; 18 | if (biosMgr->isAlreadyAdded(find)) { 19 | biosMgr->loadBiosBy(object, find, false); 20 | return object; 21 | } 22 | 23 | info.chkAndLoad(biosHld); 24 | biosMgr->storeAndFill(object, std::move(info)); 25 | return object; 26 | } 27 | 28 | extern "C" 29 | JNIEXPORT jint JNICALL 30 | Java_emu_cosmic_helpers_BiosHelper_00024Companion_setBios(JNIEnv* env, jobject thiz, jint pos) { 31 | auto group{cosmic::app->getBiosMgr()}; 32 | std::array by{0, pos}; 33 | 34 | return group->choice(by, true); 35 | } 36 | extern "C" 37 | JNIEXPORT jboolean JNICALL 38 | Java_emu_cosmic_helpers_BiosHelper_00024Companion_removeBios(JNIEnv* env, jobject thiz, jintArray posFd) { 39 | if (env->GetArrayLength(posFd) != 2) { 40 | throw cosmic::AppErr("Not supported element array of size {} passed", 41 | env->GetArrayLength(posFd)); 42 | } 43 | auto group{cosmic::app->getBiosMgr()}; 44 | jint* mangled{env->GetIntArrayElements(posFd, nullptr)}; 45 | std::array mangle{mangled[0], mangled[1]}; 46 | 47 | bool hasRemoved{group->rmFromStorage(mangle)}; 48 | 49 | env->ReleaseIntArrayElements(posFd, mangled, 0); 50 | return hasRemoved; 51 | } 52 | extern "C" 53 | JNIEXPORT void JNICALL 54 | Java_emu_cosmic_helpers_BiosHelper_00024Companion_cleanAllBios(JNIEnv* env, jobject thiz) { 55 | auto bgp{cosmic::app->getBiosMgr()}; 56 | bgp->discardAll(); 57 | } 58 | extern "C" 59 | JNIEXPORT jint JNICALL 60 | Java_emu_cosmic_helpers_BiosHelper_00024Companion_getBios(JNIEnv* env, jobject thiz, jint defaultPos) { 61 | auto biosGroup{cosmic::app->getBiosMgr()}; 62 | if (biosGroup->slotBios) 63 | return biosGroup->slotBios->position; 64 | return defaultPos; 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/console/virt_devices.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | namespace cosmic::mio { 15 | class DmaController; 16 | } 17 | namespace cosmic::console { 18 | class IntCInfra; 19 | 20 | #pragma pack(push, 1) 21 | class Vu01Package { 22 | public: 23 | Vu01Package(std::shared_ptr gif) : 24 | vpu1Dlo(vu::VectorUnit({}, vu::VuWorkMemory(vpu1Space))), 25 | vpu0Cop2(vu::VectorUnit(vpu1Dlo, vpu0Space)) { 26 | 27 | vifs[0] = vu::VifMalice(vpu0Cop2, vu::VifGifInterconnector{}); 28 | vifs[1] = vu::VifMalice(vpu1Dlo, vu::VifGifInterconnector{gif}); 29 | } 30 | void populate(std::shared_ptr infra, 31 | std::shared_ptr dma); 32 | 33 | std::array vifs; 34 | // These two vector units could run in two modes, Parallel and Serial 35 | // Parallel mode: (CPU + VU0 <-> Scratchpad) + (VU1 <-> Main Memory) -> GIF 36 | // Serial mode: (MainMemory -> (CPU + VU0) -> Scratchpad -> VU1 -> GIF 37 | vu::VectorUnit vpu1Dlo; 38 | vu::VectorUnit vpu0Cop2; 39 | 40 | std::array vpu1Space[2]; 41 | std::array vpu0Space[2]; 42 | }; 43 | #pragma pack(pop) 44 | class VirtDevices { 45 | public: 46 | VirtDevices(); 47 | void level2devsInit(std::shared_ptr& pipe); 48 | void level3devsInit(std::shared_ptr& pipe, 49 | std::shared_ptr& infra); 50 | 51 | std::shared_ptr eeR5900; 52 | std::shared_ptr mipsIop; 53 | std::shared_ptr iopDma; 54 | std::shared_ptr soundPu; 55 | 56 | std::shared_ptr decoderMpeg12; 57 | 58 | std::shared_ptr virtBlocks; 59 | std::shared_ptr VUs; 60 | 61 | std::shared_ptr gif; 62 | std::shared_ptr gs; 63 | private: 64 | std::shared_ptr dma; 65 | }; 66 | } 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/res/layout/driver_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 27 | 28 | 29 | 38 | 39 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gpu/graphics_layer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | namespace cosmic::gpu { 8 | class GraphicsLayer : public GraphicsFunctionsRef { 9 | public: 10 | GraphicsLayer(RenderApi renderMode); 11 | ~GraphicsLayer(); 12 | std::variant render; 13 | u32 loadGraphicsApi(); 14 | void updateLayer(); 15 | 16 | std::unique_ptr backend; 17 | std::optional app; 18 | std::optional instance; 19 | 20 | std::optional vkDev; 21 | std::optional deviceInfo; 22 | 23 | bool haveValidation{}; 24 | #ifndef NDEBUG 25 | VkDebugUtilsMessengerEXT debugMessenger; 26 | #endif 27 | u32 queueFamilyId{}; 28 | 29 | RenderApi graphicsApi{}; 30 | }; 31 | } 32 | namespace cosmic::gpu::vulcano { 33 | constexpr auto invQueueId{std::numeric_limits::max()}; 34 | struct PhysicalDevice { 35 | PhysicalDevice() {} 36 | std::optional physicalDev; 37 | std::optional gpuUser; 38 | vk::DeviceCreateInfo info{}; 39 | u32 desiredQueueId{invQueueId}; 40 | }; 41 | 42 | vk::raii::Instance createVulkanInstance(const vk::raii::Context& context, bool& haveValidationLayer); 43 | PhysicalDevice createPhysicalDevice(vk::raii::Instance& vki); 44 | #ifndef NDEBUG 45 | VkResult createDebugLayer( 46 | PFN_vkGetInstanceProcAddr getInstance, 47 | vk::raii::Instance& instance, 48 | const VkDebugUtilsMessengerCreateInfoEXT& pCreateInfo, 49 | const VkAllocationCallbacks& pAllocator, 50 | VkDebugUtilsMessengerEXT& pDebugMessenger); 51 | 52 | void destroyDebugUtilsMessengerExt( 53 | PFN_vkGetInstanceProcAddr getInstance, 54 | vk::raii::Instance& instance, 55 | VkDebugUtilsMessengerEXT debugMessenger, 56 | const VkAllocationCallbacks& pAllocator); 57 | 58 | VKAPI_ATTR VkBool32 VKAPI_CALL debugMessagesCallback( 59 | VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 60 | VkDebugUtilsMessageTypeFlagsEXT messageType, 61 | const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, 62 | void* pUserData); 63 | 64 | VkDebugUtilsMessengerCreateInfoEXT createDebugInfo(); 65 | #endif 66 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/creeper/vector_codes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | namespace cosmic::creeper { 5 | struct VuMicroOrder { 6 | // A VU, in microcode mode, executes more than one instruction, 7 | // but there may be a dependency between registers 8 | std::function upper, lower; 9 | struct VecAffected { 10 | 11 | std::array writeOpcodes; 12 | std::array writeOpcodesField; 13 | 14 | // Both types can consume more than one register in a single instruction 15 | std::array readUpper; 16 | std::array readUpperField; 17 | std::array readLower; 18 | std::array readLowerField; 19 | } fr; 20 | struct IntAffected { 21 | u8 writeOpcodes; 22 | u8 readUpperOpcodes; 23 | u8 readLowerOpcodes; 24 | } ir; 25 | }; 26 | 27 | class VuMicroInterpreter : public vu::VuMicroExecutor { 28 | public: 29 | VuMicroInterpreter(Ref vuCake) : 30 | vu::VuMicroExecutor(vuCake) { 31 | vu = vuMicro; 32 | } 33 | u32 executeCode() override; 34 | void setCurrentProgram(u32 crc) override; 35 | 36 | std::pair fetchPcInst() override; 37 | VuMicroOperands translateUpper(u32 upper); 38 | VuMicroOperands translateLower(u32 lower); 39 | VuMicroOperands translateLower1(u32 lower); 40 | VuMicroOperands translateLower2(u32 lower); 41 | 42 | static void waitp(VuMicroOperands& ops); 43 | static void waitq(VuMicroOperands& ops); 44 | static void maxi(VuMicroOperands& ops); 45 | 46 | static void iddai(VuMicroOperands& ops); 47 | static void mtir(VuMicroOperands& ops); 48 | static void mr32(VuMicroOperands& ops); 49 | static void mul(VuMicroOperands& ops); 50 | static void mula(VuMicroOperands& ops); 51 | static void mulabc(VuMicroOperands& ops); 52 | static void mulai(VuMicroOperands& ops); 53 | static void mulaq(VuMicroOperands& ops); 54 | 55 | static f32 fasterPs2VuMul(Reg rad, u32 id, Reg mul, u32 idx); 56 | static void vectorizeXyZw(VuMicroOperands& ops, std::function vecMathCb); 57 | private: 58 | VuMicroOrder ordered; 59 | 60 | static Ref vu; 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gs/gs_engine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | namespace cosmic::gs { 9 | struct GsPayloadDataPacket { 10 | GsPayloadDataPacket() = default; 11 | 12 | GsPayloadDataPacket(u64 bufferSize) : 13 | downloadBuffer(bufferSize) {} 14 | u32 qw128Count; 15 | os::MappedMemory downloadBuffer; 16 | u32 indexAddr; 17 | 18 | os::vec consume() { 19 | auto data{downloadBuffer[indexAddr]}; 20 | indexAddr++; 21 | qw128Count--; 22 | return data; 23 | } 24 | operator bool() const { 25 | return downloadBuffer; 26 | } 27 | }; 28 | enum GsRegisters { 29 | GsBusDir 30 | }; 31 | enum RegDesc { 32 | Primitive, 33 | RgBaQ, 34 | StPos, 35 | UvPos, 36 | Xyz2, 37 | 38 | Fog = 0xa, 39 | Ad = 0xe, 40 | 41 | Nop 42 | }; 43 | 44 | union RgBaQReg { 45 | u64 rainbow; 46 | struct { 47 | u8 r, g, b, a; 48 | f32 gsq; 49 | }; 50 | }; 51 | union CoordinatesXyz { 52 | u64 xyz; 53 | struct { 54 | u16 x, y; 55 | u32 z; 56 | }; 57 | }; 58 | 59 | class GsEngine { 60 | public: 61 | GsEngine(std::shared_ptr& memory) : 62 | shared(memory) { 63 | } 64 | ~GsEngine() { 65 | } 66 | 67 | void resetGraphics(); 68 | std::tuple readGsData(); 69 | bool isStalled(); 70 | u32 privileged(GsRegisters gsr) const; 71 | 72 | bool isSoftwareMode{}; 73 | void gsWrite(u32 addr, u64 data); 74 | 75 | struct { 76 | // Must be set appropriately for GIF->VRAM and VRAM->GIF 77 | u8 busDir; 78 | } gsPrivateRegs; 79 | 80 | gamedb::SwitchPatches gswAddrAlias{}; 81 | std::shared_ptr shared; 82 | private: 83 | GsPayloadDataPacket videoBuffer{}; 84 | 85 | // Internal registers (accessible via GIF) 86 | u64 prim; 87 | RgBaQReg palette{}; 88 | std::pair st; 89 | std::pair uv; 90 | CoordinatesXyz xyz2; 91 | u8 fog; 92 | u64 framesCount; 93 | 94 | void writePrimitive(u64 primitive); 95 | }; 96 | } 97 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/mio/mmu_tlb.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | namespace cosmic::mio { 5 | TlbCache::TlbCache(std::shared_ptr& global) : 6 | blocks(global) { 7 | std::memset(entries.data(), 0, sizeof(entries)); 8 | virtArea.resize(1024 * 1024 * 3); 9 | tlbInfo.resize(1024 * 1024); 10 | 11 | userVirt = &virtArea[0]; 12 | supervisorVirt = &virtArea[1024 * 1024]; 13 | kernelVirt = &virtArea[1024 * 1024 * 2]; 14 | 15 | virtArea.clear(); 16 | tlbInfo.clear(); 17 | constexpr u32 unmapStart{0x8000'0000}; 18 | constexpr u32 unmapEnd{0xc000'0000}; 19 | 20 | // Kernel page segments are not mapped in the TLB; we need to pass physical addresses 21 | // directly to the table entries 22 | for (auto segmentPage{unmapStart}; segmentPage != unmapEnd; segmentPage += 4096) { 23 | auto table{segmentPage / 4096}; 24 | if (table >= 1024 * 1024) { 25 | throw MioErr("Kernel TLB table {} is outside the specified range", table); 26 | } 27 | kernelVirt[table] = choiceMemSrc(segmentPage & (0x20000000 - 1)); 28 | tlbInfo[table].cacheMode = TlbCacheMode::Uncached; 29 | 30 | if (segmentPage < 0xa0000000) { 31 | tlbInfo[table].cacheMode = TlbCacheMode::Cached; 32 | } 33 | } 34 | } 35 | TlbCache::~TlbCache() { 36 | // delete[] userVirt; 37 | // delete[] supervisorVirt; 38 | // delete[] kernelVirt; 39 | 40 | std::vector infoEmpty; 41 | tlbInfo.swap(infoEmpty); 42 | } 43 | 44 | u8* TlbCache::choiceMemSrc(u32 logicalA) { 45 | u8* mapAddress{}; 46 | [[likely]] if (logicalA < 0x1000'0000) { 47 | mapAddress = blocks->mapVirtAddress(logicalA); 48 | } else if (logicalA >= 0x1fc0'0000 && logicalA < 0x2000'0000) { 49 | // Accessing the physical memory of the BIOS, not yet implemented, under construction 50 | mapAddress = blocks->mapVirtAddress(logicalA, mio::BiosMemory); 51 | } 52 | return mapAddress; 53 | } 54 | 55 | void TlbCache::tlbChangeModified(u32 page, bool value) { 56 | if (page >= 1024 * 1024) { 57 | throw MioErr("Page {} is outside the range, TLB is missing for this page", page); 58 | } 59 | tlbInfo[page].isModified = value; 60 | } 61 | bool TlbCache::isCached(u32 address) { 62 | return tlbInfo[address / 4096].cacheMode == TlbCacheMode::Cached; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/logger.h: -------------------------------------------------------------------------------- 1 | // SPDX-short-identifier: MIT, Version N/A 2 | // This file is protected by the MIT license (please refer to LICENSE.md before making any changes, copying, or redistributing this software) 3 | #pragma once 4 | 5 | #include 6 | #include 7 | namespace cosmic { 8 | enum LoggerLevel { 9 | Info = ANDROID_LOG_INFO, 10 | Debug = ANDROID_LOG_DEBUG, 11 | Verbose = ANDROID_LOG_VERBOSE, 12 | Error = ANDROID_LOG_ERROR, 13 | }; 14 | 15 | extern thread_local fmt::memory_buffer out; 16 | 17 | class GlobalLogger { 18 | public: 19 | GlobalLogger(); 20 | template 21 | void bind(LoggerLevel msgLevel, fmt::format_string& format, Args&&... args) { 22 | for (auto deny : disableLevels) { 23 | if (deny == msgLevel) { 24 | return; 25 | } 26 | } 27 | 28 | fmt::format_to(std::back_inserter(out), "{}", prodPrefix(msgLevel)); 29 | fmt::format_to(std::back_inserter(out), fmt::runtime(format), std::forward(args)...); 30 | fmt::format_to(std::back_inserter(out), "\n"); 31 | 32 | __android_log_write(static_cast(msgLevel), tag, out.data()); 33 | out.clear(); 34 | } 35 | 36 | template 37 | void success(fmt::format_string format, Args&&... args) { 38 | bind(Verbose, format, std::forward(args)...); 39 | } 40 | template 41 | void info(fmt::format_string format, Args&&... args) { 42 | bind(Info, format, std::forward(args)...); 43 | } 44 | template 45 | void debug(fmt::format_string format, Args&&... args) { 46 | bind(Debug, format, std::forward(args)...); 47 | } 48 | template 49 | void error(fmt::format_string format, Args&&... args) { 50 | bind(Error, format, std::forward(args)...); 51 | } 52 | [[noreturn]] static void cause(const char* fail) { 53 | __android_log_assert(fail, tag, "Assertion with a cause, execution flow has been broken"); 54 | } 55 | private: 56 | std::string prodPrefix(const LoggerLevel ml); 57 | DescriptorRaii logFile{}; 58 | // Don't allow these specific levels to be threaded or printed to the user 59 | std::array disableLevels{}; 60 | 61 | static constexpr auto tag{"Cosmic"}; 62 | }; 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/views/BiosViewItem.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.views 2 | 3 | import android.content.Context 4 | import android.graphics.BitmapFactory 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import emu.cosmic.adapters.GenericListContainer 8 | import emu.cosmic.adapters.GenericViewHolder 9 | import emu.cosmic.adapters.ViewBindingFactory 10 | import emu.cosmic.adapters.inflater 11 | import emu.cosmic.data.BiosInfo 12 | import emu.cosmic.databinding.BiosItemBinding 13 | import java.io.File 14 | 15 | object BiosBindingFactory : ViewBindingFactory { 16 | override fun create(parent: ViewGroup) = 17 | BiosItemBinding.inflate(parent.inflater(), parent, false) 18 | } 19 | 20 | class BiosViewItem( 21 | private val context: Context, 22 | private val bios: BiosInfo, 23 | var onDelete: ((position: Int, used: Boolean) -> Unit)? = null, 24 | var onClick: ((View) -> Unit)? = null) 25 | : GenericListContainer() { 26 | 27 | override fun getFactory(): ViewBindingFactory = BiosBindingFactory 28 | 29 | override fun bind(holder: GenericViewHolder, position: Int) { 30 | val binding = holder.binding 31 | 32 | binding.biosFullQualified.text = File(bios.biosPath).name 33 | binding.biosName.text = bios.biosName 34 | binding.biosDetails.text = bios.biosDetails 35 | 36 | val countries = listOf( 37 | "countries/us.png", "countries/jp.png", "countries/eu.png", "countries/ch.png", 38 | "countries/hk.png") 39 | val flag = when (bios.biosName.substringBefore(' ')) { 40 | "USA" -> countries[0] 41 | "Japan" -> countries[1] 42 | "Europe" -> countries[2] 43 | "China" -> countries[3] 44 | "Honk Kong" -> countries[4] 45 | else -> "" 46 | } 47 | if (flag.isNotEmpty()) { 48 | val bitmap = BitmapFactory.decodeStream(context.assets.open(flag)) 49 | binding.biosFlag.setImageBitmap(bitmap) 50 | } 51 | 52 | binding.biosChecker.apply { 53 | isChecked = bios.selected 54 | } 55 | onClick?.let { 56 | binding.biosChecker.setOnClickListener(it) 57 | binding.root.setOnClickListener(it) 58 | } 59 | } 60 | 61 | override fun compareItem(prob: GenericListContainer): Boolean 62 | = prob is BiosViewItem && 63 | prob.bios.position == bios.position && 64 | prob.bios.biosName == bios.biosName 65 | 66 | override fun isTheSame(prob: GenericListContainer): Boolean 67 | = prob is BiosViewItem && prob.bios == bios 68 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/vu/vif10_upload.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | namespace cosmic::vu { 10 | class VifGifInterconnector { 11 | public: 12 | // A gateway to directly access the GIF 13 | VifGifInterconnector() = default; 14 | VifGifInterconnector(std::shared_ptr ark) : gif(ark) {} 15 | u8 getId() { 16 | if (gif) 17 | return 1; 18 | return 0; 19 | } 20 | std::shared_ptr gif{}; 21 | }; 22 | 23 | class VectorUnit; 24 | enum VifCommandStatus { 25 | CmdIdle, 26 | WaitingForData, 27 | DecodingCommand, 28 | DecAndTrans 29 | }; 30 | enum VifStall { 31 | NotStalled, 32 | Ibt = 1, 33 | MskPath3 = 2, 34 | Stop = 4, 35 | Direct = 8, 36 | ForceBreak = 16 37 | }; 38 | struct VifStatus { 39 | VifCommandStatus command; 40 | 41 | bool isStalledVss; 42 | bool isStalledVfs; 43 | bool isStalledIntVis; 44 | bool interrupt; 45 | 46 | bool vewWaitingVu; 47 | bool vgwWaitingGif; 48 | std::variant error; 49 | }; 50 | 51 | enum FifoStates { 52 | Cooking, 53 | GifDownloading // FIFO Reverse 54 | }; 55 | 56 | class alignas(8) VifMalice { 57 | public: 58 | VifMalice() = default; 59 | VifMalice(VifMalice&) = delete; 60 | VifMalice(Ref vector, VifGifInterconnector card); 61 | 62 | void update(u32 cycles); 63 | void resetVif(); 64 | 65 | inline u32 getFifoFreeSpace() const { 66 | return fifo.size(); 67 | } 68 | inline u32 getQueueFreeSpace() const { 69 | return inQueue.size(); 70 | } 71 | bool transferDmaData(os::vec quad, bool validateFreeSpace = false); 72 | 73 | VifGifInterconnector vif2gif{}; 74 | std::shared_ptr interrupts; 75 | std::shared_ptr dmac; 76 | 77 | Ref vifVu; 78 | mio::DirectChannels vifId; 79 | private: 80 | u16 memMask{}; 81 | u8 fifoSize{}; 82 | // Amount of data not yet transferred in MPG/UNPACK 83 | u8 num; 84 | u32 mask, 85 | code; 86 | VifStatus vifS; 87 | VifFifo fifo, inQueue; 88 | 89 | u8 isVifStalled{}; 90 | 91 | // VIFn_ITOP: 0 and VIFn_ITOPS: 1 is packed here 92 | u16 tops[2]; 93 | FifoStates fifoState; 94 | }; 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/emu/cosmic/adapters/GenericViewAdapter.kt: -------------------------------------------------------------------------------- 1 | package emu.cosmic.adapters 2 | import android.view.ViewGroup 3 | import androidx.recyclerview.widget.AsyncListDiffer 4 | import androidx.recyclerview.widget.DiffUtil 5 | import androidx.recyclerview.widget.RecyclerView 6 | import androidx.recyclerview.widget.RecyclerView.ViewHolder 7 | import androidx.viewbinding.ViewBinding 8 | 9 | class GenericViewHolder(view : T) : ViewHolder(view.root) { 10 | val binding = view 11 | } 12 | 13 | open class GenericViewAdapter 14 | : RecyclerView.Adapter>() { 15 | companion object { 16 | private val viewDiffer = object : DiffUtil.ItemCallback>() { 17 | override fun areItemsTheSame(obj: GenericListContainer, 18 | prob: GenericListContainer) = obj.compareItem(prob) 19 | override fun areContentsTheSame(obj: GenericListContainer, 20 | prob: GenericListContainer) = obj.isTheSame(prob) 21 | } 22 | } 23 | 24 | private val asyncDiffer = AsyncListDiffer(this, viewDiffer) 25 | 26 | private val entities = mutableListOf>() 27 | private val viewItems: List> get() = asyncDiffer.currentList 28 | 29 | private val factory = mutableMapOf() 30 | 31 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenericViewHolder 32 | = GenericViewHolder(factory.filterValues { it == viewType }.keys.single().create(parent)) 33 | 34 | fun update() { 35 | @Suppress("unchecked_cast") 36 | asyncDiffer.submitList(entities as List>) 37 | } 38 | 39 | fun fillWith(itemList: List>) { 40 | entities.clear() 41 | entities.addAll(itemList) 42 | update() 43 | } 44 | open fun dropItem(position: Int) { 45 | entities.removeAt(position) 46 | update() 47 | } 48 | open fun addItem(item: GenericListContainer<*>, position: Int) { 49 | entities.add(position, item) 50 | update() 51 | } 52 | 53 | override fun getItemViewType(position: Int) 54 | = factory.getOrPut(viewItems[position].getFactory()) { factory.size } 55 | 56 | override fun onBindViewHolder(holder: GenericViewHolder, position: Int) { 57 | viewItems[position].apply { 58 | adaptedBy = this@GenericViewAdapter 59 | bind(holder, position) 60 | } 61 | } 62 | override fun getItemCount(): Int { 63 | return viewItems.size 64 | } 65 | } -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gs/gs_engine.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace cosmic::gs { 5 | constexpr u64 downBufferSize{2048 * 2048 / 4}; 6 | void GsEngine::resetGraphics() { 7 | videoBuffer.qw128Count = {}; 8 | videoBuffer.indexAddr = {}; 9 | 10 | framesCount = {}; 11 | if (!videoBuffer) { 12 | videoBuffer = GsPayloadDataPacket{downBufferSize}; 13 | } 14 | } 15 | std::tuple GsEngine::readGsData() { 16 | bool hasData{videoBuffer.qw128Count != 0}; 17 | os::vec vec{}; 18 | if (hasData) { 19 | const auto eve{videoBuffer.consume()}; 20 | vec[0] = eve[0]; 21 | vec[1] = eve[1]; 22 | } 23 | return std::make_tuple(hasData, vec); 24 | } 25 | 26 | bool GsEngine::isStalled() { 27 | return {}; 28 | } 29 | u32 GsEngine::privileged(GsRegisters gsr) const { 30 | if (gsr == GsBusDir) { 31 | return gsPrivateRegs.busDir; 32 | } 33 | return {}; 34 | } 35 | void GsEngine::gsWrite(u32 addr, u64 data) { 36 | addr &= 0x7f; 37 | 38 | switch (addr) { 39 | case 0x00: 40 | writePrimitive(data); 41 | break; 42 | case 0x01: 43 | palette.rainbow = data; 44 | break; 45 | case 0x02: { 46 | auto fp0{data & 0xffffff00}; 47 | auto fp1{(data >> 32) & 0xffffff00}; 48 | 49 | if ((fp0 & 0x7f800000) == 0x7f800000) 50 | fp0 = (fp0 & 0x80000000) | 0x7f7fffff; 51 | if ((fp1 & 0x7f800000) == 0x7f800000) 52 | fp1 = (fp1 & 0x80000000) | 0x7f7fffff; 53 | 54 | st = std::make_pair( 55 | *reinterpret_cast(&fp0), *reinterpret_cast(&fp1)); 56 | } 57 | break; 58 | case 0x03: 59 | uv = std::make_pair(data & 0x3fff, (data >> 16) & 0x3fff); 60 | break; 61 | case 0x05: 62 | xyz2.xyz = data; 63 | break; 64 | case 0x0a: 65 | fog = (data >> 56) & 0xff; 66 | case 0xf: 67 | break; 68 | default: 69 | // For some reason, the title Ridge Racer V uses the value 11 as a alias for the value 1 70 | ranges::for_each(gswAddrAlias, [&](const auto& path) { 71 | if (path.gameCase == addr) { 72 | gsWrite(path.rCase, data); 73 | } 74 | }); 75 | break; 76 | } 77 | 78 | } 79 | void GsEngine::writePrimitive(u64 primitive) { 80 | [[likely]] if (!isSoftwareMode) { 81 | this->prim = primitive; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/iop/iop_core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | namespace cosmic::iop { 11 | struct IoCache { 12 | u32 data; 13 | u32 tag; 14 | bool isValid; 15 | }; 16 | 17 | class IoMipsCore { 18 | public: 19 | IoMipsCore(std::shared_ptr& pipe); 20 | void resetIop(); 21 | void pulse(u32 cycles); 22 | u32 fetchByPc(); 23 | void printStatus(boost::filesystem::fstream& output); 24 | 25 | void intByIntC(bool isInt); 26 | void handleException(u8 code); 27 | u32 incPc(); 28 | void jumpTo(u8 effectiveGpr); 29 | void branchIf(bool cond, i32 offset); 30 | 31 | std::array ioGPRs; 32 | std::array ioScratch; 33 | std::array instCache; 34 | u32 cacheCtrl; 35 | u32 cacheHit, cacheMiss; 36 | std::shared_ptr iopMem; 37 | 38 | static u32 translateAddr(u32 address); 39 | bool isPcUncached(u32 pc) const; 40 | static bool isRoRegion(u32 address); 41 | 42 | std::unique_ptr timer; 43 | 44 | template 45 | T iopRead(u32 address) { 46 | address = translateAddr(address); 47 | if (isRoRegion(address)) { 48 | if constexpr (sizeof(T) == 4) 49 | return PipeRead(iopMem, address & 0x1fffffff, mio::IopDev); 50 | } 51 | u32 prime{iopPrivateAddrSolver(address & 0x1fffffff)}; 52 | return PipeRead(iopMem, prime, mio::IopDev); 53 | } 54 | template 55 | void iopWrite(u32 address, u32 value) { 56 | address = translateAddr(address); 57 | if (isRoRegion(address)) { 58 | if constexpr (sizeof(T) == 4) 59 | PipeWrite(iopMem, address & 0x1fffffff, value, mio::IopDev); 60 | return; 61 | } 62 | u32 privateAddr{iopPrivateAddrSolver(address & 0x1fffffff)}; 63 | PipeWrite(iopMem, privateAddr, value, mio::IopDev); 64 | } 65 | 66 | u32 hi, lo; 67 | u32 lastPc, 68 | ioPc, 69 | waitPc; 70 | i64 cyclesToIo; 71 | IopCop cop; 72 | bool onBranch{false}; 73 | u8 branchDelay{}; 74 | i32 mathDelay{}; 75 | 76 | void takeBranchIf(bool take, i32 pcAddr); 77 | u8 irqSpawned; 78 | private: 79 | static u32 iopPrivateAddrSolver(u32 address); 80 | 81 | std::unique_ptr interpreter; 82 | }; 83 | } 84 | 85 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/common/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | namespace cosmic { 12 | template 13 | class Ref { 14 | public: 15 | Ref() = default; 16 | Ref(T& value) : reference(&value) {} 17 | Ref(std::add_pointer::type value) : reference(value) {} 18 | auto operator*() { 19 | return reference; 20 | } 21 | auto operator->() { 22 | return reference; 23 | } 24 | operator bool() const { 25 | return reference != nullptr; 26 | } 27 | using TRef = std::add_pointer; 28 | TRef::type reference; 29 | }; 30 | static_assert(sizeof(Ref) == 8, ""); 31 | 32 | template 33 | std::enable_if_t && std::is_trivially_copyable_v, To> 35 | BitCast(const From& src) noexcept { 36 | static_assert(std::is_trivially_constructible_v, "This implementation additionally requires destination type to be trivially created"); 37 | To dst; 38 | std::memcpy(&dst, &src, sizeof(To)); 39 | return dst; 40 | } 41 | 42 | class DescriptorRaii { 43 | public: 44 | static constexpr auto invFile{-1}; 45 | using FileStat = struct stat; 46 | 47 | DescriptorRaii() : hld(-1) {} 48 | DescriptorRaii(i32 fd, bool isManaged = false) : 49 | hld(fd), closeAtDestroy(!isManaged) { 50 | if (fd != invFile) 51 | fstat(hld, &lastState); 52 | } 53 | ~DescriptorRaii() { 54 | if (hld != invFile && closeAtDestroy) 55 | close(hld); 56 | } 57 | void operator=(i32 fdNative) { 58 | hld = fdNative; 59 | if (hld != invFile) 60 | fstat(hld, &lastState); 61 | } 62 | i32 getFd() const { 63 | return hld; 64 | } 65 | 66 | void read(std::span here) { 67 | if (hld == invFile) 68 | throw IoErr("Can't read from this fd (broken), error: {}", strerror(errno)); 69 | 70 | auto attempt{::read(hld, here.data(), here.size())}; 71 | if (attempt != here.size()) 72 | throw IoErr("Read operation failed with fd {} due to an error", hld); 73 | } 74 | void readFrom(std::span here, u64 from) { 75 | lseek64(hld, BitCast(from), SEEK_SET); 76 | read(here); 77 | } 78 | private: 79 | FileStat lastState{}; 80 | i32 hld{invFile}; 81 | bool closeAtDestroy{false}; 82 | }; 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 29 | 30 | 31 | 38 | 42 | 46 | 50 | 51 | 52 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/cpp/cosmic/gs/gif_packed.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | namespace cosmic::gs { 4 | 5 | void GifBridge::uploadPackedData(GifTag& dsTag, u64 packet[2]) { 6 | RegDesc reg{}; 7 | u64 offset; 8 | 9 | if (!dsTag.isEndOfPacket) { 10 | offset = (dsTag.regsNum - dsTag.leftRegsData[0]) << 2; 11 | if (offset > 63) { 12 | 13 | } 14 | reg = static_cast((dsTag.regs >> offset) & 0xf); 15 | } 16 | 17 | switch (reg) { 18 | case RegDesc::Primitive: 19 | gs->gsWrite(0, packet[0]); 20 | break; 21 | case RegDesc::RgBaQ: { 22 | RgBaQReg color{}; 23 | // NOTES: There was a mistake in the type of bitwise operation 24 | // used to extract the values below 25 | color.r = extractPair(packet[0], 0, 0xff); 26 | color.g = extractPair(packet[0], 32, 0xff); 27 | 28 | color.b = extractPair(packet[1], 0, 0xff); 29 | color.a = extractPair(packet[1], 32, 0xff); 30 | // The internal Q register is used here and stays the same 31 | color.gsq = gsQ; 32 | 33 | gs->gsWrite(1, color.rainbow); 34 | } 35 | break; 36 | case RegDesc::StPos: { 37 | u64 neoQ; 38 | // Fixing float types, this can be remedied or disabled later... 39 | neoQ = extractPair(packet[1], 0, 0x7f800000); 40 | if ((neoQ & 0x7f800000) == 0x7f800000) 41 | neoQ = (neoQ & 0x80000000) | 0x7f7fffff; 42 | gs->gsWrite(0x02, packet[0]); 43 | 44 | gsQ = *reinterpret_cast(&neoQ); 45 | } 46 | break; 47 | case RegDesc::UvPos: { 48 | std::array uvsCods{}; 49 | uvsCods[0] = extractPair(packet[0], 0, 0x3fff); 50 | uvsCods[1] = extractPair(packet[0], 32, 0x3fff); 51 | gs->gsWrite(0x03, *reinterpret_cast(uvsCods.data())); 52 | } 53 | break; 54 | case RegDesc::Xyz2: { 55 | CoordinatesXyz c{ 56 | .x = extractPair(packet[0], 0, 0xffff), 57 | .y = extractPair(packet[0], 32, 0xffff), 58 | .z = extractPair(packet[1], 0, 0xffffffff) 59 | }; 60 | auto disableDraw{(packet[1] >> (111 - 64)) & 1}; 61 | auto address{disableDraw ? 0xd : 0x5}; 62 | 63 | gs->gsWrite(static_cast(address), c.xyz); 64 | } 65 | case RegDesc::Nop: 66 | break; 67 | case RegDesc::Fog ... RegDesc::Ad: { 68 | auto addr{static_cast(packet[1] & 0xff)}; 69 | if (addr > 0x7f) { 70 | 71 | } 72 | gs->gsWrite(addr, packet[0]); 73 | } 74 | break; 75 | default: 76 | gs->gsWrite(reg, packet[0]); 77 | break; 78 | } 79 | } 80 | } --------------------------------------------------------------------------------