├── .envrc ├── frontends ├── gamegirl-egui │ ├── src │ │ ├── dynamic.rs │ │ ├── lib.rs │ │ ├── debug │ │ │ └── nds.rs │ │ ├── main.rs │ │ ├── input │ │ │ ├── mod.rs │ │ │ └── hotkeys.rs │ │ └── gui │ │ │ └── input.rs │ ├── Trunk.toml │ ├── assets │ │ ├── favicon.ico │ │ ├── icon-1024.png │ │ ├── icon-256.png │ │ ├── icon_ios_touch_192.png │ │ └── maskable_icon_x512.png │ ├── dist │ │ ├── icon-1024.png │ │ ├── icon-256.png │ │ ├── icon_ios_touch_192.png │ │ ├── maskable_icon_x512.png │ │ ├── favicon-f402c8741ce815ec.ico │ │ └── gamegirl-egui-538acf5501d37dce_bg.wasm │ └── Cargo.toml ├── gamegirl-gtk │ ├── build.rs │ ├── resources │ │ ├── audio-x-generic-symbolic.svg │ │ ├── person-symbolic.svg │ │ ├── heart-filled-symbolic.svg │ │ ├── resources.gresource.xml │ │ ├── dragon-solid-symbolic.svg │ │ ├── gamepad-symbolic.svg │ │ └── cat-symbolic.svg │ ├── Cargo.toml │ └── src │ │ ├── gui │ │ ├── mod.rs │ │ └── input.rs │ │ ├── main.rs │ │ └── config.rs ├── gamegirl-test │ └── Cargo.toml └── corebench-egui │ ├── Cargo.toml │ └── src │ ├── tests │ ├── mod.rs │ ├── gba.rs │ └── gb.rs │ ├── main.rs │ └── gui │ ├── file_dialog.rs │ └── tests.rs ├── dyn-recompile.sh ├── components ├── bitmatch │ ├── .gitignore │ ├── Cargo.toml │ └── README.md └── armchair │ ├── src │ ├── optimizations │ │ └── jit │ │ │ ├── support.rs │ │ │ └── fields.rs │ ├── misc │ │ └── mod.rs │ ├── tracing.rs │ └── thumb │ │ └── mod.rs │ └── Cargo.toml ├── rust-toolchain.toml ├── web └── img │ ├── apotris.png │ ├── emerald.png │ └── oracle.png ├── testing └── tests │ ├── suite.gba │ ├── acid2 │ ├── cgb-acid2.gb │ └── dmg-acid2.gb │ ├── rockwrestler.nds │ ├── blargg │ ├── interrupt_time.gb │ ├── cpu_instrs │ │ ├── 05-op rp.gb │ │ ├── 06-ld r,r.gb │ │ ├── 09-op r,r.gb │ │ ├── 01-special.gb │ │ ├── 03-op sp,hl.gb │ │ ├── 04-op r,imm.gb │ │ ├── 10-bit ops.gb │ │ ├── 11-op a,(hl).gb │ │ ├── 02-interrupts.gb │ │ ├── 08-misc instrs.gb │ │ └── 07-jr,jp,call,ret,rst.gb │ ├── instr_timing │ │ ├── halt_bug.gb │ │ └── instr_timing.gb │ ├── mem_timing │ │ ├── 01-read_timing.gb │ │ ├── 02-write_timing.gb │ │ └── 03-modify_timing.gb │ └── readme.txt │ ├── mooneye │ ├── misc │ │ ├── boot_div-A.gb │ │ ├── boot_hwio-C.gb │ │ ├── boot_regs-A.gb │ │ ├── boot_div-cgb0.gb │ │ ├── boot_regs-cgb.gb │ │ ├── boot_div-cgbABCDE.gb │ │ ├── bits │ │ │ └── unused_hwio-C.gb │ │ └── ppu │ │ │ └── vblank_stat_intr-C.gb │ ├── acceptance │ │ ├── bits │ │ │ ├── reg_f.gb │ │ │ ├── mem_oam.gb │ │ │ └── unused_hwio-GS.gb │ │ ├── call_timing.gb │ │ ├── div_timing.gb │ │ ├── ei_sequence.gb │ │ ├── ei_timing.gb │ │ ├── instr │ │ │ └── daa.gb │ │ ├── intr_timing.gb │ │ ├── jp_timing.gb │ │ ├── pop_timing.gb │ │ ├── push_timing.gb │ │ ├── rapid_di_ei.gb │ │ ├── ret_timing.gb │ │ ├── reti_timing.gb │ │ ├── rst_timing.gb │ │ ├── timer │ │ │ ├── tim00.gb │ │ │ ├── tim01.gb │ │ │ ├── tim10.gb │ │ │ ├── tim11.gb │ │ │ ├── div_write.gb │ │ │ ├── rapid_toggle.gb │ │ │ ├── tima_reload.gb │ │ │ ├── tim00_div_trigger.gb │ │ │ ├── tim01_div_trigger.gb │ │ │ ├── tim10_div_trigger.gb │ │ │ ├── tim11_div_trigger.gb │ │ │ ├── tima_write_reloading.gb │ │ │ └── tma_write_reloading.gb │ │ ├── call_timing2.gb │ │ ├── di_timing-GS.gb │ │ ├── halt_ime0_ei.gb │ │ ├── jp_cc_timing.gb │ │ ├── oam_dma │ │ │ ├── basic.gb │ │ │ ├── reg_read.gb │ │ │ └── sources-GS.gb │ │ ├── oam_dma_start.gb │ │ ├── ret_cc_timing.gb │ │ ├── add_sp_e_timing.gb │ │ ├── boot_regs-dmgABC.gb │ │ ├── call_cc_timing.gb │ │ ├── call_cc_timing2.gb │ │ ├── halt_ime1_timing.gb │ │ ├── if_ie_registers.gb │ │ ├── oam_dma_restart.gb │ │ ├── oam_dma_timing.gb │ │ ├── reti_intr_timing.gb │ │ ├── boot_div-dmgABCmgb.gb │ │ ├── interrupts │ │ │ └── ie_push.gb │ │ ├── ld_hl_sp_e_timing.gb │ │ ├── ppu │ │ │ ├── stat_lyc_onoff.gb │ │ │ ├── intr_2_0_timing.gb │ │ │ ├── lcdon_timing-GS.gb │ │ │ ├── stat_irq_blocking.gb │ │ │ ├── intr_1_2_timing-GS.gb │ │ │ ├── intr_2_mode0_timing.gb │ │ │ ├── intr_2_mode3_timing.gb │ │ │ ├── vblank_stat_intr-GS.gb │ │ │ ├── intr_2_oam_ok_timing.gb │ │ │ ├── lcdon_write_timing-GS.gb │ │ │ ├── hblank_ly_scx_timing-GS.gb │ │ │ └── intr_2_mode0_timing_sprites.gb │ │ ├── boot_div-S.disabled.gb │ │ ├── boot_div2-S.disabled.gb │ │ ├── boot_hwio-S.disabled.gb │ │ ├── boot_hwio-dmgABCmgb.gb │ │ ├── halt_ime1_timing2-GS.gb │ │ ├── boot_div-dmg0.disabled.gb │ │ ├── boot_hwio-dmg0.disabled.gb │ │ ├── boot_regs-dmg0.disabled.gb │ │ ├── boot_regs-mgb.disabled.gb │ │ ├── boot_regs-sgb.disabled.gb │ │ ├── boot_regs-sgb2.disabled.gb │ │ ├── halt_ime0_nointr_timing.gb │ │ └── serial │ │ │ └── boot_sclk_align-dmgABCmgb.disabled.gb │ ├── emulator-only │ │ ├── mbc2 │ │ │ ├── ram.gb │ │ │ ├── rom_1Mb.gb │ │ │ ├── rom_2Mb.gb │ │ │ ├── bits_ramg.gb │ │ │ ├── bits_romb.gb │ │ │ ├── rom_512kb.gb │ │ │ └── bits_unused.gb │ │ ├── mbc1 │ │ │ ├── ram_64kb.gb │ │ │ ├── rom_16Mb.gb │ │ │ ├── rom_1Mb.gb │ │ │ ├── rom_2Mb.gb │ │ │ ├── rom_4Mb.gb │ │ │ ├── rom_8Mb.gb │ │ │ ├── bits_bank1.gb │ │ │ ├── bits_bank2.gb │ │ │ ├── bits_mode.gb │ │ │ ├── bits_ramg.gb │ │ │ ├── ram_256kb.gb │ │ │ ├── rom_512kb.gb │ │ │ └── multicart_rom_8Mb.disabled.gb │ │ └── mbc5 │ │ │ ├── rom_16Mb.gb │ │ │ ├── rom_1Mb.gb │ │ │ ├── rom_2Mb.gb │ │ │ ├── rom_32Mb.gb │ │ │ ├── rom_4Mb.gb │ │ │ ├── rom_64Mb.gb │ │ │ ├── rom_8Mb.gb │ │ │ └── rom_512kb.gb │ ├── manual-only │ │ └── sprite_priority.gb │ └── LICENSE │ ├── blargg_sound │ ├── cgb_sound │ │ ├── 12-wave.gb │ │ ├── 04-sweep.gb │ │ ├── 01-registers.gb │ │ ├── 02-len ctr.gb │ │ ├── 03-trigger.gb │ │ ├── 05-sweep details.gb │ │ ├── 11-regs after power.gb │ │ ├── 06-overflow on trigger.gb │ │ ├── 09-wave read while on.gb │ │ ├── 07-len sweep period sync.gb │ │ ├── 08-len ctr during power.gb │ │ └── 10-wave trigger while on.gb │ ├── dmg_sound │ │ ├── 04-sweep.gb │ │ ├── 01-registers.gb │ │ ├── 02-len ctr.gb │ │ ├── 03-trigger.gb │ │ ├── 05-sweep details.gb │ │ ├── 11-regs after power.gb │ │ ├── 06-overflow on trigger.gb │ │ ├── 09-wave read while on.gb │ │ ├── 12-wave write while on.gb │ │ ├── 07-len sweep period sync.gb │ │ ├── 08-len ctr during power.gb │ │ └── 10-wave trigger while on.gb │ └── mem_timing-2 │ │ ├── 01-read_timing.gb │ │ ├── 02-write_timing.gb │ │ └── 03-modify_timing.gb │ └── Cargo.toml ├── gamegirl ├── src │ └── frontend │ │ ├── mod.rs │ │ ├── filter.rs │ │ └── cpal.rs └── Cargo.toml ├── cores ├── gga │ ├── src │ │ ├── hw │ │ │ ├── bios │ │ │ │ ├── bios.bin │ │ │ │ └── mod.rs │ │ │ ├── serial.rs │ │ │ ├── mod.rs │ │ │ └── input.rs │ │ ├── tests │ │ │ ├── jsmolka │ │ │ │ ├── arm.gba │ │ │ │ ├── bios.gba │ │ │ │ ├── nes.gba │ │ │ │ ├── thumb.gba │ │ │ │ ├── memory.gba │ │ │ │ ├── unsafe.gba │ │ │ │ ├── ppu │ │ │ │ │ ├── hello.gba │ │ │ │ │ ├── shades.gba │ │ │ │ │ └── stripes.gba │ │ │ │ ├── save │ │ │ │ │ ├── none.gba │ │ │ │ │ ├── sram.gba │ │ │ │ │ ├── flash64.gba │ │ │ │ │ └── flash128.gba │ │ │ │ ├── README.md │ │ │ │ ├── unsafe_README.md │ │ │ │ └── LICENSE │ │ │ ├── fuzzarm │ │ │ │ ├── ARM_Any.gba │ │ │ │ ├── FuzzARM.gba │ │ │ │ ├── THUMB_Any.gba │ │ │ │ ├── ARM_DataProcessing.gba │ │ │ │ └── THUMB_DataProcessing.gba │ │ │ └── mod.rs │ │ ├── audio │ │ │ └── psg │ │ │ │ ├── README.md │ │ │ │ ├── envelope.rs │ │ │ │ └── noise_channel.rs │ │ ├── addr.rs │ │ ├── ppu │ │ │ └── render │ │ │ │ └── palette.rs │ │ └── scheduling.rs │ └── Cargo.toml ├── ggc │ ├── src │ │ └── io │ │ │ ├── bootrom │ │ │ └── bootix_dmg.bin │ │ │ ├── apu │ │ │ ├── README.md │ │ │ └── envelope.rs │ │ │ ├── joypad.rs │ │ │ └── scheduling.rs │ └── Cargo.toml ├── nds │ ├── src │ │ ├── hw │ │ │ ├── bios │ │ │ │ ├── drastic_bios_arm7.bin │ │ │ │ ├── drastic_bios_arm9.bin │ │ │ │ ├── drastic_bios_readme.txt │ │ │ │ └── mod.rs │ │ │ ├── mod.rs │ │ │ ├── audio.rs │ │ │ └── input.rs │ │ ├── graphics │ │ │ ├── capture │ │ │ │ ├── mod.rs │ │ │ │ └── registers.rs │ │ │ ├── engine3d │ │ │ │ ├── mod.rs │ │ │ │ └── registers.rs │ │ │ └── ppu │ │ │ │ └── render │ │ │ │ ├── modes.rs │ │ │ │ └── palette.rs │ │ └── cpu │ │ │ ├── mod.rs │ │ │ └── nds7.rs │ └── Cargo.toml ├── psx │ ├── src │ │ ├── gpu │ │ │ ├── frag.glsl │ │ │ └── vert.glsl │ │ ├── addr.rs │ │ ├── apu.rs │ │ ├── iso.rs │ │ ├── scheduling.rs │ │ └── cpu │ │ │ └── cop0.rs │ └── Cargo.toml └── nes │ ├── Cargo.toml │ └── src │ ├── ppu.rs │ ├── apu.rs │ ├── scheduling.rs │ ├── joypad.rs │ ├── cpu │ └── mod.rs │ ├── memory.rs │ └── cartridge.rs ├── .rustfmt.toml ├── mkcore.sh ├── .gitignore ├── .vscode └── settings.json ├── .cargo └── config.toml ├── common ├── src │ ├── common │ │ ├── time.rs │ │ ├── mod.rs │ │ └── video.rs │ ├── components │ │ └── mod.rs │ ├── testing.rs │ ├── serialize.rs │ └── macros.rs └── Cargo.toml ├── shell.nix ├── .github └── workflows │ └── release.yml └── Cargo.toml /.envrc: -------------------------------------------------------------------------------- 1 | use nix 2 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/src/dynamic.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/Trunk.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | -------------------------------------------------------------------------------- /dyn-recompile.sh: -------------------------------------------------------------------------------- 1 | sh mkcore.sh dyn-$RANDOM$RANDOM 2 | -------------------------------------------------------------------------------- /components/bitmatch/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /web/img/apotris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/web/img/apotris.png -------------------------------------------------------------------------------- /web/img/emerald.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/web/img/emerald.png -------------------------------------------------------------------------------- /web/img/oracle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/web/img/oracle.png -------------------------------------------------------------------------------- /testing/tests/suite.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/suite.gba -------------------------------------------------------------------------------- /gamegirl/src/frontend/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod cpal; 2 | pub mod filter; 3 | pub mod input; 4 | pub mod rewinder; 5 | -------------------------------------------------------------------------------- /cores/gga/src/hw/bios/bios.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/hw/bios/bios.bin -------------------------------------------------------------------------------- /testing/tests/acid2/cgb-acid2.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/acid2/cgb-acid2.gb -------------------------------------------------------------------------------- /testing/tests/acid2/dmg-acid2.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/acid2/dmg-acid2.gb -------------------------------------------------------------------------------- /testing/tests/rockwrestler.nds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/rockwrestler.nds -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/arm.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/arm.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/bios.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/bios.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/nes.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/nes.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/thumb.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/thumb.gba -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | imports_granularity="Crate" 2 | normalize_comments=true 3 | wrap_comments=true 4 | group_imports="StdExternalCrate" 5 | -------------------------------------------------------------------------------- /cores/gga/src/tests/fuzzarm/ARM_Any.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/fuzzarm/ARM_Any.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/fuzzarm/FuzzARM.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/fuzzarm/FuzzARM.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/memory.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/memory.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/unsafe.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/unsafe.gba -------------------------------------------------------------------------------- /cores/ggc/src/io/bootrom/bootix_dmg.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/ggc/src/io/bootrom/bootix_dmg.bin -------------------------------------------------------------------------------- /testing/tests/blargg/interrupt_time.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/interrupt_time.gb -------------------------------------------------------------------------------- /cores/gga/src/tests/fuzzarm/THUMB_Any.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/fuzzarm/THUMB_Any.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/ppu/hello.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/ppu/hello.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/ppu/shades.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/ppu/shades.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/save/none.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/save/none.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/save/sram.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/save/sram.gba -------------------------------------------------------------------------------- /frontends/gamegirl-egui/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/assets/favicon.ico -------------------------------------------------------------------------------- /frontends/gamegirl-egui/dist/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/dist/icon-1024.png -------------------------------------------------------------------------------- /frontends/gamegirl-egui/dist/icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/dist/icon-256.png -------------------------------------------------------------------------------- /testing/tests/mooneye/misc/boot_div-A.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/misc/boot_div-A.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/misc/boot_hwio-C.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/misc/boot_hwio-C.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/misc/boot_regs-A.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/misc/boot_regs-A.gb -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/ppu/stripes.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/ppu/stripes.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/save/flash64.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/save/flash64.gba -------------------------------------------------------------------------------- /cores/nds/src/hw/bios/drastic_bios_arm7.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/nds/src/hw/bios/drastic_bios_arm7.bin -------------------------------------------------------------------------------- /cores/nds/src/hw/bios/drastic_bios_arm9.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/nds/src/hw/bios/drastic_bios_arm9.bin -------------------------------------------------------------------------------- /frontends/gamegirl-egui/assets/icon-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/assets/icon-1024.png -------------------------------------------------------------------------------- /frontends/gamegirl-egui/assets/icon-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/assets/icon-256.png -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/05-op rp.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/05-op rp.gb -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/06-ld r,r.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/06-ld r,r.gb -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/09-op r,r.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/09-op r,r.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/misc/boot_div-cgb0.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/misc/boot_div-cgb0.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/misc/boot_regs-cgb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/misc/boot_regs-cgb.gb -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/save/flash128.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/jsmolka/save/flash128.gba -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/01-special.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/01-special.gb -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/03-op sp,hl.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/03-op sp,hl.gb -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/04-op r,imm.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/04-op r,imm.gb -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/10-bit ops.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/10-bit ops.gb -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/11-op a,(hl).gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/11-op a,(hl).gb -------------------------------------------------------------------------------- /testing/tests/blargg/instr_timing/halt_bug.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/instr_timing/halt_bug.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/12-wave.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/12-wave.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/bits/reg_f.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/bits/reg_f.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/call_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/call_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/div_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/div_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ei_sequence.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ei_sequence.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ei_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ei_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/instr/daa.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/instr/daa.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/intr_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/intr_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/jp_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/jp_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/pop_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/pop_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/push_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/push_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/rapid_di_ei.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/rapid_di_ei.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ret_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ret_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/reti_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/reti_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/rst_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/rst_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tim00.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tim00.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tim01.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tim01.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tim10.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tim10.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tim11.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tim11.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc2/ram.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc2/ram.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/misc/boot_div-cgbABCDE.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/misc/boot_div-cgbABCDE.gb -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/02-interrupts.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/02-interrupts.gb -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/08-misc instrs.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/08-misc instrs.gb -------------------------------------------------------------------------------- /testing/tests/blargg/instr_timing/instr_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/instr_timing/instr_timing.gb -------------------------------------------------------------------------------- /testing/tests/blargg/mem_timing/01-read_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/mem_timing/01-read_timing.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/04-sweep.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/04-sweep.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/04-sweep.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/04-sweep.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/bits/mem_oam.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/bits/mem_oam.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/call_timing2.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/call_timing2.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/di_timing-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/di_timing-GS.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/halt_ime0_ei.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/halt_ime0_ei.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/jp_cc_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/jp_cc_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/oam_dma/basic.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/oam_dma/basic.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/oam_dma_start.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/oam_dma_start.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ret_cc_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ret_cc_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/misc/bits/unused_hwio-C.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/misc/bits/unused_hwio-C.gb -------------------------------------------------------------------------------- /cores/gga/src/tests/fuzzarm/ARM_DataProcessing.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/fuzzarm/ARM_DataProcessing.gba -------------------------------------------------------------------------------- /cores/gga/src/tests/fuzzarm/THUMB_DataProcessing.gba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/cores/gga/src/tests/fuzzarm/THUMB_DataProcessing.gba -------------------------------------------------------------------------------- /frontends/gamegirl-egui/dist/icon_ios_touch_192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/dist/icon_ios_touch_192.png -------------------------------------------------------------------------------- /frontends/gamegirl-egui/dist/maskable_icon_x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/dist/maskable_icon_x512.png -------------------------------------------------------------------------------- /mkcore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cargo build -p gamegirl --release --features dynamic,gga,nds 4 | cp $CARGO_TARGET_DIR/release/libgamegirl.so dyn-cores/$1 5 | -------------------------------------------------------------------------------- /testing/tests/blargg/mem_timing/02-write_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/mem_timing/02-write_timing.gb -------------------------------------------------------------------------------- /testing/tests/blargg/mem_timing/03-modify_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/mem_timing/03-modify_timing.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/01-registers.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/01-registers.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/02-len ctr.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/02-len ctr.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/03-trigger.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/03-trigger.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/01-registers.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/01-registers.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/02-len ctr.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/02-len ctr.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/03-trigger.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/03-trigger.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/add_sp_e_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/add_sp_e_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_regs-dmgABC.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_regs-dmgABC.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/call_cc_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/call_cc_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/call_cc_timing2.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/call_cc_timing2.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/halt_ime1_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/halt_ime1_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/if_ie_registers.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/if_ie_registers.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/oam_dma/reg_read.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/oam_dma/reg_read.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/oam_dma_restart.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/oam_dma_restart.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/oam_dma_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/oam_dma_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/reti_intr_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/reti_intr_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/div_write.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/div_write.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/ram_64kb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/ram_64kb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/rom_16Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/rom_16Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/rom_1Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/rom_1Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/rom_2Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/rom_2Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/rom_4Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/rom_4Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/rom_8Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/rom_8Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc2/rom_1Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc2/rom_1Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc2/rom_2Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc2/rom_2Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc5/rom_16Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc5/rom_16Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc5/rom_1Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc5/rom_1Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc5/rom_2Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc5/rom_2Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc5/rom_32Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc5/rom_32Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc5/rom_4Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc5/rom_4Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc5/rom_64Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc5/rom_64Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc5/rom_8Mb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc5/rom_8Mb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/manual-only/sprite_priority.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/manual-only/sprite_priority.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/misc/ppu/vblank_stat_intr-C.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/misc/ppu/vblank_stat_intr-C.gb -------------------------------------------------------------------------------- /frontends/gamegirl-egui/assets/icon_ios_touch_192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/assets/icon_ios_touch_192.png -------------------------------------------------------------------------------- /frontends/gamegirl-egui/assets/maskable_icon_x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/assets/maskable_icon_x512.png -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_div-dmgABCmgb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_div-dmgABCmgb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/interrupts/ie_push.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/interrupts/ie_push.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ld_hl_sp_e_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ld_hl_sp_e_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/oam_dma/sources-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/oam_dma/sources-GS.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/stat_lyc_onoff.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/stat_lyc_onoff.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/rapid_toggle.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/rapid_toggle.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tima_reload.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tima_reload.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/bits_bank1.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/bits_bank1.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/bits_bank2.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/bits_bank2.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/bits_mode.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/bits_mode.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/bits_ramg.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/bits_ramg.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/ram_256kb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/ram_256kb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/rom_512kb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/rom_512kb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc2/bits_ramg.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc2/bits_ramg.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc2/bits_romb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc2/bits_romb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc2/rom_512kb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc2/rom_512kb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc5/rom_512kb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc5/rom_512kb.gb -------------------------------------------------------------------------------- /cores/psx/src/gpu/frag.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | 3 | in vec3 color; 4 | out vec4 frag_color; 5 | 6 | void main() { 7 | frag_color = vec4(color, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/dist/favicon-f402c8741ce815ec.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/dist/favicon-f402c8741ce815ec.ico -------------------------------------------------------------------------------- /testing/tests/blargg/cpu_instrs/07-jr,jp,call,ret,rst.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg/cpu_instrs/07-jr,jp,call,ret,rst.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/05-sweep details.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/05-sweep details.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/05-sweep details.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/05-sweep details.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/mem_timing-2/01-read_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/mem_timing-2/01-read_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/bits/unused_hwio-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/bits/unused_hwio-GS.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_div-S.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_div-S.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_div2-S.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_div2-S.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_hwio-S.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_hwio-S.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_hwio-dmgABCmgb.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_hwio-dmgABCmgb.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/halt_ime1_timing2-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/halt_ime1_timing2-GS.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/intr_2_0_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/intr_2_0_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/lcdon_timing-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/lcdon_timing-GS.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/stat_irq_blocking.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/stat_irq_blocking.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc2/bits_unused.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc2/bits_unused.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/11-regs after power.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/11-regs after power.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/11-regs after power.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/11-regs after power.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/mem_timing-2/02-write_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/mem_timing-2/02-write_timing.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/mem_timing-2/03-modify_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/mem_timing-2/03-modify_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_div-dmg0.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_div-dmg0.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_hwio-dmg0.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_hwio-dmg0.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_regs-dmg0.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_regs-dmg0.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_regs-mgb.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_regs-mgb.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_regs-sgb.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_regs-sgb.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/boot_regs-sgb2.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/boot_regs-sgb2.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/halt_ime0_nointr_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/halt_ime0_nointr_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/intr_1_2_timing-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/intr_1_2_timing-GS.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/intr_2_mode0_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/intr_2_mode0_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/intr_2_mode3_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/intr_2_mode3_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/vblank_stat_intr-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/vblank_stat_intr-GS.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tim00_div_trigger.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tim00_div_trigger.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tim01_div_trigger.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tim01_div_trigger.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tim10_div_trigger.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tim10_div_trigger.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tim11_div_trigger.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tim11_div_trigger.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/06-overflow on trigger.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/06-overflow on trigger.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/09-wave read while on.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/09-wave read while on.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/06-overflow on trigger.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/06-overflow on trigger.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/09-wave read while on.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/09-wave read while on.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/12-wave write while on.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/12-wave write while on.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/intr_2_oam_ok_timing.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/intr_2_oam_ok_timing.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/lcdon_write_timing-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/lcdon_write_timing-GS.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tima_write_reloading.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tima_write_reloading.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/timer/tma_write_reloading.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/timer/tma_write_reloading.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/07-len sweep period sync.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/07-len sweep period sync.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/08-len ctr during power.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/08-len ctr during power.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/cgb_sound/10-wave trigger while on.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/cgb_sound/10-wave trigger while on.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/07-len sweep period sync.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/07-len sweep period sync.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/08-len ctr during power.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/08-len ctr during power.gb -------------------------------------------------------------------------------- /testing/tests/blargg_sound/dmg_sound/10-wave trigger while on.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/blargg_sound/dmg_sound/10-wave trigger while on.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/hblank_ly_scx_timing-GS.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/hblank_ly_scx_timing-GS.gb -------------------------------------------------------------------------------- /frontends/gamegirl-egui/dist/gamegirl-egui-538acf5501d37dce_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/frontends/gamegirl-egui/dist/gamegirl-egui-538acf5501d37dce_bg.wasm -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/ppu/intr_2_mode0_timing_sprites.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/ppu/intr_2_mode0_timing_sprites.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/emulator-only/mbc1/multicart_rom_8Mb.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/emulator-only/mbc1/multicart_rom_8Mb.disabled.gb -------------------------------------------------------------------------------- /testing/tests/mooneye/acceptance/serial/boot_sclk_align-dmgABCmgb.disabled.gb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelilia/gamegirl/HEAD/testing/tests/mooneye/acceptance/serial/boot_sclk_align-dmgABCmgb.disabled.gb -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | glib_build_tools::compile_resources( 3 | &["resources"], 4 | "resources/resources.gresource.xml", 5 | "gamegirl.gresource", 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /cores/gga/src/hw/bios/mod.rs: -------------------------------------------------------------------------------- 1 | // Cult-of-GBA BIOS which can be found here: 2 | // https://github.com/Cult-of-GBA/BIOS 3 | // Thank you to it's developers! 4 | pub const BIOS: &[u8] = include_bytes!("bios.bin"); // Commit cc7162c 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | /gamegirl-egui/dist 4 | /testing/tests/c-sp 5 | *.sav 6 | bench.gb 7 | .direnv 8 | dyn-cores 9 | 10 | bios7.bin 11 | bios9.bin 12 | 13 | perf.data 14 | perf.data.old 15 | flamegraph.svg 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nixEnvSelector.nixFile": "${workspaceRoot}/shell.nix", 3 | "editor.formatOnSave": true, 4 | "rust-analyzer.showUnlinkedFileNotification": false, 5 | "rust-analyzer.cargo.targetDir": "/ethereal/cache/ra-target" 6 | } 7 | -------------------------------------------------------------------------------- /cores/gga/src/hw/serial.rs: -------------------------------------------------------------------------------- 1 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 2 | pub struct Serial { 3 | pub rcnt: u16, 4 | } 5 | 6 | impl Default for Serial { 7 | fn default() -> Self { 8 | Self { rcnt: 0x8000 } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.wasm32-unknown-unknown] 2 | rustflags = [ 3 | "-C", 4 | "link-args=-z stack-size=5500000", 5 | "-Ctarget-feature=+soft-float", 6 | ] 7 | 8 | [target.x86_64-unknown-linux-gnu] 9 | rustflags = ["-C", "linker=clang", "-C", "link-arg=-fuse-ld=lld"] 10 | -------------------------------------------------------------------------------- /testing/tests/blargg/readme.txt: -------------------------------------------------------------------------------- 1 | Blargg's Gameboy hardware test ROMs. Originally hosted at http://blargg.parodius.com/gb-tests/ 2 | before parodious.com went down. 3 | New official location: http://blargg.8bitalley.com/parodius/gb-tests/ 4 | 5 | This copy in the gamegirl repo was taken from here: https://github.com/retrio/gb-test-roms 6 | Thanks, retrio! -------------------------------------------------------------------------------- /frontends/gamegirl-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gamegirl-test" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | gamegirl = { path = "../../gamegirl", features = ["ggc", "gga", "std", "dynamic"] } 8 | png = "0.17" 9 | clap = { version = "4.5", features = ["derive"] } 10 | zip = "2.5" 11 | rayon = "1.10" 12 | indicatif = "0.17" 13 | -------------------------------------------------------------------------------- /testing/tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tests" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | gamegirl = { path = "../gamegirl", features = ["ggc", "gga"] } 10 | ansi_term = "0.12.1" 11 | png = "0.17.13" 12 | seahorse = "2.2.0" 13 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/resources/audio-x-generic-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /cores/gga/src/audio/psg/README.md: -------------------------------------------------------------------------------- 1 | # PSG APU 2 | This is the PSG APU as found on the GG. 3 | 4 | This implementation is abridged from mizu: https://github.com/Amjad50/mizu 5 | It is under the MIT license. See the linked repository for more info. 6 | Thank you to Amjad50 for mizu! 7 | 8 | (The copyright header on the source files should be ignored - it is incorrect 9 | and merely a result of automated header insertion in all source files.) -------------------------------------------------------------------------------- /cores/ggc/src/io/apu/README.md: -------------------------------------------------------------------------------- 1 | # PSG APU 2 | This is the PSG APU as found on the GG. 3 | 4 | This implementation is abridged from mizu: https://github.com/Amjad50/mizu 5 | It is under the MIT license. See the linked repository for more info. 6 | Thank you to Amjad50 for mizu! 7 | 8 | (The copyright header on the source files should be ignored - it is incorrect 9 | and merely a result of automated header insertion in all source files.) -------------------------------------------------------------------------------- /common/src/common/time.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "std")] 2 | pub fn since_unix() -> u64 { 3 | extern crate std; 4 | 5 | std::time::SystemTime::now() 6 | .duration_since(std::time::UNIX_EPOCH) 7 | .unwrap() 8 | .as_secs() 9 | } 10 | 11 | #[cfg(not(feature = "std"))] 12 | pub fn since_unix() -> u64 { 13 | extern "C" { 14 | fn get_unix_time() -> u64; 15 | } 16 | unsafe { get_unix_time() } 17 | } 18 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/resources/person-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /cores/psx/src/gpu/vert.glsl: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | in ivec2 vertex_position; 3 | in uvec3 vertex_color; 4 | 5 | out vec3 color; 6 | 7 | void main() { 8 | float x = (float(vertex_position.x) / 512) - 1.0; 9 | float y = (float(vertex_position.y) / 256) - 1.0; 10 | 11 | gl_Position.xyzw = vec4(x, y, 0.0, 1.0); 12 | color = vec3(float(vertex_color.r) / 255, 13 | float(vertex_color.g) / 255, 14 | float(vertex_color.b) / 255); 15 | } -------------------------------------------------------------------------------- /cores/psx/src/addr.rs: -------------------------------------------------------------------------------- 1 | pub const MMIOBASE: u32 = 0x1F80_1000; 2 | 3 | // DMA 4 | pub const DMABASE: u32 = 0x080; 5 | pub const DMAADDR: u32 = 0x0; 6 | pub const DMABLOCKCTRL: u32 = 0x4; 7 | pub const DMACHCTRL: u32 = 0x08; 8 | pub const DMACTRL: u32 = 0x0F0; 9 | pub const DMAINT: u32 = 0x0F4; 10 | 11 | pub const PORT_GPU: u32 = 0x2; 12 | pub const PORT_OTC: u32 = 0x6; 13 | 14 | // GPU 15 | pub const GPUREAD: u32 = 0x810; 16 | pub const GPUSTAT: u32 = 0x814; 17 | pub const GP0: u32 = 0x810; 18 | pub const GP1: u32 = 0x814; 19 | -------------------------------------------------------------------------------- /cores/nes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nes" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | common = { path = "../../common" } 10 | 11 | log.workspace = true 12 | serde = { workspace = true, optional = true } 13 | serde_arrays = { workspace = true, optional = true } 14 | modular-bitfield.workspace = true 15 | 16 | 17 | [features] 18 | serde = ["dep:serde", "dep:serde_arrays", "common/serde"] 19 | -------------------------------------------------------------------------------- /cores/ggc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ggc" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | common = { path = "../../common" } 10 | 11 | log.workspace = true 12 | serde = { workspace = true, optional = true } 13 | serde_arrays = { workspace = true, optional = true } 14 | bitflags.workspace = true 15 | 16 | 17 | [features] 18 | serde = ["dep:serde", "dep:serde_arrays", "common/serde", "bitflags/serde"] 19 | -------------------------------------------------------------------------------- /cores/gga/src/hw/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | pub mod bios; 10 | pub mod cartridge; 11 | pub mod dma; 12 | pub mod input; 13 | pub mod serial; 14 | pub mod timer; 15 | -------------------------------------------------------------------------------- /frontends/corebench-egui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "corebench-egui" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | gamegirl = { path = "../../gamegirl", features = [ 8 | "dynamic", 9 | "ggc", 10 | "gga", 11 | "std", 12 | ] } 13 | eframe = { version = "0.31.1", default-features = false, features = [ 14 | "default_fonts", 15 | "glow", 16 | "persistence", 17 | "wayland", 18 | ] } 19 | egui_plot = "0.31" 20 | libloading = "0.8" 21 | env_logger = "0.11.7" 22 | rfd = "0.15.3" 23 | futures-executor = "0.3.31" 24 | walkdir = "2" 25 | -------------------------------------------------------------------------------- /cores/nds/src/hw/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | pub mod audio; 10 | pub mod bios; 11 | pub mod cartridge; 12 | pub mod dma; 13 | pub mod input; 14 | pub mod ipc; 15 | pub mod spi; 16 | pub mod timer; 17 | -------------------------------------------------------------------------------- /cores/psx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "psx" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | common = { path = "../../common" } 10 | 11 | arrayvec.workspace = true 12 | log.workspace = true 13 | serde = { workspace = true, optional = true } 14 | serde_arrays = { workspace = true, optional = true } 15 | modular-bitfield.workspace = true 16 | bitmatch.workspace = true 17 | glow.workspace = true 18 | 19 | 20 | [features] 21 | serde = ["dep:serde", "dep:serde_arrays", "common/serde"] 22 | -------------------------------------------------------------------------------- /cores/psx/src/apu.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | #[derive(Default)] 10 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 11 | pub struct Apu { 12 | pub(super) buffer: Vec, 13 | } 14 | -------------------------------------------------------------------------------- /components/armchair/src/optimizations/jit/support.rs: -------------------------------------------------------------------------------- 1 | use crate::{interface::Bus, Cpu}; 2 | 3 | impl Cpu { 4 | pub fn tick_bus(&mut self, by: u16) { 5 | self.bus.tick(by as u64); 6 | } 7 | 8 | pub extern "C" fn set_nz_(&mut self, value: u32) { 9 | self.set_nz(true, value); 10 | } 11 | 12 | pub extern "C" fn set_nzc_(&mut self, value: u32, carry: bool) { 13 | self.set_nzc(true, value, carry); 14 | } 15 | 16 | pub extern "C" fn set_nzcv_(&mut self, value: u32, carry: bool, overflow: bool) { 17 | self.set_nzcv(true, value, carry, overflow); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cores/nes/src/ppu.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::Colour; 10 | 11 | #[derive(Default)] 12 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 13 | pub struct Ppu { 14 | pub last_frame: Option>, 15 | } 16 | -------------------------------------------------------------------------------- /common/src/components/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | //! This module contains some components shared by multiple cores. 10 | 11 | #[macro_use] 12 | pub mod io; 13 | pub mod memory_mapper; 14 | pub mod scheduler; 15 | pub mod storage; 16 | pub mod thin_pager; 17 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/resources/heart-filled-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/README.md: -------------------------------------------------------------------------------- 1 | These tests are by jsmolka and available at https://github.com/jsmolka/gba-tests. Thank you jsmolka! 2 | The original README is below; the original license is in this directory. 3 | 4 | # GBA Tests 5 | A collection of Game Boy Advance tests. 6 | 7 | ## Usage 8 | Each ROM contains multiple tests. Either all of them pass or the number of the first failed one is displayed on the screen (background mode 4 is required). You can reference the source code for the expected result. Feel free to open an issue if something is unclear. 9 | 10 | ![example](example.png) 11 | 12 | ## Build 13 | ROMs can be assembled with [FASMARM](https://arm.flatassembler.net/). 14 | -------------------------------------------------------------------------------- /cores/nds/src/graphics/capture/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use registers::DispCapCnt; 10 | 11 | mod registers; 12 | 13 | #[derive(Default)] 14 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 15 | pub struct CaptureUnit { 16 | pub cnt: DispCapCnt, 17 | } 18 | -------------------------------------------------------------------------------- /cores/nds/src/graphics/engine3d/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use registers::Disp3dCnt; 10 | 11 | mod registers; 12 | 13 | #[derive(Default)] 14 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 15 | pub struct Engine3D { 16 | pub cnt: Disp3dCnt, 17 | } 18 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/resources/resources.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | main.ui 5 | settings.ui 6 | 7 | 8 | 9 | cat-symbolic.svg 10 | audio-x-generic-symbolic.svg 11 | gamepad-symbolic.svg 12 | dragon-solid-symbolic.svg 13 | person-symbolic.svg 14 | heart-filled-symbolic.svg 15 | 16 | 17 | -------------------------------------------------------------------------------- /cores/psx/src/iso.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | #[derive(Default)] 10 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 11 | pub struct Iso { 12 | pub raw: Vec, 13 | } 14 | 15 | impl Iso { 16 | pub fn title(&self) -> String { 17 | "IDK DUDE".into() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cores/nds/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nds" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | common = { path = "../../common" } 10 | armchair = { path = "../../components/armchair" } 11 | 12 | log.workspace = true 13 | serde = { workspace = true, optional = true } 14 | serde_arrays = { workspace = true, optional = true } 15 | arrayvec.workspace = true 16 | modular-bitfield.workspace = true 17 | 18 | [features] 19 | default = [] 20 | serde = [ 21 | "dep:serde", 22 | "dep:serde_arrays", 23 | "arrayvec/serde", 24 | "common/serde", 25 | "armchair/serde", 26 | ] 27 | std = [] 28 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | #![feature(ptr_metadata)] 10 | 11 | mod app; 12 | mod debug; 13 | mod filter; 14 | mod gui; 15 | mod input; 16 | 17 | pub use app::App; 18 | use eframe::egui::Color32; 19 | 20 | /// Colour type used by the PPU for display output. 21 | pub type Colour = Color32; 22 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import { }; 3 | packages = with pkgs; [ 4 | # Build related 5 | pkg-config 6 | udev 7 | llvmPackages.bintools 8 | clang 9 | alsa-lib 10 | gtk3 11 | gtk4 12 | libadwaita 13 | librsvg 14 | wayland 15 | libxkbcommon 16 | libGL 17 | trunk 18 | 19 | # Useful tools 20 | cargo-llvm-lines 21 | cargo-bloat 22 | cargo-edit 23 | cargo-flamegraph 24 | cargo-watch 25 | gdb 26 | 27 | # Other emulators for ease of debugging 28 | mgba 29 | ]; 30 | in 31 | pkgs.mkShell { 32 | buildInputs = packages; 33 | LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath packages}"; 34 | RUST_BACKTRACE = 1; 35 | RUST_LOG = "info"; 36 | } 37 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gamegirl-gtk" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | gamegirl = { path = "../../gamegirl", features = [ 8 | "ggc", 9 | "gga", 10 | "nds", 11 | "frontend", 12 | ] } 13 | 14 | gtk = { version = "0.9.6", package = "gtk4", features = ["v4_16"] } 15 | adw = { version = "0.7.2", package = "libadwaita", features = ["v1_5"] } 16 | gilrs = { version = "0.11.0", features = ["serde-serialize"] } 17 | dirs = "6" 18 | 19 | serde.workspace = true 20 | bincode = { version = "2.0.1", features = ["serde"] } 21 | 22 | [features] 23 | default = ["savestates"] 24 | savestates = ["gamegirl/serde", "gamegirl/zstd"] 25 | 26 | [build-dependencies] 27 | glib-build-tools = "0.20.0" 28 | -------------------------------------------------------------------------------- /components/bitmatch/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bitmatch" 3 | version = "0.1.1" 4 | authors = ["Cassie Jones "] 5 | edition = "2018" 6 | license = "MPL-2.0" 7 | description = "A macro to allow matching, binding, and packing the individual bits of integers." 8 | repository = "https://github.com/porglezomp/bitmatch" 9 | documentation = "https://docs.rs/crate/bitmatch" 10 | readme = "README.md" 11 | keywords = ["bitpacking", "binary", "decoder", "matching", "macro"] 12 | categories = ["parsing", "no-std"] 13 | 14 | [lib] 15 | proc-macro = true 16 | 17 | [dependencies] 18 | syn = {version = "1.0", features = ["full", "visit", "visit-mut"] } 19 | quote = "1.0" 20 | boolean_expression = "0.3.10" 21 | proc-macro2 = "1.0" 22 | 23 | [badges] 24 | 25 | maintenance.status = "passively-maintained" 26 | -------------------------------------------------------------------------------- /cores/gga/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gga" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | common = { path = "../../common" } 10 | armchair = { path = "../../components/armchair" } 11 | 12 | log.workspace = true 13 | serde = { workspace = true, optional = true } 14 | serde_arrays = { workspace = true, optional = true } 15 | arrayvec.workspace = true 16 | modular-bitfield.workspace = true 17 | bitflags.workspace = true 18 | 19 | elf_rs = "0.3.1" 20 | 21 | [features] 22 | default = ["std"] 23 | serde = [ 24 | "dep:serde", 25 | "dep:serde_arrays", 26 | "arrayvec/serde", 27 | "common/serde", 28 | "armchair/serde", 29 | "bitflags/serde", 30 | ] 31 | std = ["common/std"] 32 | -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/unsafe_README.md: -------------------------------------------------------------------------------- 1 | # Warning 2 | I don't feel comfortable putting these tests into the test suite. The failures on hardware are probably caused by the flash card. 3 | 4 | ## SRAM Mirror 5 | This test checks the following GBATEK statement: 6 | 7 | > The 64K SRAM area is mirrored across the whole 32MB area at E000000h-FFFFFFFh, also, inside of the 64K SRAM field, 32K SRAM chips are repeated twice. 8 | 9 | It passes on No$GBA and mGBA but not on real hardware. 10 | 11 | ## Unused ROM Reads 12 | This test checks the following GBATEK statement: 13 | 14 | > Because Gamepak uses the same signal-lines for both 16bit data and for lower 16bit halfword address, the entire gamepak ROM area is effectively filled by incrementing 16bit values (Address/2 AND FFFFh). 15 | 16 | It passes on mGBA but not on No$GBA or real hardware. 17 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build Release 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | release: 9 | name: release ${{ matrix.target }} 10 | runs-on: ubuntu-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | include: 15 | - target: x86_64-unknown-linux-musl 16 | archive: tar.gz tar.xz tar.zst 17 | steps: 18 | - uses: actions/checkout@master 19 | - name: Compile and release 20 | uses: rust-build/rust-build.action@v1.4.5 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | with: 24 | RUSTTARGET: ${{ matrix.target }} 25 | ARCHIVE_TYPES: ${{ matrix.archive }} 26 | EXTRA_FILES: "README.md LICENSE-GPL LICENSE-MPL" 27 | MINIFY: true 28 | TOOLCHAIN_VERSION: nightly 29 | SRC_DIR: gamegirl-egui 30 | -------------------------------------------------------------------------------- /cores/nds/src/cpu/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | //! CPU implementations. 10 | //! Note that when it comes to timing, the ARM9 runs on the scheduler until 11 | //! the ARM7 is behind, which then runs outside the scheduler until the ARM9 is 12 | //! behind. This is repeated in a loop. 13 | //! Effectively, the ARM9 is the one handling the scheduling, with the ARM7 14 | //! being dragged along. 15 | 16 | pub mod cp15; 17 | pub mod math; 18 | mod nds7; 19 | mod nds9; 20 | 21 | pub const NDS9_CLOCK: u32 = 67_027_964; 22 | -------------------------------------------------------------------------------- /common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | log.workspace = true 10 | rubato = { version = "0.16", optional = true } 11 | 12 | serde = { workspace = true, optional = true } 13 | serde_arrays = { workspace = true, optional = true } 14 | 15 | bincode = { version = "2.0.1", optional = true } 16 | zstd = { version = "0.13.3", default-features = false, optional = true } 17 | 18 | [target.'cfg(target_arch = "wasm32")'.dependencies] 19 | web-sys = { version = "0.3.77", features = ["Storage", "Window"] } 20 | base64 = "0.22.1" 21 | 22 | [features] 23 | serde = [ 24 | "dep:serde", 25 | "dep:serde_arrays", 26 | "dep:bincode", 27 | "bincode/serde", 28 | "serde_config", 29 | ] 30 | serde_config = ["dep:serde"] 31 | std = ["dep:rubato"] 32 | -------------------------------------------------------------------------------- /components/armchair/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "armchair" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | common = { path = "../../common" } 10 | 11 | log.workspace = true 12 | serde = { workspace = true, optional = true } 13 | bitmatch.workspace = true 14 | hashbrown = "0.15" 15 | num-traits = { version = "0.2", default-features = false } 16 | num-derive = { version = "0.4", default-features = false } 17 | 18 | cranelift = { version = "0.118", optional = true } 19 | cranelift-jit = { version = "0.118", optional = true } 20 | cranelift-module = { version = "0.118", optional = true } 21 | cranelift-native = { version = "0.118", optional = true } 22 | 23 | [features] 24 | default = ["jit"] 25 | serde = ["dep:serde"] 26 | jit = ["cranelift", "cranelift-jit", "cranelift-module", "cranelift-native"] 27 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "common", 4 | "components/*", 5 | "cores/ggc", 6 | "cores/gga", 7 | "cores/nds", 8 | "gamegirl", 9 | "frontends/*", 10 | ] 11 | resolver = "2" 12 | 13 | [profile.release] 14 | panic = "abort" 15 | opt-level = 3 16 | 17 | [profile.release-unwind] 18 | inherits = "release" 19 | panic = "unwind" 20 | 21 | [profile.release-debug] 22 | inherits = "release" 23 | debug = true 24 | 25 | [profile.release-fast] 26 | inherits = "release" 27 | lto = false 28 | opt-level = 2 29 | incremental = true 30 | 31 | [profile.dev] 32 | opt-level = 1 33 | 34 | [workspace.dependencies] 35 | log = { version = "0.4.27", default-features = false } 36 | serde = { version = "1.0.219", features = ["derive", "rc"] } 37 | serde_arrays = "0.2.0" 38 | arrayvec = { version = "0.7.6", default-features = false } 39 | bitflags = "2.9.0" 40 | modular-bitfield = "0.11" 41 | bitmatch = { path = "./components/bitmatch" } 42 | glow = "0.16" 43 | -------------------------------------------------------------------------------- /frontends/corebench-egui/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | mod gb; 10 | mod gba; 11 | 12 | use gb::*; 13 | use gba::*; 14 | 15 | use crate::testsuite::TestSuite; 16 | 17 | pub const SUITES: &[(&str, fn() -> TestSuite)] = &[ 18 | ("Blargg", blargg), 19 | ("Blargg-Sound", blargg_sound), 20 | ("Mooneye Acceptance", || mooneye("acceptance")), 21 | ("Mooneye EmuOnly", || mooneye("emulator-only")), 22 | ("Mooneye Misc", || mooneye("misc")), 23 | ("GBMicrotest", || gbmicrotest()), 24 | ("{cgb,dmg}-acid2", || acid2()), 25 | ("jsmolka", || jsmolka()), 26 | ("fuzzarm", || fuzzarm()), 27 | ]; 28 | -------------------------------------------------------------------------------- /cores/gga/src/tests/jsmolka/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Julian Smolka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /testing/tests/mooneye/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2022 Joonas Javanainen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/resources/dragon-solid-symbolic.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/armchair/src/misc/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::{fmt::Debug, format, string::String}; 2 | 3 | use crate::Address; 4 | 5 | mod alu; 6 | mod operations; 7 | 8 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 9 | pub enum InstructionKind { 10 | Arm = 0, 11 | Thumb = 1, 12 | } 13 | 14 | impl InstructionKind { 15 | /// Returns the width of an instruction in bytes. 16 | pub fn width(self) -> u32 { 17 | 4 - (((self == Self::Thumb) as u32) << 1) 18 | } 19 | 20 | /// Returns the width of an instruction expressed as an address. 21 | pub fn addr_width(self) -> Address { 22 | Address(self.width()) 23 | } 24 | } 25 | 26 | pub fn condition_mnemonic(cond: u16) -> &'static str { 27 | match cond { 28 | 0x0 => "eq", 29 | 0x1 => "ne", 30 | 0x2 => "cs", 31 | 0x3 => "cc", 32 | 0x4 => "mi", 33 | 0x5 => "pl", 34 | 0x6 => "vs", 35 | 0x7 => "vc", 36 | 0x8 => "hi", 37 | 0x9 => "ls", 38 | 0xA => "ge", 39 | 0xB => "lt", 40 | 0xC => "gt", 41 | 0xD => "le", 42 | 0xE => "", 43 | _ => "nv", 44 | } 45 | } 46 | 47 | pub fn print_op(item: T) -> String { 48 | format!("{item:?}").to_lowercase() 49 | } 50 | -------------------------------------------------------------------------------- /cores/nes/src/apu.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::TimeS; 10 | 11 | use crate::{ 12 | scheduling::{ApuEvent, NesEvent}, 13 | Nes, CLOCK_HZ, 14 | }; 15 | 16 | const SAMPLE_EVERY_N_CLOCKS: TimeS = CLOCK_HZ as TimeS / 48000; 17 | 18 | #[derive(Default)] 19 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 20 | pub struct Apu { 21 | pub buffer: Vec, 22 | } 23 | 24 | impl Apu { 25 | pub fn handle_event(nes: &mut Nes, event: ApuEvent, late_by: TimeS) { 26 | match event { 27 | ApuEvent::PushSample => { 28 | nes.apu.buffer.push(0.0); 29 | nes.apu.buffer.push(0.0); 30 | nes.scheduler.schedule( 31 | NesEvent::ApuEvent(ApuEvent::PushSample), 32 | SAMPLE_EVERY_N_CLOCKS - late_by, 33 | ) 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/resources/gamepad-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /cores/nds/src/hw/audio.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::TimeS; 10 | 11 | use crate::{cpu::NDS9_CLOCK, scheduling::ApuEvent, Nds}; 12 | 13 | pub const SAMPLE_EVERY_N_CLOCKS: TimeS = (NDS9_CLOCK / 48000) as TimeS; 14 | 15 | #[derive(Default)] 16 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 17 | pub struct Apu { 18 | pub bias: u16, 19 | pub control: u16 20 | } 21 | 22 | impl Apu { 23 | /// Handle event. Since all APU events reschedule themselves, this 24 | /// function returns the time after which the event should repeat. 25 | pub fn handle_event(ds: &mut Nds, event: ApuEvent, late_by: TimeS) -> TimeS { 26 | match event { 27 | ApuEvent::PushSample => { 28 | ds.c.audio_buffer.input[0].push(0.0); 29 | ds.c.audio_buffer.input[1].push(0.0); 30 | SAMPLE_EVERY_N_CLOCKS - late_by 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/src/debug/nds.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use eframe::egui::{Context, Ui}; 10 | use gamegirl::nds::Nds; 11 | 12 | use super::Windows; 13 | use crate::App; 14 | 15 | pub fn ui_menu(app: &mut App, ui: &mut eframe::egui::Ui) { 16 | app.debugger_window_states[0] ^= ui.button("Debugger ARM9").clicked(); 17 | app.debugger_window_states[1] ^= ui.button("Debugger ARM7").clicked(); 18 | app.debugger_window_states[2] ^= ui.button("Cartridge Viewer").clicked(); 19 | } 20 | 21 | pub fn get_windows() -> Windows { 22 | &[ 23 | ("Debugger ARM9", |a, b, c, d| { 24 | super::armchair::debugger(&mut a.cpu9, b, c, d) 25 | }), 26 | ("Debugger ARM7", |a, b, c, d| { 27 | super::armchair::debugger(&mut a.cpu7, b, c, d) 28 | }), 29 | ("Cartridge", cart_info), 30 | ] 31 | } 32 | 33 | /// Window showing information about the loaded ROM/cart. 34 | pub fn cart_info(ds: &mut Nds, ui: &mut Ui, _: &mut App, _: &Context) { 35 | ui.label(format!("{:#?}", ds.cart.header())); 36 | } 37 | -------------------------------------------------------------------------------- /gamegirl/src/frontend/filter.rs: -------------------------------------------------------------------------------- 1 | use std::vec::Vec; 2 | 3 | #[derive(Default)] 4 | pub struct ScreenBuffer { 5 | pub buffer: Vec>, 6 | } 7 | 8 | impl ScreenBuffer { 9 | pub fn next_frame(&mut self, next: Vec<[u8; 4]>, blend: Blend) -> Vec<[u8; 4]> { 10 | match (blend, self.buffer.last_mut()) { 11 | (Blend::None, _) => next, 12 | 13 | (Blend::Soften, Some(last)) => { 14 | let pixels = last 15 | .iter() 16 | .zip(next.iter()) 17 | .map(|(a, b)| blend_pixel(*a, *b)) 18 | .collect(); 19 | *last = next.into(); 20 | pixels 21 | } 22 | 23 | (Blend::Accumulate, Some(last)) => { 24 | let pixels: Vec<[u8; 4]> = last 25 | .iter() 26 | .zip(next.iter()) 27 | .map(|(a, b)| blend_pixel(*a, *b)) 28 | .collect(); 29 | *last = pixels.clone(); 30 | pixels 31 | } 32 | 33 | _ => { 34 | self.buffer.push(next.clone()); 35 | next 36 | } 37 | } 38 | } 39 | } 40 | 41 | fn blend_pixel(mut a: [u8; 4], b: [u8; 4]) -> [u8; 4] { 42 | for i in 0..3 { 43 | a[i] = ((a[i] as u16 + b[i] as u16) / 2) as u8; 44 | } 45 | a 46 | } 47 | 48 | #[derive(Copy, Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] 49 | pub enum Blend { 50 | None, 51 | Soften, 52 | Accumulate, 53 | } 54 | -------------------------------------------------------------------------------- /cores/psx/src/scheduling.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::{components::scheduler::Kind, TimeS}; 10 | 11 | use crate::{PlayStation, FRAME_CLOCK, SAMPLE_CLOCK}; 12 | 13 | #[derive(Copy, Clone, Eq, PartialEq)] 14 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 15 | pub enum PsxEvent { 16 | PauseEmulation, 17 | OutputFrame, 18 | ProduceSample, 19 | } 20 | 21 | impl PsxEvent { 22 | pub fn dispatch(self, ps: &mut PlayStation, _late_by: TimeS) { 23 | match self { 24 | PsxEvent::PauseEmulation => ps.ticking = false, 25 | PsxEvent::OutputFrame => { 26 | ps.ppu.output_frame(); 27 | ps.scheduler.schedule(PsxEvent::OutputFrame, FRAME_CLOCK); 28 | } 29 | PsxEvent::ProduceSample => { 30 | ps.apu.buffer.push(0.0); 31 | ps.apu.buffer.push(0.0); 32 | ps.scheduler.schedule(PsxEvent::ProduceSample, SAMPLE_CLOCK); 33 | } 34 | } 35 | } 36 | } 37 | 38 | impl Kind for PsxEvent {} 39 | 40 | impl Default for PsxEvent { 41 | fn default() -> Self { 42 | Self::PauseEmulation 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontends/corebench-egui/src/tests/gba.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use crate::testsuite::{TestStatus, TestSuite}; 10 | 11 | pub fn jsmolka() -> TestSuite { 12 | TestSuite::new("jsmolka", 10, |gg| { 13 | let regs = gg.get_registers(); 14 | let hash = TestSuite::screen_hash(gg); 15 | 16 | if regs[13] == 0x03008014 { 17 | let ones = regs[10]; 18 | let tens = regs[9]; 19 | let hundreds = regs[8]; 20 | let test = ones + (tens * 10) + (hundreds * 100); 21 | TestStatus::FailedAt(test.to_string()) 22 | } else if [ 23 | 0x20974E0091874964, 24 | 0x94F4D344B975EB0C, 25 | 0x1A8992654BCDC4D8, 26 | 0x63E68B6E5115B556, 27 | ] 28 | .contains(&hash) 29 | { 30 | TestStatus::Success 31 | } else { 32 | TestStatus::Running 33 | } 34 | }) 35 | } 36 | 37 | pub fn fuzzarm() -> TestSuite { 38 | TestSuite::new("fuzzarm", 20, |gg| { 39 | if TestSuite::screen_hash(gg) == 0xD5170621BA472629 { 40 | TestStatus::Success 41 | } else { 42 | TestStatus::Running 43 | } 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /cores/nes/src/scheduling.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::{components::scheduler::Kind, TimeS}; 10 | use NesEvent::*; 11 | 12 | use crate::{apu::Apu, Nes}; 13 | 14 | /// All scheduler events on the NES. 15 | #[derive(Copy, Clone, Eq, PartialEq, Default)] 16 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 17 | #[repr(u16)] 18 | pub enum NesEvent { 19 | /// Pause the emulation. Used by `advance_delta` to advance by a certain 20 | /// amount. 21 | #[default] 22 | PauseEmulation, 23 | ApuEvent(ApuEvent), 24 | } 25 | 26 | impl NesEvent { 27 | /// Handle the event by delegating to the appropriate handler. 28 | pub fn dispatch(&self, nes: &mut Nes, late_by: TimeS) { 29 | match self { 30 | PauseEmulation => nes.ticking = false, 31 | ApuEvent(event) => Apu::handle_event(nes, *event, late_by), 32 | } 33 | } 34 | } 35 | 36 | impl Kind for NesEvent {} 37 | 38 | /// Events the APU generates. 39 | #[derive(Copy, Clone, Eq, PartialEq)] 40 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 41 | #[repr(u16)] 42 | pub enum ApuEvent { 43 | // Push a sample to the output. 44 | PushSample, 45 | } 46 | -------------------------------------------------------------------------------- /components/armchair/src/tracing.rs: -------------------------------------------------------------------------------- 1 | use alloc::{ 2 | format, 3 | string::{String, ToString}, 4 | }; 5 | use core::fmt::Write; 6 | 7 | use common::numutil::NumExt; 8 | 9 | use crate::{ 10 | arm::ArmInst, 11 | interface::{Bus, CpuVersion}, 12 | state::{Flag, Register}, 13 | thumb::ThumbInst, 14 | Cpu, CpuState, 15 | }; 16 | 17 | impl Cpu { 18 | pub fn trace_inst(&mut self, inst: u32) { 19 | let cpsr = self.state.cpsr(); 20 | let mnem = self.state.get_inst_mnemonic(inst); 21 | 22 | let mut buf = String::with_capacity(100); 23 | let num = ('4' as u8 + S::Version::IS_V5 as u8) as char; 24 | buf.push(num); 25 | if !self.state.pipeline_valid { 26 | buf.push('!'); 27 | } 28 | for reg in Register::from_rlist(u16::MAX) { 29 | write!(buf, "{:08X} ", self.state[reg]).ok(); 30 | } 31 | 32 | if TY::WIDTH == 2 { 33 | self.bus.debugger().add_traced_instruction(|| { 34 | format!("{buf}cpsr: {cpsr:08X} | {inst:04X}: {mnem}") 35 | }); 36 | } else { 37 | self.bus 38 | .debugger() 39 | .add_traced_instruction(|| format!("{buf}cpsr: {cpsr:08X} | {inst:08X}: {mnem}")); 40 | } 41 | } 42 | } 43 | 44 | impl CpuState { 45 | pub fn get_inst_mnemonic(&mut self, inst: u32) -> String { 46 | if self.is_flag(Flag::Thumb) { 47 | ThumbInst::of(inst.u16()).to_string() 48 | } else { 49 | ArmInst::of(inst.u32()).to_string() 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /components/bitmatch/README.md: -------------------------------------------------------------------------------- 1 | # Bitmatch 2 | 3 | [![Crates.io Library Version Number](https://img.shields.io/crates/v/bitmatch)](https://crates.io/crates/bitmatch) 4 | [![Crates.io Library License](https://img.shields.io/crates/l/bitmatch)](https://www.mozilla.org/en-US/MPL/2.0/) 5 | [![Docs.rs Documentation Version Number](https://docs.rs/bitmatch/badge.svg)](https://docs.rs/bitmatch/) 6 | 7 | The bitmatch crate provides tools for packing and unpacking integers as 8 | sequences of bits. Supports `#![no_std]`. 9 | 10 | ## Examples 11 | 12 | Using `#[bitmatch]` with a `let` unpacks the bits into separate 13 | single-character variables for each letter you use. 14 | 15 | Using `bitpack!()` re-packs the bits from those single-character variables 16 | into a single integer. 17 | 18 | ```rust 19 | use bitmatch::bitmatch; 20 | 21 | #[bitmatch] 22 | fn interleave(n: u8) -> u8 { 23 | #[bitmatch] 24 | let "xxxx_yyyy" = n; 25 | bitpack!("xyxy_xyxy") 26 | } 27 | 28 | fn main() { 29 | assert_eq!(interleave(0xF0), 0xAA); 30 | } 31 | ``` 32 | 33 | You can use `#[bitmatch]` on a `match` as well, and it will ensure that the 34 | literal portions match, and bind the variable portions. 35 | 36 | ```rust 37 | use bitmatch::bitmatch; 38 | #[bitmatch] 39 | fn decode(inst: u8) -> String { 40 | #[bitmatch] 41 | match inst { 42 | "00oo_aabb" => format!("Op {}, {}, {}", o, a, b), 43 | "0100_aaii" => format!("Val {}, {}", a, i), 44 | "01??_????" => format!("Invalid"), 45 | "10ii_aabb" => format!("Ld {}, {}, {}", a, b, i), 46 | "11ii_aabb" => format!("St {}, {}, {}", a, b, i), 47 | } 48 | } 49 | ``` 50 | 51 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/src/gui/mod.rs: -------------------------------------------------------------------------------- 1 | use adw::subclass::prelude::ObjectSubclassIsExt; 2 | use gamegirl::frontend::input::{InputAction, InputSource}; 3 | use gtk::EventControllerKey; 4 | use window::GameGirlWindow; 5 | 6 | pub mod actions; 7 | pub mod canvas; 8 | pub mod input; 9 | pub mod settings; 10 | pub mod window; 11 | 12 | pub fn key_controller(window: &GameGirlWindow) -> EventControllerKey { 13 | let window1 = window.clone(); 14 | let window2 = window.clone(); 15 | let core1 = window.core(); 16 | let core2 = window.core(); 17 | 18 | let controller = EventControllerKey::new(); 19 | controller.connect_key_pressed(move |_, key, _, _| { 20 | let key = key.to_upper(); 21 | if let Some(InputAction::Button(button)) = window1 22 | .imp() 23 | .state 24 | .borrow_mut() 25 | .options 26 | .input 27 | .key_triggered(InputSource::Key(key.into())) 28 | { 29 | core1.lock().unwrap().c_mut().input.set(0, button, true); 30 | gtk::glib::Propagation::Stop 31 | } else { 32 | gtk::glib::Propagation::Proceed 33 | } 34 | }); 35 | controller.connect_key_released(move |_, key, _, _| { 36 | let key = key.to_upper(); 37 | if let Some(InputAction::Button(button)) = window2 38 | .imp() 39 | .state 40 | .borrow_mut() 41 | .options 42 | .input 43 | .key_triggered(InputSource::Key(key.into())) 44 | { 45 | core2.lock().unwrap().c_mut().input.set(0, button, false); 46 | } 47 | }); 48 | controller 49 | } 50 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/resources/cat-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /cores/ggc/src/io/joypad.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::{numutil::NumExt, TimeS}; 10 | 11 | use super::scheduling::GGEvent; 12 | use crate::{cpu::Interrupt, io::addr::JOYP, GameGirl, T_CLOCK_HZ}; 13 | 14 | /// Joypad of the console. 15 | #[derive(Default)] 16 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 17 | pub struct Joypad { 18 | key_states: u8, 19 | } 20 | 21 | impl Joypad { 22 | pub fn read(&self, joyp: u8) -> u8 { 23 | let row_start = match joyp & 0x30 { 24 | 0x10 => 0, 25 | 0x20 => 4, 26 | _ => return 0xCF, 27 | }; 28 | let mut res = 0; 29 | for key in (0..8).skip(row_start).take(4).rev() { 30 | let key = self.key_states.is_bit(key); 31 | res <<= 1; 32 | res += (!key) as u8; 33 | } 34 | res | (joyp & 0x30) | 0b1100_0000 35 | } 36 | 37 | pub fn update(gg: &mut GameGirl) { 38 | gg.joypad.key_states = gg.c.input.state(gg.scheduler.now()).0 as u8; 39 | gg.scheduler 40 | .schedule(GGEvent::UpdateKeypad, (T_CLOCK_HZ / 120) as TimeS); 41 | let read = gg.joypad.read(gg[JOYP]); 42 | if read & 0x0F != 0x0F { 43 | gg.request_interrupt(Interrupt::Joypad); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gamegirl-egui" 3 | default-run = "gamegirl_bin" 4 | version = "0.1.0" 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [[bin]] 10 | name = "gamegirl_bin" 11 | path = "src/main.rs" 12 | 13 | [lib] 14 | crate-type = ["cdylib", "rlib"] 15 | 16 | [dependencies] 17 | gamegirl = { path = "../../gamegirl", features = [ 18 | "ggc", 19 | "gga", 20 | "nds", 21 | "frontend", 22 | ] } 23 | 24 | egui = "0.31" 25 | egui_extras = "0.31" 26 | eframe = { version = "0.31", default-features = false, features = [ 27 | "default_fonts", 28 | "glow", 29 | "persistence", 30 | "wayland", 31 | ] } 32 | egui-notify = { git = "https://github.com/ItsEthra/egui-notify", branch = "master" } 33 | 34 | rfd = "0.15.3" 35 | gilrs = { version = "0.11.0", features = ["serde-serialize"] } 36 | 37 | log.workspace = true 38 | serde.workspace = true 39 | 40 | once_cell = { version = "*", optional = true } 41 | hqx = { git = "https://github.com/CryZe/wasmboy-rs", tag = "v0.1.3", optional = true } 42 | ehttp = { version = "0.5", optional = true } 43 | 44 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 45 | env_logger = "0.11.7" 46 | futures-executor = "0.3.31" 47 | gamegirl = { path = "../../gamegirl", features = ["zstd"] } 48 | 49 | [target.'cfg(target_arch = "wasm32")'.dependencies] 50 | console_error_panic_hook = "0.1.7" 51 | tracing-wasm = "0.2.1" 52 | wasm-bindgen-futures = "0.4.50" 53 | cpal = { version = "0.15.3", features = ["wasm-bindgen"] } 54 | web-sys = "0.3.77" 55 | 56 | [features] 57 | default = ["dep:ehttp"] 58 | savestates = ["gamegirl/serde"] 59 | remote-debugger = ["gamegirl/remote-debugger", "once_cell"] 60 | filters = ["hqx"] 61 | dynamic = ["gamegirl/dynamic"] 62 | -------------------------------------------------------------------------------- /gamegirl/src/frontend/cpal.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | use std::{ 3 | boxed::Box, 4 | sync::{Arc, Mutex}, 5 | }; 6 | 7 | use common::Core; 8 | use cpal::{ 9 | traits::{DeviceTrait, HostTrait, StreamTrait}, 10 | BufferSize, SampleRate, Stream, StreamConfig, 11 | }; 12 | 13 | pub struct AudioStream(#[allow(dead_code)] Option); 14 | 15 | impl AudioStream { 16 | pub fn empty() -> Self { 17 | Self(None) 18 | } 19 | } 20 | 21 | /// Setup audio playback on the default audio device using CPAL. 22 | /// Will automatically poll the gg for audio when needed on a separate thread. 23 | /// *NOT* used for synchronization, since audio samples are requested less than 24 | /// 60 times per second, which would lead to choppy display. 25 | /// Make sure to keep the returned Stream around to prevent the audio playback 26 | /// thread from closing. 27 | pub fn setup(sys: Arc>>) -> AudioStream { 28 | let sr = { 29 | let core = sys.lock().unwrap(); 30 | core.c().config.sample_rate as u32 31 | }; 32 | let device = cpal::default_host().default_output_device().unwrap(); 33 | if let Some(stream) = device 34 | .build_output_stream( 35 | &StreamConfig { 36 | channels: 2, 37 | sample_rate: SampleRate(sr), 38 | buffer_size: BufferSize::Fixed(sr / 30), 39 | }, 40 | move |data: &mut [f32], _| { 41 | let mut core = sys.lock().unwrap(); 42 | core.produce_samples(data) 43 | }, 44 | move |err| panic!("{err}"), 45 | Some(Duration::from_secs(1)), 46 | ) 47 | .ok() 48 | { 49 | stream.play().ok(); 50 | AudioStream(Some(stream)) 51 | } else { 52 | AudioStream(None) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/src/main.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | mod gui; 3 | 4 | use std::path::PathBuf; 5 | 6 | use adw::{Application, subclass::prelude::ObjectSubclassIsExt}; 7 | use config::Options; 8 | use gamegirl::frontend::rewinder::Rewinder; 9 | use gtk::{ 10 | gio::{ 11 | self, 12 | prelude::{ApplicationExt, ApplicationExtManual}, 13 | }, 14 | glib::{self}, 15 | prelude::{GtkWindowExt, WidgetExt}, 16 | }; 17 | use gui::window::GameGirlWindow; 18 | 19 | /// App state. This struct is contained in the main app window. 20 | pub struct AppState { 21 | /// User config state. 22 | options: Options, 23 | /// Path of the currently loaded ROM. 24 | current_rom_path: Option, 25 | /// Current rewinder state. 26 | rewinder: Rewinder<5>, 27 | } 28 | 29 | impl Default for AppState { 30 | fn default() -> Self { 31 | let options = Options::default(); 32 | let rewinder = Rewinder::new(options.rewind.rewind_buffer_size); 33 | Self { 34 | options, 35 | current_rom_path: Default::default(), 36 | rewinder, 37 | } 38 | } 39 | } 40 | 41 | fn main() -> glib::ExitCode { 42 | gio::resources_register_include!("gamegirl.gresource").expect("Failed to register resources."); 43 | 44 | let app = Application::builder() 45 | .application_id("eu.catin.gamegirl") 46 | .build(); 47 | app.connect_activate(build_ui); 48 | app.connect_startup(|_| { 49 | adw::init().unwrap(); 50 | }); 51 | app.run() 52 | } 53 | 54 | fn build_ui(app: &Application) { 55 | let window = GameGirlWindow::new(app); 56 | window.add_controller(gui::key_controller(&window)); 57 | window.connect_destroy(|window| { 58 | window.save_game(); 59 | window.imp().state.borrow().options.to_disk(); 60 | }); 61 | window.present(); 62 | } 63 | -------------------------------------------------------------------------------- /common/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use debugger::Debugger; 10 | use input::Input; 11 | use options::{EmulateOptions, SystemConfig}; 12 | use video::FrameBuffer; 13 | 14 | use self::audio::AudioBuffer; 15 | 16 | pub mod audio; 17 | pub mod debugger; 18 | pub mod input; 19 | pub mod options; 20 | pub mod time; 21 | pub mod video; 22 | 23 | /// Common fields shared by all systems. 24 | #[derive(Default)] 25 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 26 | pub struct Common { 27 | #[cfg_attr(feature = "serde", serde(skip, default))] 28 | pub debugger: Debugger, 29 | pub options: EmulateOptions, 30 | pub config: SystemConfig, 31 | pub in_tick: bool, 32 | 33 | #[cfg_attr(feature = "serde", serde(skip, default))] 34 | pub video_buffer: FrameBuffer, 35 | #[cfg_attr(feature = "serde", serde(skip, default))] 36 | pub audio_buffer: AudioBuffer, 37 | pub input: Input, 38 | } 39 | 40 | impl Common { 41 | pub fn with_config(config: SystemConfig) -> Self { 42 | Self { 43 | audio_buffer: AudioBuffer::with_config(&config), 44 | config, 45 | ..Default::default() 46 | } 47 | } 48 | 49 | pub fn restore_from(&mut self, old: Self) { 50 | self.debugger = old.debugger; 51 | self.options = old.options; 52 | self.config = old.config; 53 | self.audio_buffer = old.audio_buffer; 54 | self.audio_buffer.reinit_sampler(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cores/nds/src/graphics/engine3d/registers.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::numutil::{hword, word, NumExt, U16Ext, U32Ext}; 10 | use modular_bitfield::{bitfield, specifiers::*, BitfieldSpecifier}; 11 | 12 | #[bitfield] 13 | #[repr(u16)] 14 | #[derive(Debug, Default, Copy, Clone)] 15 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 16 | pub struct Disp3dCnt { 17 | pub tex_mapping_en: bool, 18 | pub shading: PolygonShading, 19 | pub alpha_test: bool, 20 | pub alpha_blend: bool, 21 | pub antialias: bool, 22 | pub edge_mark: bool, 23 | pub fog_color: FogColor, 24 | pub fog_en: bool, 25 | pub fog_shift: B4, 26 | pub color_underflow_ack: bool, 27 | pub polygon_overflow_ack: bool, 28 | pub rear_plane: RearPlane, 29 | #[skip] 30 | __: B1, 31 | } 32 | 33 | #[derive(BitfieldSpecifier, Debug, PartialEq)] 34 | #[bits = 1] 35 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 36 | pub enum PolygonShading { 37 | Toon = 0, 38 | Highlight = 1, 39 | } 40 | 41 | #[derive(BitfieldSpecifier, Debug, PartialEq)] 42 | #[bits = 1] 43 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 44 | pub enum FogColor { 45 | AlphaColor = 0, 46 | Alpha = 1, 47 | } 48 | 49 | #[derive(BitfieldSpecifier, Debug, PartialEq)] 50 | #[bits = 1] 51 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 52 | pub enum RearPlane { 53 | Blank = 0, 54 | Bitmap = 1, 55 | } 56 | -------------------------------------------------------------------------------- /gamegirl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gamegirl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | crate-type = ["cdylib", "lib"] 10 | 11 | [dependencies] 12 | common = { path = "../common" } 13 | ggc = { path = "../cores/ggc", optional = true } 14 | gga = { path = "../cores/gga", optional = true } 15 | nds = { path = "../cores/nds", optional = true } 16 | # psx = { path = "../cores/psx", optional = true } 17 | # nes = { path = "../cores/nes", optional = true } 18 | log.workspace = true 19 | 20 | gdbstub = { version = "0.7.5", optional = true } 21 | gdbstub_arch = { version = "0.3.1", optional = true } 22 | glow = { workspace = true, optional = true } 23 | zip = { version = "2.5", default-features = false, optional = true, features = [ 24 | "deflate", 25 | "deflate64", 26 | "lzma", 27 | ] } 28 | thiserror = { version = "2.0", default-features = false } 29 | 30 | libloading = { version = "0.8", optional = true } 31 | notify = { version = "8.0.0", optional = true } 32 | 33 | cpal = { version = "0.15.3", optional = true } 34 | gilrs = { version = "0.11.0", features = ["serde-serialize"], optional = true } 35 | serde = { workspace = true, optional = true } 36 | 37 | [target.'cfg(target_arch = "wasm32")'.dependencies] 38 | web-sys = { version = "0.3.77", features = ["Storage", "Window"] } 39 | base64 = "0.22.1" 40 | 41 | [features] 42 | serde = [ 43 | "common/serde", 44 | "ggc?/serde", 45 | "gga?/serde", 46 | "nds?/serde", 47 | # "nes?/serde", 48 | # "psx?/serde", 49 | ] 50 | serde-config = ["common/serde_config"] 51 | 52 | std = ["common/std", "gga/std", "nds/std", "dep:zip"] 53 | zstd = ["common/zstd"] 54 | remote-debugger = ["dep:gdbstub", "dep:gdbstub_arch", "std"] 55 | dynamic = ["dep:libloading", "dep:notify", "std"] 56 | frontend = ["dep:cpal", "dep:gilrs", "dep:serde", "std", "serde-config"] 57 | -------------------------------------------------------------------------------- /common/src/testing.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use core::hash::Hasher; 10 | use std::{boxed::Box, hash::DefaultHasher, string::String, vec::Vec}; 11 | 12 | use crate::{common::options::SystemConfig, components::storage::GameCart, Core}; 13 | 14 | pub type TestInspector = fn(&mut Box) -> TestStatus; 15 | 16 | pub fn run_test(rom: &[u8], inspector: TestInspector) { 17 | let rom = rom.into(); 18 | let mut core = S::try_new( 19 | &mut Some(GameCart { rom, save: None }), 20 | &SystemConfig::default(), 21 | ) 22 | .unwrap(); 23 | for _ in 0..30 { 24 | core.advance_delta(1.0); 25 | let status = (inspector)(&mut core); 26 | match status { 27 | TestStatus::Running => continue, 28 | TestStatus::Success => return, 29 | TestStatus::Failed => panic!("Test failed!"), 30 | TestStatus::FailedAt(msg) => panic!("Test failed: {msg}!"), 31 | } 32 | } 33 | panic!("Test timed out!") 34 | } 35 | 36 | pub fn screen_hash(core: &mut Box) -> u64 { 37 | let mut hasher = DefaultHasher::new(); 38 | if let Some(frame) = core.c_mut().video_buffer.pop_recent() { 39 | hasher.write( 40 | &frame 41 | .into_iter() 42 | .flat_map(|t| t.into_iter()) 43 | .collect::>(), 44 | ) 45 | } 46 | hasher.finish() 47 | } 48 | 49 | #[derive(PartialEq)] 50 | pub enum TestStatus { 51 | Running, 52 | Success, 53 | Failed, 54 | FailedAt(String), 55 | } 56 | -------------------------------------------------------------------------------- /cores/nds/src/hw/bios/drastic_bios_readme.txt: -------------------------------------------------------------------------------- 1 | Custom NDS ARM7/ARM9 BIOS replacement 2 | Copyright (c) 2013, Gilead Kutnick 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1) Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | 2) Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | 26 | -- Info -- 27 | 28 | This archive contains source code and assembly for a custom BIOS replacement 29 | for the Nintendo DS system. This code is in no way affiliated with Nintendo 30 | and is not derived from Nintendo's BIOS implementation but has been implemented 31 | using publically available documentation. 32 | 33 | It can be assembled using the included Makefile along with a proper ARM gcc 34 | toolchain. Change the first four lines to point to the proper toolchain of your 35 | choice. 36 | 37 | -------------------------------------------------------------------------------- /frontends/corebench-egui/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | mod app; 10 | mod gui; 11 | mod tests; 12 | mod testsuite; 13 | 14 | use std::{ 15 | path::PathBuf, 16 | sync::{Arc, Mutex}, 17 | }; 18 | 19 | use eframe::{egui::ViewportBuilder, emath::History}; 20 | use gamegirl::{ 21 | common::Core, 22 | dummy_core, 23 | dynamic::{DynamicContext, NewCoreFn}, 24 | }; 25 | use testsuite::TestSuiteResult; 26 | 27 | use crate::app::App; 28 | 29 | pub struct DCore { 30 | c: Box, 31 | suites: Vec, 32 | bench: History, 33 | bench_iso: Arc>>, 34 | loader: NewCoreFn, 35 | idx: Option, 36 | name: String, 37 | } 38 | 39 | fn main() { 40 | env_logger::init(); 41 | let options = eframe::NativeOptions { 42 | viewport: ViewportBuilder::default().with_transparent(true), 43 | ..Default::default() 44 | }; 45 | eframe::run_native( 46 | "gamegirl core workbench", 47 | options, 48 | Box::new(|ctx| Ok(App::new(ctx))), 49 | ) 50 | .unwrap() 51 | } 52 | 53 | fn load_core(ctx: &mut DynamicContext, path: PathBuf) -> Result { 54 | ctx.load_core(&path).map(|idx| DCore { 55 | c: dummy_core(), 56 | suites: vec![], 57 | bench: History::new(10..5000, 30.0), 58 | bench_iso: Arc::new(Mutex::new(History::new(10..5000, 100.0))), 59 | loader: ctx.get_core(idx).loader, 60 | idx: Some(idx), 61 | name: path.file_name().unwrap().to_string_lossy().to_string(), 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /cores/nds/src/graphics/ppu/render/modes.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use super::{super::OverflowMode, pixels::affine_transform_point, xy2d, PpuRender, HEIGHT, WIDTH}; 10 | 11 | impl PpuRender { 12 | pub fn render_mode0(&mut self) { 13 | for bg in 0..4 { 14 | self.render_bg_text(bg); 15 | } 16 | self.finalize_scanline(0..=3); 17 | } 18 | 19 | pub fn render_mode1(&mut self) { 20 | self.render_bg_text(0); 21 | self.render_bg_text(1); 22 | self.render_bg_text(2); 23 | self.render_bg_affine(3); 24 | self.finalize_scanline(0..=3); 25 | } 26 | 27 | pub fn render_mode2(&mut self) { 28 | self.render_bg_text(0); 29 | self.render_bg_text(1); 30 | self.render_bg_affine(2); 31 | self.render_bg_affine(3); 32 | self.finalize_scanline(0..=3); 33 | } 34 | 35 | pub fn render_mode3(&mut self) { 36 | self.render_bg_text(0); 37 | self.render_bg_text(1); 38 | self.render_bg_text(2); 39 | self.render_bg_ext(3); 40 | self.finalize_scanline(0..=3); 41 | } 42 | 43 | pub fn render_mode4(&mut self) { 44 | self.render_bg_text(0); 45 | self.render_bg_text(1); 46 | self.render_bg_affine(2); 47 | self.render_bg_ext(3); 48 | self.finalize_scanline(0..=3); 49 | } 50 | 51 | pub fn render_mode5(&mut self) { 52 | self.render_bg_text(0); 53 | self.render_bg_text(1); 54 | self.render_bg_ext(2); 55 | self.render_bg_ext(3); 56 | self.finalize_scanline(0..=3); 57 | } 58 | 59 | pub fn render_mode6(&mut self) { 60 | // TODO 3D stuff 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cores/gga/src/hw/input.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | //! Input handler. 10 | //! Luckily, GGA input is dead simple compared to even GG. 11 | 12 | #![allow(unused_braces)] // modular_bitfield issue 13 | 14 | use armchair::Interrupt; 15 | use common::TimeS; 16 | use modular_bitfield::{bitfield, specifiers::B14}; 17 | 18 | use crate::{ 19 | cpu::{GgaFullBus, CPU_CLOCK}, 20 | scheduling::AdvEvent, 21 | }; 22 | 23 | #[bitfield] 24 | #[repr(u16)] 25 | #[derive(Default, Copy, Clone, Debug)] 26 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 27 | pub struct KeyControl { 28 | irq_enables: B14, 29 | global_irq: bool, 30 | irq_is_and: bool, 31 | } 32 | 33 | impl GgaFullBus { 34 | pub fn keyinput(&self) -> u16 { 35 | // GGA input is active low 36 | 0x3FF ^ self.c.input.state(self.scheduler.now()).0 37 | } 38 | 39 | /// Check if KEYCNT should cause a joypad IRQ. 40 | pub fn check_keycnt(&mut self) { 41 | let input = 0x3FF ^ self.keyinput(); 42 | let cnt = self.memory.keycnt; 43 | if cnt.global_irq() { 44 | let cond = cnt.irq_enables(); 45 | let fire = (input != self.memory.keys_prev) 46 | && if !cnt.irq_is_and() { 47 | cond & input != 0 48 | } else { 49 | cond & input == cond 50 | }; 51 | if fire { 52 | self.cpu.request_interrupt(&mut self.bus, Interrupt::Joypad); 53 | } 54 | 55 | self.scheduler 56 | .schedule(AdvEvent::UpdateKeypad, (CPU_CLOCK / 120.0) as TimeS); 57 | } 58 | 59 | self.memory.keys_prev = input; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /cores/nds/src/cpu/nds7.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use armchair::{ 10 | interface::{Arm7Dtmi, Bus, BusCpuConfig, RwType}, 11 | state::Flag::IrqDisable, 12 | Access, Address, Cpu, CpuState, Exception, 13 | }; 14 | use common::{ 15 | common::debugger::Debugger, 16 | components::memory_mapper::{MemoryMappedSystem, MemoryMapper}, 17 | numutil::NumExt, 18 | Time, 19 | }; 20 | 21 | use crate::{ 22 | addr::{IE, IF, IME}, 23 | Nds7, Nds9, NdsCpu, 24 | }; 25 | 26 | impl Bus for Nds7 { 27 | type Version = Arm7Dtmi; 28 | 29 | const CONFIG: BusCpuConfig = BusCpuConfig { 30 | exception_vector_base_address: Address(0), 31 | }; 32 | 33 | fn tick(&mut self, cycles: Time) { 34 | self.time_7 += cycles << 1; 35 | } 36 | 37 | fn handle_events(&mut self, cpu: &mut CpuState) { 38 | if self.scheduler.has_events() { 39 | while let Some(event) = self.scheduler.get_next_pending() { 40 | event.kind.dispatch(self, event.late_by); 41 | } 42 | } 43 | } 44 | 45 | fn exception_happened(&mut self, _cpu: &mut CpuState, _kind: Exception) {} 46 | 47 | fn pipeline_stalled(&mut self, _cpu: &mut CpuState) {} 48 | 49 | fn get(&mut self, _cpu: &mut CpuState, addr: Address) -> T { 50 | self.get(addr.0) 51 | } 52 | 53 | fn set(&mut self, _cpu: &mut CpuState, addr: Address, value: T) { 54 | self.set(addr.0, value) 55 | } 56 | 57 | fn wait_time( 58 | &mut self, 59 | _cpu: &mut CpuState, 60 | _addr: Address, 61 | _access: Access, 62 | ) -> u16 { 63 | 1 64 | } 65 | 66 | fn debugger(&mut self) -> &mut Debugger { 67 | &mut self.c.debugger 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /cores/nds/src/graphics/capture/registers.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::numutil::{hword, word, NumExt, U16Ext, U32Ext}; 10 | use modular_bitfield::{bitfield, specifiers::*, BitfieldSpecifier}; 11 | 12 | #[bitfield] 13 | #[repr(u32)] 14 | #[derive(Debug, Default, Copy, Clone)] 15 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 16 | pub struct DispCapCnt { 17 | pub eva: B5, 18 | #[skip] 19 | __: B3, 20 | pub evb: B5, 21 | #[skip] 22 | __: B3, 23 | vram_write_block: B2, 24 | vram_write_offs: B2, 25 | capt_size: CaptureSize, 26 | #[skip] 27 | __: B2, 28 | source_a: SourceA, 29 | source_b: SourceB, 30 | vram_read_offs: B2, 31 | #[skip] 32 | __: B1, 33 | capt_source: CaptureSource, 34 | capt_en: bool, 35 | } 36 | 37 | #[derive(BitfieldSpecifier, Debug, PartialEq)] 38 | #[bits = 2] 39 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 40 | pub enum CaptureSize { 41 | C128x128 = 0, 42 | C256x64 = 1, 43 | C256x128 = 2, 44 | C256x192 = 3, 45 | } 46 | 47 | #[derive(BitfieldSpecifier, Debug, PartialEq)] 48 | #[bits = 1] 49 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 50 | pub enum SourceA { 51 | Graphics = 0, 52 | ThreeD = 1, 53 | } 54 | 55 | #[derive(BitfieldSpecifier, Debug, PartialEq)] 56 | #[bits = 1] 57 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 58 | pub enum SourceB { 59 | Vram = 0, 60 | MemFifo = 1, 61 | } 62 | 63 | #[derive(BitfieldSpecifier, Debug, PartialEq)] 64 | #[bits = 2] 65 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 66 | pub enum CaptureSource { 67 | SrcA = 0, 68 | SrcB = 1, 69 | SrcAB = 2, 70 | SrcBA = 3, 71 | } 72 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use gamegirl_egui::App; 10 | 11 | #[cfg(not(target_arch = "wasm32"))] 12 | fn main() { 13 | use eframe::egui::ViewportBuilder; 14 | 15 | env_logger::init(); 16 | let options = eframe::NativeOptions { 17 | viewport: ViewportBuilder::default().with_transparent(true), 18 | ..Default::default() 19 | }; 20 | eframe::run_native("gamegirl", options, Box::new(|ctx| Ok(App::new(ctx)))).unwrap() 21 | } 22 | 23 | #[cfg(target_arch = "wasm32")] 24 | fn main() { 25 | console_error_panic_hook::set_once(); 26 | tracing_wasm::set_as_global_default(); 27 | eframe::WebLogger::init(log::LevelFilter::Debug).ok(); 28 | 29 | let web_options = eframe::WebOptions::default(); 30 | wasm_bindgen_futures::spawn_local(async { 31 | let start_result = eframe::WebRunner::new() 32 | .start( 33 | "the_canvas_id", 34 | web_options, 35 | Box::new(|ctx| Ok(App::new(ctx))), 36 | ) 37 | .await; 38 | 39 | // Remove the loading text and spinner: 40 | let loading_text = web_sys::window() 41 | .and_then(|w| w.document()) 42 | .and_then(|d| d.get_element_by_id("loading_text")); 43 | if let Some(loading_text) = loading_text { 44 | match start_result { 45 | Ok(_) => { 46 | loading_text.remove(); 47 | } 48 | Err(e) => { 49 | loading_text.set_inner_html( 50 | "

The app has crashed. See the developer console for details.

", 51 | ); 52 | panic!("Failed to start eframe: {e:?}"); 53 | } 54 | } 55 | } 56 | }); 57 | } 58 | -------------------------------------------------------------------------------- /cores/nds/src/hw/input.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | //! Input handler. 10 | 11 | use armchair::{Cpu, Interrupt}; 12 | use common::TimeS; 13 | use modular_bitfield::{bitfield, specifiers::B14}; 14 | 15 | use crate::{cpu::NDS9_CLOCK, scheduling::NdsEvent, CpuDevice, Nds, NdsCpu}; 16 | 17 | #[derive(Default)] 18 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 19 | pub struct Input { 20 | pub cnt: CpuDevice, 21 | keys_prev: u16, 22 | } 23 | 24 | #[bitfield] 25 | #[repr(u16)] 26 | #[derive(Default, Copy, Clone, Debug)] 27 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 28 | pub struct KeyControl { 29 | irq_enables: B14, 30 | global_irq: bool, 31 | irq_is_and: bool, 32 | } 33 | 34 | impl Nds { 35 | pub fn keyinput(&self) -> u16 { 36 | // NDS input is active low 37 | 0x3FF ^ (self.c.input.state(self.scheduler.now()).0 & 0x3FF) 38 | } 39 | 40 | pub fn keyinput_ext(&self) -> u16 { 41 | // NDS input is active low 42 | // TODO Touchscreen 43 | 0b0111_1100 | (0x3 ^ (self.c.input.state(self.scheduler.now()).0 >> 10)) 44 | } 45 | 46 | /// Check if KEYCNT should cause a joypad IRQ. 47 | pub fn check_keycnt(ds: &mut DS) { 48 | let input = 0x3FF ^ ds.keyinput(); 49 | let cnt = ds.input.cnt[DS::I]; 50 | if cnt.global_irq() { 51 | let cond = cnt.irq_enables(); 52 | let fire = (input != ds.input.keys_prev) 53 | && if !cnt.irq_is_and() { 54 | cond & input != 0 55 | } else { 56 | cond & input == cond 57 | }; 58 | if fire { 59 | ds.cpu().request_interrupt(Interrupt::Joypad); 60 | } 61 | } 62 | 63 | ds.input.keys_prev = input; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cores/nds/src/graphics/ppu/render/palette.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::{ 10 | numutil::{hword, NumExt}, 11 | Colour, 12 | }; 13 | 14 | use super::{super::PaletteMode, xy2dw, PpuRender}; 15 | 16 | impl PpuRender { 17 | /// Given a tile address and tile pixel, get the palette. 18 | pub(super) fn get_palette_obj( 19 | &self, 20 | bank: u8, 21 | mode: PaletteMode, 22 | addr: u32, 23 | x: u32, 24 | y: u32, 25 | ) -> Option { 26 | Some(match mode { 27 | PaletteMode::Palettes16 => { 28 | let addr = addr.us() + xy2dw(x.us() / 2, y.us(), 4); 29 | let value: u8 = self.obj_vram(addr); 30 | let colour = if x.is_bit(0) { value >> 4 } else { value & 0xF }; 31 | if colour == 0 { 32 | return None; 33 | } 34 | (bank * 0x10) + colour 35 | } 36 | PaletteMode::Single256 => { 37 | let addr = addr.us() + xy2dw(x.us(), y.us(), 8); 38 | let pal = self.obj_vram(addr); 39 | if pal == 0 { 40 | return None; 41 | } 42 | pal 43 | } 44 | }) 45 | } 46 | 47 | /// Turn a palette index (0-255) into a colour. 48 | pub fn idx_to_palette(&self, idx: u8) -> Colour { 49 | let addr = (idx.us() << 1) + (OBJ as usize * 0x200); 50 | let lo = self.palette[addr]; 51 | let hi = self.palette[addr + 1]; 52 | Self::hword_to_colour(hword(lo, hi)) 53 | } 54 | 55 | /// Extract a 5bit colour from a halfword. 56 | fn hword_to_colour(hword: u16) -> Colour { 57 | let r = hword.bits(0, 5).u8(); 58 | let g = hword.bits(5, 5).u8(); 59 | let b = hword.bits(10, 5).u8(); 60 | [r, g, b, 255] 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cores/nes/src/joypad.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::{components::input::Button, numutil::NumExt}; 10 | 11 | use crate::Nes; 12 | 13 | /// Joypad 1 of the console. 14 | #[derive(Default)] 15 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 16 | pub struct Joypad { 17 | key_states: [bool; 8], 18 | register: u8, 19 | strobe: bool, 20 | } 21 | 22 | impl Joypad { 23 | pub fn read(&mut self) -> u8 { 24 | let value = self.register & 1; 25 | self.register >>= 1; 26 | self.register.set_bit(7, true); 27 | value 28 | } 29 | 30 | pub fn write(&mut self, value: u8) { 31 | self.strobe = value.is_bit(0); 32 | if self.strobe { 33 | self.register = 0; 34 | self.register 35 | .set_bit(0, self.key_states[Button::A as usize]); 36 | self.register 37 | .set_bit(1, self.key_states[Button::B as usize]); 38 | self.register 39 | .set_bit(2, self.key_states[Button::Select as usize]); 40 | self.register 41 | .set_bit(3, self.key_states[Button::Start as usize]); 42 | self.register 43 | .set_bit(4, self.key_states[Button::Up as usize]); 44 | self.register 45 | .set_bit(5, self.key_states[Button::Down as usize]); 46 | self.register 47 | .set_bit(6, self.key_states[Button::Left as usize]); 48 | self.register 49 | .set_bit(7, self.key_states[Button::Right as usize]); 50 | } 51 | } 52 | 53 | /// To be called by GUI code; sets the state of a given button. 54 | pub fn set(nes: &mut Nes, button: Button, state: bool) { 55 | if button as usize >= 8 { 56 | return; // GGA buttons 57 | } 58 | nes.joypad.key_states[button as usize] = state; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cores/nes/src/cpu/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | mod inst; 10 | use common::numutil::{NumExt, U16Ext}; 11 | use modular_bitfield::bitfield; 12 | 13 | use self::inst::Inst; 14 | use crate::Nes; 15 | 16 | #[bitfield] 17 | #[repr(u8)] 18 | #[derive(Debug, Clone, Copy, Default)] 19 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 20 | pub struct CpuStatus { 21 | carry: bool, 22 | zero: bool, 23 | interrupt_disable: bool, 24 | demimal_mode: bool, 25 | break_cmd: bool, 26 | overflow: bool, 27 | negative: bool, 28 | eight: bool, 29 | } 30 | 31 | impl CpuStatus { 32 | fn set_zn(&mut self, value: u8) { 33 | self.set_zero(value == 0); 34 | self.set_negative(value.is_bit(7)); 35 | } 36 | 37 | fn set_znc(&mut self, value: u16) { 38 | self.set_zero(value.u8() == 0); 39 | self.set_negative(value.is_bit(7)); 40 | self.set_carry(value & 0xFF != 0); 41 | } 42 | } 43 | 44 | #[derive(Default)] 45 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 46 | pub struct Cpu { 47 | pub pc: u16, 48 | regs: [u8; 4], 49 | status: CpuStatus, 50 | } 51 | 52 | impl Cpu { 53 | pub fn exec_next_inst(nes: &mut Nes) { 54 | let inst = nes.read_imm(); 55 | inst::execute(nes, Inst(inst)); 56 | } 57 | 58 | pub fn trigger_int(nes: &mut Nes) { 59 | nes.push(nes.cpu.pc.low()); 60 | nes.push(nes.cpu.pc.high()); 61 | nes.push(nes.cpu.status.into()); 62 | nes.cpu.status.set_break_cmd(true); 63 | nes.cpu.pc = 0xFFFE; 64 | } 65 | 66 | pub fn get(&self, reg: Reg) -> u8 { 67 | self.regs[reg as usize] 68 | } 69 | 70 | pub fn set(&mut self, reg: Reg, value: u8) { 71 | self.regs[reg as usize] = value; 72 | } 73 | } 74 | 75 | #[derive(Copy, Clone)] 76 | pub enum Reg { 77 | A, 78 | X, 79 | Y, 80 | S, 81 | } 82 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/src/input/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use std::{collections::HashMap, fmt::Display}; 10 | 11 | use eframe::egui; 12 | pub use file_dialog::File; 13 | use gamegirl::{ 14 | common::common::input::Button::*, 15 | frontend::input::{ 16 | InputAction::{self, *}, 17 | InputSource, Key, 18 | }, 19 | }; 20 | 21 | pub mod file_dialog; 22 | mod hotkeys; 23 | 24 | pub use hotkeys::HOTKEYS; 25 | 26 | #[derive(Copy, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)] 27 | pub struct EguiKey(egui::Key); 28 | 29 | impl From for EguiKey { 30 | fn from(value: egui::Key) -> Self { 31 | Self(value) 32 | } 33 | } 34 | 35 | impl Display for EguiKey { 36 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 37 | write!(f, "{:?}", self.0) 38 | } 39 | } 40 | 41 | impl Key for EguiKey { 42 | fn is_escape(self) -> bool { 43 | self.0 == egui::Key::Escape 44 | } 45 | 46 | fn default_map() -> HashMap, InputAction> { 47 | HashMap::from([ 48 | (InputSource::Key(Self(egui::Key::X)), Button(A)), 49 | (InputSource::Key(Self(egui::Key::Z)), Button(B)), 50 | (InputSource::Key(Self(egui::Key::Enter)), Button(Start)), 51 | (InputSource::Key(Self(egui::Key::Space)), Button(Select)), 52 | (InputSource::Key(Self(egui::Key::ArrowDown)), Button(Down)), 53 | (InputSource::Key(Self(egui::Key::ArrowUp)), Button(Up)), 54 | (InputSource::Key(Self(egui::Key::ArrowLeft)), Button(Left)), 55 | (InputSource::Key(Self(egui::Key::ArrowRight)), Button(Right)), 56 | (InputSource::Key(Self(egui::Key::A)), Button(L)), 57 | (InputSource::Key(Self(egui::Key::S)), Button(R)), 58 | (InputSource::Key(Self(egui::Key::R)), Hotkey(4)), 59 | ]) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/src/input/hotkeys.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use super::file_dialog; 10 | use crate::app::App; 11 | 12 | type HotkeyFn = fn(&mut App, bool); 13 | pub const HOTKEYS: &[(&str, HotkeyFn)] = &[ 14 | ("Open ROM", |a, p| { 15 | pressed(a, p, |app| { 16 | file_dialog::open_rom(app.message_channel.0.clone()) 17 | }) 18 | }), 19 | ("Reset", |a, p| pressed(a, p, App::reset)), 20 | ("Pause", |a, p| pressed(a, p, App::pause)), 21 | ("Save", |a, p| pressed(a, p, |app| app.save_game())), 22 | ("Fast Forward (Hold)", |app, pressed| { 23 | let mut core = app.core.lock().unwrap(); 24 | let c = core.c_mut(); 25 | if pressed { 26 | c.options.speed_multiplier = app.state.options.rewinder.ff_hold_speed; 27 | } else { 28 | c.options.speed_multiplier = 1; 29 | } 30 | c.video_buffer.frameskip = c.options.speed_multiplier - 1; 31 | }), 32 | ("Fast Forward (Toggle)", |a, p| { 33 | pressed(a, p, |app| { 34 | let mut core = app.core.lock().unwrap(); 35 | let c = core.c_mut(); 36 | 37 | app.fast_forward_toggled = !app.fast_forward_toggled; 38 | if app.fast_forward_toggled { 39 | c.options.speed_multiplier = app.state.options.rewinder.ff_toggle_speed; 40 | } else { 41 | c.options.speed_multiplier = 1; 42 | } 43 | c.video_buffer.frameskip = c.options.speed_multiplier - 1; 44 | }); 45 | }), 46 | ("Rewind (Hold)", |app, pressed| { 47 | app.rewinder.rewinding = pressed; 48 | app.core 49 | .lock() 50 | .unwrap() 51 | .c_mut() 52 | .options 53 | .invert_audio_samples = pressed; 54 | }), 55 | ]; 56 | 57 | fn pressed(app: &mut App, pressed: bool, inner: fn(&mut App)) { 58 | if pressed { 59 | inner(app); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /common/src/serialize.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use alloc::vec::Vec; 10 | 11 | use bincode::config; 12 | 13 | /// Serialize an object that can be loaded with [deserialize]. 14 | /// It is (optionally zstd-compressed) bincode. 15 | #[cfg(feature = "zstd")] 16 | pub fn serialize(thing: &T, with_zstd: bool) -> Vec { 17 | if with_zstd { 18 | let mut dest = Vec::new(); 19 | let mut writer = zstd::stream::Encoder::new(&mut dest, 3).unwrap(); 20 | bincode::serde::encode_into_std_write(thing, &mut writer, config::legacy()).unwrap(); 21 | writer.finish().unwrap(); 22 | dest 23 | } else { 24 | bincode::serde::encode_to_vec(thing, config::legacy()).unwrap() 25 | } 26 | } 27 | 28 | /// Deserialize an object that was made with [serialize]. 29 | /// It is (optionally zstd-compressed) bincode. 30 | #[cfg(feature = "zstd")] 31 | pub fn deserialize(state: &[u8], with_zstd: bool) -> T { 32 | if with_zstd { 33 | let mut decoder = zstd::stream::Decoder::new(state).unwrap(); 34 | bincode::serde::decode_from_std_read(&mut decoder, config::legacy()).unwrap() 35 | } else { 36 | bincode::serde::decode_from_slice(state, config::legacy()) 37 | .unwrap() 38 | .0 39 | } 40 | } 41 | 42 | /// Serialize an object that can be loaded with [deserialize]. 43 | /// It is (optionally zstd-compressed) bincode. 44 | #[cfg(not(feature = "zstd"))] 45 | pub fn serialize(thing: &T, _with_zstd: bool) -> Vec { 46 | bincode::serde::encode_to_vec(thing, config::legacy()).unwrap() 47 | } 48 | 49 | /// Deserialize an object that was made with [serialize]. 50 | /// It is (optionally zstd-compressed) bincode. 51 | #[cfg(not(feature = "zstd"))] 52 | pub fn deserialize(state: &[u8], with_zstd: bool) -> T { 53 | bincode::serde::decode_from_slice(state, config::legacy()).unwrap() 54 | } 55 | -------------------------------------------------------------------------------- /frontends/gamegirl-egui/src/gui/input.rs: -------------------------------------------------------------------------------- 1 | use egui::{Button, Context, Frame, Margin, RichText, Sense, Ui}; 2 | use gamegirl::common::{common::input, Core}; 3 | 4 | use crate::App; 5 | 6 | pub fn render(app: &mut App, ctx: &Context) { 7 | let mut core = app.core.lock().unwrap(); 8 | button_win(ctx, &mut core, input::Button::A, "A"); 9 | button_win(ctx, &mut core, input::Button::B, "B"); 10 | button_win(ctx, &mut core, input::Button::L, "L"); 11 | button_win(ctx, &mut core, input::Button::R, "R"); 12 | button_win(ctx, &mut core, input::Button::Start, "START"); 13 | button_win(ctx, &mut core, input::Button::Select, "SELECT"); 14 | 15 | egui::Window::new("dpad") 16 | .title_bar(false) 17 | .resizable(false) 18 | .show(ctx, |ui| { 19 | egui::Grid::new("dpadgrid").show(ui, |ui| { 20 | ui.label(""); 21 | button_ui(ui, &mut core, input::Button::Up, "^"); 22 | ui.label(""); 23 | ui.end_row(); 24 | 25 | button_ui(ui, &mut core, input::Button::Left, "<"); 26 | ui.label(""); 27 | button_ui(ui, &mut core, input::Button::Right, ">"); 28 | ui.end_row(); 29 | 30 | ui.label(""); 31 | button_ui(ui, &mut core, input::Button::Down, "v"); 32 | ui.label(""); 33 | ui.end_row(); 34 | }); 35 | }); 36 | } 37 | 38 | fn button_win(ctx: &Context, core: &mut Box, button: input::Button, text: &str) { 39 | egui::Window::new(text) 40 | .title_bar(false) 41 | .resizable(false) 42 | .frame(Frame::window(&ctx.style()).inner_margin(Margin::same(10))) 43 | .show(ctx, |ui| button_ui(ui, core, button, text)); 44 | } 45 | 46 | fn button_ui(ui: &mut Ui, core: &mut Box, button: input::Button, text: &str) { 47 | ui.spacing_mut().button_padding = [10.0, 10.0].into(); 48 | let btn = ui.add( 49 | Button::new(RichText::new(text).size(40.0)) 50 | .corner_radius(50.0) 51 | .sense(Sense::drag()) 52 | .min_size([60.0; 2].into()), 53 | ); 54 | 55 | if btn.drag_stopped() { 56 | core.c_mut().input.set(0, button, false); 57 | } 58 | if btn.drag_started() { 59 | core.c_mut().input.set(0, button, true); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/src/config.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::{self, File}, 3 | io::{BufReader, BufWriter}, 4 | }; 5 | 6 | use gamegirl::{ 7 | SystemConfig, 8 | frontend::{filter::Blend, input::Input, rewinder::RewinderConfig}, 9 | }; 10 | 11 | use crate::gui::input::GtkKey; 12 | 13 | /// User-configurable options. 14 | #[derive(serde::Deserialize, serde::Serialize)] 15 | pub struct Options { 16 | /// Options passed to the system when loading a ROM. 17 | pub sys: SystemConfig, 18 | /// Input configuration. 19 | pub input: Input, 20 | /// Rewinder configuration. 21 | pub rewind: RewinderConfig, 22 | /// Texture filter to use. 23 | pub texture_filter: TextureFilter, 24 | /// Blending filter to use. 25 | pub blend_filter: Blend, 26 | /// If aspect ration should be preserved. 27 | pub preserve_aspect_ratio: bool, 28 | } 29 | 30 | impl Options { 31 | pub fn empty() -> Self { 32 | Self { 33 | sys: Default::default(), 34 | input: Input::new(), 35 | rewind: RewinderConfig::default(), 36 | texture_filter: TextureFilter::Nearest, 37 | blend_filter: Blend::None, 38 | preserve_aspect_ratio: true, 39 | } 40 | } 41 | 42 | pub fn from_disk() -> Self { 43 | let path = dirs::config_dir().unwrap().join("gamegirl/config.bin"); 44 | let data = File::open(&path).ok().and_then(|file| { 45 | bincode::serde::decode_from_reader(&mut BufReader::new(file), bincode::config::legacy()) 46 | .ok() 47 | }); 48 | data.unwrap_or_else(|| Self::empty()) 49 | } 50 | 51 | pub fn to_disk(&self) { 52 | let path = dirs::config_dir().unwrap().join("gamegirl/config.bin"); 53 | fs::create_dir(&path.parent().unwrap()).ok(); 54 | File::create(path).ok().and_then(|file| { 55 | bincode::serde::encode_into_std_write( 56 | self, 57 | &mut BufWriter::new(file), 58 | bincode::config::legacy(), 59 | ) 60 | .ok() 61 | }); 62 | } 63 | } 64 | 65 | impl Default for Options { 66 | fn default() -> Self { 67 | Self::from_disk() 68 | } 69 | } 70 | 71 | #[derive(Copy, Clone, PartialEq, serde::Deserialize, serde::Serialize)] 72 | pub enum TextureFilter { 73 | Nearest, 74 | Linear, 75 | Trilinear, 76 | } 77 | -------------------------------------------------------------------------------- /cores/gga/src/addr.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | // General 10 | pub const IE: u32 = 0x200; 11 | pub const IF: u32 = 0x202; 12 | pub const WAITCNT: u32 = 0x204; 13 | pub const HALTCNT: u32 = 0x301; 14 | pub const IME: u32 = 0x208; 15 | pub const POSTFLG: u32 = 0x300; 16 | 17 | // PPU 18 | pub const DISPCNT: u32 = 0x0; 19 | pub const GREENSWAP: u32 = 0x2; 20 | pub const DISPSTAT: u32 = 0x4; 21 | pub const VCOUNT: u32 = 0x6; 22 | pub const BG0CNT: u32 = 0x8; 23 | pub const BG1CNT: u32 = 0xA; 24 | pub const BG2CNT: u32 = 0xC; 25 | pub const BG3CNT: u32 = 0xE; 26 | pub const BG0HOFS: u32 = 0x10; 27 | pub const BG0VOFS: u32 = 0x12; 28 | pub const BG3VOFS: u32 = 0x1E; 29 | pub const BG2PA: u32 = 0x20; 30 | pub const BG2PB: u32 = 0x22; 31 | pub const BG2PC: u32 = 0x24; 32 | pub const BG2PD: u32 = 0x26; 33 | pub const BG2XL: u32 = 0x28; 34 | pub const BG2XH: u32 = 0x2A; 35 | pub const BG2YL: u32 = 0x2C; 36 | pub const BG2YH: u32 = 0x2E; 37 | pub const BG3PA: u32 = 0x30; 38 | pub const WIN0H: u32 = 0x40; 39 | pub const WIN1H: u32 = 0x42; 40 | pub const WIN0V: u32 = 0x44; 41 | pub const WIN1V: u32 = 0x46; 42 | pub const WININ: u32 = 0x48; 43 | pub const WINOUT: u32 = 0x4A; 44 | pub const MOSAIC: u32 = 0x4C; 45 | pub const BLDCNT: u32 = 0x50; 46 | pub const BLDALPHA: u32 = 0x52; 47 | pub const BLDY: u32 = 0x54; 48 | 49 | // Input 50 | pub const KEYINPUT: u32 = 0x130; 51 | pub const KEYCNT: u32 = 0x132; 52 | 53 | // Timers 54 | pub const TM0CNT_L: u32 = 0x100; 55 | pub const TM1CNT_L: u32 = 0x104; 56 | pub const TM2CNT_L: u32 = 0x108; 57 | pub const TM3CNT_L: u32 = 0x10C; 58 | pub const TM0CNT_H: u32 = 0x102; 59 | pub const TM1CNT_H: u32 = 0x106; 60 | pub const TM2CNT_H: u32 = 0x10A; 61 | pub const TM3CNT_H: u32 = 0x10E; 62 | 63 | // Audio 64 | pub const SOUNDCNT_H: u32 = 0x82; 65 | pub const SOUNDBIAS_L: u32 = 0x88; 66 | pub const FIFO_A_L: u32 = 0xA0; 67 | pub const FIFO_A_H: u32 = 0xA2; 68 | pub const FIFO_B_L: u32 = 0xA4; 69 | pub const FIFO_B_H: u32 = 0xA6; 70 | 71 | // Serial 72 | pub const SIOCNT: u32 = 0x128; 73 | pub const RCNT: u32 = 0x134; 74 | -------------------------------------------------------------------------------- /cores/gga/src/audio/psg/envelope.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | #[derive(Default)] 10 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 11 | pub struct EnvelopGenerator { 12 | starting_volume: u8, 13 | current_volume: u8, 14 | sweep_increase: bool, 15 | period: u8, 16 | 17 | envelope_can_run: bool, 18 | 19 | counter: u8, 20 | } 21 | 22 | impl EnvelopGenerator { 23 | pub fn write_envelope_register(&mut self, data: u8) { 24 | // TODO: is initial volume different? 25 | self.starting_volume = data >> 4; 26 | self.current_volume = self.starting_volume; 27 | self.sweep_increase = (data >> 3) & 1 == 1; 28 | self.period = data & 7; 29 | self.counter = self.period; 30 | } 31 | 32 | pub fn read_envelope_register(&self) -> u8 { 33 | ((self.starting_volume & 0xF) << 4) | ((self.sweep_increase as u8) << 3) | (self.period & 7) 34 | } 35 | 36 | pub fn current_volume(&self) -> u8 { 37 | self.current_volume 38 | } 39 | 40 | pub fn clock(&mut self) { 41 | self.counter = self.counter.saturating_sub(1); 42 | 43 | if self.counter == 0 { 44 | self.counter = self.period; 45 | if self.counter == 0 { 46 | self.counter = 8; 47 | } 48 | 49 | if self.envelope_can_run && self.period != 0 { 50 | if self.sweep_increase { 51 | if self.current_volume < 15 { 52 | self.current_volume += 1; 53 | } 54 | } else { 55 | self.current_volume = self.current_volume.saturating_sub(1); 56 | } 57 | 58 | if self.current_volume == 0 || self.current_volume == 15 { 59 | self.envelope_can_run = false; 60 | } 61 | } 62 | } 63 | } 64 | 65 | pub fn trigger(&mut self) { 66 | self.counter = self.period; 67 | self.current_volume = self.starting_volume; 68 | self.envelope_can_run = true; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cores/ggc/src/io/apu/envelope.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | #[derive(Default)] 10 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 11 | pub struct EnvelopGenerator { 12 | starting_volume: u8, 13 | current_volume: u8, 14 | sweep_increase: bool, 15 | period: u8, 16 | 17 | envelope_can_run: bool, 18 | 19 | counter: u8, 20 | } 21 | 22 | impl EnvelopGenerator { 23 | pub fn write_envelope_register(&mut self, data: u8) { 24 | // TODO: is initial volume different? 25 | self.starting_volume = data >> 4; 26 | self.current_volume = self.starting_volume; 27 | self.sweep_increase = (data >> 3) & 1 == 1; 28 | self.period = data & 7; 29 | self.counter = self.period; 30 | } 31 | 32 | pub fn read_envelope_register(&self) -> u8 { 33 | ((self.starting_volume & 0xF) << 4) | ((self.sweep_increase as u8) << 3) | (self.period & 7) 34 | } 35 | 36 | pub fn current_volume(&self) -> u8 { 37 | self.current_volume 38 | } 39 | 40 | pub fn clock(&mut self) { 41 | self.counter = self.counter.saturating_sub(1); 42 | 43 | if self.counter == 0 { 44 | self.counter = self.period; 45 | if self.counter == 0 { 46 | self.counter = 8; 47 | } 48 | 49 | if self.envelope_can_run && self.period != 0 { 50 | if self.sweep_increase { 51 | if self.current_volume < 15 { 52 | self.current_volume += 1; 53 | } 54 | } else { 55 | self.current_volume = self.current_volume.saturating_sub(1); 56 | } 57 | 58 | if self.current_volume == 0 || self.current_volume == 15 { 59 | self.envelope_can_run = false; 60 | } 61 | } 62 | } 63 | } 64 | 65 | pub fn trigger(&mut self) { 66 | self.counter = self.period; 67 | self.current_volume = self.starting_volume; 68 | self.envelope_can_run = true; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /common/src/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | /// A macro that can be used by systems to implement common functions 10 | /// in a generic manner, as part of the system trait. 11 | /// This macro simply grew as it became clear that some functionality 12 | /// is easily shared between systems. 13 | #[macro_export] 14 | macro_rules! common_functions { 15 | ($clock:expr, $pause_event:expr, $size:expr) => { 16 | fn advance_delta(&mut self, delta: f32) { 17 | if !self.c.debugger.running { 18 | return; 19 | } 20 | 21 | let target = 22 | ($clock as f32 * delta * self.c.options.speed_multiplier as f32) as ::common::TimeS; 23 | self.scheduler.schedule($pause_event, target); 24 | 25 | self.c.in_tick = true; 26 | while self.c.debugger.running && self.c.in_tick { 27 | self.advance(); 28 | } 29 | 30 | if self.c.audio_buffer.input[0].len() > 100_000 { 31 | self.c.audio_buffer.input[0].truncate(100); 32 | self.c.audio_buffer.input[1].truncate(100); 33 | } 34 | } 35 | 36 | #[cfg(feature = "serde")] 37 | fn save_state(&mut self) -> ::alloc::vec::Vec { 38 | ::common::serialize::serialize(self, self.c.config.compress_savestates) 39 | } 40 | 41 | #[cfg(feature = "serde")] 42 | fn load_state(&mut self, state: &[u8]) { 43 | let old_self = ::core::mem::replace( 44 | self, 45 | ::common::serialize::deserialize(state, self.c.config.compress_savestates), 46 | ); 47 | self.restore_from(old_self); 48 | } 49 | 50 | fn get_time(&self) -> ::common::Time { 51 | self.scheduler.now() 52 | } 53 | 54 | fn screen_size(&self) -> [usize; 2] { 55 | $size 56 | } 57 | 58 | fn c(&self) -> &::common::Common { 59 | &self.c 60 | } 61 | 62 | fn c_mut(&mut self) -> &mut ::common::Common { 63 | &mut self.c 64 | } 65 | }; 66 | } 67 | -------------------------------------------------------------------------------- /cores/ggc/src/io/scheduling.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::{components::scheduler::Kind, TimeS}; 10 | use GGEvent::*; 11 | 12 | use super::joypad::Joypad; 13 | use crate::{ 14 | io::{dma, dma::Hdma, ppu::Ppu}, 15 | GameGirl, 16 | }; 17 | 18 | /// All scheduler events on the GG. 19 | #[derive(Copy, Clone, Eq, PartialEq, Default)] 20 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 21 | #[repr(u16)] 22 | pub enum GGEvent { 23 | /// Pause the emulation. Used by `advance_delta` to advance by a certain 24 | /// amount. 25 | #[default] 26 | PauseEmulation, 27 | /// Update button inputs. 28 | UpdateKeypad, 29 | /// An event handled by the PPU. 30 | PpuEvent(PpuEvent), 31 | /// A DMA transfer completion. 32 | DMAFinish, 33 | /// Advance HDMA transfer. 34 | HdmaTransferStep, 35 | /// A GDMA transfer. 36 | GdmaTransfer, 37 | } 38 | 39 | impl GGEvent { 40 | /// Handle the event by delegating to the appropriate handler. 41 | pub fn dispatch(&self, gg: &mut GameGirl, late_by: TimeS) { 42 | match self { 43 | PauseEmulation => gg.c.in_tick = false, 44 | UpdateKeypad => Joypad::update(gg), 45 | PpuEvent(evt) => Ppu::handle_event(gg, *evt, late_by), 46 | DMAFinish => dma::do_oam_dma(gg), 47 | HdmaTransferStep => Hdma::handle_hdma(gg), 48 | GdmaTransfer => Hdma::handle_gdma(gg), 49 | } 50 | } 51 | } 52 | 53 | impl Kind for GGEvent {} 54 | 55 | /// Events the PPU generates. 56 | #[derive(Copy, Clone, Eq, PartialEq)] 57 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 58 | #[repr(u16)] 59 | pub enum PpuEvent { 60 | OamScanEnd, 61 | UploadEnd, 62 | HblankEnd, 63 | VblankEnd, 64 | // This happens a little after HBlank end 65 | LYIncrement, 66 | } 67 | 68 | impl PpuEvent { 69 | pub(crate) fn ordinal(self) -> u8 { 70 | // ehhh 71 | match self { 72 | PpuEvent::HblankEnd => 0, 73 | PpuEvent::VblankEnd => 1, 74 | PpuEvent::OamScanEnd => 2, 75 | PpuEvent::UploadEnd => 3, 76 | PpuEvent::LYIncrement => panic!("Not applicable!"), 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /cores/gga/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::{boxed::Box, string::ToString}; 2 | 3 | use common::testing::{self, TestStatus}; 4 | 5 | use crate::GameGirlAdv; 6 | 7 | #[test] 8 | fn jsmolka_arm() { 9 | testing::run_test(include_bytes!("jsmolka/arm.gba"), inspect_jsmolka); 10 | } 11 | 12 | #[test] 13 | fn jsmolka_bios() { 14 | testing::run_test(include_bytes!("jsmolka/bios.gba"), inspect_jsmolka); 15 | } 16 | 17 | #[test] 18 | fn jsmolka_memory() { 19 | testing::run_test(include_bytes!("jsmolka/memory.gba"), inspect_jsmolka); 20 | } 21 | 22 | #[test] 23 | fn jsmolka_nes() { 24 | testing::run_test(include_bytes!("jsmolka/nes.gba"), inspect_jsmolka); 25 | } 26 | 27 | #[test] 28 | fn jsmolka_thumb() { 29 | testing::run_test(include_bytes!("jsmolka/thumb.gba"), inspect_jsmolka); 30 | } 31 | 32 | #[test] 33 | fn jsmolka_unsafe() { 34 | testing::run_test(include_bytes!("jsmolka/unsafe.gba"), inspect_jsmolka); 35 | } 36 | 37 | #[test] 38 | fn fuzzarm_arm_any() { 39 | testing::run_test(include_bytes!("fuzzarm/ARM_Any.gba"), inspect_fuzzarm); 40 | } 41 | 42 | #[test] 43 | fn fuzzarm_arm_dataprocessing() { 44 | testing::run_test( 45 | include_bytes!("fuzzarm/ARM_DataProcessing.gba"), 46 | inspect_fuzzarm, 47 | ); 48 | } 49 | 50 | #[test] 51 | fn fuzzarm_all() { 52 | testing::run_test(include_bytes!("fuzzarm/FuzzARM.gba"), inspect_fuzzarm); 53 | } 54 | 55 | #[test] 56 | fn fuzzarm_thumb_any() { 57 | testing::run_test(include_bytes!("fuzzarm/THUMB_Any.gba"), inspect_fuzzarm); 58 | } 59 | 60 | #[test] 61 | fn fuzzarm_thumb_dataprocessing() { 62 | testing::run_test( 63 | include_bytes!("fuzzarm/THUMB_DataProcessing.gba"), 64 | inspect_fuzzarm, 65 | ); 66 | } 67 | 68 | fn inspect_jsmolka(gg: &mut Box) -> TestStatus { 69 | let hash = testing::screen_hash(gg); 70 | let regs = &gg.cpu.state.registers; 71 | 72 | if regs[13] == 0x03008014 { 73 | let ones = regs[10]; 74 | let tens = regs[9]; 75 | let hundreds = regs[8]; 76 | let test = ones + (tens * 10) + (hundreds * 100); 77 | TestStatus::FailedAt(test.to_string()) 78 | } else if [ 79 | 0x20974E0091874964, 80 | 0x94F4D344B975EB0C, 81 | 0x1A8992654BCDC4D8, 82 | 0x63E68B6E5115B556, 83 | ] 84 | .contains(&hash) 85 | { 86 | TestStatus::Success 87 | } else { 88 | TestStatus::Running 89 | } 90 | } 91 | 92 | pub fn inspect_fuzzarm(gg: &mut Box) -> TestStatus { 93 | if testing::screen_hash(gg) == 0xD5170621BA472629 { 94 | TestStatus::Success 95 | } else { 96 | TestStatus::Running 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /frontends/corebench-egui/src/gui/file_dialog.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use std::{future::Future, path::PathBuf, sync::mpsc}; 10 | 11 | use rfd::FileHandle; 12 | 13 | use crate::app::Message; 14 | 15 | /// A file picked by the user. 16 | pub struct File { 17 | /// File content in bytes 18 | pub content: Vec, 19 | } 20 | 21 | /// Open a file dialog. This operation is async and returns immediately, 22 | /// sending a [Message] once the user has picked a file. 23 | pub fn open_rom(sender: mpsc::Sender) { 24 | let task = rfd::AsyncFileDialog::new() 25 | .add_filter("GameGirl games", &["gb", "gbc", "gba", "nds", "elf", "iso"]) 26 | .pick_file(); 27 | 28 | execute(async move { 29 | let file = task.await; 30 | if let Some(file) = file { 31 | let content = file.read().await; 32 | sender.send(Message::RomOpen(File { content })).ok(); 33 | } 34 | }); 35 | } 36 | 37 | /// Open a file dialog. This operation is async and returns immediately, 38 | /// sending a [Message] once the user has picked a file. 39 | pub fn open_replay(sender: mpsc::Sender) { 40 | let task = rfd::AsyncFileDialog::new() 41 | .add_filter("GameGirl replays", &["rpl"]) 42 | .pick_file(); 43 | 44 | execute(async move { 45 | let file = task.await; 46 | if let Some(file) = file { 47 | let content = file.read().await; 48 | sender.send(Message::ReplayOpen(File { content })).ok(); 49 | } 50 | }); 51 | } 52 | 53 | /// Open a file dialog. This operation is async and returns immediately, 54 | /// sending a [Message] once the user has picked a file. 55 | pub fn open_core(sender: mpsc::Sender) { 56 | let task = rfd::AsyncFileDialog::new() 57 | .add_filter("GameGirl cores", &["so"]) 58 | .pick_file(); 59 | 60 | execute(async move { 61 | let file = task.await; 62 | if let Some(file) = file { 63 | let path = path(&file); 64 | sender.send(Message::CoreOpen(path.unwrap())).ok(); 65 | } 66 | }); 67 | } 68 | 69 | fn path(f: &FileHandle) -> Option { 70 | Some(f.path().to_path_buf()) 71 | } 72 | 73 | fn execute + Send + 'static>(f: F) { 74 | std::thread::spawn(move || futures_executor::block_on(f)); 75 | } 76 | -------------------------------------------------------------------------------- /cores/gga/src/ppu/render/palette.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::{ 10 | numutil::{hword, NumExt}, 11 | Colour, 12 | }; 13 | 14 | use super::{xy2dw, PpuRender}; 15 | use crate::ppu::PaletteMode; 16 | 17 | impl PpuRender { 18 | /// Given a tile address and tile pixel, get the palette. 19 | pub(super) fn get_palette( 20 | &self, 21 | bank: u8, 22 | mode: PaletteMode, 23 | addr: u32, 24 | x: u32, 25 | y: u32, 26 | ) -> Option { 27 | Some(match mode { 28 | PaletteMode::Palettes16 => { 29 | let addr = addr.us() + xy2dw(x.us() / 2, y.us(), 4); 30 | if addr >= self.vram.len() { 31 | return None; 32 | } 33 | let value = self.vram[addr]; 34 | let colour = if x.is_bit(0) { value >> 4 } else { value & 0xF }; 35 | if colour == 0 { 36 | return None; 37 | } 38 | (bank * 0x10) + colour 39 | } 40 | PaletteMode::Single256 => { 41 | let addr = addr.us() + xy2dw(x.us(), y.us(), 8); 42 | if addr >= self.vram.len() { 43 | return None; 44 | } 45 | let pal = self.vram[addr]; 46 | if pal == 0 { 47 | return None; 48 | } 49 | pal 50 | } 51 | }) 52 | } 53 | 54 | /// Turn a halfword in VRAM into a 5bit colour. 55 | pub fn hword_to_colour_vram(&self, addr: usize) -> Colour { 56 | let lo = self.vram[addr]; 57 | let hi = self.vram[addr + 1]; 58 | Self::hword_to_colour(hword(lo, hi)) 59 | } 60 | 61 | /// Turn a palette index (0-255) into a colour. 62 | pub fn idx_to_palette(&self, idx: u8) -> Colour { 63 | let addr = (idx.us() << 1) + (OBJ as usize * 0x200); 64 | let lo = self.palette[addr]; 65 | let hi = self.palette[addr + 1]; 66 | Self::hword_to_colour(hword(lo, hi)) 67 | } 68 | 69 | /// Extract a 5bit colour from a halfword. 70 | fn hword_to_colour(hword: u16) -> Colour { 71 | let r = hword.bits(0, 5).u8(); 72 | let g = hword.bits(5, 5).u8(); 73 | let b = hword.bits(10, 5).u8(); 74 | [r, g, b, 255] 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /cores/gga/src/scheduling.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::{components::scheduler::Kind, TimeS}; 10 | use AdvEvent::*; 11 | 12 | use crate::{ 13 | audio::{psg::GenApuEvent, Apu}, 14 | cpu::GgaFullBus, 15 | hw::timer::Timers, 16 | ppu::Ppu, 17 | }; 18 | 19 | /// All scheduler events on the GGA. 20 | #[derive(Copy, Clone, Eq, PartialEq, Default)] 21 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 22 | pub enum AdvEvent { 23 | /// Pause the emulation. Used by `advance_delta` to advance by a certain 24 | /// amount. 25 | #[default] 26 | PauseEmulation, 27 | /// Update button inputs. 28 | UpdateKeypad, 29 | /// An event handled by the PPU. 30 | PpuEvent(PpuEvent), 31 | /// An event handled by the APU. 32 | ApuEvent(ApuEvent), 33 | /// A timer overflow. 34 | TimerOverflow(u8), 35 | } 36 | 37 | impl GgaFullBus { 38 | /// Handle the event by delegating to the appropriate handler. 39 | pub fn dispatch(&mut self, event: AdvEvent, late_by: TimeS) { 40 | match event { 41 | PauseEmulation => self.bus.c.in_tick = false, 42 | UpdateKeypad => self.check_keycnt(), 43 | PpuEvent(evt) => Ppu::handle_event(self, evt, late_by), 44 | ApuEvent(evt) => { 45 | let time = Apu::handle_event(self, evt, late_by); 46 | self.scheduler.schedule(event, time); 47 | } 48 | TimerOverflow(idx) => Timers::handle_overflow_event(self, idx, late_by), 49 | } 50 | } 51 | } 52 | 53 | impl Kind for AdvEvent {} 54 | 55 | /// Events the APU generates. 56 | #[derive(Copy, Clone, Eq, PartialEq)] 57 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 58 | pub enum ApuEvent { 59 | /// Event from the generic CGB APU. 60 | Gen(GenApuEvent), 61 | /// Tick the CGB sequencer. 62 | Sequencer, 63 | /// Push a sample to the output. 64 | PushSample, 65 | } 66 | 67 | /// Events the PPU generates. 68 | #[derive(Copy, Clone, Eq, PartialEq)] 69 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 70 | pub enum PpuEvent { 71 | /// Start of HBlank. 72 | HblankStart, 73 | /// Set HBlank flag in DISPSTAT (this is delayed by 46 cycles) 74 | SetHblank, 75 | /// End of HBlank, which is the start of the next scanline. 76 | HblankEnd, 77 | } 78 | -------------------------------------------------------------------------------- /frontends/corebench-egui/src/tests/gb.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use gamegirl::common::common::debugger::Width; 10 | 11 | use crate::testsuite::{TestStatus, TestSuite}; 12 | 13 | pub fn blargg() -> TestSuite { 14 | TestSuite::new("blargg", 15, |gg| { 15 | let screen = TestSuite::screen_hash(gg); 16 | let serial = &gg.c().debugger.serial_output; 17 | 18 | // 2 tests don't properly write to serial 19 | if serial.contains("Passed") || [0xC595AEECEFF2C241, 0x115124ABCB508E19].contains(&screen) { 20 | TestStatus::Success 21 | } else if serial.contains("Failed") { 22 | TestStatus::FailedAt(serial.lines().last().unwrap().to_string()) 23 | } else { 24 | TestStatus::Running 25 | } 26 | }) 27 | } 28 | 29 | pub fn blargg_sound() -> TestSuite { 30 | TestSuite::new("blargg_sound", 30, |gg| { 31 | if gg.get_memory(0xA000, Width::Byte) == 0 { 32 | TestStatus::Success 33 | } else { 34 | TestStatus::Running 35 | } 36 | }) 37 | } 38 | 39 | pub fn mooneye(subdir: &str) -> TestSuite { 40 | TestSuite::new(&format!("mooneye/{subdir}"), 10, |gg| { 41 | let regs = gg.get_registers(); 42 | if regs[1] == 0x03 43 | && regs[2] == 0x05 44 | && regs[3] == 0x08 45 | && regs[4] == 0x0D 46 | && regs[6] == 0x15 47 | && regs[7] == 0x22 48 | { 49 | TestStatus::Success 50 | } else if regs[1] == 0x42 && regs[2] == 0x42 && regs[3] == 0x42 { 51 | TestStatus::Failed 52 | } else { 53 | TestStatus::Running 54 | } 55 | }) 56 | } 57 | 58 | pub fn gbmicrotest() -> TestSuite { 59 | TestSuite::new("c-sp/gbmicrotest", 5, |gg| { 60 | if gg.get_memory(0xFF82, Width::Byte) == 0x01 { 61 | TestStatus::Success 62 | } else if gg.get_memory(0xFF82, Width::Byte) == 0xFF { 63 | TestStatus::Failed 64 | } else { 65 | TestStatus::Running 66 | } 67 | }) 68 | } 69 | 70 | pub fn acid2() -> TestSuite { 71 | TestSuite::new("acid2", 5, |gg| { 72 | let hash = TestSuite::screen_hash(gg); 73 | if [0xB60125B2D40BCBD9, 0xD0F0889F5971A43E].contains(&hash) { 74 | TestStatus::Success 75 | } else { 76 | TestStatus::Running 77 | } 78 | }) 79 | } 80 | -------------------------------------------------------------------------------- /cores/psx/src/cpu/cop0.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::numutil::NumExt; 10 | 11 | use crate::{ 12 | cpu::inst::{Inst, InstructionHandler}, 13 | PlayStation, 14 | }; 15 | 16 | type CopLut = [InstructionHandler; 32]; 17 | const COP0: CopLut = PlayStation::cop0_table(); 18 | 19 | #[derive(Default)] 20 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 21 | pub struct Cop0 { 22 | pub(crate) sr: u32, 23 | pub(crate) cause: u32, 24 | pub(crate) epc: u32, 25 | } 26 | 27 | impl PlayStation { 28 | const fn cop0_table() -> CopLut { 29 | let mut lut: CopLut = [Self::cop0_inst; 32]; 30 | lut[0x00] = Self::mfc0; 31 | lut[0x02] = Self::cfc0; 32 | lut[0x04] = Self::mtc0; 33 | lut[0x06] = Self::ctc0; 34 | lut[0x08] = Self::bc0; 35 | lut[0x10] = Self::rfe; 36 | lut 37 | } 38 | } 39 | 40 | impl PlayStation { 41 | pub fn cop0(&mut self, inst: Inst) { 42 | let cop0 = inst.rs(); 43 | let handler = COP0[cop0.us()]; 44 | handler(self, inst); 45 | } 46 | 47 | fn mfc0(&mut self, inst: Inst) { 48 | match inst.rd() { 49 | 12 => self.cpu.set_reg(inst.rt(), self.cpu.cop0.sr), 50 | 13 => self.cpu.set_reg(inst.rt(), self.cpu.cop0.cause), 51 | 14 => self.cpu.set_reg(inst.rt(), self.cpu.cop0.epc), 52 | unknown => log::debug!("Unhandled read from COP0 register {unknown}, ignoring"), 53 | } 54 | } 55 | 56 | fn cfc0(&mut self, inst: Inst) { 57 | todo!(); 58 | } 59 | 60 | fn mtc0(&mut self, inst: Inst) { 61 | match inst.rd() { 62 | 12 => self.cpu.cop0.sr = self.cpu.reg(inst.rt()), 63 | unknown => log::debug!("Unhandled write to COP0 register {unknown}, ignoring"), 64 | } 65 | } 66 | 67 | fn ctc0(&mut self, inst: Inst) { 68 | todo!(); 69 | } 70 | 71 | fn bc0(&mut self, inst: Inst) { 72 | todo!(); 73 | } 74 | 75 | fn rfe(&mut self, inst: Inst) { 76 | if inst.0 & 0x3F != 0x10 { 77 | log::warn!("COP0 virtual memory instruction encountered, executing as RFE"); 78 | } 79 | 80 | let context = self.cpu.cop0.sr & 0x3F; 81 | self.cpu.cop0.sr &= !0x3F; 82 | self.cpu.cop0.sr |= context >> 2; 83 | } 84 | 85 | fn cop0_inst(&mut self, inst: Inst) { 86 | todo!(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /cores/nes/src/memory.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::numutil::{hword, NumExt}; 10 | 11 | use crate::{cartridge::Cartridge, cpu::Reg::*, Nes}; 12 | 13 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 14 | pub struct Memory { 15 | #[cfg_attr(feature = "serde", serde(with = "serde_arrays"))] 16 | iram: [u8; 0x800], 17 | ppu_regs: [u8; 0x8], 18 | other_regs: [u8; 0x15], 19 | } 20 | 21 | impl Nes { 22 | pub fn read_imm(&mut self) -> u8 { 23 | let value = self.read(self.cpu.pc); 24 | self.cpu.pc += 1; 25 | value 26 | } 27 | 28 | pub fn read(&mut self, addr: u16) -> u8 { 29 | self.advance_clock(1); 30 | self.get(addr) 31 | } 32 | 33 | pub fn write(&mut self, addr: u16, value: u8) { 34 | self.advance_clock(1); 35 | self.set(addr, value); 36 | } 37 | 38 | pub fn push(&mut self, value: u8) { 39 | let stack = self.cpu.get(S); 40 | self.write(hword(0x01, stack), value); 41 | self.cpu.set(S, stack.wrapping_sub(1)); 42 | self.advance_clock(1); 43 | } 44 | 45 | pub fn pop(&mut self) -> u8 { 46 | self.advance_clock(2); 47 | let stack = self.cpu.get(S).wrapping_add(1); 48 | self.cpu.set(S, stack); 49 | self.read(hword(0x01, stack)) 50 | } 51 | 52 | pub fn get(&mut self, addr: u16) -> u8 { 53 | match addr { 54 | 0x0000..=0x1FFF => self.mem.iram[addr.us() & 0x7FF], 55 | 0x2000..=0x3FFF => self.mem.ppu_regs[addr.us() & 0x8], 56 | 0x4000..=0x4015 => self.mem.other_regs[addr.us() - 0x4000], 57 | 0x4016 => self.joypad.read() | 0x40, 58 | 0x4020..=0xFFFF => Cartridge::read(self, addr), 59 | _ => 0xFF, 60 | } 61 | } 62 | 63 | pub fn set(&mut self, addr: u16, value: u8) { 64 | match addr { 65 | 0x0000..=0x1FFF => self.mem.iram[addr.us() & 0x7FF] = value, 66 | 0x2000..=0x3FFF => self.mem.ppu_regs[addr.us() & 0x8] = value, 67 | 0x4000..=0x4015 => self.mem.other_regs[addr.us() - 0x4000] = value, 68 | 0x4016 => self.joypad.write(value), 69 | 0x4020..=0xFFFF => Cartridge::write(self, addr, value), 70 | _ => (), 71 | } 72 | } 73 | } 74 | 75 | impl Default for Memory { 76 | fn default() -> Self { 77 | Self { 78 | iram: [0; 0x800], 79 | ppu_regs: [0; 0x8], 80 | other_regs: [0; 0x15], 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /frontends/gamegirl-gtk/src/gui/input.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, fmt::Display}; 2 | 3 | use gamegirl::{ 4 | common::common::input::Button::*, 5 | frontend::input::{ 6 | InputAction::{self, *}, 7 | InputSource, Key, 8 | }, 9 | }; 10 | use gtk::gdk; 11 | use serde::{ 12 | Deserialize, Serialize, 13 | de::{self, Visitor}, 14 | }; 15 | 16 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 17 | pub struct GtkKey(gdk::Key); 18 | 19 | impl From for GtkKey { 20 | fn from(value: gdk::Key) -> Self { 21 | Self(value) 22 | } 23 | } 24 | 25 | impl Display for GtkKey { 26 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 27 | write!(f, "{}", self.0.name().unwrap()) 28 | } 29 | } 30 | 31 | impl Key for GtkKey { 32 | fn is_escape(self) -> bool { 33 | self == gdk::Key::Escape.into() 34 | } 35 | 36 | fn default_map() -> HashMap, InputAction> { 37 | HashMap::from([ 38 | (InputSource::Key(Self(gdk::Key::X)), Button(A)), 39 | (InputSource::Key(Self(gdk::Key::Z)), Button(B)), 40 | (InputSource::Key(Self(gdk::Key::Return)), Button(Start)), 41 | (InputSource::Key(Self(gdk::Key::space)), Button(Select)), 42 | (InputSource::Key(Self(gdk::Key::Down)), Button(Down)), 43 | (InputSource::Key(Self(gdk::Key::Up)), Button(Up)), 44 | (InputSource::Key(Self(gdk::Key::Left)), Button(Left)), 45 | (InputSource::Key(Self(gdk::Key::Right)), Button(Right)), 46 | (InputSource::Key(Self(gdk::Key::A)), Button(L)), 47 | (InputSource::Key(Self(gdk::Key::S)), Button(R)), 48 | (InputSource::Key(Self(gdk::Key::R)), Hotkey(4)), 49 | ]) 50 | } 51 | } 52 | 53 | impl Serialize for GtkKey { 54 | fn serialize(&self, serializer: S) -> Result 55 | where 56 | S: serde::Serializer, 57 | { 58 | serializer.collect_str(&self.0.name().unwrap()) 59 | } 60 | } 61 | 62 | impl<'de> Deserialize<'de> for GtkKey { 63 | fn deserialize(deserializer: D) -> Result 64 | where 65 | D: serde::Deserializer<'de>, 66 | { 67 | struct StrVisitor; 68 | impl<'de> Visitor<'de> for StrVisitor { 69 | type Value = GtkKey; 70 | 71 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 72 | formatter.write_str("String repr of key") 73 | } 74 | 75 | fn visit_string(self, v: String) -> Result 76 | where 77 | E: serde::de::Error, 78 | { 79 | Ok(GtkKey( 80 | gdk::Key::from_name(v).ok_or(de::Error::custom("Invalid key"))?, 81 | )) 82 | } 83 | } 84 | deserializer.deserialize_string(StrVisitor) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /cores/nds/src/hw/bios/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use alloc::vec::Vec; 10 | 11 | use modular_bitfield::prelude::*; 12 | 13 | // DraStic BIOS as found in MelonDS sources: 14 | // https://github.com/melonDS-emu/melonDS/tree/5eadd67df6da429891fdfba02bf650f4fefe4ab6/freebios 15 | // Thank you to it's developers! 16 | pub const FREEBIOS7: &[u8] = include_bytes!("drastic_bios_arm7.bin"); 17 | pub const FREEBIOS9: &[u8] = include_bytes!("drastic_bios_arm9.bin"); 18 | 19 | #[derive(Debug)] 20 | #[repr(packed)] 21 | pub struct UserSettings { 22 | pub version: u16, 23 | pub color: u8, 24 | pub birthday_month: u8, 25 | pub birthday_day: u8, 26 | pub zero1: u8, 27 | pub nickname_utf16: [u16; 10], 28 | pub nickname_len: u16, 29 | pub message_utf16: [u16; 26], 30 | pub message_len: u16, 31 | pub alarm_hour: u8, 32 | pub alarm_minute: u8, 33 | pub unused: u16, 34 | pub alarm_enable: u8, 35 | pub zero2: u8, 36 | pub touch_calibration: [u8; 12], 37 | pub language: LanguageFlags, 38 | pub year: u8, 39 | pub zero3: u8, 40 | pub rtc_offset: u32, 41 | pub ff: u32, 42 | pub update_counter: u16, 43 | pub crc16: u16, 44 | } 45 | 46 | #[bitfield] 47 | #[repr(u16)] 48 | #[derive(Debug, Default, Copy, Clone)] 49 | pub struct LanguageFlags { 50 | language: B3, 51 | gba_on_lower: bool, 52 | backlight_level: B2, 53 | autostart_cart: bool, 54 | #[skip] 55 | __: B2, 56 | settings_lost: bool, 57 | settings_okay: B6, 58 | } 59 | 60 | impl UserSettings { 61 | pub fn get_bogus() -> UserSettings { 62 | let utf16 = "leela".encode_utf16(); 63 | let utf16 = utf16.collect::>(); 64 | UserSettings { 65 | version: 5, 66 | color: 3, 67 | birthday_month: 11, 68 | birthday_day: 26, 69 | zero1: 0, 70 | nickname_utf16: utf16.clone().try_into().unwrap(), 71 | nickname_len: 5, 72 | message_utf16: utf16.try_into().unwrap(), 73 | message_len: 5, 74 | alarm_hour: 23, 75 | alarm_minute: 31, 76 | unused: 0, 77 | alarm_enable: 0, 78 | zero2: 0, 79 | touch_calibration: [0; 12], 80 | language: LanguageFlags::default() 81 | .with_language(1) 82 | .with_settings_okay(0x3F), 83 | year: 24, 84 | zero3: 0, 85 | rtc_offset: 0, 86 | ff: 0xFFFFFFFF, 87 | update_counter: 23, 88 | crc16: 0, 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /components/armchair/src/optimizations/jit/fields.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | 3 | use cranelift::prelude::*; 4 | use types::*; 5 | 6 | use super::InstructionTranslator; 7 | use crate::{ 8 | interface::Bus, 9 | state::{LowRegister, Register}, 10 | Cpu, 11 | }; 12 | 13 | macro_rules! cpu_field { 14 | ($path:expr, $typ:expr, $getter:ident, $setter:ident) => { 15 | pub fn $getter(&mut self) -> Value { 16 | self.load_at_offset($typ, mem::offset_of!(Cpu, $path)) 17 | } 18 | pub fn $setter(&mut self, value: Value) { 19 | self.store_at_offset(value, mem::offset_of!(Cpu, $path)) 20 | } 21 | }; 22 | } 23 | 24 | macro_rules! cpu_register { 25 | ($index:expr, $getter:ident, $setter:ident) => { 26 | pub fn $getter(&mut self) -> Value { 27 | self.load_at_offset( 28 | types::I32, 29 | mem::offset_of!(Cpu, state.registers) + $index * mem::size_of::(), 30 | ) 31 | } 32 | pub fn $setter(&mut self, value: Value) { 33 | self.store_at_offset( 34 | value, 35 | mem::offset_of!(Cpu, state.registers) + $index * mem::size_of::(), 36 | ) 37 | } 38 | }; 39 | } 40 | 41 | impl InstructionTranslator<'_, '_, '_, S> { 42 | fn load_at_offset(&mut self, typ: Type, offset: usize) -> Value { 43 | self.builder 44 | .ins() 45 | .load(typ, MemFlags::new(), self.vals.sys, offset as i32) 46 | } 47 | 48 | fn store_at_offset(&mut self, value: Value, offset: usize) { 49 | self.builder 50 | .ins() 51 | .store(MemFlags::new(), value, self.vals.sys, offset as i32); 52 | } 53 | 54 | fn get_pointer(&mut self, offset: usize) -> Value { 55 | let offset_const = self.builder.ins().iconst(types::I64, offset as i64); 56 | self.builder.ins().iadd(self.vals.sys, offset_const) 57 | } 58 | 59 | pub fn load_reg(&mut self, reg: Register) -> Value { 60 | self.load_at_offset( 61 | types::I32, 62 | mem::offset_of!(Cpu, state.registers) + reg.0 as usize * mem::size_of::(), 63 | ) 64 | } 65 | 66 | pub fn store_reg(&mut self, reg: Register, value: Value) { 67 | self.store_at_offset( 68 | value, 69 | mem::offset_of!(Cpu, state.registers) + reg.0 as usize * mem::size_of::(), 70 | ) 71 | } 72 | 73 | pub fn load_lreg(&mut self, reg: LowRegister) -> Value { 74 | self.load_at_offset( 75 | types::I32, 76 | mem::offset_of!(Cpu, state.registers) + reg.0 as usize * mem::size_of::(), 77 | ) 78 | } 79 | 80 | pub fn store_lreg(&mut self, reg: LowRegister, value: Value) { 81 | self.store_at_offset( 82 | value, 83 | mem::offset_of!(Cpu, state.registers) + reg.0 as usize * mem::size_of::(), 84 | ) 85 | } 86 | 87 | cpu_field!(state.pipeline_valid, I8, get_valid, set_valid); 88 | cpu_field!(state.cpsr, I32, get_cpsr, set_cpsr); 89 | 90 | cpu_register!(13, load_sp, store_sp); 91 | cpu_register!(14, load_lr, store_lr); 92 | cpu_register!(15, load_pc, store_pc); 93 | } 94 | -------------------------------------------------------------------------------- /cores/nes/src/cartridge.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use std::iter; 10 | 11 | use common::numutil::NumExt; 12 | use modular_bitfield::{ 13 | bitfield, 14 | specifiers::{B2, B4}, 15 | }; 16 | 17 | use crate::Nes; 18 | 19 | #[bitfield] 20 | #[repr(u16)] 21 | #[derive(Debug, Clone, Copy)] 22 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 23 | pub struct INesHeader { 24 | mirror_is_vertical: bool, 25 | has_battery: bool, 26 | has_trainer: bool, 27 | ignore_mirroring: bool, 28 | mapper_lower: B4, 29 | vs_unisystem: bool, 30 | playchoice: bool, 31 | ines_2_hint: B2, 32 | mapper_higher: B4, 33 | } 34 | 35 | #[bitfield] 36 | #[repr(u16)] 37 | #[derive(Debug, Clone, Copy)] 38 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 39 | pub struct Nes2Header { 40 | mapper_higherer: B4, 41 | submapper: B4, 42 | prg_rom_size_msb: B4, 43 | chr_rom_size_msb: B4, 44 | } 45 | 46 | #[derive(Default)] 47 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 48 | pub struct Cartridge { 49 | prg_rom: Vec, 50 | prg_ram: Vec, 51 | chr_rom: Vec, 52 | mapper: Mapper, 53 | } 54 | 55 | impl Cartridge { 56 | pub fn read(nes: &Nes, addr: u16) -> u8 { 57 | match addr { 58 | 0x6000..=0x7FFF => nes.cart.prg_ram[addr.us() - 0x6000], 59 | 0x8000..=0xFFFF => { 60 | nes.cart.prg_rom[(addr.us() - 0x8000) & (nes.cart.prg_rom.len() - 1)] 61 | } 62 | _ => panic!(), 63 | } 64 | } 65 | 66 | pub fn write(_: &Nes, _: u16, _value: u8) {} 67 | 68 | pub fn from_rom(rom: Vec) -> Self { 69 | let prg_size = rom[4].us() * 16_384; 70 | let chr_size = rom[5].us() * 8_192; 71 | let header = INesHeader::from_bytes(rom[6..8].try_into().unwrap()); 72 | 73 | let addr = 16 + (header.has_trainer() as usize * 512); 74 | let prg_rom = rom[addr..(addr + prg_size)].to_vec(); 75 | let addr = addr + prg_size; 76 | let chr_rom = rom[addr..(addr + chr_size)].to_vec(); 77 | 78 | let mapper = header.mapper_lower() | (header.mapper_higher() << 4); 79 | let mapper = match mapper { 80 | 0 => Mapper::Nrom, 81 | _ => panic!("Unknown mapper!"), 82 | }; 83 | 84 | let mut cart = Self { 85 | prg_rom, 86 | prg_ram: Vec::new(), 87 | chr_rom, 88 | mapper, 89 | }; 90 | cart.prg_ram.extend(iter::repeat(0).take(0x2000)); 91 | cart 92 | } 93 | } 94 | 95 | #[derive(Default)] 96 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 97 | pub enum Mapper { 98 | #[default] 99 | Nrom, 100 | } 101 | -------------------------------------------------------------------------------- /common/src/common/video.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use alloc::{collections::VecDeque, vec::Vec}; 10 | 11 | use crate::Colour; 12 | 13 | /// Frame buffer for video output. Also used to implement frameskip. 14 | #[derive(Default)] 15 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 16 | pub struct FrameBuffer { 17 | /// Buffer of frames to be displayed. 18 | #[cfg_attr(feature = "serde", serde(skip, default))] 19 | buffer: VecDeque>, 20 | /// Number of frames to skip before adding a frame to the buffer. 21 | pub frameskip: usize, 22 | /// Number of frames until the next frame is added to the buffer. 23 | n_until_next: usize, 24 | } 25 | 26 | impl FrameBuffer { 27 | /// Get the oldest frame in the buffer. 28 | pub fn pop(&mut self) -> Option> { 29 | self.buffer.pop_front() 30 | } 31 | 32 | /// Get the newest framen in the buffer. 33 | pub fn pop_recent(&mut self) -> Option> { 34 | self.buffer.pop_back() 35 | } 36 | 37 | /// Notify the buffer that the system is starting to render the next frame. 38 | pub fn start_next_frame(&mut self) { 39 | if self.n_until_next == 0 { 40 | self.n_until_next = self.frameskip; 41 | } else { 42 | self.n_until_next -= 1; 43 | } 44 | } 45 | 46 | /// Returns true if the current frame should be rendered, false if it is to 47 | /// be skipped. 48 | pub fn should_render_this_frame(&self) -> bool { 49 | self.frameskip == 0 || self.n_until_next == 0 50 | } 51 | 52 | /// Push a new frame to the buffer. 53 | pub fn push(&mut self, frame: Vec) { 54 | self.buffer.push_back(frame); 55 | if self.buffer.len() > 4 { 56 | self.pop(); // Drop oldest frame to prevent large buffer and 57 | // outdated frames 58 | } 59 | } 60 | 61 | /// Do we have a frame? 62 | pub fn has_frame(&self) -> bool { 63 | !self.buffer.is_empty() 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod test { 69 | use super::*; 70 | 71 | #[test] 72 | fn test_frameskip_0() { 73 | let mut fb = FrameBuffer::default(); 74 | 75 | assert_eq!(fb.should_render_this_frame(), true); 76 | fb.start_next_frame(); 77 | assert_eq!(fb.should_render_this_frame(), true); 78 | fb.start_next_frame(); 79 | assert_eq!(fb.should_render_this_frame(), true); 80 | } 81 | 82 | #[test] 83 | fn test_frameskip_1() { 84 | let mut fb = FrameBuffer::default(); 85 | fb.frameskip = 1; 86 | 87 | assert_eq!(fb.should_render_this_frame(), true); 88 | fb.start_next_frame(); 89 | assert_eq!(fb.should_render_this_frame(), false); 90 | fb.start_next_frame(); 91 | assert_eq!(fb.should_render_this_frame(), true); 92 | fb.start_next_frame(); 93 | assert_eq!(fb.should_render_this_frame(), false); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /components/armchair/src/thumb/mod.rs: -------------------------------------------------------------------------------- 1 | pub use decode::ThumbInst; 2 | use decode::{Thumb1Op, Thumb2Op, Thumb3Op, Thumb4Op, ThumbStrLdrOp}; 3 | 4 | use crate::{ 5 | interface::{Bus, InstructionSet}, 6 | memory::{Address, RelativeOffset}, 7 | state::{LowRegister, Register}, 8 | }; 9 | 10 | pub(crate) mod decode; 11 | mod interpret; 12 | mod jit; 13 | mod trace; 14 | 15 | pub type ThumbHandler = fn(&mut C, ThumbInst); 16 | pub type ThumbInstructionSet = InstructionSet; 17 | 18 | pub const fn instruction_set() -> ThumbInstructionSet { 19 | InstructionSet { 20 | interpreter_lut: decode::get_lut_table(), 21 | cache_handler_lookup: |i| decode::get_instruction_handler(i), 22 | } 23 | } 24 | 25 | pub(crate) trait ThumbVisitor { 26 | type Output; 27 | 28 | fn thumb_unknown_opcode(&mut self, inst: ThumbInst) -> Self::Output; 29 | fn thumb_alu_imm( 30 | &mut self, 31 | kind: Thumb1Op, 32 | d: LowRegister, 33 | s: LowRegister, 34 | n: u32, 35 | ) -> Self::Output; 36 | fn thumb_2_reg( 37 | &mut self, 38 | kind: Thumb2Op, 39 | d: LowRegister, 40 | s: LowRegister, 41 | n: LowRegister, 42 | ) -> Self::Output; 43 | fn thumb_3(&mut self, kind: Thumb3Op, d: LowRegister, n: u32) -> Self::Output; 44 | fn thumb_alu(&mut self, kind: Thumb4Op, d: LowRegister, s: LowRegister) -> Self::Output; 45 | fn thumb_hi_add(&mut self, r: (Register, Register)) -> Self::Output; 46 | fn thumb_hi_cmp(&mut self, r: (Register, Register)) -> Self::Output; 47 | fn thumb_hi_mov(&mut self, r: (Register, Register)) -> Self::Output; 48 | fn thumb_hi_bx(&mut self, s: Register, blx: bool) -> Self::Output; 49 | fn thumb_ldr6(&mut self, d: LowRegister, offset: Address) -> Self::Output; 50 | fn thumb_ldrstr78( 51 | &mut self, 52 | op: ThumbStrLdrOp, 53 | d: LowRegister, 54 | b: LowRegister, 55 | o: LowRegister, 56 | ) -> Self::Output; 57 | fn thumb_ldrstr9( 58 | &mut self, 59 | op: ThumbStrLdrOp, 60 | d: LowRegister, 61 | b: LowRegister, 62 | offset: Address, 63 | ) -> Self::Output; 64 | fn thumb_ldrstr10( 65 | &mut self, 66 | str: bool, 67 | d: LowRegister, 68 | b: LowRegister, 69 | offset: Address, 70 | ) -> Self::Output; 71 | fn thumb_str_sp(&mut self, d: LowRegister, offset: Address) -> Self::Output; 72 | fn thumb_ldr_sp(&mut self, d: LowRegister, offset: Address) -> Self::Output; 73 | fn thumb_rel_addr(&mut self, sp: bool, d: LowRegister, offset: Address) -> Self::Output; 74 | fn thumb_sp_offs(&mut self, offset: RelativeOffset) -> Self::Output; 75 | fn thumb_push(&mut self, reg_list: u8, lr: bool) -> Self::Output; 76 | fn thumb_pop(&mut self, reg_list: u8, pc: bool) -> Self::Output; 77 | fn thumb_stmia(&mut self, b: LowRegister, reg_list: u8) -> Self::Output; 78 | fn thumb_ldmia(&mut self, b: LowRegister, reg_list: u8) -> Self::Output; 79 | fn thumb_bcond(&mut self, cond: u16, offset: RelativeOffset) -> Self::Output; 80 | fn thumb_swi(&mut self) -> Self::Output; 81 | fn thumb_br(&mut self, offset: RelativeOffset) -> Self::Output; 82 | fn thumb_set_lr(&mut self, offset: RelativeOffset) -> Self::Output; 83 | fn thumb_bl(&mut self, offset: Address, thumb: bool) -> Self::Output; 84 | } 85 | -------------------------------------------------------------------------------- /cores/gga/src/audio/psg/noise_channel.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use common::TimeS; 10 | 11 | use super::{envelope::EnvelopGenerator, Channel, GenApuEvent}; 12 | 13 | #[derive(Default)] 14 | #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] 15 | pub struct NoiseChannel { 16 | shift_clock_frequency: u8, 17 | step_mode_7_bits: bool, 18 | divisor_code: u8, 19 | 20 | feedback_shift_register: u16, 21 | 22 | envelope: EnvelopGenerator, 23 | 24 | channel_enabled: bool, 25 | dac_enable: bool, 26 | } 27 | 28 | impl NoiseChannel { 29 | pub fn write_noise_register(&mut self, data: u8) { 30 | self.shift_clock_frequency = data >> 4; 31 | self.step_mode_7_bits = (data >> 3) & 1 == 1; 32 | self.divisor_code = data & 7; 33 | } 34 | 35 | pub fn read_noise_register(&self) -> u8 { 36 | (self.shift_clock_frequency << 4) | ((self.step_mode_7_bits as u8) << 3) | self.divisor_code 37 | } 38 | 39 | pub fn envelope(&self) -> &EnvelopGenerator { 40 | &self.envelope 41 | } 42 | 43 | pub fn envelope_mut(&mut self) -> &mut EnvelopGenerator { 44 | &mut self.envelope 45 | } 46 | 47 | pub fn clock(&mut self) -> u32 { 48 | self.clock_feedback_register(); 49 | (self.get_frequency() as u32) << 2 50 | } 51 | } 52 | 53 | impl NoiseChannel { 54 | fn get_frequency(&self) -> u16 { 55 | (self.base_divisor() << self.shift_clock_frequency) / 4 56 | } 57 | 58 | fn base_divisor(&self) -> u16 { 59 | if self.divisor_code == 0 { 60 | 8 61 | } else { 62 | self.divisor_code as u16 * 16 63 | } 64 | } 65 | 66 | fn clock_feedback_register(&mut self) { 67 | let xor_result = 68 | (self.feedback_shift_register & 1) ^ ((self.feedback_shift_register >> 1) & 1); 69 | 70 | self.feedback_shift_register >>= 1; 71 | 72 | self.feedback_shift_register |= xor_result << 14; 73 | 74 | if self.step_mode_7_bits { 75 | self.feedback_shift_register &= !0x40; 76 | self.feedback_shift_register |= xor_result << 6; 77 | } 78 | } 79 | } 80 | 81 | impl Channel for NoiseChannel { 82 | fn output(&self) -> u8 { 83 | ((self.feedback_shift_register & 1) ^ 1) as u8 * self.envelope.current_volume() 84 | } 85 | 86 | fn muted(&self) -> bool { 87 | self.envelope.current_volume() == 0 88 | } 89 | 90 | fn trigger(&mut self, _sched: &mut impl FnMut(GenApuEvent, TimeS)) { 91 | self.envelope.trigger(); 92 | // because its 15 bits 93 | self.feedback_shift_register = 0x7FFF; 94 | } 95 | 96 | fn set_enable(&mut self, enabled: bool) { 97 | self.channel_enabled = enabled; 98 | } 99 | 100 | fn enabled(&self) -> bool { 101 | self.channel_enabled 102 | } 103 | 104 | fn set_dac_enable(&mut self, enabled: bool) { 105 | self.dac_enable = enabled; 106 | } 107 | 108 | fn dac_enabled(&self) -> bool { 109 | self.dac_enable 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /frontends/corebench-egui/src/gui/tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024 Leela Aurelia, git@elia.garden 2 | // 3 | // Unless otherwise noted, this file is released and thus subject to the 4 | // terms of the Mozilla Public License Version 2.0 (MPL-2.0) or the 5 | // GNU General Public License Version 3 (GPL-3). 6 | // If a copy of these licenses was not distributed with this file, you can 7 | // obtain them at https://mozilla.org/MPL/2.0/ and http://www.gnu.org/licenses/. 8 | 9 | use std::{sync::Arc, thread, time::Instant}; 10 | 11 | use eframe::egui::{Context, Ui}; 12 | use egui_plot::{Legend, Line, Plot, PlotPoints}; 13 | 14 | use crate::{app::App, tests::SUITES}; 15 | 16 | pub(super) fn suites(app: &mut App, _ctx: &Context, ui: &mut Ui) { 17 | ui.label("Add suites:"); 18 | for suite in SUITES { 19 | if ui.button(suite.0).clicked() { 20 | app.suites.push(Arc::new(suite.1())); 21 | app.update_test_suites(); 22 | } 23 | } 24 | 25 | ui.separator(); 26 | ui.label("Currently loaded suites:"); 27 | for suite in &app.suites { 28 | ui.horizontal(|ui| { 29 | ui.label(&suite.name); 30 | }); 31 | } 32 | } 33 | 34 | pub(super) fn bench(app: &mut App, _ctx: &Context, ui: &mut Ui) { 35 | if ui.button("Start isolated benchmark").clicked() { 36 | let cores = app 37 | .cores 38 | .iter() 39 | .map(|c| { 40 | c.bench_iso.lock().unwrap().clear(); 41 | ((c.loader)(app.rom.clone().unwrap()), c.bench_iso.clone()) 42 | }) 43 | .collect::>(); 44 | thread::spawn(|| { 45 | for (mut core, bench) in cores { 46 | for time in 0..500 { 47 | let delta = time as f64 / 5.0; 48 | let time = Instant::now(); 49 | core.advance_delta(0.1); 50 | let elapsed = time.elapsed().as_micros() as f64; 51 | bench.lock().unwrap().add(delta, elapsed / 1000.0); 52 | } 53 | } 54 | }); 55 | } 56 | 57 | ui.checkbox(&mut app.bench_iso, "Graph: Show Isolated Benchmark"); 58 | 59 | if app.bench_iso { 60 | Plot::new("benchmark") 61 | .legend(Legend::default()) 62 | .allow_scroll(false) 63 | .allow_drag(false) 64 | .include_x(100.0) 65 | .x_axis_label("Emulated Time") 66 | .y_axis_label("Time to emulate 0.2s in ms") 67 | .show(ui, |ui| { 68 | for core in app.cores.iter() { 69 | ui.line( 70 | Line::new(PlotPoints::from_iter( 71 | core.bench_iso.lock().unwrap().iter().map(|(t, s)| [t, s]), 72 | )) 73 | .name(&core.name), 74 | ); 75 | } 76 | }); 77 | } else { 78 | Plot::new("benchmark") 79 | .legend(Legend::default()) 80 | .allow_scroll(false) 81 | .allow_drag(false) 82 | .include_x(30.0) 83 | .x_axis_label("Real Time") 84 | .y_axis_label("Time to emulate 0.2s in ms") 85 | .show(ui, |ui| { 86 | for core in app.cores.iter() { 87 | ui.line( 88 | Line::new(PlotPoints::from_iter( 89 | core.bench.iter().map(|(t, s)| [t, s]), 90 | )) 91 | .name(&core.name), 92 | ); 93 | } 94 | }); 95 | } 96 | } 97 | --------------------------------------------------------------------------------